tracker r2275 - in trunk: . data data/dbus data/english data/icons data/icons/16x16 data/icons/22x22 data/icons/24x24 data/icons/32x32 data/icons/48x48 data/icons/scalable data/languages data/modules data/services docs extensions extensions/firefox-extension extensions/firefox-extension/chrome extensions/firefox-extension/chrome/content extensions/firefox-extension/chrome/content/jslib extensions/firefox-extension/chrome/content/jslib/debug extensions/firefox-extension/chrome/content/jslib/io extensions/firefox-extension/chrome/locale extensions/firefox-extension/chrome/locale/en-US extensions/firefox-extension/chrome/skin extensions/firefox-extension/chrome/skin/classic extensions/thunderbird-extension extensions/thunderbird-extension/chrome extensions/thunderbird-extension/components extensions/thunderbird-extension/content extensions/thunderbird-extension/defaults extensions/thunderbird-extension/defaults/preferences extensions/thunderbird-extension/locale extensi ons/thunderbird-extension/locale/en-US extensions/thunderbird-extension/skin extensions/thunderbird-extension/skin/classic filters filters/application filters/text po python python/FUSE python/SearchTool python/applet python/deskbar-handler python/music python/nautilus rdf-query-examples src src/libinotify src/libstemmer src/libtracker src/libtracker-common src/libtracker-db src/libtracker-gtk src/qdbm src/tracker-applet src/tracker-extract src/tracker-fts src/tracker-indexer src/tracker-indexer/modules src/tracker-preferences src/tracker-search-tool src/tracker-thumbnailer src/tracker-utils src/trackerd tests tests/common tests/libtracker-common tests/libtracker-db tests/scripts tests/scripts/data tests/tracker-fts tests/tracker-indexer tests/trackerd tests/trackerd/xesam thumbnailers thumbnailers/application thumbnailers/image thumbnailers/image/hildon utils utils/qdbm



Author: mr
Date: Fri Sep 26 10:47:33 2008
New Revision: 2275
URL: http://svn.gnome.org/viewvc/tracker?rev=2275&view=rev

Log:
Merge indexer-split branch.


Added:
   trunk/AUTHORS
   trunk/COPYING
   trunk/ChangeLog
   trunk/INSTALL
   trunk/Makefile.am
   trunk/Makefile.decl
   trunk/NEWS
   trunk/README
   trunk/TODO
   trunk/acinclude.m4
   trunk/autogen.sh   (contents, props changed)
   trunk/configure.ac
   trunk/data/
   trunk/data/Makefile.am
   trunk/data/dbus/
   trunk/data/dbus/Makefile.am
   trunk/data/dbus/org.freedesktop.Tracker.Indexer.service.in
   trunk/data/dbus/org.freedesktop.Tracker.service.in
   trunk/data/dbus/tracker-daemon.xml
   trunk/data/dbus/tracker-files.xml
   trunk/data/dbus/tracker-indexer.xml
   trunk/data/dbus/tracker-keywords.xml
   trunk/data/dbus/tracker-metadata.xml
   trunk/data/dbus/tracker-search.xml
   trunk/data/dbus/tracker-xesam.xml
   trunk/data/english/
   trunk/data/english/Makefile.am
   trunk/data/english/errmsg.sys   (contents, props changed)
   trunk/data/english/errmsg.txt
   trunk/data/icons/
   trunk/data/icons/16x16/
   trunk/data/icons/16x16/Makefile.am
   trunk/data/icons/16x16/tracker.png   (contents, props changed)
   trunk/data/icons/22x22/
   trunk/data/icons/22x22/Makefile.am
   trunk/data/icons/22x22/tracker.png   (contents, props changed)
   trunk/data/icons/24x24/
   trunk/data/icons/24x24/Makefile.am
   trunk/data/icons/24x24/tracker.png   (contents, props changed)
   trunk/data/icons/32x32/
   trunk/data/icons/32x32/Makefile.am
   trunk/data/icons/32x32/tracker.png   (contents, props changed)
   trunk/data/icons/48x48/
   trunk/data/icons/48x48/Makefile.am
   trunk/data/icons/48x48/tracker.png   (contents, props changed)
   trunk/data/icons/Makefile.am
   trunk/data/icons/scalable/
   trunk/data/icons/scalable/Makefile.am
   trunk/data/icons/scalable/tracker.svg
   trunk/data/languages/
   trunk/data/languages/Makefile.am
   trunk/data/languages/stopwords.da
   trunk/data/languages/stopwords.de
   trunk/data/languages/stopwords.en
   trunk/data/languages/stopwords.es
   trunk/data/languages/stopwords.fi
   trunk/data/languages/stopwords.fr
   trunk/data/languages/stopwords.hu
   trunk/data/languages/stopwords.it
   trunk/data/languages/stopwords.nb
   trunk/data/languages/stopwords.nl
   trunk/data/languages/stopwords.pt
   trunk/data/languages/stopwords.ru   (contents, props changed)
   trunk/data/languages/stopwords.sv
   trunk/data/libtracker-gtk.pc.in
   trunk/data/modules/
   trunk/data/modules/Makefile.am
   trunk/data/modules/applications.module
   trunk/data/modules/evolution.module
   trunk/data/modules/files.module
   trunk/data/modules/gaim-conversations.module
   trunk/data/services/
   trunk/data/services/Makefile.am
   trunk/data/services/application.metadata
   trunk/data/services/audio.metadata
   trunk/data/services/default.metadata
   trunk/data/services/default.service
   trunk/data/services/document.metadata
   trunk/data/services/email.metadata
   trunk/data/services/file.metadata
   trunk/data/services/image.metadata
   trunk/data/services/video.metadata
   trunk/data/services/xesam-convenience.metadata
   trunk/data/services/xesam-convenience.service
   trunk/data/services/xesam-metadata.mmapping
   trunk/data/services/xesam-service.smapping
   trunk/data/services/xesam-virtual.metadata
   trunk/data/services/xesam.metadata
   trunk/data/services/xesam.service
   trunk/data/sqlite-cache.sql
   trunk/data/sqlite-contents.sql
   trunk/data/sqlite-email.sql
   trunk/data/sqlite-metadata.sql
   trunk/data/sqlite-service-triggers.sql
   trunk/data/sqlite-service-types.sql
   trunk/data/sqlite-service.sql
   trunk/data/sqlite-stored-procs.sql
   trunk/data/sqlite-tracker-triggers.sql
   trunk/data/sqlite-tracker.sql
   trunk/data/sqlite-triggers.sql
   trunk/data/sqlite-user-data.sql
   trunk/data/sqlite-xesam.sql
   trunk/data/tracker-stop-words.txt
   trunk/data/tracker.pc.in
   trunk/data/trackerd.desktop.in.in
   trunk/docs/
   trunk/docs/Makefile.am
   trunk/docs/tracker-applet.1
   trunk/docs/tracker-extract.1
   trunk/docs/tracker-files.1
   trunk/docs/tracker-meta-folder.1
   trunk/docs/tracker-preferences.1
   trunk/docs/tracker-query.1
   trunk/docs/tracker-search-tool.1
   trunk/docs/tracker-search.1
   trunk/docs/tracker-services.7
   trunk/docs/tracker-stats.1
   trunk/docs/tracker-status.1
   trunk/docs/tracker-tag.1
   trunk/docs/tracker-thumbnailer.1
   trunk/docs/tracker.cfg.5
   trunk/docs/trackerd.1
   trunk/extensions/
   trunk/extensions/firefox-extension/
   trunk/extensions/firefox-extension/Makefile
   trunk/extensions/firefox-extension/README
   trunk/extensions/firefox-extension/chrome/
   trunk/extensions/firefox-extension/chrome.manifest
   trunk/extensions/firefox-extension/chrome/content/
   trunk/extensions/firefox-extension/chrome/content/beagleAddFilter.js   (contents, props changed)
   trunk/extensions/firefox-extension/chrome/content/beagleAddFilter.xul   (contents, props changed)
   trunk/extensions/firefox-extension/chrome/content/beagleOverlay.js
   trunk/extensions/firefox-extension/chrome/content/beagleOverlay.xul
   trunk/extensions/firefox-extension/chrome/content/beaglePrefs.js
   trunk/extensions/firefox-extension/chrome/content/beaglePrefs.xul
   trunk/extensions/firefox-extension/chrome/content/contents.rdf
   trunk/extensions/firefox-extension/chrome/content/i18n.js
   trunk/extensions/firefox-extension/chrome/content/indexBookmark.js
   trunk/extensions/firefox-extension/chrome/content/indexLink.js
   trunk/extensions/firefox-extension/chrome/content/indexLink.xul   (contents, props changed)
   trunk/extensions/firefox-extension/chrome/content/jslib/
   trunk/extensions/firefox-extension/chrome/content/jslib/debug/
   trunk/extensions/firefox-extension/chrome/content/jslib/debug/debug.js
   trunk/extensions/firefox-extension/chrome/content/jslib/io/
   trunk/extensions/firefox-extension/chrome/content/jslib/io/dir.js
   trunk/extensions/firefox-extension/chrome/content/jslib/io/dirUtils.js
   trunk/extensions/firefox-extension/chrome/content/jslib/io/file.js
   trunk/extensions/firefox-extension/chrome/content/jslib/io/fileUtils.js
   trunk/extensions/firefox-extension/chrome/content/jslib/io/filesystem.js
   trunk/extensions/firefox-extension/chrome/content/jslib/jslib.js
   trunk/extensions/firefox-extension/chrome/content/jslib/modules.js
   trunk/extensions/firefox-extension/chrome/content/json.js
   trunk/extensions/firefox-extension/chrome/content/md5.js
   trunk/extensions/firefox-extension/chrome/content/utils.js   (contents, props changed)
   trunk/extensions/firefox-extension/chrome/locale/
   trunk/extensions/firefox-extension/chrome/locale/en-US/
   trunk/extensions/firefox-extension/chrome/locale/en-US/beagle.dtd
   trunk/extensions/firefox-extension/chrome/locale/en-US/beagle.properties
   trunk/extensions/firefox-extension/chrome/locale/en-US/contents.rdf
   trunk/extensions/firefox-extension/chrome/skin/
   trunk/extensions/firefox-extension/chrome/skin/classic/
   trunk/extensions/firefox-extension/chrome/skin/classic/beagle-big.png   (contents, props changed)
   trunk/extensions/firefox-extension/chrome/skin/classic/beagle-disabled.png   (contents, props changed)
   trunk/extensions/firefox-extension/chrome/skin/classic/beagle-error.png   (contents, props changed)
   trunk/extensions/firefox-extension/chrome/skin/classic/beagle.png   (contents, props changed)
   trunk/extensions/firefox-extension/chrome/skin/classic/contents.rdf
   trunk/extensions/firefox-extension/chrome/skin/classic/overlay.css   (contents, props changed)
   trunk/extensions/firefox-extension/firefox-extension-xesam.xpi   (contents, props changed)
   trunk/extensions/firefox-extension/install.rdf
   trunk/extensions/thunderbird-extension/
   trunk/extensions/thunderbird-extension/Makefile
   trunk/extensions/thunderbird-extension/chrome/
   trunk/extensions/thunderbird-extension/chrome.manifest
   trunk/extensions/thunderbird-extension/components/
   trunk/extensions/thunderbird-extension/components/TrackerCommandLine.js
   trunk/extensions/thunderbird-extension/components/TrackerIndexer.js
   trunk/extensions/thunderbird-extension/components/TrackerQueue.js
   trunk/extensions/thunderbird-extension/components/TrackerSettings.js
   trunk/extensions/thunderbird-extension/content/
   trunk/extensions/thunderbird-extension/content/contents.rdf
   trunk/extensions/thunderbird-extension/content/tracker.css
   trunk/extensions/thunderbird-extension/content/tracker.js
   trunk/extensions/thunderbird-extension/content/tracker.xul
   trunk/extensions/thunderbird-extension/content/trackerIndexer.js
   trunk/extensions/thunderbird-extension/content/trackerMailWindow.xul
   trunk/extensions/thunderbird-extension/content/trackerMessenger.xul
   trunk/extensions/thunderbird-extension/content/trackerPrefs.xul
   trunk/extensions/thunderbird-extension/content/trackerQueue.js
   trunk/extensions/thunderbird-extension/content/trackerService.js
   trunk/extensions/thunderbird-extension/content/trackerSettings.js
   trunk/extensions/thunderbird-extension/content/trackerUnindex.js
   trunk/extensions/thunderbird-extension/content/trackerUnindex.xul
   trunk/extensions/thunderbird-extension/content/trackerUtils.js
   trunk/extensions/thunderbird-extension/defaults/
   trunk/extensions/thunderbird-extension/defaults/preferences/
   trunk/extensions/thunderbird-extension/defaults/preferences/default.js
   trunk/extensions/thunderbird-extension/install.rdf
   trunk/extensions/thunderbird-extension/locale/
   trunk/extensions/thunderbird-extension/locale/en-US/
   trunk/extensions/thunderbird-extension/locale/en-US/contents.rdf
   trunk/extensions/thunderbird-extension/locale/en-US/strings.properties
   trunk/extensions/thunderbird-extension/locale/en-US/tracker.dtd
   trunk/extensions/thunderbird-extension/skin/
   trunk/extensions/thunderbird-extension/skin/classic/
   trunk/extensions/thunderbird-extension/skin/classic/contents.rdf
   trunk/extensions/thunderbird-extension/skin/classic/overlay.css
   trunk/extensions/thunderbird-extension/skin/classic/tracker-disabled.png   (contents, props changed)
   trunk/extensions/thunderbird-extension/skin/classic/tracker-error.png   (contents, props changed)
   trunk/extensions/thunderbird-extension/skin/classic/tracker.png   (contents, props changed)
   trunk/extensions/thunderbird-extension/tracker.xpi   (contents, props changed)
   trunk/filters/
   trunk/filters/Makefile.am
   trunk/filters/application/
   trunk/filters/application/Makefile.am
   trunk/filters/application/csv_filter   (contents, props changed)
   trunk/filters/application/msword_filter   (contents, props changed)
   trunk/filters/application/pdf_filter   (contents, props changed)
   trunk/filters/application/tab-separated-values_filter   (contents, props changed)
   trunk/filters/application/vnd.ms-excel_filter   (contents, props changed)
   trunk/filters/application/vnd.oasis.opendocument.presentation-template_filter   (contents, props changed)
   trunk/filters/application/vnd.oasis.opendocument.presentation_filter   (contents, props changed)
   trunk/filters/application/vnd.oasis.opendocument.spreadsheet-template_filter   (contents, props changed)
   trunk/filters/application/vnd.oasis.opendocument.spreadsheet_filter   (contents, props changed)
   trunk/filters/application/vnd.oasis.opendocument.text-template_filter   (contents, props changed)
   trunk/filters/application/vnd.oasis.opendocument.text_filter   (contents, props changed)
   trunk/filters/application/vnd.stardivision.writer_filter   (contents, props changed)
   trunk/filters/application/vnd.sun.xml.calc.template_filter   (contents, props changed)
   trunk/filters/application/vnd.sun.xml.calc_filter   (contents, props changed)
   trunk/filters/application/vnd.sun.xml.draw_filter   (contents, props changed)
   trunk/filters/application/vnd.sun.xml.impress.template_filter   (contents, props changed)
   trunk/filters/application/vnd.sun.xml.impress_filter   (contents, props changed)
   trunk/filters/application/vnd.sun.xml.writer.template_filter   (contents, props changed)
   trunk/filters/application/vnd.sun.xml.writer_filter   (contents, props changed)
   trunk/filters/application/x-abiword_filter   (contents, props changed)
   trunk/filters/application/x-gnumeric_filter   (contents, props changed)
   trunk/filters/text/
   trunk/filters/text/Makefile.am
   trunk/filters/text/csv_filter   (contents, props changed)
   trunk/filters/text/djvu_filter   (contents, props changed)
   trunk/filters/text/html_filter   (contents, props changed)
   trunk/filters/text/spreadsheet_filter   (contents, props changed)
   trunk/filters/text/tab-separated-values_filter   (contents, props changed)
   trunk/filters/text/x-comma-separated-values_filter   (contents, props changed)
   trunk/filters/text/x-tex_filter   (contents, props changed)
   trunk/filters/text/xml_filter   (contents, props changed)
   trunk/po/
   trunk/po/ChangeLog
   trunk/po/LINGUAS
   trunk/po/POTFILES.in
   trunk/po/POTFILES.skip
   trunk/po/ar.po
   trunk/po/be.po
   trunk/po/be latin po
   trunk/po/ca.po
   trunk/po/cs.po
   trunk/po/de.po
   trunk/po/dz.po
   trunk/po/el.po
   trunk/po/en_GB.po
   trunk/po/es.po
   trunk/po/et.po
   trunk/po/fi.po
   trunk/po/fr.po
   trunk/po/gl.po
   trunk/po/he.po
   trunk/po/hu.po
   trunk/po/it.po
   trunk/po/ja.po
   trunk/po/ko.po
   trunk/po/lt.po
   trunk/po/mk.po
   trunk/po/nb.po
   trunk/po/nl.po
   trunk/po/oc.po
   trunk/po/pl.po
   trunk/po/pt.po
   trunk/po/pt_BR.po
   trunk/po/ru.po
   trunk/po/sk.po
   trunk/po/sl.po
   trunk/po/sv.po
   trunk/po/th.po
   trunk/po/zh_CN.po
   trunk/python/
   trunk/python/FUSE/
   trunk/python/FUSE/trackerfs.py
   trunk/python/Makefile.am
   trunk/python/SearchTool/
   trunk/python/SearchTool/COPYING
   trunk/python/SearchTool/README
   trunk/python/SearchTool/mainform.py
   trunk/python/SearchTool/trackergui.py   (contents, props changed)
   trunk/python/applet/
   trunk/python/applet/applet.py   (contents, props changed)
   trunk/python/applet/applet.svg
   trunk/python/deskbar-handler/
   trunk/python/deskbar-handler/Makefile.am
   trunk/python/deskbar-handler/README
   trunk/python/deskbar-handler/tracker-handler-static.py
   trunk/python/deskbar-handler/tracker-handler.py
   trunk/python/deskbar-handler/tracker-module.py
   trunk/python/music/
   trunk/python/music/lyrics.py
   trunk/python/nautilus/
   trunk/python/nautilus/README
   trunk/python/nautilus/tracker-tags-tab.py
   trunk/rdf-query-examples/
   trunk/rdf-query-examples/80s-music.rdf
   trunk/rdf-query-examples/90s-music.rdf
   trunk/rdf-query-examples/all-documents.rdf
   trunk/rdf-query-examples/big-documents.rdf
   trunk/rdf-query-examples/wallpapers.rdf
   trunk/src/
   trunk/src/Makefile.am
   trunk/src/libinotify/
   trunk/src/libinotify/.cvsignore
   trunk/src/libinotify/Makefile.am
   trunk/src/libinotify/inotify-handle.c
   trunk/src/libinotify/inotify-handle.h
   trunk/src/libinotify/inotify-listhash.c
   trunk/src/libinotify/inotify-listhash.h
   trunk/src/libinotify/inotify-log.h
   trunk/src/libinotify/inotify-monitor.c
   trunk/src/libinotify/inotify-monitor.h
   trunk/src/libinotify/libinotify.h
   trunk/src/libinotify/linux-inotify-syscalls.h
   trunk/src/libstemmer/
   trunk/src/libstemmer/Copyright
   trunk/src/libstemmer/Makefile.am
   trunk/src/libstemmer/api.c
   trunk/src/libstemmer/api.h
   trunk/src/libstemmer/header.h
   trunk/src/libstemmer/libstemmer.c
   trunk/src/libstemmer/libstemmer.h
   trunk/src/libstemmer/modules.h
   trunk/src/libstemmer/stem_UTF_8_danish.c
   trunk/src/libstemmer/stem_UTF_8_danish.h
   trunk/src/libstemmer/stem_UTF_8_dutch.c
   trunk/src/libstemmer/stem_UTF_8_dutch.h
   trunk/src/libstemmer/stem_UTF_8_english.c
   trunk/src/libstemmer/stem_UTF_8_english.h
   trunk/src/libstemmer/stem_UTF_8_finnish.c
   trunk/src/libstemmer/stem_UTF_8_finnish.h
   trunk/src/libstemmer/stem_UTF_8_french.c
   trunk/src/libstemmer/stem_UTF_8_french.h
   trunk/src/libstemmer/stem_UTF_8_german.c
   trunk/src/libstemmer/stem_UTF_8_german.h
   trunk/src/libstemmer/stem_UTF_8_hungarian.c
   trunk/src/libstemmer/stem_UTF_8_hungarian.h
   trunk/src/libstemmer/stem_UTF_8_italian.c
   trunk/src/libstemmer/stem_UTF_8_italian.h
   trunk/src/libstemmer/stem_UTF_8_norwegian.c
   trunk/src/libstemmer/stem_UTF_8_norwegian.h
   trunk/src/libstemmer/stem_UTF_8_porter.c
   trunk/src/libstemmer/stem_UTF_8_porter.h
   trunk/src/libstemmer/stem_UTF_8_portuguese.c
   trunk/src/libstemmer/stem_UTF_8_portuguese.h
   trunk/src/libstemmer/stem_UTF_8_russian.c
   trunk/src/libstemmer/stem_UTF_8_russian.h
   trunk/src/libstemmer/stem_UTF_8_spanish.c
   trunk/src/libstemmer/stem_UTF_8_spanish.h
   trunk/src/libstemmer/stem_UTF_8_swedish.c
   trunk/src/libstemmer/stem_UTF_8_swedish.h
   trunk/src/libstemmer/utilities.c
   trunk/src/libtracker/
   trunk/src/libtracker-common/
   trunk/src/libtracker-common/Makefile.am
   trunk/src/libtracker-common/tracker-config.c
   trunk/src/libtracker-common/tracker-config.h
   trunk/src/libtracker-common/tracker-configuration.c
   trunk/src/libtracker-common/tracker-configuration.h
   trunk/src/libtracker-common/tracker-dbus.c
   trunk/src/libtracker-common/tracker-dbus.h
   trunk/src/libtracker-common/tracker-field-data.c
   trunk/src/libtracker-common/tracker-field-data.h
   trunk/src/libtracker-common/tracker-field.c
   trunk/src/libtracker-common/tracker-field.h
   trunk/src/libtracker-common/tracker-file-utils.c
   trunk/src/libtracker-common/tracker-file-utils.h
   trunk/src/libtracker-common/tracker-hal.c
   trunk/src/libtracker-common/tracker-hal.h
   trunk/src/libtracker-common/tracker-ioprio.c
   trunk/src/libtracker-common/tracker-ioprio.h
   trunk/src/libtracker-common/tracker-language.c
   trunk/src/libtracker-common/tracker-language.h
   trunk/src/libtracker-common/tracker-log.c
   trunk/src/libtracker-common/tracker-log.h
   trunk/src/libtracker-common/tracker-module-config.c
   trunk/src/libtracker-common/tracker-module-config.h
   trunk/src/libtracker-common/tracker-nfs-lock.c
   trunk/src/libtracker-common/tracker-nfs-lock.h
   trunk/src/libtracker-common/tracker-ontology.c
   trunk/src/libtracker-common/tracker-ontology.h
   trunk/src/libtracker-common/tracker-os-dependant-unix.c
   trunk/src/libtracker-common/tracker-os-dependant-win.c
   trunk/src/libtracker-common/tracker-os-dependant.h
   trunk/src/libtracker-common/tracker-parser.c
   trunk/src/libtracker-common/tracker-parser.h
   trunk/src/libtracker-common/tracker-service.c
   trunk/src/libtracker-common/tracker-service.h
   trunk/src/libtracker-common/tracker-type-utils.c
   trunk/src/libtracker-common/tracker-type-utils.h
   trunk/src/libtracker-common/tracker-utils.c
   trunk/src/libtracker-common/tracker-utils.h
   trunk/src/libtracker-db/
   trunk/src/libtracker-db/Makefile.am
   trunk/src/libtracker-db/tracker-db-action.c
   trunk/src/libtracker-db/tracker-db-action.h
   trunk/src/libtracker-db/tracker-db-dbus.c
   trunk/src/libtracker-db/tracker-db-dbus.h
   trunk/src/libtracker-db/tracker-db-file-info.c
   trunk/src/libtracker-db/tracker-db-file-info.h
   trunk/src/libtracker-db/tracker-db-index-item.c
   trunk/src/libtracker-db/tracker-db-index-item.h
   trunk/src/libtracker-db/tracker-db-index-manager.c
   trunk/src/libtracker-db/tracker-db-index-manager.h
   trunk/src/libtracker-db/tracker-db-index.c
   trunk/src/libtracker-db/tracker-db-index.h
   trunk/src/libtracker-db/tracker-db-interface-sqlite.c
   trunk/src/libtracker-db/tracker-db-interface-sqlite.h
   trunk/src/libtracker-db/tracker-db-interface.c
   trunk/src/libtracker-db/tracker-db-interface.h
   trunk/src/libtracker-db/tracker-db-manager.c
   trunk/src/libtracker-db/tracker-db-manager.h
   trunk/src/libtracker-gtk/
   trunk/src/libtracker-gtk/Makefile.am
   trunk/src/libtracker-gtk/tracker-gtk.h
   trunk/src/libtracker-gtk/tracker-keyword-store.c
   trunk/src/libtracker-gtk/tracker-keyword-store.h
   trunk/src/libtracker-gtk/tracker-metadata-tile.c
   trunk/src/libtracker-gtk/tracker-metadata-tile.h
   trunk/src/libtracker-gtk/tracker-tag-bar.c
   trunk/src/libtracker-gtk/tracker-tag-bar.h
   trunk/src/libtracker-gtk/tracker-ui.c
   trunk/src/libtracker-gtk/tracker-ui.h
   trunk/src/libtracker-gtk/tracker-utils.c
   trunk/src/libtracker-gtk/tracker-utils.h
   trunk/src/libtracker/COPYING.LIB
   trunk/src/libtracker/Makefile.am
   trunk/src/libtracker/tracker.c
   trunk/src/libtracker/tracker.h
   trunk/src/qdbm/
   trunk/src/qdbm/Makefile.am
   trunk/src/qdbm/depot.c
   trunk/src/qdbm/depot.h
   trunk/src/qdbm/myconf.c
   trunk/src/qdbm/myconf.h
   trunk/src/tracker-applet/
   trunk/src/tracker-applet/Makefile.am
   trunk/src/tracker-applet/tracker-applet-default.png   (contents, props changed)
   trunk/src/tracker-applet/tracker-applet-indexing1.png   (contents, props changed)
   trunk/src/tracker-applet/tracker-applet-indexing2.png   (contents, props changed)
   trunk/src/tracker-applet/tracker-applet-marshallers.c
   trunk/src/tracker-applet/tracker-applet-marshallers.h
   trunk/src/tracker-applet/tracker-applet-paused.png   (contents, props changed)
   trunk/src/tracker-applet/tracker-applet-prefs.glade
   trunk/src/tracker-applet/tracker-applet.c
   trunk/src/tracker-applet/tracker-applet.desktop.in.in
   trunk/src/tracker-applet/tracker-applet.h
   trunk/src/tracker-extract/
   trunk/src/tracker-extract/Makefile.am
   trunk/src/tracker-extract/tracker-extract-abw.c
   trunk/src/tracker-extract/tracker-extract-exif.c
   trunk/src/tracker-extract/tracker-extract-gstreamer.c
   trunk/src/tracker-extract/tracker-extract-html.c
   trunk/src/tracker-extract/tracker-extract-imagemagick.c
   trunk/src/tracker-extract/tracker-extract-jpeg.c
   trunk/src/tracker-extract/tracker-extract-libxine.c
   trunk/src/tracker-extract/tracker-extract-mp3.c
   trunk/src/tracker-extract/tracker-extract-mplayer.c
   trunk/src/tracker-extract/tracker-extract-msoffice.c
   trunk/src/tracker-extract/tracker-extract-oasis.c
   trunk/src/tracker-extract/tracker-extract-pdf.c
   trunk/src/tracker-extract/tracker-extract-png.c
   trunk/src/tracker-extract/tracker-extract-ps.c
   trunk/src/tracker-extract/tracker-extract-tiff.c
   trunk/src/tracker-extract/tracker-extract-totem.c
   trunk/src/tracker-extract/tracker-extract-vorbis.c
   trunk/src/tracker-extract/tracker-extract-xmp.c
   trunk/src/tracker-extract/tracker-extract.c
   trunk/src/tracker-extract/tracker-extract.h
   trunk/src/tracker-extract/tracker-xmp.c
   trunk/src/tracker-extract/tracker-xmp.h
   trunk/src/tracker-fts/
   trunk/src/tracker-fts/Makefile.am
   trunk/src/tracker-fts/tracker-fts-hash.c
   trunk/src/tracker-fts/tracker-fts-hash.h
   trunk/src/tracker-fts/tracker-fts.c
   trunk/src/tracker-fts/tracker-fts.h
   trunk/src/tracker-indexer/
   trunk/src/tracker-indexer/Makefile.am
   trunk/src/tracker-indexer/modules/
   trunk/src/tracker-indexer/modules/Makefile.am
   trunk/src/tracker-indexer/modules/applications.c
   trunk/src/tracker-indexer/modules/dummy.c
   trunk/src/tracker-indexer/modules/evolution.c
   trunk/src/tracker-indexer/modules/files.c
   trunk/src/tracker-indexer/modules/gaim-conversations.c
   trunk/src/tracker-indexer/tracker-dbus.c
   trunk/src/tracker-indexer/tracker-dbus.h
   trunk/src/tracker-indexer/tracker-indexer-db.c
   trunk/src/tracker-indexer/tracker-indexer-db.h
   trunk/src/tracker-indexer/tracker-indexer-module.c
   trunk/src/tracker-indexer/tracker-indexer-module.h
   trunk/src/tracker-indexer/tracker-indexer.c
   trunk/src/tracker-indexer/tracker-indexer.h
   trunk/src/tracker-indexer/tracker-main.c
   trunk/src/tracker-indexer/tracker-marshal-main.c
   trunk/src/tracker-indexer/tracker-marshal.list
   trunk/src/tracker-indexer/tracker-metadata-utils.c
   trunk/src/tracker-indexer/tracker-metadata-utils.h
   trunk/src/tracker-indexer/tracker-metadata.c
   trunk/src/tracker-indexer/tracker-metadata.h
   trunk/src/tracker-indexer/tracker-module.h
   trunk/src/tracker-preferences/
   trunk/src/tracker-preferences/Makefile.am
   trunk/src/tracker-preferences/tracker-preferences-dialogs.c
   trunk/src/tracker-preferences/tracker-preferences-dialogs.h
   trunk/src/tracker-preferences/tracker-preferences-main.c
   trunk/src/tracker-preferences/tracker-preferences-utils.c
   trunk/src/tracker-preferences/tracker-preferences-utils.h
   trunk/src/tracker-preferences/tracker-preferences.c
   trunk/src/tracker-preferences/tracker-preferences.desktop.in.in
   trunk/src/tracker-preferences/tracker-preferences.glade
   trunk/src/tracker-preferences/tracker-preferences.h
   trunk/src/tracker-search-tool/
   trunk/src/tracker-search-tool/Makefile.am
   trunk/src/tracker-search-tool/sexy-icon-entry.c
   trunk/src/tracker-search-tool/sexy-icon-entry.h
   trunk/src/tracker-search-tool/thumbnail_frame.png   (contents, props changed)
   trunk/src/tracker-search-tool/tracker-search-tool-callbacks.c
   trunk/src/tracker-search-tool/tracker-search-tool-callbacks.h
   trunk/src/tracker-search-tool/tracker-search-tool-support.c
   trunk/src/tracker-search-tool/tracker-search-tool-support.h
   trunk/src/tracker-search-tool/tracker-search-tool.c
   trunk/src/tracker-search-tool/tracker-search-tool.desktop.in.in
   trunk/src/tracker-search-tool/tracker-search-tool.h
   trunk/src/tracker-thumbnailer/
   trunk/src/tracker-thumbnailer/Makefile.am
   trunk/src/tracker-thumbnailer/tracker-thumbnailer.c
   trunk/src/tracker-utils/
   trunk/src/tracker-utils/Makefile.am
   trunk/src/tracker-utils/tracker-files.c
   trunk/src/tracker-utils/tracker-meta-folder.c
   trunk/src/tracker-utils/tracker-query.c
   trunk/src/tracker-utils/tracker-search.c
   trunk/src/tracker-utils/tracker-stats.c
   trunk/src/tracker-utils/tracker-status.c
   trunk/src/tracker-utils/tracker-tag.c
   trunk/src/tracker-utils/tracker-unique.c
   trunk/src/trackerd/
   trunk/src/trackerd/Makefile.am
   trunk/src/trackerd/mingw-compat.h
   trunk/src/trackerd/tracker-crawler.c
   trunk/src/trackerd/tracker-crawler.h
   trunk/src/trackerd/tracker-daemon.c
   trunk/src/trackerd/tracker-daemon.h
   trunk/src/trackerd/tracker-db.c
   trunk/src/trackerd/tracker-db.h
   trunk/src/trackerd/tracker-dbus.c
   trunk/src/trackerd/tracker-dbus.h
   trunk/src/trackerd/tracker-files.c
   trunk/src/trackerd/tracker-files.h
   trunk/src/trackerd/tracker-keywords.c
   trunk/src/trackerd/tracker-keywords.h
   trunk/src/trackerd/tracker-main.c
   trunk/src/trackerd/tracker-main.h
   trunk/src/trackerd/tracker-marshal-main.c
   trunk/src/trackerd/tracker-marshal.list
   trunk/src/trackerd/tracker-metadata.c
   trunk/src/trackerd/tracker-metadata.h
   trunk/src/trackerd/tracker-monitor.c
   trunk/src/trackerd/tracker-monitor.h
   trunk/src/trackerd/tracker-processor.c
   trunk/src/trackerd/tracker-processor.h
   trunk/src/trackerd/tracker-query-tree.c
   trunk/src/trackerd/tracker-query-tree.h
   trunk/src/trackerd/tracker-rdf-query.c
   trunk/src/trackerd/tracker-rdf-query.h
   trunk/src/trackerd/tracker-search.c
   trunk/src/trackerd/tracker-search.h
   trunk/src/trackerd/tracker-status.c
   trunk/src/trackerd/tracker-status.h
   trunk/src/trackerd/tracker-utils.c
   trunk/src/trackerd/tracker-utils.h
   trunk/src/trackerd/tracker-xesam-live-search.c
   trunk/src/trackerd/tracker-xesam-live-search.h
   trunk/src/trackerd/tracker-xesam-manager.c
   trunk/src/trackerd/tracker-xesam-manager.h
   trunk/src/trackerd/tracker-xesam-query.c
   trunk/src/trackerd/tracker-xesam-query.h
   trunk/src/trackerd/tracker-xesam-session.c
   trunk/src/trackerd/tracker-xesam-session.h
   trunk/src/trackerd/tracker-xesam.c
   trunk/src/trackerd/tracker-xesam.h
   trunk/tests/
   trunk/tests/Makefile.am
   trunk/tests/common/
   trunk/tests/common/Makefile.am
   trunk/tests/common/tracker-test-helpers.c
   trunk/tests/common/tracker-test-helpers.h
   trunk/tests/libtracker-common/
   trunk/tests/libtracker-common/Makefile.am
   trunk/tests/libtracker-common/non-utf8.txt
   trunk/tests/libtracker-common/tracker-dbus-test.c
   trunk/tests/libtracker-common/tracker-field-test.c
   trunk/tests/libtracker-common/tracker-file-utils-test.c
   trunk/tests/libtracker-common/tracker-ontology-test.c
   trunk/tests/libtracker-common/tracker-parser-test.c
   trunk/tests/libtracker-common/tracker-type-utils-test.c
   trunk/tests/libtracker-db/
   trunk/tests/libtracker-db/Makefile.am
   trunk/tests/libtracker-db/example.index   (contents, props changed)
   trunk/tests/libtracker-db/run-test-in-tmp-dir.sh
   trunk/tests/libtracker-db/tracker-db-dbus-test.c
   trunk/tests/libtracker-db/tracker-db-manager-common.c
   trunk/tests/libtracker-db/tracker-db-manager-common.h
   trunk/tests/libtracker-db/tracker-db-manager-test-attach.c
   trunk/tests/libtracker-db/tracker-db-manager-test-custom.c
   trunk/tests/libtracker-db/tracker-db-manager-test-unattach.c
   trunk/tests/libtracker-db/tracker-index-reader-test.c
   trunk/tests/libtracker-db/tracker-index-writer-test.c
   trunk/tests/libtracker-db/union-performance.c
   trunk/tests/scripts/
   trunk/tests/scripts/Makefile.am
   trunk/tests/scripts/data/
   trunk/tests/scripts/data/common.sql
   trunk/tests/scripts/data/email-contents.sql
   trunk/tests/scripts/data/email-meta.sql
   trunk/tests/scripts/data/file-contents.sql
   trunk/tests/scripts/data/file-meta.sql
   trunk/tests/scripts/data/xesam.sql
   trunk/tests/scripts/dummy_data_start.sh.in   (contents, props changed)
   trunk/tests/scripts/dummy_data_stop.sh.in   (contents, props changed)
   trunk/tests/scripts/testing.txt
   trunk/tests/scripts/xdg_dirs.source
   trunk/tests/scripts/xdg_dirs.unsource
   trunk/tests/tracker-fts/
   trunk/tests/tracker-fts/Makefile.am
   trunk/tests/tracker-fts/tracker-fts-test.c
   trunk/tests/tracker-indexer/
   trunk/tests/tracker-indexer/Makefile.am
   trunk/tests/tracker-indexer/tracker-metadata-utils-test.c
   trunk/tests/trackerd/
   trunk/tests/trackerd/Makefile.am
   trunk/tests/trackerd/README
   trunk/tests/trackerd/tracker-xesam-hit-test.c
   trunk/tests/trackerd/tracker-xesam-hit-test.h
   trunk/tests/trackerd/tracker-xesam-hits-test.c
   trunk/tests/trackerd/tracker-xesam-hits-test.h
   trunk/tests/trackerd/tracker-xesam-session-test.c
   trunk/tests/trackerd/tracker-xesam-session-test.h
   trunk/tests/trackerd/tracker-xesam-test.c
   trunk/tests/trackerd/tracker-xesam-test.h
   trunk/tests/trackerd/xesam/
   trunk/tests/trackerd/xesam/Makefile.am
   trunk/tests/trackerd/xesam/README
   trunk/tests/trackerd/xesam/gtestextensions.c
   trunk/tests/trackerd/xesam/gtestextensions.h
   trunk/tests/trackerd/xesam/xesam-g-debug-private.h
   trunk/tests/trackerd/xesam/xesam-g-globals-private.h
   trunk/tests/trackerd/xesam/xesam-g-test-query-builder.c
   trunk/tests/trackerd/xesam/xesam-g-test-query-builder.h
   trunk/tests/trackerd/xesam/xesam-g-testsearcher.c
   trunk/tests/trackerd/xesam/xesam-g-testsearcher.h
   trunk/tests/trackerd/xesam/xesam-g-utils.h
   trunk/thumbnailers/
   trunk/thumbnailers/Makefile.am
   trunk/thumbnailers/application/
   trunk/thumbnailers/application/Makefile.am
   trunk/thumbnailers/application/pdf_thumbnailer   (contents, props changed)
   trunk/thumbnailers/application/vnd.oasis.opendocument.graphics_thumbnailer   (contents, props changed)
   trunk/thumbnailers/application/vnd.oasis.opendocument.presentation_thumbnailer   (contents, props changed)
   trunk/thumbnailers/application/vnd.oasis.opendocument.spreadsheet_thumbnailer   (contents, props changed)
   trunk/thumbnailers/application/vnd.oasis.opendocument.text_thumbnailer   (contents, props changed)
   trunk/thumbnailers/image/
   trunk/thumbnailers/image/Makefile.am
   trunk/thumbnailers/image/gif_thumbnailer   (contents, props changed)
   trunk/thumbnailers/image/hildon/
   trunk/thumbnailers/image/hildon/gif_thumbnailer   (contents, props changed)
   trunk/thumbnailers/image/hildon/jpeg_thumbnailer   (contents, props changed)
   trunk/thumbnailers/image/hildon/png_thumbnailer   (contents, props changed)
   trunk/thumbnailers/image/hildon/tiff_thumbnailer   (contents, props changed)
   trunk/thumbnailers/image/jpeg_thumbnailer   (contents, props changed)
   trunk/thumbnailers/image/png_thumbnailer   (contents, props changed)
   trunk/thumbnailers/image/tiff_thumbnailer   (contents, props changed)
   trunk/tracker.spec
   trunk/utils/
   trunk/utils/Makefile.am
   trunk/utils/qdbm/
   trunk/utils/qdbm/Makefile.am
   trunk/utils/qdbm/print-words.c
   trunk/utils/qdbm/search-word.c

Added: trunk/AUTHORS
==============================================================================
--- (empty file)
+++ trunk/AUTHORS	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,38 @@
+Anders Aagaard <aagaande at gmail com>
+Anders Rune Jensen <anders iola dk>
+Baptiste Mille-Mathias <baptist millemathias gmail com>
+Carlos Garnacho <carlos at imendio com>
+Christoph Laimburg <christoph laimburg at rolmail net>
+Dan Nicolaescu <dann at ics uci edu>
+Deji Akingunola <dakingun gmail com>
+Edward Duffy <eduffy at gmail com>
+Eskil Bylund <eskil at letterboxes org>
+Eugenio <me at eugesoftware com>
+Fabien VALLON <fabien at sonappart net>
+Gergan Penkov <gergan at gmail com>
+Halton Huo <halton huo at sun com>
+Ivan Frade <ivan frade at nokia com>
+Jaime Frutos Morales <acidborg at gmail com>
+Jamie McCracken <jamiemcc at gnome org>
+Jedy Wang <jedy wang at sun com>
+Jerry Tan <jerry tan at sun com>
+John Stowers <john.stowers at gmail com>
+Julien <julienc psychologie-fr org>
+Laurent Aguerreche <laurent.aguerreche at free fr>
+Luca Ferretti <elle.uca at libero it>
+Marcus Fritzsch <fritschy at googlemail com>
+Martyn Russell <martyn at imendio com>
+Michael Biebl <mbiebl at gmail com>
+Michal Pryc <michal pryc at sun com>
+Mikael Ottela <mikael ottela at ixonos com>
+Mikkel Kamstrup Erlandsen <mikkel kamstrup gmail com>
+Nate Nielsen  <nielsen at memberwewbs com>
+Neil Patel <njpatel at gmail com>
+Phillip Van Hoof <pvanhoof at gnome org>
+Richard Quirk <quirky at zoom co uk>
+Saleem Abdulrasool <compnerd at gentoo org>
+Samuel Cormier-Iijima <sciyoshi at gmail com>
+Tobutaz <tobutaz gmail com>
+Tom <tpgww at onepost net>
+Tshepang Lekhonkhobe <tshepang at gmail com>
+Ulrik Mikaelsson <ulrik mikaelsson gmail com>

Added: trunk/COPYING
==============================================================================
--- (empty file)
+++ trunk/COPYING	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  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
+this service 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 make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  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.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+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
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the 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 a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE 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.
+
+		     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
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision 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, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This 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 Library General
+Public License instead of this License.

Added: trunk/INSTALL
==============================================================================
--- (empty file)
+++ trunk/INSTALL	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,237 @@
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
+2006, 2007 Free Software Foundation, Inc.
+
+This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+Briefly, the shell commands `./configure; make; make install' should
+configure, build, and install this package.  The following
+more-detailed instructions are generic; see the `README' file for
+instructions specific to this package.
+
+   The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation.  It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions.  Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+   It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring.  Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.
+
+   If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release.  If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+   The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'.  You need `configure.ac' if
+you want to change it or regenerate `configure' using a newer version
+of `autoconf'.
+
+The simplest way to compile this package is:
+
+  1. `cd' to the directory containing the package's source code and type
+     `./configure' to configure the package for your system.
+
+     Running `configure' might take a while.  While running, it prints
+     some messages telling which features it is checking for.
+
+  2. Type `make' to compile the package.
+
+  3. Optionally, type `make check' to run any self-tests that come with
+     the package.
+
+  4. Type `make install' to install the programs and any data files and
+     documentation.
+
+  5. You can remove the program binaries and object files from the
+     source code directory by typing `make clean'.  To also remove the
+     files that `configure' created (so you can compile the package for
+     a different kind of computer), type `make distclean'.  There is
+     also a `make maintainer-clean' target, but that is intended mainly
+     for the package's developers.  If you use it, you may have to get
+     all sorts of other programs in order to regenerate files that came
+     with the distribution.
+
+  6. Often, you can also type `make uninstall' to remove the installed
+     files again.
+
+Compilers and Options
+=====================
+
+Some systems require unusual options for compilation or linking that the
+`configure' script does not know about.  Run `./configure --help' for
+details on some of the pertinent environment variables.
+
+   You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment.  Here
+is an example:
+
+     ./configure CC=c99 CFLAGS=-g LIBS=-lposix
+
+   *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory.  To do this, you can use GNU `make'.  `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script.  `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+   With a non-GNU `make', it is safer to compile the package for one
+architecture at a time in the source code directory.  After you have
+installed the package for one architecture, use `make distclean' before
+reconfiguring for another architecture.
+
+Installation Names
+==================
+
+By default, `make install' installs the package's commands under
+`/usr/local/bin', include files under `/usr/local/include', etc.  You
+can specify an installation prefix other than `/usr/local' by giving
+`configure' the option `--prefix=PREFIX'.
+
+   You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files.  If you
+pass the option `--exec-prefix=PREFIX' to `configure', the package uses
+PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files still use the regular prefix.
+
+   In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files.  Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+   If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System).  The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+   For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+There may be some features `configure' cannot figure out automatically,
+but needs to determine by the type of machine the package will run on.
+Usually, assuming the package is built to be run on the _same_
+architectures, `configure' can figure that out, but if it prints a
+message saying it cannot guess the machine type, give it the
+`--build=TYPE' option.  TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+     CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+     OS KERNEL-OS
+
+   See the file `config.sub' for the possible values of each field.  If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+   If you are _building_ compiler tools for cross-compiling, you should
+use the option `--target=TYPE' to select the type of system they will
+produce code for.
+
+   If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+If you want to set default values for `configure' scripts to share, you
+can create a site shell script called `config.site' that gives default
+values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists.  Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+Variables not defined in a site shell script can be set in the
+environment passed to `configure'.  However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost.  In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'.  For example:
+
+     ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script).
+
+Unfortunately, this technique does not work for `CONFIG_SHELL' due to
+an Autoconf bug.  Until the bug is fixed you can use this workaround:
+
+     CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+`configure' Invocation
+======================
+
+`configure' recognizes the following options to control how it operates.
+
+`--help'
+`-h'
+     Print a summary of the options to `configure', and exit.
+
+`--version'
+`-V'
+     Print the version of Autoconf used to generate the `configure'
+     script, and exit.
+
+`--cache-file=FILE'
+     Enable the cache: use and save the results of the tests in FILE,
+     traditionally `config.cache'.  FILE defaults to `/dev/null' to
+     disable caching.
+
+`--config-cache'
+`-C'
+     Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+     Do not print messages saying which checks are being made.  To
+     suppress all normal output, redirect it to `/dev/null' (any error
+     messages will still be shown).
+
+`--srcdir=DIR'
+     Look for the package's source code in directory DIR.  Usually
+     `configure' can determine that directory automatically.
+
+`configure' also accepts some other, not widely useful, options.  Run
+`configure --help' for more details.
+

Added: trunk/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,43 @@
+include $(top_srcdir)/Makefile.decl
+
+SUBDIRS = data src filters thumbnailers docs po python utils
+
+if HAVE_UNIT_TESTS
+SUBDIRS += tests
+endif
+
+EXTRA_DIST = 					\
+	rdf-query-examples/80s-music.rdf 	\
+	rdf-query-examples/90s-music.rdf 	\
+	rdf-query-examples/all-documents.rdf 	\
+	rdf-query-examples/big-documents.rdf 	\
+	rdf-query-examples/wallpapers.rdf 	\
+	intltool-extract.in 			\
+	intltool-merge.in 			\
+	intltool-update.in 
+
+CLEANFILES = $(autostart_DATA)
+
+DISTCLEANFILES = 				\
+	intltool-extract 			\
+	intltool-merge 				\
+	intltool-update
+
+DISTCHECK_CONFIGURE_FLAGS =			\
+	--with-session-bus-services-dir="\$(datadir)"/dbus-1/services \
+	--disable-deskbar-applet
+
+if OLD_EXEC_REMOVE_ALL
+install-exec-hook:
+	rm -Rf $(DESTDIR)$(bindir)/trackerd
+	rm -Rf $(DESTDIR)$(bindir)/tracker-indexer
+	rm -Rf $(DESTDIR)$(bindir)/tracker-thumbnailer
+	rm -Rf $(DESTDIR)$(bindir)/tracker-extract
+endif
+
+if OLD_DATA_REMOVE_ALL
+install-data-hook:
+	rm -Rf $(DESTDIR)$(DBUS_SERVICES_DIR)/tracker.service
+	rm -Rf $(DESTDIR)$(datadir)/tracker/sqlite-service-stored-procs.sql
+	rm -Rf $(DESTDIR)$(datadir)/tracker/tracker-introspect.xml
+endif	
\ No newline at end of file

Added: trunk/Makefile.decl
==============================================================================
--- (empty file)
+++ trunk/Makefile.decl	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,64 @@
+#
+# Testing rules for make
+# Original file: http://svn.gnome.org/svn/glib/trunk/Makefile.decl
+#
+# test: run all tests in cwd and subdirs
+# test-report: run tests in subdirs and generate report
+# perf-report: run tests in subdirs with -m perf and generate report
+# full-report: like test-report: with -m perf and -m slow
+#
+
+GTESTER = gtester 			# for non-GLIB packages
+GTESTER_REPORT = gtester-report
+
+# initialize variables for unconditional += appending
+TEST_PROGS =
+
+### testing rules
+
+# test: run all tests in cwd and subdirs
+test:	${TEST_PROGS}
+	@ test -z "${TEST_PROGS}" || ${GTESTER} --verbose ${TEST_PROGS}
+	@ for subdir in $(SUBDIRS) . ; do \
+	    test "$$subdir" = "." -o "$$subdir" = "po" || \
+	    ( cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $? ; \
+	  done
+# test-report: run tests in subdirs and generate report
+# perf-report: run tests in subdirs with -m perf and generate report
+# full-report: like test-report: with -m perf and -m slow
+test-report perf-report full-report:	${TEST_PROGS}
+	@test -z "${TEST_PROGS}" || { \
+	  case $@ in \
+	  test-report) test_options="-k";; \
+	  perf-report) test_options="-k -m=perf";; \
+	  full-report) test_options="-k -m=perf -m=slow";; \
+	  esac ; \
+	  if test -z "$$GTESTER_LOGDIR" ; then	\
+	    ${GTESTER} --verbose $$test_options -o test-report.xml ${TEST_PROGS} ; \
+	  elif test -n "${TEST_PROGS}" ; then \
+	    ${GTESTER} --verbose $$test_options -o `mktemp "$$GTESTER_LOGDIR/log-XXXXXX"` ${TEST_PROGS} ; \
+	  fi ; \
+	}
+	@ ignore_logdir=true ; \
+	  if test -z "$$GTESTER_LOGDIR" ; then \
+	    GTESTER_LOGDIR=`mktemp -d "\`pwd\`/.testlogs-XXXXXX"`; export GTESTER_LOGDIR ; \
+	    ignore_logdir=false ; \
+	  fi ; \
+	  for subdir in $(SUBDIRS) . ; do \
+	    test "$$subdir" = "." -o "$$subdir" = "po" || \
+	    ( cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $? ; \
+	  done ; \
+	  $$ignore_logdir || { \
+	    echo '<?xml version="1.0"?>' > $  xml ; \
+	    echo '<report-collection>'  >> $  xml ; \
+	    for lf in `ls -L "$$GTESTER_LOGDIR"/.` ; do \
+	      sed '1,1s/^<?xml\b[^>?]*?>//' <"$$GTESTER_LOGDIR"/"$$lf" >> $  xml ; \
+	    done ; \
+	    echo >> $  xml ; \
+	    echo '</report-collection>' >> $  xml ; \
+	    rm -rf "$$GTESTER_LOGDIR"/ ; \
+	    ${GTESTER_REPORT} --version 2>/dev/null 1>&2 ; test "$$?" != 0 || ${GTESTER_REPORT} $  xml >$  html ; \
+	  }
+.PHONY: test test-report perf-report full-report
+# run make test as part of make check
+check-local: test

Added: trunk/NEWS
==============================================================================
--- (empty file)
+++ trunk/NEWS	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,335 @@
+Tracker 0.6.6
+============= (03 Mar 2008)
+
+  Applet
+  * HIG fix for preference window.
+  * Progress for index merges added.
+
+  Tracker daemon
+  * Fixed shutdown of daemon when paused to prevent hangs.
+  * Fixed an unfinished final index merge step when next restarted.
+
+
+
+Tracker 0.6.5
+============= (27 Feb 2008)
+
+  Applet
+  * Removed popup window and replaced with tooltip displaying status
+    and progress.
+  * New Applet preferences to turn off animation/hide icon/set
+    smart pause.
+  * Smart pause feature which can auto-pause the Tracker daemon
+    whenever the user presses a key or moves the mouse, thereby
+    improving system responsiveness during indexing.
+  * Annoying merge messages have now been removed.
+  * Reindex option added.
+  * About box added.
+  * Now hidden if indexing is disabled.
+  * Bugs with constant animation fixed.
+  * Animation rate slowed right down so as to be less annoying.
+
+  Tracker daemon
+  * Temporary blacklisting of frequently changing files (they are
+    indexed after one hour or on next restart of Tracker daemon).
+    This prevents constant indexing from downloads and torrents.
+  * More blacklisting of common tmp formats and vmware files.
+  * Memory leak fixes.
+  * Removed CPU-heavy corruption scan which can last long time and
+    occurs on battery too at startup when tracker daemon was not
+    previously shut down properly. Instead we now use bigger
+    transactions with fsync on to prevent corruption in the first
+    place (only slightly more disk IO is used during indexing as a
+    result).
+  * Fixed crashers.
+  * Fixed D-Bus shutdown/reindex call.
+
+
+
+Tracker 0.6.4
+============= (11 Dec 2007)
+
+  * Made indexing more robust by pausing if disk space is low or
+    index grows too big.
+  * Limit log file size to 10MB.
+  * New Tracker Applet - animates when indexing, provides ability to
+    pause indexing as well as viewing status and progress feedback
+    from indexer, statistics, and notification warnings; it can
+    also launch Tracker Preferences and Tracker Search Tool.
+  * New power management options enable much better customization.
+  * Ignored files fixes.
+  * Deskbar/tracker integration fixes.
+  * Made most prefs live and affect Tracker in real time; others
+    will prompt for restart and/or reindex where necessary.
+  * Shell script fixes
+  * Fixed Imap bug with embedded Auth in URI.
+  * Built-in corruption check and scan when Tracker is not shut down
+    cleanly - prevents infinite looping.
+  * Fix index deletions.
+  * Many more bug fixes and stability improvements.
+
+
+
+Tracker 0.6.3
+============= (25 Sep 2007)
+
+  * Fixed memory leaks.
+  * New index merging which allows for much better performance and
+    much less disk hogging I/O.
+  * Battery throttling - Tracker now stops indexing when on battery.
+  * Many more optimisations - transactions, use of O_NOATIME and
+    posix_fadvise everywhere.
+  * Check files exist before including in search results.
+  * Fixed disabling of indexing option in tracker-preferences.
+  * HIG fixes to tracker-preferences screen.
+  * Fixed resetting of stats.
+
+  note:
+    This version will cause your hard drive to be re-indexed so
+    that stats are fixed (previous version broke them).
+
+
+
+Tracker 0.6.2
+============= (25 Sep 2007)
+
+  * New Sqlite-based indexer which utilises the new incremental blob
+    I/O in sqlite 3.4.
+  * Highly-optimised email indexing (up to 5x faster).
+  * Dramatically reduced disk access and disk contention.
+  * Indexer now pauses for a grace period when non-Tracker processes
+    write to disk (providing changed files are being watched by
+    Tracker) - minimises slowdowns when compiling or checking out
+    source code.
+  * Makes use of idle class disk IO scheduling if available.
+  * Makes preliminary use of NO_ATIME (some disk access still uses
+    fopen).
+  * Fixed restore of user metadata on re-index (keywords are
+    auto-restored).
+  * Added increased number of (junk) files to automatically ignore.
+  * Improved stopwords.
+  * New Deskbar handler that uses the new Deskbar api (2.19+).
+  * Fixed old Deskbar handler to remove race condition causing
+    crashes.
+  * Fixed a number of annoying bugs in email indexing and Tracker
+    Preferences.
+
+  note:
+    This version will cause your hard drive to be re-indexed due to
+    the new Sqlite indexer backend.
+
+
+Tracker 0.6.1
+============= (08 Aug 2007)
+
+  * translations and bugfixes
+
+
+Tracker 0.6.0 "better late than never"
+============= (24 Jul 2007)
+
+  * Evolution Emails (mbox/pop/imap) now indexed.
+  * Gaim/Pidgin chat logs now indexed.
+  * Applications now indexed.
+  * New files indexed - all spreadsheets, csv (these require
+    Gnumeric to be installed) and oo draw.
+  * Vastly improved and new search UI using a sidebar for categories
+    and a new metadata tile.
+  * Tagging support in the UI.
+  * New tracker-preferences UI for easy setting of prefs.
+  * Added libtracker-gtk widget toolkit.
+  * Now uses XDG directories.
+  * Optimised triple store.
+  * Handles multiple triple stores (files, emails and user data are
+    all separate dbs).
+  * New IDF based ranking algorithm.
+  * XMP metadata support added.
+  * Made use of async calls in tracker-search-tool to prevent it
+    from freezing.
+  * Added a 60 second delay before indexing to prevent slowdowns on
+    desktop start-up.
+  * Added support for crawling directories without watching overhead
+    (useful for indexing source code without causing slowdowns when
+     compiling).
+
+
+Tracker 0.5.4 "Speed Daemon"
+============= (26 Jan 2007)
+
+  * Indexing at ludicrous speed - massively optimised indexing so
+    its now 10x faster than previous version. Indexing speeds are
+    now around 100 text files per second (which is about the
+    maximum possible considering the I/O time to read 100 files from
+    a hard drive).
+  * Index while you work - now scheduled even better so it should
+    never slow the system down whilst allowing users to work without
+    being affected by or even noticing it.
+  * Also provides additional --throttle command line parameter to
+    customise throttling even further so no need to endure noisy
+    fans or hot laptops while indexing.
+  * New verbosity param to control how much data is shown on screen
+    and in log.
+  * Fixed MPlayer backend which is now used if gstreamer backend
+    returns no metadata
+  * Improved reaping of all spawned apps - no more zombies!
+  * Improved mime and text file detection.
+  * Added improved deskbar-handler.
+  * Got Tracker to run nicely on FUSE based filesystems.
+
+
+Tracker 0.5.3
+============= (24 Dec 2006)
+
+  * Fixed memory leaks when indexing (typical mem usage should now
+    be ~6MB RSS when indexing large numbers of files).
+  * New improved metadata engine now sports multiple values per
+    metadata item and rdf style recursive metadata relationships.
+  * Support for Dublin Core types (especially for metadata
+    relationships).
+  * Changed command line options for trackerd to be similiar to
+    other programs.
+  * Metadata names now to reflect rdf style syntax (Eg DC:Keywords).
+  * Improved indexing of numeric data so that only meaningful
+    numbers are indexed (IE must contain 5 or more consecutive
+    numbers to be indexable).
+  * Improved indexing of filenames so that hyphens and underscores
+    are used as word breaks to create a delimited filename in
+    addition to the full filename.
+  * Improved snippet generator to start/end on a word break if
+    possible.
+
+
+Tracker 0.5.2
+============= (21 Nov 2006)
+
+  * Now has l10n/i18n support.
+  * Added timeout to all external processes and metadata extractors
+    used by Tracker.
+  * Added missing OpenOffice templates to our filters.
+  * Fixed filters to use different packages like w3m for indexing
+    html.
+  * Fixed Debian build and added new packages for data files and
+    translations.
+  * Imporved t-s-t and made snippets wordwrap, made snippets async,
+    used Tracker's mime facility, improved highlighting and fixed
+    crasher.
+  * Limited tracker-extract's memory usage using setrlimit.
+  * Added desktop files for tracker-search-tool.
+
+
+Tracker 0.5.1
+============= (07 Nov 2006)
+
+  * Inlined a threadsafe version of sqlite to prevent common errors
+    like content not being indexed. This can be overriden for those
+    with a threadsafe version of SQlite.
+  * New Gstreamer-based audio/video extractor on by default.
+  * Made extractors more robust.
+  * Fixed a number of compiler warnings.
+  * Tidied up the tracker-search-tool to have buttons more evenly-sized
+  * Fixed a few leaks and made the D-Bus interface more robust.
+  * Imporved IOprio detection.
+
+
+Tracker 0.5.0
+============= (25 Oct 2006)
+
+  * Completely new backend that has been moved from Mysql to the
+    nimble Sqlite database and QDBM inverted word index.
+  * New tracker-search-tool GUI which allows you to search for your
+    files in a google-like fashion with results depicted with
+    google-style search snippets.
+  * Revamped Rdf Query engine.
+  * New lighter design with big dependencies like libextractor
+    having been dropped in favour of lighter and optional metadata
+    extractors.
+  * Added new extractors that can use Totem or MPlayer.
+  * New options to tune Tracker for your system. We now have a
+    --enable-low-memory setting for use with computers with less
+    RAM and a --turbo mode for those  who want indexing to take
+    place as fast as possible.
+  * New snowball stemmers have been integrated into Tracker to
+    provide some of the highest quality open source stemming around.
+  * New language-specific stopword lists to prevent indexng of
+    common words (which are of little value).
+  * Can optionally make use of Pango word breaking for full
+    internationalisation support.
+
+
+Tracker 0.0.4 "Now indexing at Warp speed"
+============= (16 May 2006)
+
+  * Hugely optimised indexing when many files are waiting to be
+    indexed (especially when you first run trackerd).
+  * Mass queueing of files no longer blocks the main thread, meaning
+    super fast searches can still be performed during heavy indexing.
+  * Eliminated CPU bottlenecks and improved thread synchronisations
+    so Tracker now hits the ground running when indexing (approx
+    500+ files indexed per minute on inotify-enabled systems).
+  * Extended metadata support for more Exif fields.
+  * Added more service types.
+  * Imporved build and support for FC5 (includes RPMs).
+  * Redesigned Database around version 5 of the *embedded*
+    in-process MySQL database library.
+  * Now uses the auto-repair facility provided by MySQL to
+    automatically repair damaged database files so you need never
+    worry about losing your precious data.
+  * Moved virtually all DB logic into stored procedures which
+    provides a clean seperation of DB logic and application logic.
+  * Added support for parsing dates in various formats including
+    conversion to/from ISO 8601 format
+  * Fixed MSWord filter to prevent looping (WvText causes inotify to
+    report file write change causing endless looping).
+  * Redesigned DB structure to be more generic and service orientated
+  * Added support for service types (first class objects) to DB.
+  * Cleaned up code warnings and fixed potential crasher.
+  * Fixed issues with argv handling.
+  * Fixed build issue for Fedora Core 5.
+
+
+Tracker 0.0.3
+============= (29 Mar 2006)
+
+  * Made D-Bus interface more generic
+  * Corrected search results of hyphenated search terms.
+  * Added support for limiting no. of hits for searches.
+  * Added optional support for sorting search results by relevance
+    score.
+  * A few minor bug fixes (Updating file name metadata correctly
+    during move, correcting use of hyphenated search terms).
+  * Streamlined the inlined version of libextractor and improved
+    build system and fixed bug in previous version which prevented
+    compilation of the inlined version.
+
+
+Tracker 0.0.2
+============= (27 Feb 2006)
+
+  * Full inotify support (only compatible with Linux kernels 2.6.13
+    or higher) with optimisations for detected moves.
+  * Fallback to FAM/GAMIN and manual polling for non-Linux and/or
+    non-inotify systems.
+  * Improved build system which allows you to specify which file
+    monitoring backend to use.
+  * Valgrind'ed to remove all leaks.
+
+
+16 Jan 2006
+  *	Added support for Tracker as a Nautilus Search Backend.
+
+
+05 Jan 2006 
+  * Added polling mechanism to watch files in absence of FAM/iNotify
+	or when FAM/iNotify watch limits have been exceeded.
+
+13 Dec 2005 
+  * Imported Tracker into GNOME CVS.
+
+
+08 Dec 2005
+  * Pre-release of Tracker launched.
+
+15 Sep 2005
+  * Tracker project started.
+
+

Added: trunk/README
==============================================================================
--- (empty file)
+++ trunk/README	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,436 @@
+Table of Contents
+
+    1 Introduction
+    2 Use Cases
+    3 Features
+    4 Minimum dependencies
+        4.1 Run-time dependencies (also needed for build)
+        4.2 Additional recommended dependencies
+        4.3 For building Tracker's Deskbar-applet backend
+        4.4 Optional run-time dependency
+    5 Compilation
+        5.1 Notes on Solaris
+        5.2 Compile Options
+    6 Running Tracker
+        6.1 Setting Inotify Watch Limit
+    7 Tracker & Nautilus Search
+    8 Tracker & Deskbar applet
+    9 Tracker tools
+
+
+
+1 Introduction
+
+  Tracker is a powerful desktop-neutral first class object
+  database, tag/metadata database, search tool and indexer.
+
+  Tracker is also extremely fast and very memory-efficient
+  when compared with some other competing frameworks and has by
+  far the fastest and most memory-efficient Nautilus search and
+  Deskbar backends currently available.
+
+  It consists of a common object database that allows entities to
+  have an almost infinte number of properties, metadata (both
+  embedded/harvested as well as user definable), a comprehensive
+  database of keywords/tags and links to other entities.
+
+  It provides additional features for file-based objects
+  including context linking and audit trails for a file object.
+
+  It has the ability to index, store, harvest metadata, retrieve
+  and search all types of files and other first class objects.
+
+  Supported first class objects include:
+
+  * Files, Documents, Music, Images, Videos, Applications, Emails,
+    Conversations, History
+
+  Planned support:
+
+  * Appointments, Contacts, Projects, Tasks, Bookmarks, Playlists,
+    Notes
+
+  All discussion related to tracker happens on the Tracker
+  mailing list
+  (http://mail.gnome.org/mailman/listinfo/tracker-list) and/or
+  IRC channel #tracker on irc.gimp.net
+
+  Bugs should be filed at http://bugzilla.gnome.org.
+
+  More infomation on Tracker can be found at
+  http://tracker-project.org.
+
+
+
+2 Use Cases
+
+  Tracker is the most powerful open source metadata database and
+  indexer framework currently available and because it is built
+  around a combination indexer and sql database and not a
+  dedicated indexer, it has much more powerful use cases:
+
+  * Provide search and indexing facilities similiar to those on
+  other systems (Windows Vista and Mac OS X).
+
+  * Common database storage for all first class objects (EG a
+  common music/photo/contacts/email/bookmarks/history database)
+  complete with additional metadata and tags/keywords.
+
+  * Comprehensive one stop solution for all applications needing
+  an object database, powerful search (via RDF Query), first class
+  methods, related metadata and user-definable metadata/tags.
+
+  * Can provide a full semantic desktop with metadata everywhere.
+
+  * Can provide powerful criteria-based searching suitable for
+  creating smart file dialogs and vfolder systems.
+
+  * Can provide a more intelligent desktop using statistical
+  metadata.
+
+
+
+3 Features
+
+  * Desktop-neutral design (it's a freedesktop product built
+  around other freedesktop technologies like D-Bus and XDGMime
+  but contains no GNOME-specific dependencies besides GLib).
+
+  * Very memory efficient and non-leaking (typical RAM usage 4 - 30
+  MB). Unlike some other indexers, Tracker is designed and built
+  to run well on systems with lower memory (256MB or less). It
+  should even be efficient enough to use on some mobile devices.
+
+  * Non-bloated and written in C for maximum efficiency.
+
+  * Small size and minimal dependencies makes it easy to bundle
+  into various distros, including live cds.
+
+  * Fast indexing and unobtrusive - no need to index stuff
+  overnight. Tracker runs at nice+10 so it should have a minimal
+  impact on your system. With the addition of detection of mouse
+  and keyboard events via tracker-applet (described below), there
+  is an option to auto-pause indexing in order to improve
+  responsiveness. This is in addition to Tracker's built-in check
+  if there's heavy disk I/O in order to auto-pause, so not to
+  slow other processes.
+
+  * Provides option to disable indexing when running on battery.
+
+  * Implements the freedesktop specification for metadata
+  (http://freedesktop.org/wiki/Standards/shared-filemetadata-spec).
+
+  * Extracts embedded File, Image, Document and Audio type
+  metadata from files.
+
+  * Supports the WC3's RDF Query syntax for querying metadata
+
+  * Provides support for both free text search (like Beagle/Google)
+  as well as structured searches using RDF Query.
+
+  * Responds in real time to file system changes to keep its
+  metadata database up to date and in sync.
+
+  * Fully extensible with custom metadata - you can store,
+  retrieve, register and search via RDF Query all your own custom
+  metadata.
+
+  * Can extract a file's contents as plain text and index them.
+
+  * Provides text filters for PDF, MS Office, OpenOffice (all
+  versions), HTML and PS files.
+
+  * Can provide thumbnailing on the fly.
+
+  * It auto-pauses indexing when running low on diskspace.
+
+
+
+4 Minimum dependencies
+
+
+4.1 Run-time dependencies (also needed for build)
+
+  * SQLite 3.4
+  * libdbus 0.60
+  * dbus-glib bindings 0.60
+  * GLib 2.14
+  * zlib
+  * intltool 0.3.5
+  * GMime
+
+
+4.2 Additional recommended dependencies
+
+  * GStreamer 0.10 + plugins for audio/video file indexing
+  * xsltproc
+  * w3m
+  * wv 1.0.2
+  * poppler (pdftotext)
+  * odt2txt 0.4 (indexing OpenOffice/ODF documents)
+  * libvorbis
+  * libpng
+  * libexif
+  * libgsf
+  * GTK and GNOME stack (for GUI tools)
+  * libglade 2.5
+  * unac (accent stripper)
+  * exempi
+  * libxml2
+
+
+4.3 For building Tracker's Deskbar-applet backend
+
+  * python-dev 2.3
+  * python-gtk2-dev 2.3
+  * deskbar-applet 2.16
+
+
+4.4 Optional run-time dependency
+
+  * xdg-utils (provides some functionality needed by
+  tracker-search-tool when in a non-GNOME environment)
+
+
+
+5 Compilation
+
+  To compile and install Tracker, use the following commands :
+
+        ./configure --prefix=/usr --sysconfdir=/etc
+        make
+        sudo make install
+
+  If you install using any other prefix, you might have problems
+  with files not being installed correctly. (You may need to copy
+  and amend the dbus service file to the correct directory and/or
+  might need to update ld_conf if you install into non-standard
+  directories.)
+
+
+5.1 Notes on Solaris
+
+  To compile Tracker with GCC on Solaris uses the following
+  commands :
+
+        ./configure --prefix=/usr --sysconfdir=/etc --with-pic \
+        CFLAGS=-D_POSIX_PTHREAD_SEMANTICS
+        make
+        sudo make install
+
+  To compile Tracker with SUN Studio on Solaris uses the
+  following commands, because there are some problems to compile
+  exiv2 using SUN C++ compiler :
+
+        ./configure --prefix=/usr --sysconfdir=/etc --with-pic \
+        --disable-warnings \
+        --disable-exiv2 CFLAGS=-D_POSIX_PTHREAD_SEMANTICS
+        make
+        sudo make install
+
+
+5.2 Compile Options
+
+  Tracker has several compiler options to enable/disable certain
+  features. The following is a (hopefully complete) listing of
+  what's available:
+
+  --enable-debug-code : build with debug symbols
+
+  --enable-video-extractor=(gstreamer, xine, external, auto)
+
+  --enable-file-monitoring=(inotify, fam, polling, auto)
+
+  --disable-preferences : disables tracker-preferences capplet
+
+  --enable-deskbar-applet=(auto, handler, module) : enables
+  Deskbar-applet support; 'auto' should do since it automatically
+  chooses whether to install the 'handler' (for Deskbar-applet
+  >=2.16) or the 'module' (for Deskbar-applet >=2.19)
+
+  --with-deskbar-applet-dir=(directory where Deskbar-applet
+  should find tracker-handler; this should be automatically
+  detected, perhaps in
+  /usr/lib/deskbar-applet/{handlers,modules-2.20-compatible})
+
+  --disable-gui : disables tracker-search-tool build
+
+  --disable-pdf : disables the PDF data extractor
+
+  --disable-exif : disables the exif data extractor
+
+  --disable-libtrackergtk : disables libtracker-gtk build
+
+  --disable-gsf : disables the GSF data extractor
+
+  --disable-warnings : disables GCC warnings
+
+  --disable-unac : disables accent stripping
+
+  --disable-libxml2 : disables HTML/XML extractors (full-text
+  will still be available)
+
+  --disable-xmp : disables XMP extraction
+
+  --with-session-bus-services-dir=(path to D-Bus services
+  directory): this should be automatically detected
+
+  --enable-external-qdbm : use system qdbm instead of one included
+  in Tracker
+
+  --disable-hal : disables HAL support for ac-power detection
+
+  --disable-trackerapplet : disables Tracker's notification applet
+
+
+
+6 Running Tracker
+
+  To run Tracker, you need to manually start the Tracker daemon,
+  trackerd. By default trackerd will index your entire home
+  directory.
+
+  You can also pass a directory root to be indexed as a command
+  line parameter if you dont want your entire home directory
+  indexed. EG "trackerd -i /home/jamie/Documents" (if you want
+  your home directory indexed when explicily specifying index
+  directory roots then you must add your home directory to the
+  arguments: EG trackerd -i /home/jamie -i /mnt/share)
+
+  You can disable indexing by passing "--no-indexing"
+
+  You can enable a low memory usage mode (recommended for
+  machines with less than 256MB of RAM) by passing
+  "--enable-low-memory"
+
+  You can artificially throttle indexing by passing
+  "--throttle=VALUE" where VALUE is in the range 0-20 (with 0,
+  the default, being fastest and 20 being slowest). Default is
+  0. You should only change this value if you want to prevent
+  noisy fans or hot laptops arising from cpu intensive indexing.
+  Tracker should have a negligible impact on the system (as it
+  is scheduled) so you can safely work with it on full throttle
+  without experiencing slow downs.
+
+  You can specify directory roots to be excluded from being
+  watched or indexed by passing "--exclude=DIRECTORY" for each
+  directory root.
+
+  You can specify logging verbosity by passing "--verbosity". Valid
+  values are from 0 to 3, ranging from least to most verbose
+  respectively.
+
+  Yet another option is "--language" which allows for specifying
+  the language to use for stemmer and stop-words list.
+
+  All the above options (and more) can be set by editing Tracker
+  config file "~/.config/tracker/tracker.cfg" which is created
+  with specific defaults when non-existent (EG when trackerd is
+  ran for the first time). Ensure that you restart trackerd for
+  the changes to take effect. "tracker.cfg" also provides options
+  that allows Tracker to only index a subset of your home
+  directory as well as other folders not in your home directory
+  by setting WatchDirectoryRoots to a semicolon-delimited list of
+  directories (full path required!)
+
+  EG:
+
+  "WatchDirectoryRoots=directory1;directory2;directory3""
+
+  An additional option is the "--reindex" option which indexes
+  user data from scratch, removing the need to remove Tracker
+  database manually.
+
+  On the first run, Tracker will automatically create a new
+  database and start populating it with metadata by browsing
+  through the user's home directory and/or the root folder(s)
+  specified.
+
+  On subsequent runs, Tracker will start up much much faster and
+  will only ever incrementally index files (IE files that have
+  changed since last index).
+
+  If installed correctly, the Tracker daemon (trackerd) can also
+  be started automatically via Dbus activation (EG by running
+  tracker-search SEARCHTERM)
+
+
+6.1 Setting Inotify Watch Limit
+
+  When watching large numbers of folders, its ppossible to exceed
+  the default number of inotify watches. In order to get real time
+  updates when this value is exceeded it is necessary to increase
+  the number of allowed watches. This can be done as follows:
+
+  1. Add this line to /etc/sysctl.conf:
+     "fs.inotify.max_user_watches = (number of folders to be
+      watched; default is 8192)"
+
+  2. Reboot the system OR (on a Debian-like system) run
+     "sudo /etc/init.d/procps restart"
+
+
+
+7 Tracker & Nautilus Search
+
+  Once you have installed Tracker and have some indexed contents,
+  you should now compile Nautilus (ver 2.13.4 or higher) which
+  should auto-detect that Tracker is installed and automatically
+  compile in Tracker support. You are now ready to appreciate a
+  powerful and super efficient C-based indexer in all its
+  glory... happy hunting!
+
+  To make sure trackerd always starts when you login to GNOME,
+  you will need to add it to gnome-session (select sessions from
+  preferences menu, select startup program tab and then add
+  /usr/bin/trackerd). For non-GNOME installations, see the
+  desktop docs for how to achieve similar.
+
+
+
+8 Tracker & Deskbar applet
+
+  Tracker is also integrated in GNOME's deskbar applet. See
+  Compile Options above on how to get it built.
+
+
+
+9 Tracker tools
+
+  Tracker comes with a number of utilities that you can use:
+
+  * "tracker-applet" - notification applet which has various
+  utilities like displaying Tracker status, indexing progress,
+  and live statistics, pausing indexing, and launching both
+  tracker-search-tool and tracker-preferences
+
+  * "tracker-extract FILE" - this extracts embedded metadata from
+  FILE and prints to stdout
+
+  * "tracker-files" - returns files filtered by the mime type or
+  their category (see the manpage)
+
+  * "tracker-meta-folder" - return list of files indexed by Tracker
+  for a folder
+
+  * "tracker-preferences" - GUI tool to set Tracker preferences
+
+  * "tracker-query" - this reads an RDF Query that specifies the
+  search criteria for various fields. It prints to STDOUT all
+  matching files. You can see some example queries in the
+  RDF-Query-examples folder. You can run the examples as
+  "tracker-query < RDFFILE"
+
+  * "tracker-search SEARCHTERM" - this perfoms a google like search
+  using SEARCHTERM to retrieve all matching files where
+  SEARCHTERM appears in any searchable metadata
+
+  * "tracker-search-tool SEARCHTERM" - GUI search utility
+
+  * "tracker-stats" - this displays the current number of indexed
+  items by category
+
+  * "tracker-status" - queries status of trackerd
+
+  * "tracker-tag" - used for setting and searching tags/keywords
+

Added: trunk/TODO
==============================================================================
--- (empty file)
+++ trunk/TODO	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,11 @@
+* support for ignoring diacritics in search (version 0.6.1)
+* support for Thunderbird 2 after it's released
+* .desktop file indexing
+* chat logs
+* Rhythmbox integration
+* Tomboy notes indexing
+* Epiphany integration
+* Brasero integration
+* manpage indexing
+* DocBook indexing
+* search filtering (EG search within specific folders)

Added: trunk/acinclude.m4
==============================================================================
--- (empty file)
+++ trunk/acinclude.m4	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,92 @@
+dnl #########################################################################
+AC_DEFUN([AX_COMPARE_VERSION], [
+  # Used to indicate true or false condition
+  ax_compare_version=false
+
+  # Convert the two version strings to be compared into a format that
+  # allows a simple string comparison.  The end result is that a version
+  # string of the form 1.12.5-r617 will be converted to the form
+  # 0001001200050617.  In other words, each number is zero padded to four
+  # digits, and non digits are removed.
+  AS_VAR_PUSHDEF([A],[ax_compare_version_A])
+  A=`echo "$1" | sed -e 's/\([[0-9]]*\)/Z\1Z/g' \
+                     -e 's/Z\([[0-9]]\)Z/Z0\1Z/g' \
+                     -e 's/Z\([[0-9]][[0-9]]\)Z/Z0\1Z/g' \
+                     -e 's/Z\([[0-9]][[0-9]][[0-9]]\)Z/Z0\1Z/g' \
+                     -e 's/[[^0-9]]//g'`
+
+  AS_VAR_PUSHDEF([B],[ax_compare_version_B])
+  B=`echo "$3" | sed -e 's/\([[0-9]]*\)/Z\1Z/g' \
+                     -e 's/Z\([[0-9]]\)Z/Z0\1Z/g' \
+                     -e 's/Z\([[0-9]][[0-9]]\)Z/Z0\1Z/g' \
+                     -e 's/Z\([[0-9]][[0-9]][[0-9]]\)Z/Z0\1Z/g' \
+                     -e 's/[[^0-9]]//g'`
+
+  dnl # In the case of le, ge, lt, and gt, the strings are sorted as necessary
+  dnl # then the first line is used to determine if the condition is true.
+  dnl # The sed right after the echo is to remove any indented white space.
+  m4_case(m4_tolower($2),
+  [lt],[
+    ax_compare_version=`echo "x$A
+x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/false/;s/x${B}/true/;1q"`
+  ],
+  [gt],[
+    ax_compare_version=`echo "x$A
+x$B" | sed 's/^ *//' | sort | sed "s/x${A}/false/;s/x${B}/true/;1q"`
+  ],
+  [le],[
+    ax_compare_version=`echo "x$A
+x$B" | sed 's/^ *//' | sort | sed "s/x${A}/true/;s/x${B}/false/;1q"`
+  ],
+  [ge],[
+    ax_compare_version=`echo "x$A
+x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/true/;s/x${B}/false/;1q"`
+  ],[
+    dnl Split the operator from the subversion count if present.
+    m4_bmatch(m4_substr($2,2),
+    [0],[
+      # A count of zero means use the length of the shorter version.
+      # Determine the number of characters in A and B.
+      ax_compare_version_len_A=`echo "$A" | awk '{print(length)}'`
+      ax_compare_version_len_B=`echo "$B" | awk '{print(length)}'`
+
+      # Set A to no more than B's length and B to no more than A's length.
+      A=`echo "$A" | sed "s/\(.\{$ax_compare_version_len_B\}\).*/\1/"`
+      B=`echo "$B" | sed "s/\(.\{$ax_compare_version_len_A\}\).*/\1/"`
+    ],
+    [[0-9]+],[
+      # A count greater than zero means use only that many subversions
+      A=`echo "$A" | sed "s/\(\([[0-9]]\{4\}\)\{m4_substr($2,2)\}\).*/\1/"`
+      B=`echo "$B" | sed "s/\(\([[0-9]]\{4\}\)\{m4_substr($2,2)\}\).*/\1/"`
+    ],
+    [.+],[
+      AC_WARNING(
+        [illegal OP numeric parameter: $2])
+    ],[])
+
+    # Pad zeros at end of numbers to make same length.
+    ax_compare_version_tmp_A="$A`echo $B | sed 's/./0/g'`"
+    B="$B`echo $A | sed 's/./0/g'`"
+    A="$ax_compare_version_tmp_A"
+
+    # Check for equality or inequality as necessary.
+    m4_case(m4_tolower(m4_substr($2,0,2)),
+    [eq],[
+      test "x$A" = "x$B" && ax_compare_version=true
+    ],
+    [ne],[
+      test "x$A" != "x$B" && ax_compare_version=true
+    ],[
+      AC_WARNING([illegal OP parameter: $2])
+    ])
+  ])
+
+  AS_VAR_POPDEF([A])dnl
+  AS_VAR_POPDEF([B])dnl
+
+  dnl # Execute ACTION-IF-TRUE / ACTION-IF-FALSE.
+  if test "$ax_compare_version" = "true" ; then
+    m4_ifvaln([$4],[$4],[:])dnl
+    m4_ifvaln([$5],[else $5])dnl
+  fi
+]) dnl AX_COMPARE_VERSION

Added: trunk/autogen.sh
==============================================================================
--- (empty file)
+++ trunk/autogen.sh	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,21 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+PKG_NAME="tracker"
+REQUIRED_AUTOMAKE_VERSION=1.9
+
+(test -f $srcdir/configure.ac \
+  && test -f $srcdir/README) || {
+    echo -n "**Error**: Directory "\`$srcdir\'" does not look like the"
+    echo " top-level $PKG_NAME directory"
+    exit 1
+}
+
+which gnome-autogen.sh || {
+    echo "You need to install gnome-common from the GNOME CVS"
+    exit 1
+}
+. gnome-autogen.sh

Added: trunk/configure.ac
==============================================================================
--- (empty file)
+++ trunk/configure.ac	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,1133 @@
+# -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
+# This file is part of Tracker.
+
+AC_PREREQ(2.59)
+AC_INIT([tracker],[0.6.6],[tracker-list gnome org])
+
+AC_CONFIG_SRCDIR([src/trackerd/tracker-main.c])
+AM_INIT_AUTOMAKE([dist-bzip2])
+
+AC_SUBST(PACKAGE_URL, [http://www.tracker-project.org])
+
+# Checks for programs.
+AC_PROG_CC
+AC_PROG_LN_S
+AC_PROG_INSTALL
+AC_PROG_MAKE_SET
+AC_PROG_LIBTOOL
+
+CFLAGS="$CFLAGS"
+
+# Checks for header files.
+AC_HEADER_STDC
+AC_CHECK_HEADERS([fcntl.h sitdlib.h string.h sys/time.h unistd.h linux/unistd.h])
+
+AC_CHECK_HEADER([zlib.h], 
+		[], 
+		[AC_MSG_ERROR([You must have zlib.h and zlib installed])])
+
+# Can posix_fadvise be used
+AC_CHECK_DECLS(posix_fadvise, [], [], [
+#define _XOPEN_SOURCE 600
+#include <fcntl.h>])
+
+# Checks for functions
+AC_CHECK_FUNCS([posix_fadvise])
+AC_CHECK_FUNCS([getline])
+
+# Library Checks
+DBUS_REQUIRED=0.60
+GLIB_REQUIRED=2.16.0
+PANGO_REQUIRED=1.0.0
+GMIME_REQUIRED=2.1.0
+
+PKG_CHECK_MODULES(GLIB2, [glib-2.0 >= $GLIB_REQUIRED])
+AC_SUBST(GLIB2_CFLAGS)
+AC_SUBST(GLIB2_LIBS)
+
+# Check for gthread 2.0
+PKG_CHECK_MODULES(GTHREAD, [ gthread-2.0 >= $GLIB_REQUIRED])
+AC_SUBST(GTHREAD_CFLAGS)
+AC_SUBST(GTHREAD_LIBS)
+
+# Check for gobject 2.0
+PKG_CHECK_MODULES(GOBJECT, [gobject-2.0 >= $GLIB_REQUIRED])
+AC_SUBST(GOBJECT_CFLAGS)
+AC_SUBST(GOBJECT_LIBS)
+
+# Check for gmodule 2.0
+PKG_CHECK_MODULES(GMODULE, [gmodule-2.0 >= $GLIB_REQUIRED])
+AC_SUBST(GMODULE_CFLAGS)
+AC_SUBST(GMODULE_LIBS)
+
+PKG_CHECK_MODULES(GIO, [gio-2.0 >= $GLIB_REQUIRED])
+AC_SUBST(GIO_CFLAGS)
+AC_SUBST(GIO_LIBS)
+
+# Check for pango
+PKG_CHECK_MODULES(PANGO, [pango >= $PANGO_REQUIRED])
+AC_SUBST(PANGO_CFLAGS)
+AC_SUBST(PANGO_LIBS)
+
+# Check for GMime
+PKG_CHECK_MODULES(GMIME, [gmime-2.0 >= $GMIME_REQUIRED])
+AC_SUBST(GMIME_CFLAGS)
+AC_SUBST(GMIME_LIBS)
+
+# Check for Dbus 0.50 or higher
+PKG_CHECK_MODULES(DBUS, [dbus-1 >= $DBUS_REQUIRED dbus-glib-1 >= $DBUS_REQUIRED])
+AC_SUBST(DBUS_CFLAGS)
+AC_SUBST(DBUS_LIBS)
+
+# Check for libpng 1.2 or higher
+PKG_CHECK_MODULES(LIBPNG, [libpng >= 1.2])
+AC_SUBST(LIBPNG_CFLAGS)
+AC_SUBST(LIBPNG_LIBS)
+
+# Check for GConf
+PKG_CHECK_MODULES(GCONF, [gconf-2.0 >= 2.2.0], have_gconf=yes, have_gconf=no)
+AC_SUBST(GCONF_CFLAGS)
+AC_SUBST(GCONF_LIBS)
+
+AM_CONDITIONAL(HAVE_GCONF, test "$have_gconf" = "yes")
+
+# Check we have the DBUS binding tool we need
+AC_PATH_PROG(DBUSBINDINGTOOL, dbus-binding-tool)
+if test -z $DBUSBINDINGTOOL; then
+   AC_MSG_ERROR([Could not find 'dbus-binding-tool'])
+fi
+
+GLIB_GENMARSHAL=`$PKG_CONFIG glib-2.0 --variable=glib_genmarshal`
+AC_SUBST(GLIB_GENMARSHAL)
+
+####################################################################
+# DBus Service
+####################################################################
+
+dnl DBus services dir
+AC_ARG_WITH([session_bus_services_dir],
+            AS_HELP_STRING([--with-session-bus-services-dir], 
+	                   [Path to DBus services directory]))
+
+if test "x$with_session_bus_services_dir" = "x" ; then
+   services_dir="`pkg-config --variable session_bus_services_dir dbus-1`"
+else
+   services_dir="$with_session_bus_services_dir"
+fi
+
+DBUS_SERVICES_DIR="$services_dir"
+AC_SUBST(DBUS_SERVICES_DIR)
+
+
+####################################################################
+# Deskbar Applet Handler/Module
+####################################################################
+
+AC_ARG_ENABLE([deskbar_applet],
+              AS_HELP_STRING([--enable-deskbar-applet=ARG], 
+	      		     [Enable support for deskbar applet (handler, module, auto)]),,
+              [enable_deskbar_applet=auto])
+
+if test "x$enable_deskbar_applet" = "xauto" ; then
+   if pkg-config --atleast-version=2.19 deskbar-applet ; then
+      enable_deskbar_applet="module"
+   elif pkg-config --atleast-version=2.16 deskbar-applet ; then
+      enable_deskbar_applet="handler"
+   else
+      enable_deskbar_applet="no"
+   fi
+fi
+
+AM_CONDITIONAL(USING_DESKBAR_HANDLER, test "x$enable_deskbar_applet" = "xhandler")
+AM_CONDITIONAL(USING_DESKBAR_MODULE, test "x$enable_deskbar_applet" = "xmodule")
+
+AC_ARG_WITH([deskbar_applet_dir],
+            AS_HELP_STRING([--with-deskbar-applet-dir], [Path to Deskbar handler/module directory]))
+
+if test "x$enable_deskbar_applet" = "xhandler" ; then
+   if test "x$with_deskbar_applet_dir" = "x" ; then
+      handler_dir="`pkg-config --variable handlersdir deskbar-applet`"
+   else
+      handler_dir="$with_deskbar_applet_dir"
+   fi
+
+   if test "x$handler_dir" = "x" ; then
+      handler_dir="\$(exec_prefix)/lib/deskbar-applet/handlers"
+   fi
+fi
+
+DESKBAR_HANDLER_DIR="$handler_dir"
+AC_SUBST(DESKBAR_HANDLER_DIR)
+
+if test "x$enable_deskbar_applet" = "xmodule" ; then
+   if test "x$with_deskbar_applet_dir" = "x" ; then
+       module_dir="`pkg-config --variable modulesdir deskbar-applet`"
+   else
+       module_dir="$with_deskbar_applet_dir"
+   fi
+
+   if test "x$module_dir" = "x" ; then
+      module_dir="\$(exec_prefix)/lib/deskbar-applet/modules-2.20-compatible"
+   fi
+fi
+
+DESKBAR_MODULE_DIR="$module_dir"
+AC_SUBST(DESKBAR_MODULE_DIR)
+
+####################################################################
+# Compiler warning checks
+####################################################################
+
+AC_ARG_ENABLE(warnings,
+              AS_HELP_STRING([--disable-warnings], [disable GCC warnings]),,
+              [enable_warnings=yes])
+
+# Only use -Wall if we have gcc
+if test "x$GCC" = "xyes"; then
+   if test "x$enable_warnings" = "xyes"; then
+      CFLAGS="\
+	-Wall \
+	-Wunused \
+	-Wchar-subscripts \
+	-Wmissing-declarations \
+	-Wmissing-prototypes \
+	-Wnested-externs \
+	-Wpointer-arith \
+	-Wsign-compare \
+	$CFLAGS"
+   fi
+fi
+
+####################################################################
+# gettext/intltool support
+####################################################################
+
+GETTEXT_PACKAGE=AC_PACKAGE_NAME
+AC_SUBST(GETTEXT_PACKAGE)
+AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE,"$GETTEXT_PACKAGE", [The gettext translation domain])
+
+AM_GLIB_GNU_GETTEXT
+IT_PROG_INTLTOOL([0.35.0])
+
+####################################################################
+# Check if glib includes GTests framework
+#
+# If glib is installed in a non-standard location (like /opt/xxx):
+# * PKG_CONFIG_PATH must be set correctly
+# * glib binaries must be in the PATH (i.e PATH=$PATH:/opt/xxx/bin)
+####################################################################
+
+GLIB_WITH_UNIT_TESTING=2.15.0
+
+have_xesam_glib=no
+
+AC_ARG_ENABLE(unit_tests,
+	      AS_HELP_STRING([--enable-unit-tests=@<:@no/yes/auto@:>@],
+			     [Enable unit tests (if available)]), ,
+	      [enable_unit_tests=auto])
+
+if test "x$enable_unit_tests" != "xno" ; then
+   glib_pkgconfig_tmp="glib-2.0 >= $GLIB_WITH_UNIT_TESTING"
+   PKG_CHECK_MODULES(GLIB_UNIT_TEST, 
+                     $glib_pkgconfig_tmp,  
+                     [have_unit_tests=yes], 
+                     [have_unit_tests=no])
+
+   if test "x$have_unit_tests" = "xyes" ; then
+      AC_DEFINE(HAVE_UNIT_TEST, 1, [Unit test framework available in glib])
+
+      AC_PATH_PROG(sqlite_exec, sqlite3)
+      if test -z $sqlite_exec; then
+         AC_MSG_ERROR([Could not find 'sqlite3'])
+      fi
+
+      glib_pkgconfig=$glib_pkgconfig_tmp
+      AC_PATH_PROG(GTESTER, [gtester], [no] )
+      if test "x$GTESTER" = "xno" ; then
+      	 AC_MSG_ERROR([*** Gtester is not in the path])
+      else
+	PKG_CHECK_MODULES(XESAM_GLIB, 
+		xesam-glib, [have_xesam_glib=yes], [have_xesam_glib=no])
+      fi
+   fi
+else
+   have_unit_tests=no
+   glib_pkgconfig=
+fi
+
+if test "x$enable_unit_tests" = "xyes"; then
+   if test "x$have_unit_tests" != "xyes"; then
+      AC_MSG_ERROR([Couldn't find unit test compatible glib.])
+   fi
+fi
+
+AM_CONDITIONAL(HAVE_UNIT_TESTS, test "x$have_unit_tests" = "xyes")
+AM_CONDITIONAL(HAVE_XESAM_GLIB, test "x$have_xesam_glib" = "xyes")
+
+####################################################################
+# External QDBM check
+####################################################################
+
+QDBM_REQUIRED=1.8
+
+AC_ARG_ENABLE(external_qdbm, 
+	      AS_HELP_STRING([--enable-external-qdbm],
+			     [build using system's qdbm]),,
+              [enable_external_qdbm=no])
+
+if test "x$enable_external_qdbm" = "xyes"; then
+   PKG_CHECK_MODULES(QDBM, [ qdbm >= $QDBM_REQUIRED ])
+   AM_CONDITIONAL(USING_EXTERNAL_QDBM, true)
+else
+   QDBM_CFLAGS="-I\$(top_srcdir)/src/qdbm"
+   QDBM_LIBS="\$(top_builddir)/src/qdbm/libqdbm-private.la"
+   AM_CONDITIONAL(USING_EXTERNAL_QDBM, false)
+fi
+
+AC_SUBST(QDBM_CFLAGS)
+AC_SUBST(QDBM_LIBS)
+
+##################################################################
+# Check for libxml2
+##################################################################
+
+LIBXML2_REQUIRED=0.6
+
+AC_ARG_ENABLE(libxml2, 
+	      AS_HELP_STRING([--disable-libxml2],
+			     [Disable HTML/XML extractors (full-text will still be available)]),,
+	      [enable_libxml2=yes])
+
+if test "x$enable_libxml2" = "xyes"; then
+   PKG_CHECK_MODULES(LIBXML2, 
+	 	    [libxml-2.0 >= $LIBXML2_REQUIRED],
+		    [have_libxml2=yes],
+		    [have_libxml2=no])
+   AC_SUBST(LIBXML2_CFLAGS)
+   AC_SUBST(LIBXML2_LIBS)
+else
+   have_libxml2="no (disabled)"
+fi
+
+if test "x$have_libxml2" = "xyes"; then
+   AC_DEFINE(HAVE_LIBXML2, [], [Define if we have libxml2])
+fi
+
+AM_CONDITIONAL(HAVE_LIBXML2, test "x$have_libxml2" = "xyes")
+
+##################################################################
+# Check for HAL
+##################################################################
+
+AC_ARG_ENABLE(hal, 
+	      AS_HELP_STRING([--disable-hal],
+			     [Disable HAL support for ac power detection]),,
+	      [enable_hal=yes])
+
+if test "x$enable_hal" = "xyes"; then
+   PKG_CHECK_MODULES(HAL, 
+    	  	     [hal >= 0.5 hal-storage], 
+		     [have_hal=yes] , 
+		     [have_hal=no])
+   AC_SUBST(HAL_CFLAGS)
+   AC_SUBST(HAL_LIBS)
+else
+   have_hal="no (disabled)"
+fi
+
+if test "x$have_hal" = "xyes"; then
+   AC_DEFINE(HAVE_HAL, [], [Define if we have HAL])
+fi
+
+AM_CONDITIONAL(HAVE_HAL, test "x$have_hal" = "xyes")
+
+####################################################################
+# check for GStreamer or Xine. Otherwise, call an external video
+# player (Totem or MPlayer).
+####################################################################
+
+AC_ARG_ENABLE(video-extractor,
+	      AS_HELP_STRING([--enable-video-extractor=ARG],
+	                     [enables one of the (gstreamer, xine, external, auto) video extractor backends]),,
+              [enable_video_extractor=auto])
+
+PKG_CHECK_MODULES(GSTREAMER,
+		  [gstreamer-0.10 >= 0.10.0],
+		  [have_libgstreamer=yes],
+		  [have_libgstreamer=no])
+
+AC_SUBST(GSTREAMER_CFLAGS)
+AC_SUBST(GSTREAMER_LIBS)
+
+PKG_CHECK_MODULES(XINE,
+		  [libxine >= 1.0],
+		  [have_libxine=yes],
+		  [have_libxine=no])
+
+AC_SUBST(XINE_CFLAGS)
+AC_SUBST(XINE_LIBS)
+
+if test "x$enable_video_extractor" = "xauto"; then
+   if test "$have_libgstreamer" = "yes"; then
+      videos_handler="GStreamer"
+      videos_are_handled="yes"
+   elif test "$have_libxine" = "yes"; then
+      videos_handler="Xine"
+      videos_are_handled="yes"
+   else
+      videos_are_handled="?"
+      videos_handler="An external video player will be called"
+   fi
+elif test "x$enable_video_extractor" = "xgstreamer"; then
+   if test "$have_libgstreamer" = "yes"; then
+      videos_handler="GStreamer"
+      videos_are_handled="yes"
+   else
+      AC_MSG_ERROR([***Gstreamer requested but not found - exiting!])
+   fi
+elif test "x$enable_video_extractor" = "xxine"; then
+   if test "$have_libxine" = "yes"; then
+      videos_handler="Xine"
+      videos_are_handled="yes"
+   else
+      AC_MSG_ERROR([***libxine requested but not found - exiting!])
+   fi
+else
+   videos_are_handled="?"
+   videos_handler="An external video player will be called"
+fi
+
+if test "$videos_handler" = "GStreamer"; then
+   AC_DEFINE(HAVE_GSTREAMER, [], [Define if we have GStreamer])
+   AM_CONDITIONAL(HAVE_GSTREAMER, true)
+   AM_CONDITIONAL(HAVE_LIBXINE, false)
+   AM_CONDITIONAL(USING_EXTERNAL_VIDEO_PLAYER, false)
+elif test "$videos_handler" = "Xine"; then
+   AC_DEFINE(HAVE_LIBXINE, [], [Define if we have Libxine])
+   AM_CONDITIONAL(HAVE_LIBXINE, true)
+   AM_CONDITIONAL(HAVE_GSTREAMER, false)
+   AM_CONDITIONAL(USING_EXTERNAL_VIDEO_PLAYER, false)
+else
+   AC_DEFINE(USING_EXTERNAL_VIDEO_PLAYER, [], [Define that Tracker will try to use external video players])
+   AM_CONDITIONAL(USING_EXTERNAL_VIDEO_PLAYER, true)
+   AM_CONDITIONAL(HAVE_GSTREAMER, false)
+   AM_CONDITIONAL(HAVE_LIBXINE, false)
+fi
+
+####################################################################
+# Windows check
+####################################################################
+
+AC_MSG_CHECKING(for WIN32)
+AC_TRY_COMPILE(,
+	[
+	#ifndef WIN32
+	#error
+	#endif
+	],
+	native_win32=yes; AC_MSG_RESULT(yes),
+	native_win32=no; AC_MSG_RESULT(no),
+)
+
+AM_CONDITIONAL(OS_WIN32, test "$native_win32" = "yes")
+
+if test "$native_win32" = "yes" ; then
+   AC_DEFINE(OS_WIN32, 1, [Define if we are on win32])
+fi
+
+####################################################################
+#  libinotify
+####################################################################
+
+AC_MSG_CHECKING(for inotify)
+AC_TRY_COMPILE(,
+	[
+        #include <sys/inotify.h>
+
+        inotify_init(void);
+	],
+	have_inotify=no; AC_MSG_RESULT(no),
+	have_inotify=yes; AC_MSG_RESULT(yes),
+)
+
+AM_CONDITIONAL(HAVE_INOTIFY, test "$have_inotify" = "yes")
+
+if test "$have_inotify" = "yes" ; then
+   AC_DEFINE(HAVE_INOTIFY, 1, [Define if we have inotify])
+fi
+
+####################################################################
+#  SQLite check
+####################################################################
+
+SQLITE_REQUIRED=3.4
+
+PKG_CHECK_MODULES(SQLITE3, [sqlite3 >= $SQLITE_REQUIRED])
+
+AC_SUBST(SQLITE3_CFLAGS)
+AC_SUBST(SQLITE3_LIBS)
+
+##################################################################
+# Enable SQLite FTS support?
+##################################################################
+
+AC_ARG_ENABLE([sqlite_fts],
+              AS_HELP_STRING([--enable-sqlite-fts], [Enable SQLite FTS support]),,
+              [enable_sqlite_fts=no])
+
+if test "x$enable_sqlite_fts" == "xno" ; then
+   enable_sqlite_fts="no (disabled)"
+fi
+
+AM_CONDITIONAL(ENABLE_SQLITE_FTS, test "$enable_sqlite_fts" = "yes")
+
+##################################################################
+# Enable UNAC support?
+##################################################################
+
+AC_ARG_ENABLE([unac], 
+              AS_HELP_STRING([--disable-unac], [Disable unac accent stripper support]),,
+	      [enable_unac=yes])
+
+if test "x$enable_unac" != "xno" ; then
+   PKG_CHECK_MODULES(UNAC, 
+		     [unac >= 1.0.0],
+		     [enable_unac=yes],
+		     [enable_unac=no])
+
+   AC_SUBST([UNAC_CFLAGS])
+   AC_SUBST([UNAC_LIBS])
+else
+   enable_unac="no (disabled)"
+fi
+
+if test "$enable_unac" = "yes"; then
+   AC_DEFINE(HAVE_UNAC, [], [Define if we have unac lib])
+fi
+
+AM_CONDITIONAL(ENABLE_UNAC, test "$enable_unac" = "yes")
+
+##################################################################
+# Enable building libtracker-gtk?
+##################################################################
+
+LIBTRACKERGTK_GTK_REQUIRED=2.8.20
+
+AC_ARG_ENABLE([libtrackergtk], 
+              AS_HELP_STRING([--disable-libtrackergtk], [Disable libtrackergtk]),,
+	      [enable_libtrackergtk=yes])
+
+if test "x$enable_libtrackergtk" != "xno" ; then
+   libtrackergtk_modules="\
+        	glib-2.0    >=  $GLIB_REQUIRED, \
+        	gtk+-2.0    >=  $LIBTRACKERGTK_GTK_REQUIRED, \
+        	dbus-1      >=  $DBUS_REQUIRED, \
+        	dbus-glib-1 >=  $DBUS_REQUIRED"
+
+   PKG_CHECK_MODULES(LIBTRACKERGTK, 
+		     [$libtrackergtk_modules],
+		     [enable_libtrackergtk=yes], 
+		     [enable_libtrackergtk=no])
+
+   AC_SUBST([LIBTRACKERGTK_CFLAGS])
+   AC_SUBST([LIBTRACKERGTK_LIBS])
+else
+   enable_libtrackergtk="no (disabled)"
+fi
+
+AM_CONDITIONAL(ENABLE_LIBTRACKERGTK, test "$enable_libtrackergtk" = "yes")
+
+##################################################################
+# Enable building tracker-applet notification icon?
+##################################################################
+
+LIBNOTIFY_GTK2_REQUIRED=2.10
+LIBNOTIFY_GLIB_REQUIRED=2.10
+LIBNOTIFY_REQUIRED=0.4.3
+
+AC_ARG_ENABLE([trackerapplet], 
+	      AS_HELP_STRING([--disable-trackerapplet], [Disable trackerapplet]),,
+	      [enable_trackerapplet=yes])
+
+if test "x$enable_trackerapplet" != "xno" ; then
+   trackerapplet_modules="\
+        	glib-2.0    >=  $LIBNOTIFY_GLIB_REQUIRED, \
+        	gtk+-2.0    >=  $LIBNOTIFY_GTK2_REQUIRED, \
+        	dbus-1      >=  $DBUS_REQUIRED, \
+        	dbus-glib-1 >=  $DBUS_REQUIRED, \
+		libnotify   >=  $LIBNOTIFY_REQUIRED"
+
+   PKG_CHECK_MODULES(TRACKERAPPLET, 
+   		     [$trackerapplet_modules],
+		     [enable_trackerapplet=yes], 
+   		     [enable_trackerapplet=no])
+
+   AC_SUBST([TRACKERAPPLET_CFLAGS])
+   AC_SUBST([TRACKERAPPLET_LIBS])
+else
+   enable_trackerapplet="no (disabled)"
+fi
+
+AM_CONDITIONAL(ENABLE_TRACKERAPPLET, test "$enable_trackerapplet" = "yes")
+
+##################################################################
+# Check for GNOME/GTK dependencies to build tracker search tool
+##################################################################
+
+GTK_REQUIRED=2.8.0
+LIBGNOME_DESKTOP_REQUIRED=2.9.91
+LIBGNOME_REQUIRED=2.13.2
+LIBGNOMEUI_REQUIRED=2.13.7
+GNOMEVFS_REQUIRED=2.8.4
+
+AC_ARG_ENABLE(gui, 
+              AS_HELP_STRING([--disable-gui], [Disable building of the tracker-search-tool]),,
+	      [enable_gui=yes])
+
+if test "x$enable_gui" = "xyes"; then
+   PKG_CHECK_MODULES(GNOME_UTILS, 
+		     [gtk+-2.0 >= $GTK_REQUIRED
+	              libgnome-2.0 >= $LIBGNOME_REQUIRED
+		      libgnomeui-2.0 >= $LIBGNOMEUI_REQUIRED],
+	             [have_gnome=yes], 
+		     [have_gnome=no])
+   if test "$have_gnome" = "yes"; then
+       PKG_CHECK_MODULES(GNOMEVFS, 
+			 [gnome-vfs-2.0 >= $GNOMEVFS_REQUIRED
+			  gnome-vfs-module-2.0 >= $GNOMEVFS_REQUIRED],
+		         [have_gnome=yes], 
+			 [have_gnome=no])
+   fi
+
+   if test "$have_gnome" = "yes"; then
+      PKG_CHECK_MODULES(GNOMEDESKTOP, 
+			[gnome-desktop-2.0 >= $LIBGNOME_DESKTOP_REQUIRED],
+        		[have_gnome=yes], 
+			[have_gnome=no])
+   fi
+
+   AC_SUBST(GNOME_UTILS_CFLAGS)
+   AC_SUBST(GNOME_UTILS_LIBS)
+
+   AC_SUBST(GNOMEVFS_CFLAGS)
+   AC_SUBST(GNOMEVFS_LIBS)
+
+   AC_SUBST(GNOMEDESKTOP_CFLAGS)
+   AC_SUBST(GNOMEDESKTOP_LIBS)
+else
+   have_gnome="no (disabled)"
+fi
+
+# do not build if libtracker-gtk is not being built
+if test "x$enable_libtrackergtk" != "xyes"; then
+   have_gnome="no (disabled as libtracker-gtk is not being built)"
+fi
+
+AM_CONDITIONAL(HAVE_GNOME, test "$have_gnome" = "yes")
+if test "$have_gnome" = "yes"; then
+   GNOME_COMMON_INIT
+fi
+
+##################################################################
+# Checks for tracker-preferences
+##################################################################
+
+PREFERENCES_GLADE_REQUIRED=2.5
+PREFERENCES_GTK_REQUIRED=2.8.0
+
+AC_ARG_ENABLE([preferences],
+              AS_HELP_STRING([--disable-preferences], [Disable the preferences dialog]),,
+              [enable_preferences=yes])
+
+if test "x$enable_preferences" != "xno" ; then
+   PKG_CHECK_MODULES(GLIB2, 
+   	             [glib-2.0 >= $PREFERENCES_GLIB_REQUIRED],
+		     [enable_preference=yes], 
+		     [enable_preference=no])
+
+   AC_SUBST([GLIB2_CFLAGS])
+   AC_SUBST([GLIB2_LIBS])
+
+   if test "$enable_preferences" = "yes"; then
+      PKG_CHECK_MODULES(GTK2, 
+      			[gtk+-2.0 >= $PREFERENCES_GTK_REQUIRED],
+			[enable_preference=yes], 
+			[enable_preference=no])
+
+      AC_SUBST([GTK2_CFLAGS])
+      AC_SUBST([GTK2_LIBS])
+   fi
+
+   if test "$enable_preferences" = "yes"; then
+      PKG_CHECK_MODULES(LIBGLADE,  
+      		        [libglade-2.0 >= $PREFERENCES_GLADE_REQUIRED], 
+			[enable_preferences=yes], 
+			[enable_preferences=no])
+
+      AC_SUBST([LIBGLADE_CFLAGS])
+      AC_SUBST([LIBGLADE_LIBS])
+   fi
+else
+   enable_preferences="no (disabled)"
+fi
+
+AM_CONDITIONAL(ENABLE_PREFERENCES, test "$enable_preferences" = "yes")
+
+##################################################################
+# Check for poppler's glib bingings
+##################################################################
+
+POPPLER_GLIB_REQUIRED=0.4.5
+CAIRO_REQUIRED=1.0
+GDK_REQUIRED=1.0
+
+AC_ARG_ENABLE(pdf, 
+	      AS_HELP_STRING([--disable-pdf], [Disable PDF data extractor]),,
+	      [enable_pdf=yes])
+
+if test "x$enable_pdf" = "xyes"; then
+   PKG_CHECK_MODULES(POPPLER_GLIB, 
+   		     [poppler-glib >= $POPPLER_GLIB_REQUIRED
+		      cairo >= $CAIRO_REQUIRED
+		      gdk-2.0 >= $GDK_REQUIRED],
+		     [have_poppler=yes],
+		     [have_poppler=no])
+
+   AC_SUBST(POPPLER_GLIB_CFLAGS)
+   AC_SUBST(POPPLER_GLIB_LIBS)
+else
+   have_poppler="no (disabled)"
+fi
+
+AM_CONDITIONAL(HAVE_POPPLER_GLIB, test "$have_poppler" = "yes")
+
+if test "$have_poppler" = "yes" ; then
+   AC_DEFINE(HAVE_POPPLER, 1, [Define if we have poppler])
+fi
+
+##################################################################
+# Check for libexif
+##################################################################
+
+LIBEXIF_REQUIRED=0.6
+
+AC_ARG_ENABLE(exif, 
+              AS_HELP_STRING([--disable-exif],[Disable exif data extractor]),,
+	      [enable_exif=yes])
+
+if test "x$enable_exif" = "xyes"; then
+   PKG_CHECK_MODULES(LIBEXIF, 
+	             [libexif >= $LIBEXIF_REQUIRED],
+		     [have_libexif=yes],
+		     [have_libexif=no])
+
+   AC_SUBST(LIBEXIF_CFLAGS)
+   AC_SUBST(LIBEXIF_LIBS)
+else
+   have_libexif="no (disabled)"
+fi
+
+AM_CONDITIONAL(HAVE_LIBEXIF, test "$have_libexif" = "yes")
+
+if test "$have_libexif" = "yes" ; then
+   AC_DEFINE(HAVE_LIBEXIF, 1, [Define if we have libexif])
+fi
+
+##################################################################
+# Check for libgsf
+##################################################################
+
+LIBGSF_REQUIRED=1.13
+
+AC_ARG_ENABLE(gsf, 
+              AS_HELP_STRING([--disable-gsf], [Disable GSF data extractor]),,
+	      [enable_gsf=yes])
+
+if test "x$enable_gsf" = "xyes"; then
+   PKG_CHECK_MODULES(LIBGSF,
+                     [libgsf-1 >= $LIBGSF_REQUIRED],
+		     [have_libgsf=yes],
+		     [have_libgsf=no])
+
+   AC_SUBST(LIBGSF_CFLAGS)
+   AC_SUBST(LIBGSF_LIBS)
+else
+   have_libgsf="no (disabled)"
+fi
+
+AM_CONDITIONAL(HAVE_LIBGSF, test "$have_libgsf" = "yes")
+
+if test "$have_libgsf" = "yes" ; then
+   AC_DEFINE(HAVE_LIBGSF, 1, [Define if we have libgsf])
+fi
+
+##################################################################
+# Check for libjpeg
+##################################################################
+
+# FIXME This should be package based. Unfortunately in several main
+# distros, it is not.
+
+OLD_CFLAGS="$CFLAGS"
+OLD_LIBS="$LIBS"
+CFLAGS=""
+LIBS=""
+
+AC_ARG_ENABLE(jpeg, 
+	      AS_HELP_STRING([--disable-jpeg], [Disable jpeg extractor]),,
+	      [enable_jpeg=yes])
+
+if test "x$enable_jpeg" = "xyes"; then
+   AC_CHECK_HEADER(jpeglib.h,
+   AC_CHECK_LIB(jpeg, jpeg_CreateCompress))
+
+   have_libjpeg=${ac_cv_lib_jpeg_jpeg_CreateCompress:-no}
+
+   LIBJPEG_CFLAGS="$CFLAGS"
+   LIBJPEG_LIBS="$LIBS"
+
+   AC_SUBST(LIBJPEG_CFLAGS)
+   AC_SUBST(LIBJPEG_LIBS)
+else
+   have_libjpeg="no (disabled)"
+fi
+
+AM_CONDITIONAL(HAVE_LIBJPEG, test "$have_libjpeg" = "yes")
+
+if test "$have_libjpeg" = "yes" ; then
+   AC_DEFINE(HAVE_LIBJPEG, 1, [Define if we have libjpeg])
+fi
+
+CFLAGS="$OLD_CFLAGS"
+LIBS="$OLD_LIBS"
+
+##################################################################
+# Check for libtiff
+##################################################################
+
+#
+# FIXME This should be package based. Unfortunately in several main
+# distros, it is not.
+
+OLD_CFLAGS="$CFLAGS"
+OLD_LIBS="$LIBS"
+CFLAGS=""
+LIBS=""
+
+AC_ARG_ENABLE(tiff, 
+              AS_HELP_STRING([--disable-tiff], [Disable tiff extractor]),,
+	      [enable_tiff=yes])
+
+if test "x$enable_tiff" = "xyes"; then
+   AC_CHECK_HEADER(tiff.h,
+   AC_CHECK_LIB(tiff, TIFFOpen))
+
+   have_libtiff=${ac_cv_lib_tiff_TIFFOpen:-no}
+
+   LIBTIFF_CFLAGS="$CFLAGS"
+   LIBTIFF_LIBS="$LIBS"
+
+   AC_SUBST(LIBTIFF_CFLAGS)
+   AC_SUBST(LIBTIFF_LIBS)
+else
+   have_libtiff="no (disabled)"
+fi
+
+AM_CONDITIONAL(HAVE_LIBTIFF, test "$have_libtiff" = "yes")
+
+if test "$have_libtiff" = "yes" ; then
+   AC_DEFINE(HAVE_LIBTIFF, 1, [Define if we have libtiff])
+fi
+
+CFLAGS="$OLD_CFLAGS"
+LIBS="$OLD_LIBS"
+
+####################################################################
+# Check ioprio support
+####################################################################
+
+AC_MSG_CHECKING([[ioprio support]])
+have_ioprio=no
+
+AC_RUN_IFELSE(
+[AC_LANG_PROGRAM([[
+	#include <stdlib.h>
+	#include <errno.h>
+	#include <sys/syscall.h>
+	#include <unistd.h>
+]], 
+[[
+        inline int ioprio_get (int which, int who) 
+	{
+	       return syscall (__NR_ioprio_get, which, who);
+	}
+
+	int main ()
+	{
+	       return ioprio_get (1, 0);
+	}
+]]
+)],
+[have_ioprio=yes],[])
+
+if test "$have_ioprio" = "yes" ; then
+   AC_DEFINE(HAVE_IOPRIO, 1, [Define if we have ioprio])
+fi
+
+AC_MSG_RESULT([$have_ioprio])
+
+##################################################################
+# Check for exempi
+##################################################################
+
+EXEMPI_REQUIRED=1.99.2
+
+AC_ARG_ENABLE(xmp, 
+              AS_HELP_STRING([--disable-xmp], [Disable XMP extraction]),,
+	      [enable_xmp=yes])
+
+if test "x$enable_xmp" = "xyes"; then
+   PKG_CHECK_MODULES(EXEMPI,
+		     [exempi-2.0 >= $EXEMPI_REQUIRED],
+		     [have_exempi=yes], 
+		     [have_exempi=no])
+
+   AC_SUBST(EXEMPI_CFLAGS)
+   AC_SUBST(EXEMPI_LIBS)
+else
+   have_exempi="no (disabled)"
+fi
+
+AM_CONDITIONAL(HAVE_EXEMPI, test "$have_exempi" = "yes")
+
+if test "$have_exempi" = "yes" ; then
+   AC_DEFINE(HAVE_EXEMPI, 1, [Define if we have exempi])
+fi
+
+##################################################################
+# Check for Imagemagick
+##################################################################
+
+AC_ARG_ENABLE(imagemagick, 
+              AS_HELP_STRING([--disable-imagemagick], [Disable Imagemagick thumbnailer]),,
+	      [enable_imagemagick=yes])
+
+if test "x$enable_imagemagick" = "xyes"; then
+   AC_CHECK_PROG(HAVE_IMAGEMAGICK,convert,"yes","no",)
+   have_imagemagick="$HAVE_IMAGEMAGICK"
+else
+   have_imagemagick="no (disabled)"
+fi
+
+AM_CONDITIONAL(HAVE_IMAGEMAGICK, test "$have_imagemagick" = "yes")
+
+if test "$have_imagemagick" = "yes" ; then
+   AC_DEFINE(HAVE_IMAGEMAGICK, 1, [Define if we have imagemagick])
+fi
+
+##################################################################
+# Check for Hildon-thumbnail
+##################################################################
+
+PKG_CHECK_MODULES(HILDON_THUMBNAIL, 
+		  hildon-thumbnail,
+		  [has_hildon_thumbnail=yes],
+		  [has_hildon_thumbnail=no])
+
+if test "x$has_hildon_thumbnail" = "xyes"; then
+   AC_CHECK_PROG(HAVE_HILDON_THUMBNAIL,hildon-thumb-gdk-pixbuf,"yes","no",)
+   have_hildon_thumbnail="$HAVE_HILDON_THUMBNAIL"
+else
+   HILDON_THUMBNAIL_CFLAGS=""
+   HILDON_THUMBNAIL_LIBS=""
+   have_hildon_thumbnail="no (disabled)"
+fi
+
+AM_CONDITIONAL(HAVE_HILDON_THUMBNAIL, test "$have_hildon_thumbnail" = "yes")
+
+if test "$have_hildon_thumbnail" = "yes" ; then
+   AC_DEFINE(HAVE_HILDON_THUMBNAIL, 1, [Define if we have hildon-thumbnail])
+fi
+
+AC_SUBST(HILDON_THUMBNAIL_CFLAGS)
+AC_SUBST(HILDON_THUMBNAIL_LIBS)
+
+AC_CONFIG_LINKS(tests/tracker-indexer/tracker-dbus.c:src/tracker-indexer/tracker-dbus.c
+                tests/tracker-indexer/tracker-dbus.h:src/tracker-indexer/tracker-dbus.h
+                tests/tracker-indexer/tracker-indexer.c:src/tracker-indexer/tracker-indexer.c
+                tests/tracker-indexer/tracker-indexer.h:src/tracker-indexer/tracker-indexer.h
+                tests/tracker-indexer/tracker-indexer-db.c:src/tracker-indexer/tracker-indexer-db.c
+                tests/tracker-indexer/tracker-indexer-db.h:src/tracker-indexer/tracker-indexer-db.h
+                tests/tracker-indexer/tracker-indexer-module.c:src/tracker-indexer/tracker-indexer-module.c
+                tests/tracker-indexer/tracker-indexer-module.h:src/tracker-indexer/tracker-indexer-module.h
+                tests/tracker-indexer/tracker-marshal-main.c:src/tracker-indexer/tracker-marshal-main.c
+		tests/tracker-indexer/tracker-metadata.c:src/tracker-indexer/tracker-metadata.c 
+                tests/tracker-indexer/tracker-metadata.h:src/tracker-indexer/tracker-metadata.h
+                tests/tracker-indexer/tracker-metadata-utils.c:src/tracker-indexer/tracker-metadata-utils.c
+                tests/tracker-indexer/tracker-metadata-utils.h:src/tracker-indexer/tracker-metadata-utils.h
+                tests/tracker-indexer/tracker-module.h:src/tracker-indexer/tracker-module.h
+)
+
+##################################################################
+# Check for older tracker project files which can cause problems
+##################################################################
+
+old_exec_message=""
+old_data_message=""
+
+AC_CHECK_FILE("${prefix}/bin/trackerd", old_exec_trackerd=yes,,)
+AC_CHECK_FILE("${prefix}/bin/tracker-indexer", old_exec_tracker_indexer=yes,,)
+AC_CHECK_FILE("${prefix}/bin/tracker-extract", old_exec_tracker_extract=yes,,)
+AC_CHECK_FILE("${prefix}/bin/tracker-thumbnailer", old_exec_tracker_thumbnailer=yes,,)
+AC_CHECK_FILE("${DBUS_SERVICES_DIR}/tracker.service", old_data_dbus_service=yes,)
+AC_CHECK_FILE("${prefix}/share/tracker/tracker-introspect.xml", old_data_dbus_xml=yes,,)
+AC_CHECK_FILE("${prefix}/share/tracker/sqlite-service-stored-procs.sql", old_data_stored_procs=yes,,)
+
+if test "x$old_exec_trackerd" == "xyes" -o \
+        "x$old_exec_tracker_indexer" == "xyes" -o \
+        "x$old_exec_tracker_extract" == "xyes" -o \
+        "x$old_exec_tracker_thumbnailer" == "xyes"; then
+   old_exec_message="
+        Old Tracker executable files were found in your path.
+        (trackerd, tracker-indexer, tracker-thumbnailer, tracker-extract)"
+   old_file_action="
+	** These files will be removed as part of the installation **"
+fi
+
+if test "x$old_data_dbus_service" == "xyes" -o \
+        "x$old_data_dbus_xml" == "xyes" -o \
+        "x$old_data_stored_procs" == "xyes"; then
+   old_data_message="
+        Old Tracker data files were found in the prefix you are installing to."
+   old_file_action="
+	** These files will be removed as part of the installation **"
+fi
+
+AM_CONDITIONAL(OLD_EXEC_REMOVE_ALL, test -n "$old_exec_message")
+AM_CONDITIONAL(OLD_DATA_REMOVE_ALL, test -n "$old_data_message")
+
+##################################################################
+# Write generated files
+##################################################################
+
+AC_CONFIG_FILES([
+	data/dbus/Makefile
+	data/icons/16x16/Makefile
+	data/icons/22x22/Makefile
+	data/icons/24x24/Makefile
+	data/icons/32x32/Makefile
+	data/icons/48x48/Makefile
+	data/icons/Makefile
+	data/icons/scalable/Makefile
+	data/languages/Makefile
+	data/libtracker-gtk.pc
+	data/Makefile
+	data/modules/Makefile
+	data/services/Makefile
+	data/tracker.pc
+	data/trackerd.desktop.in
+	docs/Makefile
+	filters/application/Makefile
+	filters/Makefile
+	filters/text/Makefile
+	Makefile
+	po/Makefile.in
+	python/deskbar-handler/Makefile
+	python/Makefile
+	src/libinotify/Makefile
+	src/libstemmer/Makefile
+	src/libtracker-common/Makefile
+	src/libtracker-db/Makefile
+	src/libtracker-gtk/Makefile
+	src/libtracker/Makefile
+	src/Makefile
+	src/qdbm/Makefile
+	src/tracker-applet/Makefile
+	src/tracker-applet/tracker-applet.desktop.in
+	src/trackerd/Makefile
+	src/tracker-extract/Makefile
+	src/tracker-fts/Makefile
+	src/tracker-indexer/Makefile
+	src/tracker-indexer/modules/Makefile
+	src/tracker-preferences/Makefile
+	src/tracker-preferences/tracker-preferences.desktop.in
+	src/tracker-search-tool/Makefile
+	src/tracker-search-tool/tracker-search-tool.desktop.in
+	src/tracker-thumbnailer/Makefile
+	src/tracker-utils/Makefile
+	tests/common/Makefile
+	tests/libtracker-common/Makefile
+	tests/libtracker-db/Makefile
+	tests/Makefile
+	tests/scripts/dummy_data_start.sh
+	tests/scripts/dummy_data_stop.sh
+	tests/scripts/Makefile
+	tests/trackerd/Makefile
+	tests/trackerd/xesam/Makefile
+	tests/tracker-fts/Makefile
+	tests/tracker-indexer/Makefile
+	thumbnailers/application/Makefile
+	thumbnailers/image/Makefile
+	thumbnailers/Makefile
+	utils/Makefile
+	utils/qdbm/Makefile
+])
+
+AC_CONFIG_HEADERS([config.h])
+
+AC_OUTPUT
+
+echo "
+Build Configuration:
+
+	Prefix:					${prefix}
+	Source code location:			${srcdir}
+	Compiler:				${CC}
+
+	Win32:					$native_win32
+
+	Enable warnings:			$enable_warnings
+	Enable debug symbols:    		$enable_debug_code
+	Enable unit tests:			$have_unit_tests
+	Enable unac accent stripper:	  	$enable_unac
+
+	Support for file monitoring:           	gio
+	Support for ioprio:			$have_ioprio
+	Support for HAL:                    	$have_hal
+
+Applications:
+
+	Build with SQLite FTS support:		$enable_sqlite_fts
+
+	Build deskbar-applet:			$enable_deskbar_applet
+	Build tracker-search-tool:	        $have_gnome
+	Build tracker-preferences:		$enable_preferences
+	Build tracker-applet:	                $enable_trackerapplet
+	Build libtracker-gtk:			$enable_libtrackergtk
+
+Metadata Extractors:
+
+	Support png:				yes
+	Support pdf:				$have_poppler
+	Support jpeg:				$have_libjpeg (xmp: $have_exempi exif: $have_libexif)
+	Support tiff:				$have_libtiff (xmp: $have_exempi exif: yes)
+	Support ms & openoffice (gsf):	        $have_libgsf
+	Support xml / html formats:		$have_libxml2
+	Support embedded / sidecar xmp:		$have_exempi
+	Support video files:			$videos_are_handled ($videos_handler)
+
+Thumbnailers:
+
+	Have Imagemagick			$have_imagemagick
+	Have hildon-thumbnail			$have_hildon_thumbnail
+
+Warning:
+
+        You must make sure SQLite is compiled with --enable-threadsafe
+
+	$old_exec_message
+	$old_data_message
+	$old_file_action
+
+"

Added: trunk/data/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/data/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,41 @@
+include $(top_srcdir)/Makefile.decl
+
+SUBDIRS = dbus modules languages icons services
+
+trackerd.desktop: trackerd.desktop.in
+	@sed -e "s|@libexec[ ]|${libexecdir}|" $< > $@
+
+autostartdir = $(sysconfdir)/xdg/autostart
+autostart_in_files = trackerd.desktop.in
+autostart_DATA = trackerd.desktop
+
+ INTLTOOL_DESKTOP_RULE@
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = tracker.pc
+
+if ENABLE_LIBTRACKERGTK
+pkgconfig_DATA += libtracker-gtk.pc
+endif
+
+configdir = $(datadir)/tracker
+
+config_DATA =						\
+	sqlite-tracker.sql 				\
+	sqlite-cache.sql 				\
+	sqlite-contents.sql				\
+	sqlite-email.sql				\
+	sqlite-service-triggers.sql 			\
+	sqlite-tracker-triggers.sql 			\
+	sqlite-user-data.sql 				\
+	sqlite-stored-procs.sql 			\
+	sqlite-service.sql				\
+	sqlite-service-types.sql 			\
+	sqlite-metadata.sql 				\
+	sqlite-xesam.sql
+
+CLEANFILES = $(autostart_DATA)
+
+DISTCLEANFILES = $(autostart_in_files)
+
+EXTRA_DIST = $(config_DATA)

Added: trunk/data/dbus/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/data/dbus/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,31 @@
+include $(top_srcdir)/Makefile.decl
+
+configdir = $(datadir)/tracker
+
+config_DATA =						\
+	tracker-daemon.xml				\
+	tracker-files.xml				\
+	tracker-keywords.xml				\
+	tracker-metadata.xml				\
+	tracker-search.xml				\
+	tracker-xesam.xml				\
+	tracker-indexer.xml
+
+# Services
+org.freedesktop.Tracker_servicedir = $(DBUS_SERVICES_DIR)
+org.freedesktop.Tracker_service_DATA = org.freedesktop.Tracker.service
+
+org.freedesktop.Tracker.Indexer_servicedir = $(DBUS_SERVICES_DIR)
+org.freedesktop.Tracker.Indexer_service_DATA = org.freedesktop.Tracker.Indexer.service
+
+%.service: %.service.in
+	@sed -e "s|@libexecdir[ ]|${libexecdir}|" $< > $@
+
+EXTRA_DIST = 						\
+	$(org.freedesktop.Tracker_service_DATA)		\
+	$(org.freedesktop.Tracker.Indexer_service_DATA)	\
+	$(config_DATA)
+
+CLEANFILES = 						\
+	$(org.freedesktop.Tracker_service_DATA)		\
+	$(org.freedesktop.Tracker.Indexer_service_DATA)

Added: trunk/data/dbus/org.freedesktop.Tracker.Indexer.service.in
==============================================================================
--- (empty file)
+++ trunk/data/dbus/org.freedesktop.Tracker.Indexer.service.in	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.freedesktop.Tracker.Indexer
+Exec= libexecdir@/tracker-indexer

Added: trunk/data/dbus/org.freedesktop.Tracker.service.in
==============================================================================
--- (empty file)
+++ trunk/data/dbus/org.freedesktop.Tracker.service.in	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.freedesktop.Tracker
+Exec= libexecdir@/trackerd

Added: trunk/data/dbus/tracker-daemon.xml
==============================================================================
--- (empty file)
+++ trunk/data/dbus/tracker-daemon.xml	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,190 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- 
+   The "service" input parameters are a string representing the
+   service type. A list of service types can be obtained by calling
+   method GetServices and will *potentially* include the following:
+
+   "Files" 
+   "Folders" *
+   "Documents" *
+   "Images" *
+   "Music" *
+   "Videos" *
+   "Text Files" *
+   "Development Files" *
+   "Other Files" *
+   "VFS Files"
+   "VFS Folders" **
+   "VFS Documents" **
+   "VFS Images" **
+   "VFS Music" **
+   "VFS Videos" **
+   "VFS Text Files" **
+   "VFS Development Files" **
+   "VFS Other Files" **
+   "Conversations"
+   "Playlists"
+   "Applications"	
+   "Contacts"
+   "Emails"	
+   "EmailAttachments"
+   "Notes"
+   "Appointments"
+   "Tasks"
+   "Bookmarks"
+   "History"
+   "Projects"
+   
+   
+   (*) - these services are a subset of "Files" service.
+   (**) - these services are a subset of "VFSFiles" service.
+   
+   Services may also have a corresponding metadata class associated
+   with them (EG Files has "File" class, Documents "Doc" etc see the
+   spec at:
+   
+   http://freedesktop.org/wiki/Standards/shared-filemetadata-spec 
+
+   for more details on metadata classes).
+   
+   The "id" input parameters can represent, in the case of a file, the
+   full path and name of the file. In other cases, "id" can also be a
+   unique name or URI for the specified service. 
+   
+   The "id" field uniquely identifies an entity in Tracker regardless
+   of its service type. The live_query_id parameters in some search
+   and retrieval methods is used to indicate whether the query should
+   be "live". Liveness means the query can receive signals in real
+   time to remove hits that are no longer valid and add new ones that
+   are. These signals are emitted in response to file or volume
+   notifications. A value of -1 should be passed if live query
+   functionality is not desired. A live_query_id can be obtained in
+   the future from the LiveQuery Interface.  
+  -->
+
+<node name="/">
+  <interface name="org.freedesktop.Tracker">
+   
+    <!-- Returns the version number of tracker as an integer as XXXXXX
+	 where each XX pair represents part of the version (e.g. "100" is
+	 returned for version 0.1.0 and "14523" would represent version
+	 1.45.23).
+      -->
+    <method name="GetVersion">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="i" name="version" direction="out" />
+    </method>
+    
+    <!-- Return the status of tracker daemon - status is one of
+	 "Initializing", "Watching", "Indexing", "Pending",
+	 "Optimizing", "Idle", "Shutdown".
+      --> 
+    <method name="GetStatus">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="status" direction="out" />
+    </method>
+    
+    <!-- Gets all implemented services and also returns any
+	 corresponding metadata type class for the service (i.e. "File",
+	 "Doc", "Image" etc). 
+	 If main_services_only is set to true then only the major
+	 services are returned.
+	 Output is in dict format: (service -> description, parent).
+    --> 
+    <method name="GetServices">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="b" name="main_services_only" direction="in" />
+      <arg type="a{sv}" name="result" direction="out" />
+    </method>
+        
+    <!-- Get statistics for services that have been indexed. Output is
+	 in string array format: [service, no of entities]  
+      --> 
+    <method name="GetStats">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="aas" name="service_stats" direction="out" />
+    </method>
+        
+    <!-- Sets boolean options in tracker - option can be one of
+	 "Pause", "EnableIndexing", "LowMemoryMode", "IndexFileContents",
+	 "EnableEvolution".
+      -->
+    <method name="SetBoolOption">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="option" direction="in" />
+      <arg type="b" name="value" direction="in" />
+    </method>
+    
+    <!-- Sets integer based option values in tracker - option can be
+	 one of "Throttle", "IndexDelay".
+      -->
+    <method name="SetIntOption">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="option" direction="in" />
+      <arg type="i" name="value" direction="in" />
+    </method>
+    
+    <!-- Shutdown tracker service with optional reindex. -->
+    <method name="Shutdown">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="b" name="reindex" direction="in" />
+    </method>
+        
+    <!-- Prompts tracker to send progress/state signals -->
+    <method name="PromptIndexSignals">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+    </method>
+    
+    <!-- Signal change in index status - states are:
+	 "Initializing", "Watching", "Indexing", "Pending",
+	 "Optimizing", "Idle", "Shutdown".
+
+	 - is_initial_index indicates if this is the first time index
+	 (or reindex).
+	 - in_merge indicates merge indexing is taking place (or
+	 reindex).
+	 - is_manual_paused indicates if user has manually paused the
+	 indexer.
+	 - is_battery_paused indicates if indexer is paused whilst on
+	 battery power.
+	 - is_io_paused indicates if indexer has paused itself as it
+	 has detected other apps writing to disk.
+      -->
+    <signal name="IndexStateChange">
+      <arg type="s" name="state" />
+      <arg type="b" name="initial_index" />
+      <arg type="b" name="in_merge" />
+      <arg type="b" name="is_manual_paused" />
+      <arg type="b" name="is_battery_paused" />
+      <arg type="b" name="is_io_paused" />
+      <arg type="b" name="is_indexing_enabled" />
+    </signal>
+    
+    <!-- Signal when index has completed.  -->
+    <signal name="IndexFinished">
+      <arg type="d" name="seconds_elapsed"/>
+    </signal>
+    
+    <!-- Signal progress of indexing, returns current service
+	 (Files/Conversations/Emails).
+	 - folders_processed = number of folders/mail boxes indexed.
+	 - folders_total = total number of folders/mail boxes.
+      -->
+    <signal name="IndexProgress">
+      <arg type="s" name="service"/>
+      <arg type="s" name="current_uri"/>
+      <arg type="i" name="items_done"/>
+      <arg type="i" name="items_remaining"/>
+      <arg type="i" name="items_total"/>
+      <arg type="d" name="seconds_elapsed"/>
+    </signal>
+
+    <!-- Signal whenever the count of a category changed. Look at GetStats for
+	 the format of service_stats. 
+      -->
+    <signal name="ServiceStatisticsUpdated">
+      <arg type="aas" name="statistics"/>
+    </signal>
+  </interface>
+</node>

Added: trunk/data/dbus/tracker-files.xml
==============================================================================
--- (empty file)
+++ trunk/data/dbus/tracker-files.xml	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- File Specific calls -->
+<node name="/org/freedesktop/Tracker">
+  <interface name="org.freedesktop.Tracker.Files">
+    <!-- Determines if the file is in tracker's database. The option
+	 auto_create if set to TRUE will register the file in the
+	 database if not already present.
+      -->
+    <method name="Exist">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="uri" direction="in" />
+      <arg type="b" name="auto_create" direction="in" />
+      <arg type="b" name="result" direction="out" />
+    </method>
+
+    <!-- Creates a new entry for a file in tracker's database. This
+	 method is ueful when you want to tell Tracker about a file
+	 which it cannot see (EG a VFS file) or the file is located
+	 outside the watch/crawl path.
+      --> 
+    <method name="Create">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="uri" direction="in" />
+      <arg type="b" name="is_directory" direction="in" />
+      <arg type="s" name="mime" direction="in" />
+      <arg type="i" name="size" direction="in" />
+      <arg type="i" name="mtime" direction="in" />
+    </method>
+    
+    <!-- Removes the file entry from tracker's database. -->
+    <method name="Delete">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="uri" direction="in" />
+    </method>
+
+    <!-- Get the Service subtype for the file. -->
+    <method name="GetServiceType">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="uri" direction="in" />
+      <arg type="s" name="service_type" direction="out" />
+    </method>
+
+    <!-- Get the "File.Content" field for a file and allows you to
+	 specify the offset and amount of text to retrieve.
+      -->
+    <method name="GetTextContents">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
+      <arg type="s" name="uri" direction="in" />
+      <arg type="i" name="offset"  direction="in" />
+      <arg type="i" name="max_length"  direction="in" />				
+      <arg type="s" name="content" direction="out" />
+    </method>
+    
+    <!-- Retrieves a chunk of matching text of specified length that
+	 contains the search text in the File.Content field.
+      -->
+    <method name="SearchTextContents">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
+      <arg type="s" name="uri" direction="in" />
+      <arg type="s" name="text"  direction="in" />
+      <arg type="i" name="length"  direction="in" />
+      <arg type="s" name="result" direction="out" />
+    </method>
+
+    <!-- Retrieves all files that match a service description. -->
+    <method name="GetByServiceType">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="i" name="live_query_id" direction="in" />
+      <arg type="s" name="file_service" direction="in" />
+      <arg type="i" name="offset" direction="in" />
+      <arg type="i" name="max_hits" direction="in" />
+      <arg type="as" name="result" direction="out" />
+    </method>	
+
+    <!-- Retrieves all non-vfs files of the specified mime type(s). -->
+    <method name="GetByMimeType">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="i" name="live_query_id" direction="in" />
+      <arg type="as" name="mime_types" direction="in" />
+      <arg type="i" name="offset" direction="in" />
+      <arg type="i" name="max_hits" direction="in" />
+      <arg type="as" name="result" direction="out" />
+    </method>	
+
+    <!-- Retrieves all vfs files of the specified mime type(s). -->
+    <method name="GetByMimeTypeVfs">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="i" name="live_query_id" direction="in" />
+      <arg type="as" name="mime_types" direction="in" />
+      <arg type="i" name="offset" direction="in" />
+      <arg type="i" name="max_hits" direction="in" />
+      <arg type="as" name="result" direction="out" />
+    </method>
+
+    <!-- returns mtime of file in seconds since epoch -->
+    <method name="GetMTime">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
+      <arg type="s" name="uri" direction="in" />
+      <arg type="i" name="result" direction="out" />
+    </method>
+
+    <!-- Retrieves all non-vfs files in a folder complete with all
+	 requested metadata fields. StringArray output is [uri,
+	 field1, field2...].
+      -->
+    <method name="GetMetadataForFilesInFolder">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="i" name="live_query_id" direction="in" />
+      <arg type="s" name="uri" direction="in" />
+      <arg type="as" name="fields" direction="in" />
+      <arg type="aas" name="values" direction="out" />
+    </method>
+
+    <!-- Specific calls for Nautilus, mostly used by Nautilus search. -->
+    <method name="SearchByTextAndMime">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
+      <arg type="s" name="text" direction="in" />
+      <arg type="as" name="mimes" direction="in" />
+      <arg type="as" name="result" direction="out" />
+    </method>
+    <method name="SearchByTextAndLocation">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
+      <arg type="s" name="text" direction="in" />
+      <arg type="s" name="location" direction="in" />
+      <arg type="as" name="result" direction="out" />
+    </method>
+    <method name="SearchByTextAndMimeAndLocation">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
+      <arg type="s" name="text" direction="in" />
+      <arg type="as" name="mimes" direction="in" />
+      <arg type="s" name="location" direction="in" />
+      <arg type="as" name="result" direction="out" />
+    </method>
+-->
+    <!-- End deprecated calls section. -->
+  </interface>
+</node>

Added: trunk/data/dbus/tracker-indexer.xml
==============================================================================
--- (empty file)
+++ trunk/data/dbus/tracker-indexer.xml	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+<busconfig>
+    <policy context="default">
+      <deny send_interface="org.freedesktop.Tracker.Indexer"/>
+      <allow send_destination="org.freedesktop.Tracker"/>
+    </policy>
+</busconfig>
+-->
+
+<node name="/">
+  <interface name="org.freedesktop.Tracker.Indexer">  
+    <method name="FilesCheck">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="module" direction="in" />
+      <arg type="as" name="files" direction="in" />
+    </method>
+    <method name="FilesUpdate">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="module" direction="in" />
+      <arg type="as" name="files" direction="in" />
+    </method>
+    <method name="FilesDelete">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="module" direction="in" />
+      <arg type="as" name="files" direction="in" />
+    </method>
+    <method name="FileMove">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="module" direction="in" />
+      <arg type="s" name="file_from" direction="in" />
+      <arg type="s" name="file_to" direction="in" />
+    </method>
+
+    <method name="PropertySet">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="service_type" direction="in" />
+      <arg type="s" name="uri" direction="in" />
+      <arg type="s" name="property" direction="in"/>
+      <arg type="as" name="values" direction="in"/>
+    </method>      
+
+    <method name="PropertyRemove">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="service_type" direction="in" />
+      <arg type="s" name="uri" direction="in" />
+      <arg type="s" name="property" direction="in"/>
+      <arg type="as" name="values" direction="in"/>
+    </method>
+
+    <method name="Pause">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+    </method>
+    <method name="PauseForDuration">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="u" name="seconds" direction="in" />
+    </method>
+    <method name="Continue">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+    </method>
+    <method name="Shutdown">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+    </method>
+
+    <!-- Signals --> 
+    <signal name="Status">
+      <arg type="d" name="elapsed_time" />
+      <arg type="s" name="current_module_name" />
+      <arg type="u" name="items_done" />
+      <arg type="u" name="items_remaining" />
+    </signal>
+    <signal name="Started" />
+    <signal name="Paused" />
+    <signal name="Continued" />
+    <signal name="Finished">
+      <arg type="d" name="elapsed_time" />
+      <arg type="u" name="items_done" />
+      <arg type="b" name="interrupted" />
+    </signal>
+    <signal name="ModuleStarted">
+      <arg type="s" name="module_name" />
+    </signal>
+    <signal name="ModuleFinished">
+      <arg type="s" name="module_name" />
+    </signal>
+  </interface>
+</node>

Added: trunk/data/dbus/tracker-keywords.xml
==============================================================================
--- (empty file)
+++ trunk/data/dbus/tracker-keywords.xml	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<node name="/">
+  <!-- Calls for tags/keywords for any service object or file. -->
+  <interface name="org.freedesktop.Tracker.Keywords">
+
+    <!-- Gets a list of all unique keywords/tags that are in use by
+	 the specified service irrespective of the uri or id of the
+	 entity. Returns an array of string arrays in format [Keyword,
+	 KeywordCount].
+      -->
+    <method name="GetList">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="service_type" direction="in" />
+      <arg type="aas" name="keywords" direction="out" />
+    </method>
+
+    <!-- Gets all unique keywords/tags for specified service and id. -->
+    <method name="Get">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="service_type" direction="in" />
+      <arg type="s" name="uri" direction="in" />
+      <arg type="as" name="keywords" direction="out" />
+    </method>
+
+    <!-- Adds new keywords/tags for specified service and id -->
+    <method name="Add">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="service_type" direction="in" />
+      <arg type="s" name="uri" direction="in" />
+      <arg type="as" name="keywords" direction="in" />
+    </method>
+
+    <!-- Removes all specified keywords/tags for specified service and
+	 id.
+      -->
+    <method name="Remove">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="service_type" direction="in" />
+      <arg type="s" name="uri" direction="in" />
+      <arg type="as" name="keywords" direction="in" />
+    </method>
+
+    <!-- Removes all keywords/tags for specified service and id. -->
+    <method name="RemoveAll">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="service_type" direction="in" />
+      <arg type="s" name="uri" direction="in" />
+    </method>
+
+    <!-- Searches specified service for matching keyword/tag and
+	 returns an array of matching id values for the service.
+      --> 
+    <method name="Search">
+      <annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="i" name="live_query_id" direction="in" />
+      <arg type="s" name="service_type" direction="in" />
+      <arg type="as" name="keywords" direction="in" />
+      <arg type="i" name="offset" direction="in" />
+      <arg type="i" name="max_hits" direction="in" />
+      <arg type="as" name="result" direction="out" />
+    </method>
+
+    <!-- emitted whenever tags on an entity change -->
+    <signal name="KeywordAdded">
+      <arg type="s" name="service"/>
+      <arg type="s" name="uri" />
+      <arg type="s" name="keyword" />
+    </signal>
+
+    <signal name="KeywordRemoved">
+      <arg type="s" name="service"/>
+      <arg type="s" name="uri" />
+      <arg type="s" name="keyword" />
+    </signal>
+  </interface>
+</node>

Added: trunk/data/dbus/tracker-metadata.xml
==============================================================================
--- (empty file)
+++ trunk/data/dbus/tracker-metadata.xml	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<node name="/org/freedesktop/Tracker">
+  <interface name="org.freedesktop.Tracker.Metadata">
+
+    <!-- Retrieves an array of metadata values for the specified array
+	 of metadata keys for a service and id pair.
+      --> 
+    <method name="Get">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/> 
+      <arg type="s" name="service_type" direction="in" />
+      <arg type="s" name="uri" direction="in" />
+      <arg type="as" name="keys" direction="in" />
+      <arg type="as" name="metadata" direction="out" />
+    </method>
+    
+    <!-- Sets specified metadata keys to the specified metadata values
+	 for a service and id pair.
+      -->
+    <method name="Set">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/> 
+      <arg type="s" name="service_type" direction="in" />
+      <arg type="s" name="uri" direction="in" />
+      <arg type="as" name="keys" direction="in" />
+      <arg type="as" name="metadata" direction="in" />
+    </method>
+       
+    <!-- Gets all details of a named metadata type (e.g. "Video:FrameRate" or "Audio:Title"). 
+	 data_type: keyword, index, fulltext, string, integer, dobule, date, blob, struct, link
+    -->		
+    <method name="GetTypeDetails">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/> 
+      <arg type="s" name="metadata" direction="in" />
+      <arg type="s" name="data_type" direction="out" />
+      <arg type="b" name="is_embedded" direction="out" />
+      <arg type="b" name="is_writable" direction="out" />
+    </method>
+    
+    <!-- Returns an array of all metadata types that are registered
+	 for a certain service type. You can enter "*" as the service type to get all
+	 registered metadata types .
+      --> 
+    <method name="GetRegisteredTypes">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/> 
+      <arg type="s" name="service_type" direction="in" />
+      <arg type="as" name="result" direction="out" />
+    </method>
+       
+    <!-- Returns an array of all service types that are registered.
+      -->
+    <method name="GetRegisteredClasses">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="as" name="result" direction="out" />
+    </method>
+
+    <!-- returns an array of all unique values of given metadata types. Optionally a rdf query can be
+	 used to filter the results. The results are sorted based on the metadata fields either in
+	 ascending or descending order. 
+      -->
+    <method name="GetUniqueValues">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="service" direction="in" />
+      <arg type="as" name="meta_types" direction="in" />
+      <arg type="s" name="query" direction="in" />
+      <arg type="b" name="descending" direction="in" />
+      <arg type="i" name="offset" direction="in" />
+      <arg type="i" name="max_hits" direction="in" />
+      <arg type="aas" name="result" direction="out" />
+    </method>
+
+    <method name="GetSum">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="service" direction="in" />
+      <arg type="s" name="field" direction="in" />
+      <arg type="s" name="query" direction="in" />
+      <arg type="i" name="result" direction="out" />
+    </method>
+
+    <method name="GetCount">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="service" direction="in" />
+      <arg type="s" name="field" direction="in" />
+      <arg type="s" name="query" direction="in" />
+      <arg type="i" name="result" direction="out" />
+    </method>
+
+    <method name="GetUniqueValuesWithCount">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="service" direction="in" />
+      <arg type="as" name="meta_types" direction="in" />
+      <arg type="s" name="query" direction="in" />
+      <arg type="s" name="count_field" direction="in" />
+      <arg type="b" name="descending" direction="in" />
+      <arg type="i" name="offset" direction="in" />
+      <arg type="i" name="max_hits" direction="in" />
+      <arg type="aas" name="result" direction="out" />
+    </method>
+
+  </interface>
+</node>

Added: trunk/data/dbus/tracker-search.xml
==============================================================================
--- (empty file)
+++ trunk/data/dbus/tracker-search.xml	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- Generic search interface (see other interfaces for file/keyword
+     specific searches). 
+  -->
+<node name="/org/freedesktop/Tracker">
+  <interface name="org.freedesktop.Tracker.Search">
+    <!-- Returns no of hits for the search_text on the servce. -->
+    <method name="GetHitCount">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="service" direction="in" />
+      <arg type="s" name="search_text" direction="in" />
+      <arg type="i" name="result" direction="out" />
+    </method>
+
+    <!--  returns [service name, no. of hits] for the search_text -->
+    <method name="GetHitCountAll">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="search_text" direction="in" />
+      <arg type="aas" name="result" direction="out" />
+    </method>
+
+    <!-- searches specified service for entities that match the
+	 specified search_text. Returns uri of all hits.
+      -->
+    <method name="Text">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="i" name="live_query_id" direction="in" />
+      <arg type="s" name="service" direction="in" />
+      <arg type="s" name="search_text" direction="in" />
+      <arg type="i" name="offset" direction="in" />
+      <arg type="i" name="max_hits" direction="in" />
+      <arg type="as" name="result" direction="out" />
+    </method>
+
+    <!-- More detailed version of above. Searches specified service
+	 for entities that match the specified search_text. Returns
+	 hits in array format [uri, service, mime].
+      --> 
+    <method name="TextDetailed">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="i" name="live_query_id" direction="in" />
+      <arg type="s" name="service" direction="in" />
+      <arg type="s" name="search_text" direction="in" />
+      <arg type="i" name="offset" direction="in" />
+      <arg type="i" name="max_hits" direction="in" />
+      <arg type="aas" name="result" direction="out" />
+    </method>
+
+    <!-- Returns a search snippet of text with matchinhg text enclosed
+	 in bold tags. 
+      -->
+    <method name="GetSnippet">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="service" direction="in" />
+      <arg type="s" name="id" direction="in" />
+      <arg type="s" name="search_text" direction="in" />
+      <arg type="s" name="result" direction="out" />
+    </method>
+
+    <!-- searches a specific metadata field (field parameter) for a
+	 search term (search_text). The result is an array of uri/id's.
+      -->
+    <method name="Metadata">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
+      <arg type="s" name="service" direction="in" />
+      <arg type="s" name="field" direction="in" />
+      <arg type="s" name="search_text"  direction="in" />
+      <arg type="i" name="offset" direction="in" />
+      <arg type="i" name="max_hits" direction="in" />
+      <arg type="as" name="result" direction="out" />
+    </method>
+
+    <!-- Searches specified service for matching entities.
+	 - The service parameter specifies the service which the query
+	 will be performed on.
+	 - The fields parameter specifies an array of aditional metadata
+	 fields to return in addition to the id field (which is
+	 returned as the "key" in the resultant dict/hashtable) and
+	 the service category. This can be null.
+	 - The optional search_text paramter specifies the text to
+	 search for in a full text search of all indexed fields. 
+	 - The optional keyword search, a single keyword may be used
+	 here to filter the results.
+	 - The optional query_condition parameter specifies an
+	 xml-based rdf query condition which is used to filter out the
+	 results.
+	 - The Offset parameter sets the start row of the returned
+	 result set (useful for paging/cursors). A value of 0 should
+	 be passed to get rows from the beginning. 
+	 - The max_hits parameter limits the size of the result set.
+	 - The sort_by_service parameter optionally sorts results by
+	 their service category (if FALSE no service sorting is done).
+	 - The result is a array of stringarrays in format [uri,
+	 service, field1, field2...] where field1 is the first
+	 specified field in the fields paramter and so on.
+      -->
+    <method name="Query">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="i" name="live_query_id" direction="in" />
+      <arg type="s" name="service" direction="in" />
+      <arg type="as" name="fields" direction="in" />
+      <arg type="s" name="search_text" direction="in" />
+      <arg type="s" name="keyword" direction="in" />
+      <arg type="s" name="query_condition" direction="in" />
+      <arg type="b" name="sort_by_service" direction="in" />
+      <arg type="as" name="sort_fields" direction="in" />
+      <arg type="b" name="sort_descending" direction="in" />
+      <arg type="i" name="offset" direction="in" />
+      <arg type="i" name="max_hits" direction="in" />
+      <arg type="aas" name="result" direction="out" />
+    </method>
+
+    <!-- Suggests an alternate spelling based on the word index. -->
+    <method name="Suggest">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="search_text" direction="in" />
+      <arg type="i" name="maxdist" direction="in" />
+      <arg type="s" name="result" direction="out" />
+    </method>
+  </interface>
+</node>

Added: trunk/data/dbus/tracker-xesam.xml
==============================================================================
--- (empty file)
+++ trunk/data/dbus/tracker-xesam.xml	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- Generic search interface (see other interfaces for file/keyword
+     specific searches). 
+  -->
+<node name="/org/freedesktop/xesam">
+  <interface name="org.freedesktop.xesam.Search">
+
+    <method name="NewSession">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="session" direction="out" />
+    </method>
+
+
+    <method name="SetProperty">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="session" direction="in" />
+      <arg type="s" name="prop" direction="in" />
+      <arg type="v" name="val" direction="in" />
+      <arg type="v" name="new_val" direction="out" />
+    </method>
+
+    <method name="GetProperty">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="session" direction="in" />
+      <arg type="s" name="prop" direction="in" />
+      <arg type="v" name="value" direction="out" />
+    </method>
+
+    <method name="CloseSession">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="session" direction="in" />
+    </method>
+
+
+    <method name="NewSearch">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="session" direction="in" />
+      <arg type="s" name="query_xml" direction="in" />
+      <arg type="s" name="search" direction="out" />
+    </method>
+
+    <method name="StartSearch">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="search" direction="in" />
+    </method>
+
+    <method name="GetHitCount">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="search" direction="in" />
+      <arg type="u" name="count" direction="out" />
+    </method>
+
+    <method name="GetHits">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="search" direction="in" />
+      <arg type="u" name="count" direction="in" />
+      <arg type="aav" name="hits" direction="out" />
+    </method>
+
+
+    <method name="GetHitData">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="search" direction="in" />
+      <arg type="au" name="hit_ids" direction="in" />
+      <arg type="as" name="fields" direction="in" />
+      <arg type="aav" name="hit_data" direction="out" />
+    </method>
+
+    <method name="CloseSearch">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="search" direction="in" />
+    </method>
+
+    <method name="GetState">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="as" name="state_info" direction="out" />
+    </method>
+
+    <signal name="HitsAdded">
+      <arg type="s" name="search" />
+      <arg type="u" name="count" />
+    </signal>
+
+    <signal name="HitsRemoved">
+      <arg type="s" name="search" />
+      <arg type="au" name="hit_ids" />
+    </signal>
+
+    <signal name="HitsModified">
+      <arg type="s" name="search" />
+      <arg type="au" name="hit-ids" />
+    </signal>
+
+    <signal name="SearchDone">
+      <arg type="s" name="search" />
+    </signal>
+
+    <signal name="StateChanged">
+      <arg type="as" name="state_info" />
+    </signal>
+
+  </interface>
+  <interface name="org.freedesktop.xesam.PagedSearch">
+      <method name="GetRangeHits">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="search" direction="in" />
+      <arg type="u" name="a" direction="in" />
+      <arg type="u" name="b" direction="in" />
+      <arg type="aav" name="hits" direction="out" />
+    </method>
+
+    <method name="GetRangeHitData">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="search" direction="in" />
+      <arg type="u" name="a" direction="in" />
+      <arg type="u" name="b" direction="in" />
+      <arg type="as" name="fields" direction="in" />
+      <arg type="aav" name="hit_data" direction="out" />
+    </method>
+  </interface>
+
+</node>

Added: trunk/data/english/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/data/english/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,5 @@
+englishdir = $(datadir)/tracker/english
+
+english_DATA = errmsg.sys errmsg.txt
+
+EXTRA_DIST = $(english_DATA)

Added: trunk/data/english/errmsg.sys
==============================================================================
Binary file. No diff available.

Added: trunk/data/english/errmsg.txt
==============================================================================
--- (empty file)
+++ trunk/data/english/errmsg.txt	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,5623 @@
+languages czech=cze latin2, danish=dan latin1, dutch=nla latin1, english=eng latin1, estonian=est latin7, french=fre latin1, german=ger latin1, greek=greek greek, hungarian=hun latin2, italian=ita latin1, japanese=jpn ujis, japanese-sjis=jps sjis, korean=kor euckr, norwegian-ny=norwegian-ny latin1, norwegian=nor latin1, polish=pol latin2, portuguese=por latin1, romanian=rum latin2, russian=rus koi8r, serbian=serbian cp1250, slovak=slo latin2, spanish=spa latin1, swedish=swe latin1, ukrainian=ukr koi8u;
+
+default-language eng
+
+start-error-number 1000
+
+ER_HASHCHK  
+	eng "hashchk"
+ER_NISAMCHK  
+	eng "isamchk"
+ER_NO  
+	cze "NE"
+	dan "NEJ"
+	nla "NEE"
+	eng "NO"
+	est "EI"
+	fre "NON"
+	ger "Nein"
+	greek "ÏÉ
+	hun "NEM"
+	kor "¾ÆÏÀ
+	nor "NEI"
+	norwegian-ny "NEI"
+	pol "NIE"
+	por "NÃ"
+	rum "NU"
+	rus "î"
+	serbian "NE"
+	slo "NIE"
+	ukr "î
+ER_YES  
+	cze "ANO"
+	dan "JA"
+	nla "JA"
+	eng "YES"
+	est "JAH"
+	fre "OUI"
+	ger "Ja"
+	greek "ÍÉ
+	hun "IGEN"
+	ita "SI"
+	kor "¿¹"
+	nor "JA"
+	norwegian-ny "JA"
+	pol "TAK"
+	por "SIM"
+	rum "DA"
+	rus "ä
+	serbian "DA"
+	slo "Áo"
+	spa "SI"
+	ukr "ô
+ER_CANT_CREATE_FILE  
+	cze "Nemohu vytvo-Bøoubor '%-.64s' (chybový %d)"
+	dan "Kan ikke oprette filen '%-.64s' (Fejlkode: %d)"
+	nla "Kan file '%-.64s' niet aanmaken (Errcode: %d)"
+	eng "Can't create file '%-.64s' (errno: %d)"
+	est "Ei suuda luua faili '%-.64s' (veakood: %d)"
+	fre "Ne peut cré le fichier '%-.64s' (Errcode: %d)"
+	ger "Kann Datei '%-.64s' nicht erzeugen (Fehler: %d)"
+	greek "Áýççïã ôáåõ-.64s' (êéòèòd)"
+	hun "A '%-.64s' file nem hozhato letre (hibakod: %d)"
+	ita "Impossibile creare il file '%-.64s' (errno: %d)"
+	jpn "'%-.64s' ¥Õ¡¥¤¥ëºî¤Þ»¤órrno: %d)"
+	kor "ÈÀ '%-.64s'¸¦ ¸¸µé ¸ø´ÏÙ (¿¡·¯¹ø%d)"
+	nor "Kan ikke opprette fila '%-.64s' (Feilkode: %d)"
+	norwegian-ny "Kan ikkje opprette fila '%-.64s' (Feilkode: %d)"
+	pol "Nie mo¿na stworzyæliku '%-.64s' (Kod b³ê: %d)"
+	por "Nãpode criar o arquivo '%-.64s' (erro no. %d)"
+	rum "Nu pot sa creez fisierul '%-.64s' (Eroare: %d)"
+	rus "îÏÍÖÏÓÚÁØÆÊ '%-.64s' (ÏÉË: %d)"
+	serbian "Ne mogu da kreiram file '%-.64s' (errno: %d)"
+	slo "Nemô vytvori» sú'%-.64s' (chybový %d)"
+	spa "No puedo crear archivo '%-.64s' (Error: %d)"
+	swe "Kan inte skapa filen '%-.64s' (Felkod: %d)"
+	ukr "îÍÖ Ó×ÒÔ ÆÊ '%-.64s' (ÐÍÌÁ %d)"
+ER_CANT_CREATE_TABLE  
+	cze "Nemohu vytvo-Bøabulku '%-.64s' (chybový %d)"
+	dan "Kan ikke oprette tabellen '%-.64s' (Fejlkode: %d)"
+	nla "Kan tabel '%-.64s' niet aanmaken (Errcode: %d)"
+	eng "Can't create table '%-.64s' (errno: %d)"
+	jps "'%-.64s' ƒe[ƒuƒ‹‚ªì‚ܹ‚ñrrno: %d)",
+	est "Ei suuda luua tabelit '%-.64s' (veakood: %d)"
+	fre "Ne peut cré la table '%-.64s' (Errcode: %d)"
+	ger "Kann Tabelle '%-.64s' nicht erzeugen (Fehler: %d)"
+	greek "Áýççïã ôðê'%-.64s' (êéòèòd)"
+	hun "A '%-.64s' tabla nem hozhato letre (hibakod: %d)"
+	ita "Impossibile creare la tabella '%-.64s' (errno: %d)"
+	jpn "'%-.64s' ¥Æ¼¥Öëºî¤Þ»¤órrno: %d)"
+	kor "ÅÀºí%-.64s'¸¦ ¸¸µé ¸ø´ÏÙ (¿¡·¯¹ø%d)"
+	nor "Kan ikke opprette tabellen '%-.64s' (Feilkode: %d)"
+	norwegian-ny "Kan ikkje opprette tabellen '%-.64s' (Feilkode: %d)"
+	pol "Nie mo¿na stworzyæabeli '%-.64s' (Kod b³ê: %d)"
+	por "Nãpode criar a tabela '%-.64s' (erro no. %d)"
+	rum "Nu pot sa creez tabla '%-.64s' (Eroare: %d)"
+	rus "îÏÍÖÏÓÚÁØÔÂÉÕ'%-.64s' (ÏÉË: %d)"
+	serbian "Ne mogu da kreiram tabelu '%-.64s' (errno: %d)"
+	slo "Nemô vytvori» tabuµku '%-.64s' (chybový %d)"
+	spa "No puedo crear tabla '%-.64s' (Error: %d)"
+	swe "Kan inte skapa tabellen '%-.64s' (Felkod: %d)"
+	ukr "îÍÖ Ó×ÒÔ ÔÂÉÀ'%-.64s' (ÐÍÌÁ %d)"
+ER_CANT_CREATE_DB  
+	cze "Nemohu vytvo-Bøatabá '%-.64s' (chybový %d)"
+	dan "Kan ikke oprette databasen '%-.64s' (Fejlkode: %d)"
+	nla "Kan database '%-.64s' niet aanmaken (Errcode: %d)"
+	eng "Can't create database '%-.64s' (errno: %d)"
+	jps "'%-.64s' ƒf[ƒ^ƒx[ƒX‚ªì‚ܹ‚ñrrno: %d)",
+	est "Ei suuda luua andmebaasi '%-.64s' (veakood: %d)"
+	fre "Ne peut cré la base '%-.64s' (Erreur %d)"
+	ger "Kann Datenbank '%-.64s' nicht erzeugen (Fehler: %d)"
+	greek "Áýççïã ôâçäïí '%-.64s' (êéòèòd)"
+	hun "Az '%-.64s' adatbazis nem hozhato letre (hibakod: %d)"
+	ita "Impossibile creare il database '%-.64s' (errno: %d)"
+	jpn "'%-.64s' ¥Ç¼¥¿¥Ù¼¥¹¤¬ºî¤Þ»¤órrno: %d)"
+	kor "µ¥Àź£À½º '%-.64s'¸¦ ¸¸µé ¸ø´ÏÙ. (¿¡·¯¹ø%d)"
+	nor "Kan ikke opprette databasen '%-.64s' (Feilkode: %d)"
+	norwegian-ny "Kan ikkje opprette databasen '%-.64s' (Feilkode: %d)"
+	pol "Nie mo¿na stworzyæazy danych '%-.64s' (Kod b³ê: %d)"
+	por "Nãpode criar o banco de dados '%-.64s' (erro no. %d)"
+	rum "Nu pot sa creez baza de date '%-.64s' (Eroare: %d)"
+	rus "îÏÍÖÏÓÚÁØÂÚ ÄÎÙ '%-.64s' (ÏÉË: %d)"
+	serbian "Ne mogu da kreiram bazu '%-.64s' (errno: %d)"
+	slo "Nemô vytvori» databá '%-.64s' (chybový %d)"
+	spa "No puedo crear base de datos '%-.64s' (Error: %d)"
+	swe "Kan inte skapa databasen '%-.64s' (Felkod: %d)"
+	ukr "îÍÖ Ó×ÒÔ ÂÚ ÄÎÉ '%-.64s' (ÐÍÌÁ %d)"
+ER_DB_CREATE_EXISTS  
+	cze "Nemohu vytvo-Bøatabá '%-.64s'; databá ji¾ existuje"
+	dan "Kan ikke oprette databasen '%-.64s'; databasen eksisterer"
+	nla "Kan database '%-.64s' niet aanmaken; database bestaat reeds"
+	eng "Can't create database '%-.64s'; database exists"
+	jps "'%-.64s' ƒf[ƒ^ƒx[ƒX‚ªì‚ܹ‚ñ‚É»‚Ìf[ƒ^ƒx[ƒX‚ª‘¶Ýµ‚Ü·",
+	est "Ei suuda luua andmebaasi '%-.64s': andmebaas juba eksisteerib"
+	fre "Ne peut cré la base '%-.64s'; elle existe dé"
+	ger "Kann Datenbank '%-.64s' nicht erzeugen. Datenbank existiert bereits"
+	greek "Áýççïã ôâçäïí '%-.64s'; ÇâçåìùÜ÷Þç+	hun "Az '%-.64s' adatbazis nem hozhato letre Az adatbazis mar letezik"
+	ita "Impossibile creare il database '%-.64s'; il database esiste"
+	jpn "'%-.64s' ¥Ç¼¥¿¥Ù¼¥¹¤¬ºî¤Þ»¤ó¤Ë½¤ÎǼ¥¿¥Ù¼¥¹¤¬Âºß·¤Þ¹"
+	kor "µ¥Àź£À½º '%-.64s'¸¦ ¸¸µé ¸ø´ÏÙ. µ¥Àź£À½º°¡ ÁÀÇ"
+	nor "Kan ikke opprette databasen '%-.64s'; databasen eksisterer"
+	norwegian-ny "Kan ikkje opprette databasen '%-.64s'; databasen eksisterer"
+	pol "Nie mo¿na stworzyæazy danych '%-.64s'; baza danych ju¿ istnieje"
+	por "Nãpode criar o banco de dados '%-.64s'; este banco de dados jáxiste"
+	rum "Nu pot sa creez baza de date '%-.64s'; baza de date exista deja"
+	rus "îÏÍÖÏÓÚÁØÂÚ ÄÎÙ '%-.64s'. âÁÄÎÙ ÕÅÓÝÓ×Å"
+	serbian "Ne mogu da kreiram bazu '%-.64s'; baza veæostoji."
+	slo "Nemô vytvori» databá '%-.64s'; databá existuje"
+	spa "No puedo crear base de datos '%-.64s'; la base de datos ya existe"
+	swe "Databasen '%-.64s' existerar redan"
+	ukr "îÍÖ Ó×ÒÔ ÂÚ ÄÎÉ '%-.64s'. âÁÄÎÉ ¦ÓÕ"
+ER_DB_DROP_EXISTS  
+	cze "Nemohu zru-B¹it databá '%-.64s', databá neexistuje"
+	dan "Kan ikke slette (droppe) '%-.64s'; databasen eksisterer ikke"
+	nla "Kan database '%-.64s' niet verwijderen; database bestaat niet"
+	eng "Can't drop database '%-.64s'; database doesn't exist"
+	jps "'%-.64s' ƒf[ƒ^ƒx[ƒX‚ðüܹ‚ñ»‚Ìf[ƒ^ƒx[ƒX‚ª‚È¢‚ÌÅ·.",
+	est "Ei suuda kustutada andmebaasi '%-.64s': andmebaasi ei eksisteeri"
+	fre "Ne peut effacer la base '%-.64s'; elle n'existe pas"
+	ger "Kann Datenbank '%-.64s' nicht löen; Datenbank nicht vorhanden"
+	greek "Áýçéñ ôâçäïí '%-.64s'. Çâçåìùíðå
+	hun "A(z) '%-.64s' adatbazis nem szuntetheto meg. Az adatbazis nem letezik"
+	ita "Impossibile cancellare '%-.64s'; il database non esiste"
+	jpn "'%-.64s' ¥Ç¼¥¿¥Ù¼¥¹¤òþ¤Ç­¤Þ»¤ó½¤ÎǼ¥¿¥Ù¼¥¹¤¬¤Ê¤¤Îǹ."
+	kor "µ¥Àź£À½º '%-.64s'¸¦ Á°ÅÏöǽÀÏÙ µ¥Àź£À½º°¡ ÁÀÇÁ ¾Ê½ "
+	nor "Kan ikke fjerne (drop) '%-.64s'; databasen eksisterer ikke"
+	norwegian-ny "Kan ikkje fjerne (drop) '%-.64s'; databasen eksisterer ikkje"
+	pol "Nie mo¿na usun?æazy danych '%-.64s'; baza danych nie istnieje"
+	por "Nãpode eliminar o banco de dados '%-.64s'; este banco de dados nãexiste"
+	rum "Nu pot sa drop baza de date '%-.64s'; baza da date este inexistenta"
+	rus "îÏÍÖÏÕÁÉØÂÚ ÄÎÙ '%-.64s'. ôÊÂÚ ÄÎÙ ÎÔ
+	serbian "Ne mogu da izbrišem bazu '%-.64s'; baza ne postoji."
+	slo "Nemô zmaza» databá '%-.64s'; databá neexistuje"
+	spa "No puedo eliminar base de datos '%-.64s'; la base de datos no existe"
+	swe "Kan inte radera databasen '%-.64s'; databasen finns inte"
+	ukr "îÍÖ ×ÄÌÔ ÂÚ ÄÎÉ '%-.64s'. âÁÄÎÉ Î ¦ÓÕ"
+ER_DB_DROP_DELETE  
+	cze "Chyba p-Bø¹eníatabá (nemohu vymazat '%-.64s', chyba %d)"
+	dan "Fejl ved sletning (drop) af databasen (kan ikke slette '%-.64s', Fejlkode %d)"
+	nla "Fout bij verwijderen database (kan '%-.64s' niet verwijderen, Errcode: %d)"
+	eng "Error dropping database (can't delete '%-.64s', errno: %d)"
+	jps "ƒf[ƒ^ƒx[ƒX”jŠü[ ('%-.64s' ‚ðœ‚Å«‚ܹ‚ñrrno: %d)",
+	est "Viga andmebaasi kustutamisel (ei suuda kustutada faili '%-.64s', veakood: %d)"
+	fre "Ne peut effacer la base '%-.64s' (erreur %d)"
+	ger "Fehler beim Löen der Datenbank ('%-.64s' kann nicht gelöt werden, Fehler: %d)"
+	greek "Ðñéô ðëáá ôéñ ôâçäïí (áíç äãö%-.64s', êéòèòd)"
+	hun "Adatbazis megszuntetesi hiba ('%-.64s' nem torolheto, hibakod: %d)"
+	ita "Errore durante la cancellazione del database (impossibile cancellare '%-.64s', errno: %d)"
+	jpn "¥Ç¼¥¿¥Ù¼¥¹Ç´þ¥¨¥é ('%-.64s' ¤òüÞ»¤órrno: %d)"
+	kor "µ¥Àź£À½º Á°Å¿¡·¯('%-.64s'¸¦ »èÇ ¼öÀ´ÏÙ ¿¡·¯¹ø%d)"
+	nor "Feil ved fjerning (drop) av databasen (kan ikke slette '%-.64s', feil %d)"
+	norwegian-ny "Feil ved fjerning (drop) av databasen (kan ikkje slette '%-.64s', feil %d)"
+	pol "B³?d podczas usuwania bazy danych (nie mo¿na usun?æ%-.64s', b³?d %d)"
+	por "Erro ao eliminar banco de dados (nãpode eliminar '%-.64s' - erro no. %d)"
+	rum "Eroare dropuind baza de date (nu pot sa sterg '%-.64s', Eroare: %d)"
+	rus "ïÂÁÐÉÕÁÅÉ ÂÚ ÄÎÙ (Î×ÚÏÎ ÕÁÉØ'%-.64s', ÏÉË: %d)"
+	serbian "Ne mogu da izbrišem bazu (ne mogu da izbrišem '%-.64s', errno: %d)"
+	slo "Chyba pri mazaníatabá (nemô zmaza» '%-.64s', chybový %d)"
+	spa "Error eliminando la base de datos(no puedo borrar '%-.64s', error %d)"
+	swe "Fel vid radering av databasen (Kan inte radera '%-.64s'. Felkod: %d)"
+	ukr "îÍÖ ×ÄÌÔ ÂÚ ÄÎÉ (îÍÖ ×ÄÌÔ '%-.64s', ÐÍÌÁ %d)"
+ER_DB_DROP_RMDIR  
+	cze "Chyba p-Bø¹eníatabá (nemohu vymazat adresá'%-.64s', chyba %d)"
+	dan "Fejl ved sletting af database (kan ikke slette folderen '%-.64s', Fejlkode %d)"
+	nla "Fout bij verwijderen database (kan rmdir '%-.64s' niet uitvoeren, Errcode: %d)"
+	eng "Error dropping database (can't rmdir '%-.64s', errno: %d)"
+	jps "ƒf[ƒ^ƒx[ƒX”jŠü[ ('%-.64s' ‚ðdir ‚Å«‚ܹ‚ñrrno: %d)",
+	est "Viga andmebaasi kustutamisel (ei suuda kustutada kataloogi '%-.64s', veakood: %d)"
+	fre "Erreur en effaçt la base (rmdir '%-.64s', erreur %d)"
+	ger "Fehler beim Löen der Datenbank (Verzeichnis '%-.64s' kann nicht gelöt werden, Fehler: %d)"
+	greek "Ðñéô ðëáá ôéñ ôâçäïí (áíç äãöïöëõ-.64s', êéòèòd)"
+	hun "Adatbazis megszuntetesi hiba ('%-.64s' nem szuntetheto meg, hibakod: %d)"
+	ita "Errore durante la cancellazione del database (impossibile rmdir '%-.64s', errno: %d)"
+	jpn "¥Ç¼¥¿¥Ù¼¥¹Ç´þ¥¨¥é ('%-.64s' ¤òdir ¤Ç­¤Þ»¤órrno: %d)"
+	kor "µ¥Àź£À½º Á°Å¿¡·¯(rmdir '%-.64s'¸¦ Ç ¼öÀ´ÏÙ ¿¡·¯¹ø%d)"
+	nor "Feil ved sletting av database (kan ikke slette katalogen '%-.64s', feil %d)"
+	norwegian-ny "Feil ved sletting av database (kan ikkje slette katalogen '%-.64s', feil %d)"
+	pol "B³?d podczas usuwania bazy danych (nie mo¿na wykonaæmdir '%-.64s', b³?d %d)"
+	por "Erro ao eliminar banco de dados (nãpode remover diretó '%-.64s' - erro no. %d)"
+	rum "Eroare dropuind baza de date (nu pot sa rmdir '%-.64s', Eroare: %d)"
+	rus "îÏÍÖÏÕÁÉØÂÚ ÄÎÙ (Î×ÚÏÎ ÕÁÉØËÔÌÇ'%-.64s', ÏÉË: %d)"
+	serbian "Ne mogu da izbrišem bazu (ne mogu da izbrišem direktorijum '%-.64s', errno: %d)"
+	slo "Chyba pri mazaníatabá (nemô vymaza» adresá'%-.64s', chybový %d)"
+	spa "Error eliminando la base de datos (No puedo borrar directorio '%-.64s', error %d)"
+	swe "Fel vid radering av databasen (Kan inte radera biblioteket '%-.64s'. Felkod: %d)"
+	ukr "îÍÖ ×ÄÌÔ ÂÚ ÄÎÉ (îÍÖ ×ÄÌÔ ÔË '%-.64s', ÐÍÌÁ %d)"
+ER_CANT_DELETE_FILE  
+	cze "Chyba p-Bømazu '%-.64s' (chybový %d)"
+	dan "Fejl ved sletning af '%-.64s' (Fejlkode: %d)"
+	nla "Fout bij het verwijderen van '%-.64s' (Errcode: %d)"
+	eng "Error on delete of '%-.64s' (errno: %d)"
+	jps "'%-.64s' ‚Ì킪ƒGƒ‰[ (errno: %d)",
+	est "Viga '%-.64s' kustutamisel (veakood: %d)"
+	fre "Erreur en effaçt '%-.64s' (Errcode: %d)"
+	ger "Fehler beim Löen von '%-.64s' (Fehler: %d)"
+	greek "Ðñéô ðëáá ôéñ '%-.64s' (êéòèòd)"
+	hun "Torlesi hiba: '%-.64s' (hibakod: %d)"
+	ita "Errore durante la cancellazione di '%-.64s' (errno: %d)"
+	jpn "'%-.64s' ¤Î郎¥¨¥é (errno: %d)"
+	kor "'%-.64s' »è Á ¿¡·¯ (¿¡·¯¹ø%d)"
+	nor "Feil ved sletting av '%-.64s' (Feilkode: %d)"
+	norwegian-ny "Feil ved sletting av '%-.64s' (Feilkode: %d)"
+	pol "B³?d podczas usuwania '%-.64s' (Kod b³ê: %d)"
+	por "Erro na remoç de '%-.64s' (erro no. %d)"
+	rum "Eroare incercind sa delete '%-.64s' (Eroare: %d)"
+	rus "ïÂÁÐÉÕÁÅÉ '%-.64s' (ÏÉË: %d)"
+	serbian "Greška pri brisanju '%-.64s' (errno: %d)"
+	slo "Chyba pri mazaní%-.64s' (chybový %d)"
+	spa "Error en el borrado de '%-.64s' (Error: %d)"
+	swe "Kan inte radera filen '%-.64s' (Felkod: %d)"
+	ukr "îÍÖ ×ÄÌÔ '%-.64s' (ÐÍÌÁ %d)"
+ER_CANT_FIND_SYSTEM_REC  
+	cze "Nemohu -Bèt záam v systévéabulce"
+	dan "Kan ikke læ posten i systemfolderen"
+	nla "Kan record niet lezen in de systeem tabel"
+	eng "Can't read record in system table"
+	jps "system table ‚ÌŒƒR[ƒh‚ðÞ–‚ª‚Å«‚ܹ‚ñµ‚½",
+	est "Ei suuda lugeda kirjet süsest tabelist"
+	fre "Ne peut lire un enregistrement de la table 'system'"
+	ger "Datensatz in der Systemtabelle nicht lesbar"
+	greek "ÁýçííçãáòüêôóÞáò	hun "Nem olvashato rekord a rendszertablaban"
+	ita "Impossibile leggere il record dalla tabella di sistema"
+	jpn "system table ¤Î졼¥Éòब¤Ç­¤Þ»¤ó·¤¿"
+	kor "system ÅÀºí¼­ ·¹Äµå ÀÀ ¼ö½ÀÏÙ"
+	nor "Kan ikke lese posten i systemkatalogen"
+	norwegian-ny "Kan ikkje lese posten i systemkatalogen"
+	pol "Nie mo¿na odczytaæekordu z tabeli systemowej"
+	por "Nãpode ler um registro numa tabela do sistema"
+	rum "Nu pot sa citesc cimpurile in tabla de system (system table)"
+	rus "îÏÍÖÏÐÏÉÁØÚÐÓ ×ÓÓÅÎÊÔÂÉÅ
+	serbian "Ne mogu da proèam slog iz sistemske tabele"
+	slo "Nemô èa» záam v systévej tabuµke"
+	spa "No puedo leer el registro en la tabla del sistema"
+	swe "Hittar inte posten i systemregistret"
+	ukr "îÍÖ ÚÉÁÉÚÐÓÚÓÓÅΧ ÔÂɦ"
+ER_CANT_GET_STAT  
+	cze "Nemohu z-Bíat stav '%-.64s' (chybový %d)"
+	dan "Kan ikke læ status af '%-.64s' (Fejlkode: %d)"
+	nla "Kan de status niet krijgen van '%-.64s' (Errcode: %d)"
+	eng "Can't get status of '%-.64s' (errno: %d)"
+	jps "'%-.64s' ‚ÌXƒeƒCƒ^ƒX‚ª“¾‚ç‚ܹ‚ñerrno: %d)",
+	est "Ei suuda lugeda '%-.64s' olekut (veakood: %d)"
+	fre "Ne peut obtenir le status de '%-.64s' (Errcode: %d)"
+	ger "Kann Status von '%-.64s' nicht ermitteln (Fehler: %d)"
+	greek "ÁýçÞçëïñ ã ôêÜô ô'%-.64s' (êéòèòd)"
+	hun "A(z) '%-.64s' statusza nem allapithato meg (hibakod: %d)"
+	ita "Impossibile leggere lo stato di '%-.64s' (errno: %d)"
+	jpn "'%-.64s' ¤Î¹¥Æ¤¥¿¥¹¤¬Æ¤é¤Þ»¤óerrno: %d)"
+	kor "'%-.64s'À »ó¦ ¾ò¸ø´ÏÙ (¿¡·¯¹ø%d)"
+	nor "Kan ikke lese statusen til '%-.64s' (Feilkode: %d)"
+	norwegian-ny "Kan ikkje lese statusen til '%-.64s' (Feilkode: %d)"
+	pol "Nie mo¿na otrzymaætatusu '%-.64s' (Kod b³ê: %d)"
+	por "Nãpode obter o status de '%-.64s' (erro no. %d)"
+	rum "Nu pot sa obtin statusul lui '%-.64s' (Eroare: %d)"
+	rus "îÏÍÖÏÐÌÞÔ ÓÁÕÎÀÉÆÒÁÉ Ï'%-.64s' (ÏÉË: %d)"
+	serbian "Ne mogu da dobijem stanje file-a '%-.64s' (errno: %d)"
+	slo "Nemô zisti» stav '%-.64s' (chybový %d)"
+	spa "No puedo obtener el estado de '%-.64s' (Error: %d)"
+	swe "Kan inte lä filinformationen (stat) frå'%-.64s' (Felkod: %d)"
+	ukr "îÍÖ ÏÒÍÔ ÓÁÕ '%-.64s' (ÐÍÌÁ %d)"
+ER_CANT_GET_WD  
+	cze "Chyba p-Bøi¹»ová pracovnídresá(chybový %d)"
+	dan "Kan ikke læ aktive folder (Fejlkode: %d)"
+	nla "Kan de werkdirectory niet krijgen (Errcode: %d)"
+	eng "Can't get working directory (errno: %d)"
+	jps "working directory ‚ð邪‚Å«‚ܹ‚ñµ‚½ (errno: %d)",
+	est "Ei suuda identifitseerida jooksvat kataloogi (veakood: %d)"
+	fre "Ne peut obtenir le rértoire de travail (Errcode: %d)"
+	ger "Kann Arbeitsverzeichnis nicht ermitteln (Fehler: %d)"
+	greek "Ïöëòãßòíñê(êéòèòd)"
+	hun "A munkakonyvtar nem allapithato meg (hibakod: %d)"
+	ita "Impossibile leggere la directory di lavoro (errno: %d)"
+	jpn "working directory ¤ò뤬¤Ç­¤Þ»¤ó·¤¿ (errno: %d)"
+	kor "¼öµð並 ÃÁ ¸ø´ÏÙ (¿¡·¯¹ø%d)"
+	nor "Kan ikke lese aktiv katalog(Feilkode: %d)"
+	norwegian-ny "Kan ikkje lese aktiv katalog(Feilkode: %d)"
+	pol "Nie mo¿na rozpoznaæktualnego katalogu (Kod b³ê: %d)"
+	por "Nãpode obter o diretó corrente (erro no. %d)"
+	rum "Nu pot sa obtin directorul current (working directory) (Eroare: %d)"
+	rus "îÏÍÖÏÏÒÄÌÔ ÒÂÞÊËÔÌÇ(ÏÉË: %d)"
+	serbian "Ne mogu da dobijem trenutni direktorijum (errno: %d)"
+	slo "Nemô zisti» pracovnýsá(chybový %d)"
+	spa "No puedo acceder al directorio (Error: %d)"
+	swe "Kan inte inte lä aktivt bibliotek. (Felkod: %d)"
+	ukr "îÍÖ ×ÚÁÉÉÒÂÞ ÔË (ÐÍÌÁ %d)"
+ER_CANT_LOCK  
+	cze "Nemohu uzamknout soubor (chybov-Bý %d)"
+	dan "Kan ikke lå fil (Fejlkode: %d)"
+	nla "Kan de file niet blokeren (Errcode: %d)"
+	eng "Can't lock file (errno: %d)"
+	jps "ƒtƒ ƒCƒ‹‚ðbƒN‚Å«‚ܹ‚ñrrno: %d)",
+	est "Ei suuda lukustada faili (veakood: %d)"
+	fre "Ne peut verrouiller le fichier (Errcode: %d)"
+	ger "Datei kann nicht gesperrt werden (Fehler: %d)"
+	greek "Ô áå ä ìñíêéè (êéòèòd)"
+	hun "A file nem zarolhato. (hibakod: %d)"
+	ita "Impossibile il locking il file (errno: %d)"
+	jpn "¥Õ¡¥¤¥ë¥í¥¯¤Ç­¤Þ»¤órrno: %d)"
+	kor "ÈÀÀ À±×öck) ¸ø´ÏÙ (¿¡·¯¹ø%d)"
+	nor "Kan ikke lå fila (Feilkode: %d)"
+	norwegian-ny "Kan ikkje lå fila (Feilkode: %d)"
+	pol "Nie mo¿na zablokowaæliku (Kod b³ê: %d)"
+	por "Nãpode travar o arquivo (erro no. %d)"
+	rum "Nu pot sa lock fisierul (Eroare: %d)"
+	rus "îÏÍÖÏÐÓÁÉØÂÏÉÏË Î ÆÊÅ(ÏÉË: %d)"
+	serbian "Ne mogu da zakljuè file (errno: %d)"
+	slo "Nemô zamknúbor (chybový %d)"
+	spa "No puedo bloquear archivo: (Error: %d)"
+	swe "Kan inte lå filen. (Felkod: %d)"
+	ukr "îÍÖ ÚÂÏÕÁÉÆÊ (ÐÍÌÁ %d)"
+ER_CANT_OPEN_FILE  
+	cze "Nemohu otev-Bøoubor '%-.64s' (chybový %d)"
+	dan "Kan ikke åe fil: '%-.64s' (Fejlkode: %d)"
+	nla "Kan de file '%-.64s' niet openen (Errcode: %d)"
+	eng "Can't open file: '%-.64s' (errno: %d)"
+	jps "'%-.64s' ƒtƒ ƒCƒ‹‚ð­Ž–‚ª‚Å«‚ܹ‚ñrrno: %d)",
+	est "Ei suuda avada faili '%-.64s' (veakood: %d)"
+	fre "Ne peut ouvrir le fichier: '%-.64s' (Errcode: %d)"
+	ger "Kann Datei '%-.64s' nicht öen (Fehler: %d)"
+	greek "Äíßáäá íáéåôñï'%-.64s' (êéòèòd)"
+	hun "A '%-.64s' file nem nyithato meg (hibakod: %d)"
+	ita "Impossibile aprire il file: '%-.64s' (errno: %d)"
+	jpn "'%-.64s' ¥Õ¡¥¤¥ë³«¤¯»öÇ­¤Þ»¤órrno: %d)"
+	kor "ÈÀÀ ¿­Á ¸ø´ÏÙ: '%-.64s' (¿¡·¯¹ø%d)"
+	nor "Kan ikke åe fila: '%-.64s' (Feilkode: %d)"
+	norwegian-ny "Kan ikkje åe fila: '%-.64s' (Feilkode: %d)"
+	pol "Nie mo¿na otworzyæliku: '%-.64s' (Kod b³ê: %d)"
+	por "Nãpode abrir o arquivo '%-.64s' (erro no. %d)"
+	rum "Nu pot sa deschid fisierul: '%-.64s' (Eroare: %d)"
+	rus "îÏÍÖÏÏËÙØÆÊ: '%-.64s' (ÏÉË: %d)"
+	serbian "Ne mogu da otvorim file: '%-.64s' (errno: %d)"
+	slo "Nemô otvori» sú '%-.64s' (chybový %d)"
+	spa "No puedo abrir archivo: '%-.64s' (Error: %d)"
+	swe "Kan inte anväa '%-.64s' (Felkod: %d)"
+	ukr "îÍÖ ×ÄÒÔ ÆÊ: '%-.64s' (ÐÍÌÁ %d)"
+ER_FILE_NOT_FOUND  
+	cze "Nemohu naj-Bísoubor '%-.64s' (chybový %d)"
+	dan "Kan ikke finde fila: '%-.64s' (Fejlkode: %d)"
+	nla "Kan de file: '%-.64s' niet vinden (Errcode: %d)"
+	eng "Can't find file: '%-.64s' (errno: %d)"
+	jps "'%-.64s' ƒtƒ ƒCƒ‹‚ðt‚¯‚邪‚Å«‚ܹ‚ñrrno: %d)",
+	est "Ei suuda leida faili '%-.64s' (veakood: %d)"
+	fre "Ne peut trouver le fichier: '%-.64s' (Errcode: %d)"
+	ger "Kann Datei '%-.64s' nicht finden (Fehler: %d)"
+	greek "Äíñêôñï'%-.64s' (êéòèòd)"
+	hun "A(z) '%-.64s' file nem talalhato (hibakod: %d)"
+	ita "Impossibile trovare il file: '%-.64s' (errno: %d)"
+	jpn "'%-.64s' ¥Õ¡¥¤¥ë¸«É¤±¤ë¤¬¤Ç­¤Þ»¤órrno: %d)"
+	kor "ÈÀÀ ÃÁ ¸ø´ÏÙ: '%-.64s' (¿¡·¯¹ø%d)"
+	nor "Kan ikke finne fila: '%-.64s' (Feilkode: %d)"
+	norwegian-ny "Kan ikkje finne fila: '%-.64s' (Feilkode: %d)"
+	pol "Nie mo¿na znale¥æliku: '%-.64s' (Kod b³ê: %d)"
+	por "Nãpode encontrar o arquivo '%-.64s' (erro no. %d)"
+	rum "Nu pot sa gasesc fisierul: '%-.64s' (Eroare: %d)"
+	rus "îÏÍÖÏÎÊÉÆÊ: '%-.64s' (ÏÉË: %d)"
+	serbian "Ne mogu da pronaðfile: '%-.64s' (errno: %d)"
+	slo "Nemô ná» sú '%-.64s' (chybový %d)"
+	spa "No puedo encontrar archivo: '%-.64s' (Error: %d)"
+	swe "Hittar inte filen '%-.64s' (Felkod: %d)"
+	ukr "îÍÖ ÚÁÔ ÆÊ: '%-.64s' (ÐÍÌÁ %d)"
+ER_CANT_READ_DIR  
+	cze "Nemohu -Bèt adresá'%-.64s' (chybový %d)"
+	dan "Kan ikke læ folder '%-.64s' (Fejlkode: %d)"
+	nla "Kan de directory niet lezen van '%-.64s' (Errcode: %d)"
+	eng "Can't read dir of '%-.64s' (errno: %d)"
+	jps "'%-.64s' ƒfƒBƒŒƒNƒgƒŠ‚ª“Çßܹ‚ñrrno: %d)",
+	est "Ei suuda lugeda kataloogi '%-.64s' (veakood: %d)"
+	fre "Ne peut lire le rértoire de '%-.64s' (Errcode: %d)"
+	ger "Verzeichnis von '%-.64s' nicht lesbar (Fehler: %d)"
+	greek "Äíßáäá íäâôïÜåïô'%-.64s' (êéòèòd)"
+	hun "A(z) '%-.64s' konyvtar nem olvashato. (hibakod: %d)"
+	ita "Impossibile leggere la directory di '%-.64s' (errno: %d)"
+	jpn "'%-.64s' ¥Ç£¥ì¥ÈêƤ᤻¤órrno: %d)"
+	kor "'%-.64s'µð並 ÀÁ ¸ø´ÏÙ (¿¡·¯¹ø%d)"
+	nor "Kan ikke lese katalogen '%-.64s' (Feilkode: %d)"
+	norwegian-ny "Kan ikkje lese katalogen '%-.64s' (Feilkode: %d)"
+	pol "Nie mo¿na odczytaæatalogu '%-.64s' (Kod b³ê: %d)"
+	por "Nãpode ler o diretó de '%-.64s' (erro no. %d)"
+	rum "Nu pot sa citesc directorul '%-.64s' (Eroare: %d)"
+	rus "îÏÍÖÏÐÏÉÁØËÔÌÇ'%-.64s' (ÏÉË: %d)"
+	serbian "Ne mogu da proèam direktorijum '%-.64s' (errno: %d)"
+	slo "Nemô èa» adresá'%-.64s' (chybový %d)"
+	spa "No puedo leer el directorio de '%-.64s' (Error: %d)"
+	swe "Kan inte lä fråbibliotek '%-.64s' (Felkod: %d)"
+	ukr "îÍÖ ÐÏÉÁÉÔË '%-.64s' (ÐÍÌÁ %d)"
+ER_CANT_SET_WD  
+	cze "Nemohu zm-Bìt adresána '%-.64s' (chybový %d)"
+	dan "Kan ikke skifte folder til '%-.64s' (Fejlkode: %d)"
+	nla "Kan de directory niet veranderen naar '%-.64s' (Errcode: %d)"
+	eng "Can't change dir to '%-.64s' (errno: %d)"
+	jps "'%-.64s' ƒfƒBƒŒƒNƒgƒŠ‚Échdir ‚Å«‚ܹ‚ñrrno: %d)",
+	est "Ei suuda siseneda kataloogi '%-.64s' (veakood: %d)"
+	fre "Ne peut changer le rértoire pour '%-.64s' (Errcode: %d)"
+	ger "Kann nicht in das Verzeichnis '%-.64s' wechseln (Fehler: %d)"
+	greek "Áýçëãôôïïêáã ó%-.64s' (êéòèòd)"
+	hun "Konyvtarvaltas nem lehetseges a(z) '%-.64s'-ba. (hibakod: %d)"
+	ita "Impossibile cambiare la directory in '%-.64s' (errno: %d)"
+	jpn "'%-.64s' ¥Ç£¥ì¥Èê chdir ¤Ç­¤Þ»¤órrno: %d)"
+	kor "'%-.64s'µðä·ÎÀµ¿Ç ¼ö¾ú´Ù (¿¡·¯¹ø%d)"
+	nor "Kan ikke skifte katalog til '%-.64s' (Feilkode: %d)"
+	norwegian-ny "Kan ikkje skifte katalog til '%-.64s' (Feilkode: %d)"
+	pol "Nie mo¿na zmieniæatalogu na '%-.64s' (Kod b³ê: %d)"
+	por "Nãpode mudar para o diretó '%-.64s' (erro no. %d)"
+	rum "Nu pot sa schimb directorul '%-.64s' (Eroare: %d)"
+	rus "îÏÍÖÏÐÒÊÉ×ËÔÌÇ'%-.64s' (ÏÉË: %d)"
+	serbian "Ne mogu da promenim direktorijum na '%-.64s' (errno: %d)"
+	slo "Nemô vojs» do adresá '%-.64s' (chybový %d)"
+	spa "No puedo cambiar al directorio de '%-.64s' (Error: %d)"
+	swe "Kan inte byta till '%-.64s' (Felkod: %d)"
+	ukr "îÍÖ ÐÒÊÉÕÔË '%-.64s' (ÐÍÌÁ %d)"
+ER_CHECKREAD  
+	cze "Z-Báam byl zmìn od poslední èní tabulce '%-.64s'"
+	dan "Posten er æret siden sidste læing '%-.64s'"
+	nla "Record is veranderd sinds de laatste lees activiteit in de tabel '%-.64s'"
+	eng "Record has changed since last read in table '%-.64s'"
+	est "Kirje tabelis '%-.64s' on muutunud viimasest lugemisest saadik"
+	fre "Enregistrement modifiéepuis sa derniè lecture dans la table '%-.64s'"
+	ger "Datensatz hat sich seit dem letzten Zugriff auf Tabelle '%-.64s' geäert"
+	greek "Çåñ ÝåáÜåá ôôõáï ðáóç á ôðê'%-.64s'"
+	hun "A(z) '%-.64s' tablaban talalhato rekord megvaltozott az utolso olvasas ota"
+	ita "Il record e` cambiato dall'ultima lettura della tabella '%-.64s'"
+	kor "ÅÀºí%-.64s'¿¡¼­ ¸¶Á¸·À·ÎÀÀ È Record°¡ º¯°æ¾ú´Ù"
+	nor "Posten har blitt endret siden den ble lest '%-.64s'"
+	norwegian-ny "Posten har vorte endra sidan den sist vart lesen '%-.64s'"
+	pol "Rekord zosta³ zmieniony od ostaniego odczytania z tabeli '%-.64s'"
+	por "Registro alterado desde a úa leitura da tabela '%-.64s'"
+	rum "Cimpul a fost schimbat de la ultima citire a tabelei '%-.64s'"
+	rus "úØÉÍÎÌÓ ÓÍÍÎÁÐÓÅÎÊ×ÂÒÉ×ÔÂÉÅ'%-.64s'"
+	serbian "Slog je promenjen od zadnjeg èanja tabele '%-.64s'"
+	slo "Záam bol zmenenýosledné èania v tabuµke '%-.64s'"
+	spa "El registro ha cambiado desde la ultima lectura de la tabla '%-.64s'"
+	swe "Posten har födrats sedan den läes i register '%-.64s'"
+	ukr "ú ÂÌ Ú¦ÎÎ ÚÞÓ ÏÔÎØÇ ÞÔÎÑÚÔÂɦ '%-.64s'"
+ER_DISK_FULL  
+	cze "Disk je pln-Bý, èána uvolnì nìké mía ..."
+	dan "Ikke mere diskplads (%s). Venter påt fårigjort plads..."
+	nla "Schijf vol (%s). Aan het wachten totdat er ruimte vrij wordt gemaakt..."
+	eng "Disk full (%s); waiting for someone to free some space..."
+	jps "Disk full (%s). ’N‚©‚ª‰½‚©‚ðç‚ÜÅÜÁÄ­‚¾‚³‚¢...",
+	est "Ketas tä (%s). Ootame kuni tekib vaba ruumi..."
+	fre "Disque plein (%s). J'attend que quelqu'un libè de l'espace..."
+	ger "Festplatte voll (%-.64s). Warte, bis jemand Platz schafft ..."
+	greek "Äíðå÷òïßê(%s). Ðñë ðìå íåõù÷ò"
+	hun "A lemez megtelt (%s)."
+	ita "Disco pieno (%s). In attesa che qualcuno liberi un po' di spazio..."
+	jpn "Disk full (%s). 䫤¬²¿¤«¤òé¤ÞÇÞÃƯ¤Àµ¤¤..."
+	kor "Disk full (%s). ´Ù¥ »çÀ Á¿ï±î ±â¸³´ÏÙ.."
+	nor "Ikke mer diskplass (%s). Venter på fårigjort plass..."
+	norwegian-ny "Ikkje meir diskplass (%s). Ventar på fårigjort plass..."
+	pol "Dysk pe³ny (%s). Oczekiwanie na zwolnienie miejsca..."
+	por "Disco cheio (%s). Aguardando alguéliberar algum espaç.."
+	rum "Hard-disk-ul este plin (%s). Astept sa se elibereze ceva spatiu..."
+	rus "äËÚÐÌÅ. (%s). ïÄÅ, ÐË ËÏÔ Î ÕÅÅ ÐÓÅÓ ÍÓÒ.."
+	serbian "Disk je pun (%s). Èkam nekoga da doð oslobodi nešto mesta..."
+	slo "Disk je plný, èána uvoµnenie miesta..."
+	spa "Disco lleno (%s). Esperando para que se libere algo de espacio..."
+	swe "Disken äfull (%s). Väar tills det finns ledigt utrymme..."
+	ukr "äËÚÐ×ÅÉ (%s). ÷ËÀ ÄË Ú¦ÌÎÔÓ ÔÏÉÍÓÑ.."
+ER_DUP_KEY 23000 
+	cze "Nemohu zapsat, zdvojen-Bý v tabulce '%-.64s'"
+	dan "Kan ikke skrive, flere ens nø i tabellen '%-.64s'"
+	nla "Kan niet schrijven, dubbele zoeksleutel in tabel '%-.64s'"
+	eng "Can't write; duplicate key in table '%-.64s'"
+	jps "table '%-.64s' ‚Ékey ‚ªd•¡‚µ‚Ä¢‚Ä‘‚«‚±‚ßܹ‚ñ+	est "Ei saa kirjutada, korduv võtabelis '%-.64s'"
+	fre "Ecriture impossible, doublon dans une clée la table '%-.64s'"
+	ger "Kann nicht speichern, Grund: doppelter Schlüin Tabelle '%-.64s'"
+	greek "Äíßáäá çá÷óçé õ÷Þçôðê'%-.64s'"
+	hun "Irasi hiba, duplikalt kulcs a '%-.64s' tablaban."
+	ita "Scrittura impossibile: chiave duplicata nella tabella '%-.64s'"
+	jpn "table '%-.64s' ¤Ëkey ¤¬½Å£¤·¤Æ¤¤Æñ³¤á¤»¤ó	kor "±âÇ ¼öÀ´ÏÙ, ÅÀºí%-.64s'¿¡¼­ Áº¹ Å"
+	nor "Kan ikke skrive, flere like nø i tabellen '%-.64s'"
+	norwegian-ny "Kan ikkje skrive, flere like nyklar i tabellen '%-.64s'"
+	pol "Nie mo¿na zapisaæpowtóne klucze w tabeli '%-.64s'"
+	por "Nãpode gravar. Chave duplicada na tabela '%-.64s'"
+	rum "Nu pot sa scriu (can't write), cheie duplicata in tabela '%-.64s'"
+	rus "îÏÍÖÏÐÏÚÅÔ ÚÐÓ, ÄÂÉÕÝÊÑËÀ ×ÔÂÉÅ'%-.64s'"
+	serbian "Ne mogu da pišem pošto postoji duplirani kljuè tabeli '%-.64s'"
+	slo "Nemô zapí», duplikákµú tabuµke '%-.64s'"
+	spa "No puedo escribir, clave duplicada en la tabla '%-.64s'"
+	swe "Kan inte skriva, dubbel söckel i register '%-.64s'"
+	ukr "îÍÖ ÚÐÓÔ, ÄÂÀÞÊÑËÀ ×ÔÂɦ '%-.64s'"
+ER_ERROR_ON_CLOSE  
+	cze "Chyba p-Bøvíní%-.64s' (chybový %d)"
+	dan "Fejl ved lukning af '%-.64s' (Fejlkode: %d)"
+	nla "Fout bij het sluiten van '%-.64s' (Errcode: %d)"
+	eng "Error on close of '%-.64s' (errno: %d)"
+	est "Viga faili '%-.64s' sulgemisel (veakood: %d)"
+	fre "Erreur a la fermeture de '%-.64s' (Errcode: %d)"
+	ger "Fehler beim Schließn von '%-.64s' (Fehler: %d)"
+	greek "Ðñéô ðëáëíôô%-.64s' (êéòèòd)"
+	hun "Hiba a(z) '%-.64s' zarasakor. (hibakod: %d)"
+	ita "Errore durante la chiusura di '%-.64s' (errno: %d)"
+	kor "'%-.64s'´ÝÂÁ ¿¡·¯ (¿¡·¯¹ø%d)"
+	nor "Feil ved lukking av '%-.64s' (Feilkode: %d)"
+	norwegian-ny "Feil ved lukking av '%-.64s' (Feilkode: %d)"
+	pol "B³?d podczas zamykania '%-.64s' (Kod b³ê: %d)"
+	por "Erro ao fechar '%-.64s' (erro no. %d)"
+	rum "Eroare inchizind '%-.64s' (errno: %d)"
+	rus "ïÂÁÐÉÚËÙÉ '%-.64s' (ÏÉË: %d)"
+	serbian "Greška pri zatvaranju '%-.64s' (errno: %d)"
+	slo "Chyba pri zatvání%-.64s' (chybový %d)"
+	spa "Error en el cierre de '%-.64s' (Error: %d)"
+	swe "Fick fel vid stäning av '%-.64s' (Felkod: %d)"
+	ukr "îÍÖ ÚËÉÉ'%-.64s' (ÐÍÌÁ %d)"
+ER_ERROR_ON_READ  
+	cze "Chyba p-Bøeníouboru '%-.64s' (chybový %d)"
+	dan "Fejl ved læing af '%-.64s' (Fejlkode: %d)"
+	nla "Fout bij het lezen van file '%-.64s' (Errcode: %d)"
+	eng "Error reading file '%-.64s' (errno: %d)"
+	jps "'%-.64s' ƒtƒ ƒCƒ‹‚ÌÇÝž‚ÝGƒ‰[ (errno: %d)",
+	est "Viga faili '%-.64s' lugemisel (veakood: %d)"
+	fre "Erreur en lecture du fichier '%-.64s' (Errcode: %d)"
+	ger "Fehler beim Lesen der Datei '%-.64s' (Fehler: %d)"
+	greek "Ðü êÜôáãóïáåõ-.64s' (êéòèòd)"
+	hun "Hiba a '%-.64s'file olvasasakor. (hibakod: %d)"
+	ita "Errore durante la lettura del file '%-.64s' (errno: %d)"
+	jpn "'%-.64s' ¥Õ¡¥¤¥ëƤßþ¤ß¨¥é (errno: %d)"
+	kor "'%-.64s'ÈÀ À±â¡·¯ (¿¡·¯¹ø%d)"
+	nor "Feil ved lesing av '%-.64s' (Feilkode: %d)"
+	norwegian-ny "Feil ved lesing av '%-.64s' (Feilkode: %d)"
+	pol "B³?d podczas odczytu pliku '%-.64s' (Kod b³ê: %d)"
+	por "Erro ao ler arquivo '%-.64s' (erro no. %d)"
+	rum "Eroare citind fisierul '%-.64s' (errno: %d)"
+	rus "ïÂÁÞÅÉ ÆÊÁ'%-.64s' (ÏÉË: %d)"
+	serbian "Greška pri èanju file-a '%-.64s' (errno: %d)"
+	slo "Chyba pri èaníú '%-.64s' (chybový %d)"
+	spa "Error leyendo el fichero '%-.64s' (Error: %d)"
+	swe "Fick fel vid läing av '%-.64s' (Felkod %d)"
+	ukr "îÍÖ ÐÏÉÁÉÆÊ '%-.64s' (ÐÍÌÁ %d)"
+ER_ERROR_ON_RENAME  
+	cze "Chyba p-Bøejmenová '%-.64s' na '%-.64s' (chybový %d)"
+	dan "Fejl ved omdøg af '%-.64s' til '%-.64s' (Fejlkode: %d)"
+	nla "Fout bij het hernoemen van '%-.64s' naar '%-.64s' (Errcode: %d)"
+	eng "Error on rename of '%-.64s' to '%-.64s' (errno: %d)"
+	jps "'%-.64s' ‚ð-.64s' ‚Érename ‚Å«‚ܹ‚ñrrno: %d)",
+	est "Viga faili '%-.64s' üimetamisel '%-.64s'-ks (veakood: %d)"
+	fre "Erreur en renommant '%-.64s' en '%-.64s' (Errcode: %d)"
+	ger "Fehler beim Umbenennen von '%-.64s' in '%-.64s' (Fehler: %d)"
+	greek "Ðü êÜôìïìß ôáåõ-.64s' to '%-.64s' (êéòèòd)"
+	hun "Hiba a '%-.64s' file atnevezesekor. (hibakod: %d)"
+	ita "Errore durante la rinominazione da '%-.64s' a '%-.64s' (errno: %d)"
+	jpn "'%-.64s' ¤ò-.64s' ¤Ërename ¤Ç­¤Þ»¤órrno: %d)"
+	kor "'%-.64s'¸¦ '%-.64s'·ÎÀ¸§ º¯°æ ¿¡·¯ (¿¡·¯¹ø%d)"
+	nor "Feil ved omdø av '%-.64s' til '%-.64s' (Feilkode: %d)"
+	norwegian-ny "Feil ved omdøg av '%-.64s' til '%-.64s' (Feilkode: %d)"
+	pol "B³?d podczas zmieniania nazwy '%-.64s' na '%-.64s' (Kod b³ê: %d)"
+	por "Erro ao renomear '%-.64s' para '%-.64s' (erro no. %d)"
+	rum "Eroare incercind sa renumesc '%-.64s' in '%-.64s' (errno: %d)"
+	rus "ïÂÁÐÉÐÒÉÅÏÁÉ '%-.64s' ×'%-.64s' (ÏÉË: %d)"
+	serbian "Greška pri promeni imena '%-.64s' na '%-.64s' (errno: %d)"
+	slo "Chyba pri premenování%-.64s' na '%-.64s' (chybový %d)"
+	spa "Error en el renombrado de '%-.64s' a '%-.64s' (Error: %d)"
+	swe "Kan inte byta namn frå'%-.64s' till '%-.64s' (Felkod: %d)"
+	ukr "îÍÖ ÐÒÊÅÕÁÉ'%-.64s' Õ'%-.64s' (ÐÍÌÁ %d)"
+ER_ERROR_ON_WRITE  
+	cze "Chyba p-Bøpisu do souboru '%-.64s' (chybový %d)"
+	dan "Fejl ved skriving av filen '%-.64s' (Fejlkode: %d)"
+	nla "Fout bij het wegschrijven van file '%-.64s' (Errcode: %d)"
+	eng "Error writing file '%-.64s' (errno: %d)"
+	jps "'%-.64s' ƒtƒ ƒCƒ‹‚ð­Ž–‚ª‚Å«‚ܹ‚ñrrno: %d)",
+	est "Viga faili '%-.64s' kirjutamisel (veakood: %d)"
+	fre "Erreur d'éiture du fichier '%-.64s' (Errcode: %d)"
+	ger "Fehler beim Speichern der Datei '%-.64s' (Fehler: %d)"
+	greek "Ðü êÜôáèåçïáåõ-.64s' (êéòèòd)"
+	hun "Hiba a '%-.64s' file irasakor. (hibakod: %d)"
+	ita "Errore durante la scrittura del file '%-.64s' (errno: %d)"
+	jpn "'%-.64s' ¥Õ¡¥¤¥ë½ñöÇ­¤Þ»¤órrno: %d)"
+	kor "'%-.64s'ÈÀ ±â Á ¿¡·¯ (¿¡·¯¹ø%d)"
+	nor "Feil ved skriving av fila '%-.64s' (Feilkode: %d)"
+	norwegian-ny "Feil ved skriving av fila '%-.64s' (Feilkode: %d)"
+	pol "B³?d podczas zapisywania pliku '%-.64s' (Kod b³ê: %d)"
+	por "Erro ao gravar arquivo '%-.64s' (erro no. %d)"
+	rum "Eroare scriind fisierul '%-.64s' (errno: %d)"
+	rus "ïÂÁÚÐÓ ×ÆÊ '%-.64s' (ÏÉË: %d)"
+	serbian "Greška pri upisu '%-.64s' (errno: %d)"
+	slo "Chyba pri záse do sú '%-.64s' (chybový %d)"
+	spa "Error escribiendo el archivo '%-.64s' (Error: %d)"
+	swe "Fick fel vid skrivning till '%-.64s' (Felkod %d)"
+	ukr "îÍÖ ÚÐÓÔ ÆÊ '%-.64s' (ÐÍÌÁ %d)"
+ER_FILE_USED  
+	cze "'%-.64s' je zam-Bè proti zmìm"
+	dan "'%-.64s' er lå mod opdateringer"
+	nla "'%-.64s' is geblokeerd tegen veranderingen"
+	eng "'%-.64s' is locked against change"
+	jps "'%-.64s' ‚͍ƒbƒN‚³‚ê‚¢‚Ü·",
+	est "'%-.64s' on lukustatud muudatuste vastu"
+	fre "'%-.64s' est verrouilléontre les modifications"
+	ger "'%-.64s' ist füerungen gesperrt"
+	greek "'%-.64s' ä åôïáááò	hun "'%-.64s' a valtoztatas ellen zarolva"
+	ita "'%-.64s' e` soggetto a lock contro i cambiamenti"
+	jpn "'%-.64s' ¤Ïí¥¯¤µ¤ì¤¤¤Þ¹"
+	kor "'%-.64s'°¡ º¯°æ ¼öµµ·ÏÀ°ÜÖ¾´ÏÙ"
+	nor "'%-.64s' er lå mot oppdateringer"
+	norwegian-ny "'%-.64s' er lå mot oppdateringar"
+	pol "'%-.64s' jest zablokowany na wypadek zmian"
+	por "'%-.64s' estáom travamento contra alteraçs"
+	rum "'%-.64s' este blocat pentry schimbari (loccked against change)"
+	rus "'%-.64s' ÚÂÏÉÏÁ ÄÑÉÍÎÎÊ
+	serbian "'%-.64s' je zakljuè za upis"
+	slo "'%-.64s' je zamknutýi zmená
+	spa "'%-.64s' esta bloqueado contra cambios"
+	swe "'%-.64s' älå mot använing"
+	ukr "'%-.64s' ÚÂÏÏÁÉ Î ×ÅÅÎ Ú¦Î
+ER_FILSORT_ABORT  
+	cze "T-Bøíøeno"
+	dan "Sortering afbrudt"
+	nla "Sorteren afgebroken"
+	eng "Sort aborted"
+	jps "Sort ’†’f",
+	est "Sorteerimine katkestatud"
+	fre "Tri alphabéque abandonné+	ger "Sortiervorgang abgebrochen"
+	greek "Çääáááíéòõç"
+	hun "Sikertelen rendezes"
+	ita "Operazione di ordinamento abbandonata"
+	jpn "Sort ÃÃ"
+	kor "¼Ò®°¡ Á´ÜÇú´Ù"
+	nor "Sortering avbrutt"
+	norwegian-ny "Sortering avbrote"
+	pol "Sortowanie przerwane"
+	por "Ordenaç abortada"
+	rum "Sortare intrerupta"
+	rus "óÉÏË ÐÅ×Î"
+	serbian "Sortiranje je prekinuto"
+	slo "Triedenie preru¹ené+	spa "Ordeancion cancelada"
+	swe "Sorteringen avbruten"
+	ukr "óÕÁÎ ÐÒÒÁÏ
+ER_FORM_NOT_FOUND  
+	cze "Pohled '%-.64s' pro '%-.64s' neexistuje"
+	dan "View '%-.64s' eksisterer ikke for '%-.64s'"
+	nla "View '%-.64s' bestaat niet voor '%-.64s'"
+	eng "View '%-.64s' doesn't exist for '%-.64s'"
+	jps "View '%-.64s' ‚ª '%-.64s' ‚É肳‚ê‚¢‚ܹ‚ñ+	est "Vaade '%-.64s' ei eksisteeri '%-.64s' jaoks"
+	fre "La vue (View) '%-.64s' n'existe pas pour '%-.64s'"
+	ger "View '%-.64s' existiert fü.64s' nicht"
+	greek "Ô View '%-.64s' ä õ÷ã '%-.64s'"
+	hun "A(z) '%-.64s' nezet nem letezik a(z) '%-.64s'-hoz"
+	ita "La view '%-.64s' non esiste per '%-.64s'"
+	jpn "View '%-.64s' ¤¬ '%-.64s' ¤Ëꤵ¤ì¤¤¤Þ»¤ó	kor "ºä%-.64s'°¡ '%-.64s'¿¡¼­´ÂÁÀÇÁ ¾Ê¾´ÏÙ"
+	nor "View '%-.64s' eksisterer ikke for '%-.64s'"
+	norwegian-ny "View '%-.64s' eksisterar ikkje for '%-.64s'"
+	pol "Widok '%-.64s' nie istnieje dla '%-.64s'"
+	por "Visã'%-.64s' nãexiste para '%-.64s'"
+	rum "View '%-.64s' nu exista pentru '%-.64s'"
+	rus "ðÓÁÌÎÅ'%-.64s' Î ÓÝÓ×Å ÄÑ'%-.64s'"
+	serbian "View '%-.64s' ne postoji za '%-.64s'"
+	slo "Pohµad '%-.64s' neexistuje pre '%-.64s'"
+	spa "La vista '%-.64s' no existe para '%-.64s'"
+	swe "Formulä'%-.64s' finns inte i '%-.64s'"
+	ukr "÷Ñ '%-.64s' Î ¦ÓÕ ÄÑ'%-.64s'"
+ER_GET_ERRNO  
+	cze "Obsluha tabulky vr-Bála chybu %d"
+	dan "Modtog fejl %d fra tabel håteringen"
+	nla "Fout %d van tabel handler"
+	eng "Got error %d from storage engine"
+	est "Tabeli handler tagastas vea %d"
+	fre "Reçl'erreur %d du handler de la table"
+	ger "Fehler %d (Speicher-Engine)"
+	greek "ÅÞèìõëï %d á ô÷éÞðê(table handler)"
+	hun "%d hibajelzes a tablakezelotol"
+	ita "Rilevato l'errore %d dal gestore delle tabelle"
+	jpn "Got error %d from table handler"
+	kor "ÅÀºíandler¿¡¼­ %d ¿¡·¯°¡ ¹ßý½ÀÏÙ"
+	nor "Mottok feil %d fra tabell håterer"
+	norwegian-ny "Mottok feil %d fra tabell handterar"
+	pol "Otrzymano b³?d %d z obs³ugi tabeli"
+	por "Obteve erro %d no manipulador de tabelas"
+	rum "Eroarea %d obtinuta din handlerul tabelei"
+	rus "ðÞÎ ÏÉË %d Ï ÏÒÂÔÉÁÔÂÉ"
+	serbian "Handler tabela je vratio grešku %d"
+	slo "Obsluha tabuµky vrála chybu %d"
+	spa "Error %d desde el manejador de la tabla"
+	swe "Fick felkod %d frådatabashanteraren"
+	ukr "ïÉÁÏÐÍÌÕ%d ×ÄÄÓÒÐÏÁÔÂɦ"
+ER_ILLEGAL_HA  
+	cze "Obsluha tabulky '%-.64s' nem-Báento parametr"
+	dan "Denne mulighed eksisterer ikke for tabeltypen '%-.64s'"
+	nla "Tabel handler voor '%-.64s' heeft deze optie niet"
+	eng "Table storage engine for '%-.64s' doesn't have this option"
+	est "Tabeli '%-.64s' handler ei toeta antud operatsiooni"
+	fre "Le handler de la table '%-.64s' n'a pas cette option"
+	ger "Diese Option gibt es nicht (Speicher-Engine fü.64s')"
+	greek "Ï÷éÞ ðê(table handler) ã '%-.64s' ä äèåáÞôåëÞ
+	hun "A(z) '%-.64s' tablakezelonek nincs ilyen opcioja"
+	ita "Il gestore delle tabelle per '%-.64s' non ha questa opzione"
+	jpn "Table handler for '%-.64s' doesn't have this option"
+	kor "'%-.64s'À ÅÀºíandler´ÂÀ·¯Ç ¿ÉÇ» Á°ø ¾Ê¾´ÏÙ"
+	nor "Tabell håtereren for '%-.64s' har ikke denne muligheten"
+	norwegian-ny "Tabell håteraren for '%-.64s' har ikkje denne moglegheita"
+	pol "Obs³uga tabeli '%-.64s' nie posiada tej opcji"
+	por "Manipulador de tabela para '%-.64s' nãtem esta opç"
+	rum "Handlerul tabelei pentru '%-.64s' nu are aceasta optiune"
+	rus "ïÁÏÞËÔÂÉÙ'%-.64s' Î ÐÄÅÖ×Å ÜÕ×ÚÏÎÓØ
+	serbian "Handler tabela za '%-.64s' nema ovu opciju"
+	slo "Obsluha tabuµky '%-.64s' nemáento parameter"
+	spa "El manejador de la tabla de '%-.64s' no tiene esta opcion"
+	swe "Registrets databas har inte denna facilitet"
+	ukr "äËÉÔÒÔÂɦ '%-.64s' Πͤ ä§ ×ÁÔ×Ó¦"
+ER_KEY_NOT_FOUND  
+	cze "Nemohu naj-Bízáam v '%-.64s'"
+	dan "Kan ikke finde posten i '%-.64s'"
+	nla "Kan record niet vinden in '%-.64s'"
+	eng "Can't find record in '%-.64s'"
+	jps "'%-.64s'‚ÌÈ©‚ÉŒƒR[ƒh‚ªŒ©•t‚©‚肹‚ñ+	est "Ei suuda leida kirjet '%-.64s'-s"
+	fre "Ne peut trouver l'enregistrement dans '%-.64s'"
+	ger "Kann Datensatz in '%-.64s' nicht finden"
+	greek "Áýçíñ åñòï%-.64s'"
+	hun "Nem talalhato a rekord '%-.64s'-ben"
+	ita "Impossibile trovare il record in '%-.64s'"
+	jpn "'%-.64s'¤ÎÊ«¤Ë졼¥É¬¸«É¤«¤ê¤»¤ó	kor "'%-.64s'¿¡¼­ ·¹Äµå ÃÀ ¼öÀ´ÏÙ"
+	nor "Kan ikke finne posten i '%-.64s'"
+	norwegian-ny "Kan ikkje finne posten i '%-.64s'"
+	pol "Nie mo¿na znale¥æekordu w '%-.64s'"
+	por "Nãpode encontrar registro em '%-.64s'"
+	rum "Nu pot sa gasesc recordul in '%-.64s'"
+	rus "îÏÍÖÏÎÊÉÚÐÓ ×'%-.64s'"
+	serbian "Ne mogu da pronaðslog u '%-.64s'"
+	slo "Nemô ná» záam v '%-.64s'"
+	spa "No puedo encontrar el registro en '%-.64s'"
+	swe "Hittar inte posten"
+	ukr "îÍÖ ÚÐÓÔ Õ'%-.64s'"
+ER_NOT_FORM_FILE  
+	cze "Nespr-Báánformace v souboru '%-.64s'"
+	dan "Forkert indhold i: '%-.64s'"
+	nla "Verkeerde info in file: '%-.64s'"
+	eng "Incorrect information in file: '%-.64s'"
+	jps "ƒtƒ ƒCƒ‹ '%-.64s' ‚Ìinfo ‚ªŠÔá‚Ä¢‚邤‚Å·",
+	est "Vigane informatsioon failis '%-.64s'"
+	fre "Information erronnédans le fichier: '%-.64s'"
+	ger "Falsche Information in Datei '%-.64s'"
+	greek "Ëè ðïñ óáå: '%-.64s'"
+	hun "Ervenytelen info a file-ban: '%-.64s'"
+	ita "Informazione errata nel file: '%-.64s'"
+	jpn "¥Õ¡¥¤¥ë%-.64s' ¤Îinfo ¤¬´Öã¤Æ¤¤ë¤¦¤Ç¹"
+	kor "ÈÀÀ ºÎ¤ÈÇ Áº¸: '%-.64s'"
+	nor "Feil informasjon i filen: '%-.64s'"
+	norwegian-ny "Feil informasjon i fila: '%-.64s'"
+	pol "Niew³a?ciwa informacja w pliku: '%-.64s'"
+	por "Informaç incorreta no arquivo '%-.64s'"
+	rum "Informatie incorecta in fisierul: '%-.64s'"
+	rus "îÏÒËÎÑÉÆÒÁÉ ×ÆÊÅ'%-.64s'"
+	serbian "Pogrešna informacija u file-u: '%-.64s'"
+	slo "Nespráa informáa v sú: '%-.64s'"
+	spa "Informacion erronea en el archivo: '%-.64s'"
+	swe "Felaktig fil: '%-.64s'"
+	ukr "èÎ ¦ÎÏÍÃÑÕÆʦ: '%-.64s'"
+ER_NOT_KEYFILE  
+	cze "Nespr-Báý pro tabulku '%-.64s'; pokuste se ho opravit"
+	dan "Fejl i indeksfilen til tabellen '%-.64s'; prø reparere den"
+	nla "Verkeerde zoeksleutel file voor tabel: '%-.64s'; probeer het te repareren"
+	eng "Incorrect key file for table '%-.64s'; try to repair it"
+	jps "'%-.64s' ƒe[ƒuƒ‹‚Ìkey file ‚ªŠÔá‚Ä¢‚邤‚Å·. C•œ‚ðÄ­‚¾‚³‚¢",
+	est "Tabeli '%-.64s' võfail on vigane; proovi seda parandada"
+	fre "Index corrompu dans la table: '%-.64s'; essayez de le rérer"
+	ger "Fehlerhafte Index-Datei füelle '%-.64s'; versuche zu reparieren"
+	greek "Ëè áå ôíéòey file) ã ôðê '%-.64s'; Ðñë äñôï
+	hun "Ervenytelen kulcsfile a tablahoz: '%-.64s'; probalja kijavitani!"
+	ita "File chiave errato per la tabella : '%-.64s'; prova a riparalo"
+	jpn "'%-.64s' ¥Æ¼¥Öë key file ¤¬´Öã¤Æ¤¤ë¤¦¤Ç¹. ½¤É¤òƯ¤Àµ¤¤"
+	kor "'%-.64s' ÅÀºí ºÎ¤ÈÇ Å ÁÀ. ¼öÏÃÀ"
+	nor "Tabellen '%-.64s' har feil i nøfilen; forsøreparer den"
+	norwegian-ny "Tabellen '%-.64s' har feil i nykkelfila; prøreparere den"
+	pol "Niew³a?ciwy plik kluczy dla tabeli: '%-.64s'; spró go naprawiæ+	por "Arquivo de íice incorreto para tabela '%-.64s'; tente reparáo"
+	rum "Cheia fisierului incorecta pentru tabela: '%-.64s'; incearca s-o repari"
+	rus "îÏÒËÎÊÉÄËÎÊÆÊ ÄÑÔÂÉÙ '%-.64s'. ðÏÕÔ ×ÓÔÎ×Ô ÅÏ
+	serbian "Pogrešan key file za tabelu: '%-.64s'; probajte da ga ispravite"
+	slo "Nespráy kµúe tabuµku '%-.64s'; pokúsa ho opravi»"
+	spa "Clave de archivo erronea para la tabla: '%-.64s'; intente repararlo"
+	swe "Fatalt fel vid hantering av register '%-.64s'; kön reparation"
+	ukr "èÎÊÆÊ ËÀÅ ÄÑÔÂɦ: '%-.64s'; óÂÊÅÊÇ ×ÄÏÉÉ
+ER_OLD_KEYFILE  
+	cze "Star-Býovýor pro '%-.64s'; opravte ho."
+	dan "Gammel indeksfil for tabellen '%-.64s'; reparer den"
+	nla "Oude zoeksleutel file voor tabel '%-.64s'; repareer het!"
+	eng "Old key file for table '%-.64s'; repair it!"
+	jps "'%-.64s' ƒe[ƒuƒ‹‚ÍâŒ`Ž®‚Ìkey file ‚Ìæ‚Å·; C•œ‚ðÄ­‚¾‚³‚¢",
+	est "Tabeli '%-.64s' võfail on aegunud; paranda see!"
+	fre "Vieux fichier d'index pour la table '%-.64s'; rérez le!"
+	ger "Alte Index-Datei füelle '%-.64s'. Bitte reparieren"
+	greek "Ðëüß ôíéòey file) ã ôðê'%-.64s'; Ðñë äñôï
+	hun "Regi kulcsfile a '%-.64s'tablahoz; probalja kijavitani!"
+	ita "File chiave vecchio per la tabella '%-.64s'; riparalo!"
+	jpn "'%-.64s' ¥Æ¼¥Öë¸Å¤·Á°¤Îkey file ¤Îè¤Ç¹; ½¤É¤òƯ¤Àµ¤¤"
+	kor "'%-.64s' ÅÀºí ÀÀ¹öÇÅ ÁÀ. ¼öÏÃÀ"
+	nor "Gammel nøfil for tabellen '%-.64s'; reparer den!"
+	norwegian-ny "Gammel nykkelfil for tabellen '%-.64s'; reparer den!"
+	pol "Plik kluczy dla tabeli '%-.64s' jest starego typu; napraw go!"
+	por "Arquivo de íice desatualizado para tabela '%-.64s'; repare-o!"
+	rum "Cheia fisierului e veche pentru tabela '%-.64s'; repar-o!"
+	rus "óÙ ÉÄËÎÊÆÊ ÄÑÔÂÉÙ'%-.64s'; ÏÒÍÎÉÕÔ ÅÏ"
+	serbian "Zastareo key file za tabelu '%-.64s'; ispravite ga"
+	slo "Starýovýr pre '%-.64s'; opravte ho!"
+	spa "Clave de archivo antigua para la tabla '%-.64s'; reparelo!"
+	swe "Gammal nyckelfil '%-.64s'; reparera registret"
+	ukr "óÉ ÆÊ ËÀÅ ÄÑÔÂɦ '%-.64s'; ÷Ï¦Ô ÊÇ!"
+ER_OPEN_AS_READONLY  
+	cze "'%-.64s' je jen pro -Bèní+	dan "'%-.64s' er skrivebeskyttet"
+	nla "'%-.64s' is alleen leesbaar"
+	eng "Table '%-.64s' is read only"
+	jps "'%-.64s' ‚ÍÇÝž‚Ýê‚Å·",
+	est "Tabel '%-.64s' on ainult lugemiseks"
+	fre "'%-.64s' est en lecture seulement"
+	ger "Tabelle '%-.64s' ist nur lesbar"
+	greek "'%-.64s' åôåéüáãó+	hun "'%-.64s' irasvedett"
+	ita "'%-.64s' e` di sola lettura"
+	jpn "'%-.64s' ¤ÏÉßþ¤ßì¤Ç¹"
+	kor "ÅÀºí%-.64s'´ÂÀ±â¿ëÔÏÙ"
+	nor "'%-.64s' er skrivebeskyttet"
+	norwegian-ny "'%-.64s' er skrivetryggja"
+	pol "'%-.64s' jest tylko do odczytu"
+	por "Tabela '%-.64s' éomente para leitura"
+	rum "Tabela '%-.64s' e read-only"
+	rus "ôÉÁ'%-.64s' ÐÅÎÚÁÅÁÔÌË ÄÑÞÅÉ"
+	serbian "Tabelu '%-.64s' je dozvoljeno samo èati"
+	slo "'%-.64s' is èa» only"
+	spa "'%-.64s' es de solo lectura"
+	swe "'%-.64s' äskyddad mot födring"
+	ukr "ôÉÑ'%-.64s' ÔÌË ÄÑÞÔÎÑ
+ER_OUTOFMEMORY HY001 S1001
+	cze "M-Bá pamì. Pørtujte daemona a zkuste znovu (je potø%d bytù	dan "Ikke mere hukommelse. Genstart serveren og prøen (mangler %d bytes)"
+	nla "Geen geheugen meer. Herstart server en probeer opnieuw (%d bytes nodig)"
+	eng "Out of memory; restart server and try again (needed %d bytes)"
+	jps "Out of memory. ƒf[ƒ‚ƒ“‚ðXƒ^[ƒg‚µ‚ÄÝÄ­‚¾‚³‚¢ (%d bytes •K—v)",
+	est "Mä  sai otsa. Proovi MySQL uuesti käitada (puudu jä%d baiti)"
+	fre "Manque de méire. Redérrez le dén et réssayez (%d octets néssaires)"
+	ger "Kein Speicher vorhanden (%d Bytes benöt). Bitte Server neu starten"
+	greek "Äíðåäèé ìì Ðïáóð, åíéíò ääáádemon) (÷Üïá%d bytes)"
+	hun "Nincs eleg memoria. Inditsa ujra a demont, es probalja ismet. (%d byte szukseges.)"
+	ita "Memoria esaurita. Fai ripartire il demone e riprova (richiesti %d bytes)"
+	jpn "Out of memory. ¥Ç¼¥â¤ò¹¥¿¡¼¥È·¤ÆßƯ¤Àµ¤¤ (%d bytes ÉÍ)"
+	kor "Out of memory. µ¥¸óÀ ½ÇàÄ´ÙýÃÛÏÃÀ(needed %d bytes)"
+	nor "Ikke mer minne. Star påytt tjenesten og prøjen (trengte %d byter)"
+	norwegian-ny "Ikkje meir minne. Start påytt tenesten og prøjen (trengte %d bytar)"
+	pol "Zbyt ma³o pamiê. Uruchom ponownie demona i spró ponownie (potrzeba %d bajtó
+	por "Sem memó. Reinicie o programa e tente novamente (necessita de %d bytes)"
+	rum "Out of memory. Porneste daemon-ul din nou si incearca inca o data (e nevoie de %d bytes)"
+	rus "îÏÔÔÞÏÐÍÔ. ðÚÐÓÉÅÓÒÅ ÉÐÐÏÕÔ ÅÅÒÚ(ÎÖÏ%d ÂÊ)"
+	serbian "Nema memorije. Restartujte MySQL server i probajte ponovo (potrebno je %d byte-ova)"
+	slo "Má pamä. Re¹tartujte daemona a skúznova (je potrebný bytov)"
+	spa "Memoria insuficiente. Reinicie el demonio e intentelo otra vez (necesita %d bytes)"
+	swe "Oväat slut påinnet, starta om programmet och fök påytt (Behö %d bytes)"
+	ukr "âËÐÍѦ. òÁÔÊÅÓÒÅ Ô ÓÒÂÊÅÚÏÕ(ÐÔ¦ÂÏ%d Âʦ×"
+ER_OUT_OF_SORTMEMORY HY001 S1001
+	cze "M-Bá pamì pro tøíZvy¹te velikost tøí bufferu"
+	dan "Ikke mere sorteringshukommelse. Ø sorteringshukommelse (sort buffer size) for serveren"
+	nla "Geen geheugen om te sorteren. Verhoog de server sort buffer size"
+	eng "Out of sort memory; increase server sort buffer size"
+	jps "Out of sort memory. sort buffer size ‚ª‘«‚è‚¢‚æ‚Å·.",
+	est "Mä sai sorteerimisel otsa. Suurenda MySQL-i sorteerimispuhvrit"
+	fre "Manque de méire pour le tri. Augmentez-la."
+	ger "Kein Speicher zum Sortieren vorhanden. sort_buffer_size sollte im Server erhöwerden"
+	greek "Äíðåäèé ììã ôíé. Áîôïort buffer size ã ôééó(demon)"
+	hun "Nincs eleg memoria a rendezeshez. Novelje a rendezo demon puffermeretet"
+	ita "Memoria per gli ordinamenti esaurita. Incrementare il 'sort_buffer' al demone"
+	jpn "Out of sort memory. sort buffer size ¤¬Â¤ê¤¤¤è¤Ç¹."
+	kor "Out of sort memory. daemon sort bufferÀ űâ Á°¡½Ã°¼¼¿ä+	nor "Ikke mer sorteringsminne. Ø sorteringsminnet (sort buffer size) for tjenesten"
+	norwegian-ny "Ikkje meir sorteringsminne. Auk sorteringsminnet (sorteringsbffer storleik) for tenesten"
+	pol "Zbyt ma³o pamiê dla sortowania. Zwiêz wielko?æufora demona dla sortowania"
+	por "Sem memó para ordenaç. Aumente tamanho do 'buffer' de ordenaç"
+	rum "Out of memory pentru sortare. Largeste marimea buffer-ului pentru sortare in daemon (sort buffer size)"
+	rus "îÏÔÔÞÏÐÍÔ ÄÑÓÒÉÏË. õÉØÅÒÚÅ ÂÆÒ ÓÒÉÏË Î ÓÒÅÅ
+	serbian "Nema memorije za sortiranje. Poveæte velièu sort buffer-a MySQL server-u"
+	slo "Má pamä pre triedenie, zvýeµkos» triediaceho bufferu"
+	spa "Memoria de ordenacion insuficiente. Incremente el tamano del buffer de ordenacion"
+	swe "Sorteringsbufferten räer inte till. Kontrollera startparametrarna"
+	ukr "âËÐÍѦ ÄÑÓÒÕÁÎ. ôÁÚ¦ÌÛÔ ÒÚ¦ÒÂÆÒ ÓÒÕÁÎ ÕÓÒÅÁ
+ER_UNEXPECTED_EOF  
+	cze "Neo-Bèánýc souboru pøení%-.64s' (chybový %d)"
+	dan "Uventet afslutning påil (eof) ved læing af filen '%-.64s' (Fejlkode: %d)"
+	nla "Onverwachte eof gevonden tijdens het lezen van file '%-.64s' (Errcode: %d)"
+	eng "Unexpected EOF found when reading file '%-.64s' (errno: %d)"
+	jps "'%-.64s' ƒtƒ ƒCƒ‹‚ðÝž‚݆‚ÉEOF ‚ª—\ŠúŠ‚Å»‚ꂵ‚½. (errno: %d)",
+	est "Ootamatu faililõäend faili '%-.64s' lugemisel (veakood: %d)"
+	fre "Fin de fichier inattendue en lisant '%-.64s' (Errcode: %d)"
+	ger "Unerwartetes Ende beim Lesen der Datei '%-.64s' (Fehler: %d)"
+	greek "Êôçéêáçáãó âèåðäç ôÝïôáåõ-.64s' (êéòèòd)"
+	hun "Varatlan filevege-jel a '%-.64s'olvasasakor. (hibakod: %d)"
+	ita "Fine del file inaspettata durante la lettura del file '%-.64s' (errno: %d)"
+	jpn "'%-.64s' ¥Õ¡¥¤¥ëƤßþ¤ßæ EOF ¤¬Í´ü긽¤ì¤·¤¿. (errno: %d)"
+	kor "'%-.64s' ÈÀÀ À´ÂµµÁ À¸øofÀ ¹ßß(¿¡·¯¹ø%d)"
+	nor "Uventet slutt påil (eof) ved lesing av filen '%-.64s' (Feilkode: %d)"
+	norwegian-ny "Uventa slutt påil (eof) ved lesing av fila '%-.64s' (Feilkode: %d)"
+	pol "Nieoczekiwany 'eof' napotkany podczas czytania z pliku '%-.64s' (Kod b³ê: %d)"
+	por "Encontrado fim de arquivo inesperado ao ler arquivo '%-.64s' (erro no. %d)"
+	rum "Sfirsit de fisier neasteptat in citirea fisierului '%-.64s' (errno: %d)"
+	rus "îÖÄÎÙ ËÎÃÆÊÁ'%-.64s' (ÏÉË: %d)"
+	serbian "Neoèivani kraj pri èanju file-a '%-.64s' (errno: %d)"
+	slo "Neoèánýec sú pri èaní%-.64s' (chybový %d)"
+	spa "Inesperado fin de ficheroU mientras leiamos el archivo '%-.64s' (Error: %d)"
+	swe "Oväat filslut vid läing frå'%-.64s' (Felkod: %d)"
+	ukr "èÎÊËÎà ÆÊÕ'%-.64s' (ÐÍÌÁ %d)"
+ER_CON_COUNT_ERROR 08004 
+	cze "P-Bø mnoho spojení+	dan "For mange forbindelser (connections)"
+	nla "Te veel verbindingen"
+	eng "Too many connections"
+	jps "Ú±‚ª‘½‚·‚¬‚Ü·",
+	est "Liiga palju samaaegseid üsi"
+	fre "Trop de connections"
+	ger "Zu viele Verbindungen"
+	greek "ÕÜ÷ ðÝ óÝå..."
+	hun "Tul sok kapcsolat"
+	ita "Troppe connessioni"
+	jpn "À¤¬Â¤¹¤®¤Þ¹"
+	kor "³Ê« ¸¹À ¿¬°á. max_connectionÀ Á°¡ ½Ã°½ÃÀ.."
+	nor "For mange tilkoblinger (connections)"
+	norwegian-ny "For mange tilkoplingar (connections)"
+	pol "Zbyt wiele po³?czeñ	por "Excesso de conexõ
+	rum "Prea multe conectiuni"
+	rus "óËÍÍÏÏÓÅÉÅÉ"
+	serbian "Previše konekcija"
+	slo "Prí¹ mnoho spojení+	spa "Demasiadas conexiones"
+	swe "Föåa anslutningar"
+	ukr "úÁÏÚ¤ÄÁØ
+ER_OUT_OF_RESOURCES  
+	cze "M-Bá prostoru/pamì pro thread"
+	dan "Udgå for trå/hukommelse"
+	nla "Geen thread geheugen meer; controleer of mysqld of andere processen al het beschikbare geheugen gebruikt. Zo niet, dan moet u wellicht 'ulimit' gebruiken om mysqld toe te laten meer geheugen te benutten, of u kunt extra swap ruimte toevoegen"
+	eng "Out of memory; check if mysqld or some other process uses all available memory; if not, you may have to use 'ulimit' to allow mysqld to use more memory or you can add more swap space"
+	jps "Out of memory;  mysqld ‚©‚»‚̼‚ÌvƒƒZƒX‚ªƒƒ‚ƒŠ[‚ðÄg‚ÁÄ¢‚éŠm”F‚µ‚Ä­‚¾‚³‚¢. ƒƒ‚ƒŠ[‚ð¢ØÁÄ¢‚È¢êA'ulimit' ‚ðè‚Ämysqld ‚́ƒ‚ƒŠ[Žg—pŒÀE—Êð­‚·‚éAswap space ‚ðâ‚ÄÝÄ­‚¾‚³‚¢",
+	est "Mä sai otsa. Võlik, et aitab swap-i lisamine võä 'ulimit' abil MySQL-le rohkema mä kasutamise lubamine"
+	fre "Manque de 'threads'/méire"
+	ger "Kein Speicher mehr vorhanden. Prüie, ob mysqld oder ein anderer Prozess den gesamten Speicher verbraucht. Wenn nicht, sollten Sie mit 'ulimit' dafügen, dass mysqld mehr Speicher benutzen darf, oder mehr Swap-Speicher einrichten"
+	greek "Ðü ìôéÝé ìì(Out of thread space/memory)"
+	hun "Elfogyott a thread-memoria"
+	ita "Fine dello spazio/memoria per i thread"
+	jpn "Out of memory;  mysqld ¤«¤½¤Î¾¤Î×í¥¹¤¬¥á¥ê¤òÆÈÃƤ¤ë³Î§¤·¤Æ¯¤Àµ¤¤. ¥á¥ê¤ò¤À¤ÃƤ¤Ê¤¾ì¡¢'ulimit' ¤òê¤Æmysqld ¤Îá¥ê»ÈѦΤò¯¤¹¤ë¡¢swap space ¤òä¤ÆßƯ¤Àµ¤¤"
+	kor "Out of memory;  mysqld³ª ¶ÇÙ¥ Ƿμ¼­¿¡¼­ »ç°¡´ÉѸÞ𦠻çÇÁ ÃÅǽÃÀ ¸¸¾à׸Á ¾ÊÙélimit ¸íÀ À¿¿ë¿© ´õº ¸Þ𦠻çÇ ¼öµµ·ÏǰŪ ½º¿Ò½ºÆÀ½º¸¦ Á°¡½Ã°½ÃÀ
+	nor "Tomt for tråplass/minne"
+	norwegian-ny "Tomt for tråplass/minne"
+	pol "Zbyt ma³o miejsca/pamiê dla w?tku"
+	por "Sem memó. Verifique se o mysqld ou algum outro processo estásando toda memó disponíl. Se nã vocêode ter que usar 'ulimit' para permitir ao mysqld usar mais memó ou vocêode adicionar mais áa de 'swap'"
+	rum "Out of memory;  Verifica daca mysqld sau vreun alt proces foloseste toate memoria disponbila. Altfel, trebuie sa folosesi 'ulimit' ca sa permiti lui memoria disponbila. Altfel, trebuie sa folosesi 'ulimit' ca sa permiti lui mysqld sa foloseasca mai multa memorie ori adauga mai mult spatiu pentru swap (swap space)"
+	rus "îÏÔÔÞÏÐÍÔ; ÕÏÔ×ÒÔÓ, ÞÏmysqld ÉÉËËÊÌ ÄÕÏ ÐÏÅÓÎ ÚÎÍÅ ×ÀÄÓÕÎÀÐÍÔ. åÉÎÔ Ô × ÍÖÔ ÉÐÌÚ×Ô ulimit, ÞÏÙ×ÄÌÔ ÄÑmysqld ÂÌÛ ÐÍÔ, ÉÉÕÅÉÉØÏßÍÆÊÁÐÄÁË"
+	serbian "Nema memorije; Proverite da li MySQL server ili neki drugi proces koristi svu slobodnu memoriju. (UNIX: Ako ne, probajte da upotrebite 'ulimit' komandu da biste dozvolili daemon-u da koristi više memorije ili probajte da dodate više swap memorije)"
+	slo "Má miesta-pamä pre vláo"
+	spa "Memoria/espacio de tranpaso insuficiente"
+	swe "Fick slut påinnet.  Kontrollera om mysqld eller nån annan process anväer allt tillgäligt minne. Om inte, fök anväa 'ulimit' eller allokera mera swap"
+	ukr "âËÐÍѦ;  ð×ÒÅÞ mysqld ÁÏÑ¦Ó ¦Î¦ ÐÏÅÉ×ËÒÓÏÕÔ ÕÀÄÓÕÎ ÐÍÑØ ñ¦, Ô × ÍÖÔ ÓÏÉÔÔÓ 'ulimit', ÁÉÄÚÏÉÉmysqld ×ËÒÓÏÕÁÉÂÌÛ ÐÍѦ ÁÏ× ÍÖÔ ÄÄÔ ÂÌÛ ÍÓÑÐÄÓÁ"
+ER_BAD_HOST_ERROR 08S01 
+	cze "Nemohu zjistit jm-Bé stroje pro Va¹i adresu"
+	dan "Kan ikke fåæsnavn for din adresse"
+	nla "Kan de hostname niet krijgen van uw adres"
+	eng "Can't get hostname for your address"
+	jps "‚»‚Ìaddress ‚Ìhostname ‚ªˆø‚¹‚ñ
+	est "Ei suuda lahendada IP aadressi masina nimeks"
+	fre "Ne peut obtenir de hostname pour votre adresse"
+	ger "Kann Hostnamen füse Adresse nicht erhalten"
+	greek "Äíãåíôïostname ã ôaddress ó
+	hun "A gepnev nem allapithato meg a cimbol"
+	ita "Impossibile risalire al nome dell'host dall'indirizzo (risoluzione inversa)"
+	jpn "¤½¤Îaddress ¤Îhostname ¤¬°ú¤»¤ó+	kor "´çÀ ÄÇÅÀ ȽºÆÀ¸§À ¾ò¼öÀ´ÏÙ"
+	nor "Kan ikke fåak i vertsnavn for din adresse"
+	norwegian-ny "Kan ikkje fåak i vertsnavn for di adresse"
+	pol "Nie mo¿na otrzymaæazwy hosta dla twojego adresu"
+	por "Nãpode obter nome do 'host' para seu endereç
+	rum "Nu pot sa obtin hostname-ul adresei tale"
+	rus "îÏÍÖÏÐÌÞÔ ÉÑÈÓÁÄÑ×ÛÇ ÁÒÓ"
+	serbian "Ne mogu da dobijem ime host-a za vašu IP adresu"
+	slo "Nemô zisti» meno hostiteµa pre va¹u adresu"
+	spa "No puedo obtener el nombre de maquina de tu direccion"
+	swe "Kan inte hitta 'hostname' föin adress"
+	ukr "îÍÖ ×ÚÁÉɦÍÑÈÓÕÄÑ×Û§ ÁÒÓ"
+ER_HANDSHAKE_ERROR 08S01 
+	cze "Chyba p-Bøtavová spojení+	dan "Forkert håtryk (handshake)"
+	nla "Verkeerde handshake"
+	eng "Bad handshake"
+	est "Vä handshake"
+	fre "Mauvais 'handshake'"
+	ger "Ungür Handshake"
+	greek "Çáãñ (handshake) ä Ýé óÜ
+	hun "A kapcsolatfelvetel nem sikerult (Bad handshake)"
+	ita "Negoziazione impossibile"
+	nor "Feil håtrykk (handshake)"
+	norwegian-ny "Feil handtrykk (handshake)"
+	pol "Z³y uchwyt(handshake)"
+	por "Negociaç de acesso falhou"
+	rum "Prost inceput de conectie (bad handshake)"
+	rus "îÏÒËÎÅÐÉÅÓ×Å
+	serbian "Loš poèak komunikacije (handshake)"
+	slo "Chyba pri nadvävanípojenia"
+	spa "Protocolo erroneo"
+	swe "Fel vid initiering av kommunikationen med klienten"
+	ukr "î¦ÒÁÕÔÎ×ÁÚ'ÑË"
+ER_DBACCESS_DENIED_ERROR 42000 
+	cze "P-Bøp pro u¾ivatele '%-.32s'@'%-.64s' k databá '%-.64s' neníovolen"
+	dan "Adgang næet bruger: '%-.32s'@'%-.64s' til databasen '%-.64s'"
+	nla "Toegang geweigerd voor gebruiker: '%-.32s'@'%-.64s' naar database '%-.64s'"
+	eng "Access denied for user '%-.32s'@'%-.64s' to database '%-.64s'"
+	jps "ƒ†[ƒU[ '%-.32s'@'%-.64s' ‚Ì'%-.64s' ƒf[ƒ^ƒx[ƒX‚ÖÌAƒNƒZƒX‚ðÛµ‚Ü·",
+	est "Ligipä keelatud kasutajale '%-.32s'@'%-.64s' andmebaasile '%-.64s'"
+	fre "Accèrefuséour l'utilisateur: '%-.32s'@'@%-.64s'. Base '%-.64s'"
+	ger "Benutzer '%-.32s'@'%-.64s' hat keine Zugriffsberechtigung füenbank '%-.64s'"
+	greek "Äíðñé ðâçôñç'%-.32s'@'%-.64s' óâçåìù-.64s'"
+	hun "A(z) '%-.32s'@'%-.64s' felhasznalo szamara tiltott eleres az '%-.64s' adabazishoz."
+	ita "Accesso non consentito per l'utente: '%-.32s'@'%-.64s' al database '%-.64s'"
+	jpn "¥æ¥¶¡¼ '%-.32s'@'%-.64s' ¤Î'%-.64s' ¥Ç¼¥¿¥Ù¼¥¹¤Ø΢¥¯¥»¥¹¤òÝ·¤Þ¹"
+	kor "'%-.32s'@'%-.64s' »çÀ´Â'%-.64s' µ¥Àź£À½º¿¡ Á±ÙÌ°ÅεÇú´Ù"
+	nor "Tilgang nektet for bruker: '%-.32s'@'%-.64s' til databasen '%-.64s' nektet"
+	norwegian-ny "Tilgang ikkje tillate for brukar: '%-.32s'@'%-.64s' til databasen '%-.64s' nekta"
+	por "Acesso negado para o usuáo '%-.32s'@'%-.64s' ao banco de dados '%-.64s'"
+	rum "Acces interzis pentru utilizatorul: '%-.32s'@'%-.64s' la baza de date '%-.64s'"
+	rus "ä ÐÌÚ×ÔÌ '%-.32s'@'%-.64s' ÄÓÕ ËÂÚ ÄÎÙ '%-.64s' ÚËÙ"
+	serbian "Pristup je zabranjen korisniku '%-.32s'@'%-.64s' za bazu '%-.64s'"
+	slo "Zakánýtup pre u¾íteµa: '%-.32s'@'%-.64s' k databá '%-.64s'"
+	spa "Acceso negado para usuario: '%-.32s'@'%-.64s' para la base de datos '%-.64s'"
+	swe "Anväare '%-.32s'@'%-.64s' äej beräigad att anväa databasen %-.64s"
+	ukr "äÔÐÚÂÒÎÎ ÄÑËÒÓÕÁÁ '%-.32s'@'%-.64s' Ä ÂÚ ÄÎÉ '%-.64s'"
+ER_ACCESS_DENIED_ERROR 28000 
+	cze "P-Bøp pro u¾ivatele '%-.32s'@'%-.64s' (s heslem %s)"
+	dan "Adgang næet bruger: '%-.32s'@'%-.64s' (Bruger adgangskode: %s)"
+	nla "Toegang geweigerd voor gebruiker: '%-.32s'@'%-.64s' (Wachtwoord gebruikt: %s)"
+	eng "Access denied for user '%-.32s'@'%-.64s' (using password: %s)"
+	jps "ƒ†[ƒU[ '%-.32s'@'%-.64s' ‚ðÛµ‚Ü·.uUsing password: %s)",
+	est "Ligipä keelatud kasutajale '%-.32s'@'%-.64s' (kasutab parooli: %s)"
+	fre "Accèrefuséour l'utilisateur: '%-.32s'@'@%-.64s' (mot de passe: %s)"
+	ger "Benutzer '%-.32s'@'%-.64s' hat keine Zugriffsberechtigung (verwendetes Passwort: %-.64s)"
+	greek "Äíðñé ðâçôñç'%-.32s'@'%-.64s' (÷çassword: %s)"
+	hun "A(z) '%-.32s'@'%-.64s' felhasznalo szamara tiltott eleres. (Hasznalja a jelszot: %s)"
+	ita "Accesso non consentito per l'utente: '%-.32s'@'%-.64s' (Password: %s)"
+	jpn "¥æ¥¶¡¼ '%-.32s'@'%-.64s' ¤òÝ·¤Þ¹.uUsing password: %s)"
+	kor "'%-.32s'@'%-.64s' »çÀ´ÂÁ±ÙÌ°ÅεÇú´Ù (using password: %s)"
+	nor "Tilgang nektet for bruker: '%-.32s'@'%-.64s' (Bruker passord: %s)"
+	norwegian-ny "Tilgang ikke tillate for brukar: '%-.32s'@'%-.64s' (Brukar passord: %s)"
+	por "Acesso negado para o usuáo '%-.32s'@'%-.64s' (senha usada: %s)"
+	rum "Acces interzis pentru utilizatorul: '%-.32s'@'%-.64s' (Folosind parola: %s)"
+	rus "äÔÐÚËÙ ÄÑÐÌÚ×ÔÌ '%-.32s'@'%-.64s' (ÂÌÉÐÌÚ×ÎÐÒÌ: %s)"
+	serbian "Pristup je zabranjen korisniku '%-.32s'@'%-.64s' (koristi lozinku: '%s')"
+	slo "Zakánýtup pre u¾íteµa: '%-.32s'@'%-.64s' (pou¾itie hesla: %s)"
+	spa "Acceso negado para usuario: '%-.32s'@'%-.64s' (Usando clave: %s)"
+	swe "Anväare '%-.32s'@'%-.64s' äej beräigad att logga in (Anväer lö: %s)"
+	ukr "äÔÐÚÂÒÎÎ ÄÑËÒÓÕÁÁ '%-.32s'@'%-.64s' (÷ÒÓÁÏÐÒÌ: %s)"
+ER_NO_DB_ERROR 3D000 
+	cze "Nebyla vybr-Bá ¾ááatabá"
+	dan "Ingen database valgt"
+	nla "Geen database geselecteerd"
+	eng "No database selected"
+	jps "ƒf[ƒ^ƒx[ƒX‚ª‘I‘ðê‚¢‚ܹ‚ñ
+	est "Andmebaasi ei ole valitud"
+	fre "Aucune base n'a é séctionné
+	ger "Keine Datenbank ausgewät"
+	greek "Äíð÷åÜçåìù	hun "Nincs kivalasztott adatbazis"
+	ita "Nessun database selezionato"
+	jpn "¥Ç¼¥¿¥Ù¼¥¹¤¬Á¤µ¤ì¤¤¤Þ»¤ó+	kor "¼±ÅµÈµ¥Àź£À½º°¡ ¾ø´Ù"
+	nor "Ingen database valgt"
+	norwegian-ny "Ingen database vald"
+	pol "Nie wybrano ¿adnej bazy danych"
+	por "Nenhum banco de dados foi selecionado"
+	rum "Nici o baza de data nu a fost selectata inca"
+	rus "âÁÄÎÙ Î ×ÂÁÁ
+	serbian "Ni jedna baza nije selektovana"
+	slo "Nebola vybranáatabá"
+	spa "Base de datos no seleccionada"
+	swe "Ingen databas i använing"
+	ukr "âÕÄÎÉ Î ×ÂÁÏ
+ER_UNKNOWN_COM_ERROR 08S01 
+	cze "Nezn-Bá pø"
+	dan "Ukendt kommando"
+	nla "Onbekend commando"
+	eng "Unknown command"
+	jps "‚»‚ÌRƒ}ƒ“ƒh‚ͽH",
+	est "Tundmatu kä"
+	fre "Commande inconnue"
+	ger "Unbekannter Befehl"
+	greek "Áíôíë
+	hun "Ervenytelen parancs"
+	ita "Comando sconosciuto"
+	jpn "¤½¤Î³¥ÞóÏ¿¡©"
+	kor "¸í¾î ¹ºÁ ¸ðÚî..."
+	nor "Ukjent kommando"
+	norwegian-ny "Ukjent kommando"
+	pol "Nieznana komenda"
+	por "Comando desconhecido"
+	rum "Comanda invalida"
+	rus "îÚÅÔÁ ËÍÎÁËÍÕÉÁÉÎÏÏÐÏÏÏÁ
+	serbian "Nepoznata komanda"
+	slo "Nezná príz"
+	spa "Comando desconocido"
+	swe "Okä commando"
+	ukr "î¦ÄÍ ËÍÎÁ
+ER_BAD_NULL_ERROR 23000 
+	cze "Sloupec '%-.64s' nem-Bùýl"
+	dan "Kolonne '%-.64s' kan ikke væ NULL"
+	nla "Kolom '%-.64s' kan niet null zijn"
+	eng "Column '%-.64s' cannot be null"
+	jps "Column '%-.64s' ‚Ínull ‚ÉÍÅ«‚È¢‚ÌÅ·",
+	est "Tulp '%-.64s' ei saa omada nullvätust"
+	fre "Le champ '%-.64s' ne peut êe vide (null)"
+	ger "Feld '%-.64s' darf nicht NULL sein"
+	greek "Ô ðï%-.64s' ä ìñíåáêül)"
+	hun "A(z) '%-.64s' oszlop erteke nem lehet nulla"
+	ita "La colonna '%-.64s' non puo` essere nulla"
+	jpn "Column '%-.64s' ¤Ïnull ¤ËÏÇ­¤Ê¤¤Îǹ"
+	kor "Ä·³ '%-.64s'´Â³ÎNull)À µÇéÈËÏÙ "
+	nor "Kolonne '%-.64s' kan ikke vere null"
+	norwegian-ny "Kolonne '%-.64s' kan ikkje vere null"
+	pol "Kolumna '%-.64s' nie mo¿e byæull"
+	por "Coluna '%-.64s' nãpode ser vazia"
+	rum "Coloana '%-.64s' nu poate sa fie null"
+	rus "óÂÃ'%-.64s' Î ÍÖÔÐÉÉÁØ×ÌÞÎ NULL"
+	serbian "Kolona '%-.64s' ne može biti NULL"
+	slo "Pole '%-.64s' nemôby» null"
+	spa "La columna '%-.64s' no puede ser nula"
+	swe "Kolumn '%-.64s' fåinte vara NULL"
+	ukr "óÂÃ '%-.64s' Î ÍÖ ÂÔ ÎÌÏÉ"
+ER_BAD_DB_ERROR 42000 
+	cze "Nezn-Bá databá '%-.64s'"
+	dan "Ukendt database '%-.64s'"
+	nla "Onbekende database '%-.64s'"
+	eng "Unknown database '%-.64s'"
+	jps "'%-.64s' ‚Èñf[ƒ^ƒx[ƒX‚Ím‚肹‚ñ
+	est "Tundmatu andmebaas '%-.64s'"
+	fre "Base '%-.64s' inconnue"
+	ger "Unbekannte Datenbank '%-.64s'"
+	greek "ÁíôÜçåìù-.64s'"
+	hun "Ervenytelen adatbazis: '%-.64s'"
+	ita "Database '%-.64s' sconosciuto"
+	jpn "'%-.64s' ¤ÊóǼ¥¿¥Ù¼¥¹¤ÏÎꤻ¤ó+	kor "µ¥Àź£À½º '%-.64s'´Â¾ËöÀ"
+	nor "Ukjent database '%-.64s'"
+	norwegian-ny "Ukjent database '%-.64s'"
+	pol "Nieznana baza danych '%-.64s'"
+	por "Banco de dados '%-.64s' desconhecido"
+	rum "Baza de data invalida '%-.64s'"
+	rus "îÚÅÔÁ ÂÚ ÄÎÙ '%-.64s'"
+	serbian "Nepoznata baza '%-.64s'"
+	slo "Nezná databá '%-.64s'"
+	spa "Base de datos desconocida '%-.64s'"
+	swe "Okä databas: '%-.64s'"
+	ukr "î¦ÄÍ ÂÚ ÄÎÉ '%-.64s'"
+ER_TABLE_EXISTS_ERROR 42S01 
+	cze "Tabulka '%-.64s' ji-B¾ existuje"
+	dan "Tabellen '%-.64s' findes allerede"
+	nla "Tabel '%-.64s' bestaat al"
+	eng "Table '%-.64s' already exists"
+	jps "Table '%-.64s' ‚Íù‚è‚·",
+	est "Tabel '%-.64s' juba eksisteerib"
+	fre "La table '%-.64s' existe dé"
+	ger "Tabelle '%-.64s' bereits vorhanden"
+	greek "Ïðê '%-.64s' õ÷Þç+	hun "A(z) '%-.64s' tabla mar letezik"
+	ita "La tabella '%-.64s' esiste gia`"
+	jpn "Table '%-.64s' ¤Ïû¤ê¤¹"
+	kor "ÅÀºí%-.64s'´ÂÀ¹ÌÁÀÇ"
+	nor "Tabellen '%-.64s' eksisterer allerede"
+	norwegian-ny "Tabellen '%-.64s' eksisterar allereide"
+	pol "Tabela '%-.64s' ju¿ istnieje"
+	por "Tabela '%-.64s' jáxiste"
+	rum "Tabela '%-.64s' exista deja"
+	rus "ôÉÁ'%-.64s' ÕÅÓÝÓ×Å"
+	serbian "Tabela '%-.64s' veæostoji"
+	slo "Tabuµka '%-.64s' u¾ existuje"
+	spa "La tabla  '%-.64s' ya existe"
+	swe "Tabellen '%-.64s' finns redan"
+	ukr "ôÉÑ'%-.64s' ×ŦÓÕ"
+ER_BAD_TABLE_ERROR 42S02 
+	cze "Nezn-Bá tabulka '%-.100s'"
+	dan "Ukendt tabel '%-.100s'"
+	nla "Onbekende tabel '%-.100s'"
+	eng "Unknown table '%-.100s'"
+	jps "table '%-.100s' ‚Í ‚肹‚ñ
+	est "Tundmatu tabel '%-.100s'"
+	fre "Table '%-.100s' inconnue"
+	ger "Unbekannte Tabelle '%-.100s'"
+	greek "Áíôðê '%-.100s'"
+	hun "Ervenytelen tabla: '%-.100s'"
+	ita "Tabella '%-.100s' sconosciuta"
+	jpn "table '%-.100s' ¤Ï¢¤ê¤»¤ó+	kor "ÅÀºí%-.100s'´Â¾ËöÀ"
+	nor "Ukjent tabell '%-.100s'"
+	norwegian-ny "Ukjent tabell '%-.100s'"
+	pol "Nieznana tabela '%-.100s'"
+	por "Tabela '%-.100s' desconhecida"
+	rum "Tabela '%-.100s' este invalida"
+	rus "îÚÅÔÁ ÔÂÉÁ'%-.100s'"
+	serbian "Nepoznata tabela '%-.100s'"
+	slo "Nezná tabuµka '%-.100s'"
+	spa "Tabla '%-.100s' desconocida"
+	swe "Okä tabell '%-.100s'"
+	ukr "î¦ÄÍ ÔÂÉÑ'%-.100s'"
+ER_NON_UNIQ_ERROR 23000 
+	cze "Sloupec '%-.64s' v %s nen-Bícela jasnýan "Felt: '%-.64s' i tabel %s er ikke entydigt"
+	nla "Kolom: '%-.64s' in %s is niet eenduidig"
+	eng "Column '%-.64s' in %-.64s is ambiguous"
+	est "Vä '%-.64s' %-.64s-s ei ole ü
+	fre "Champ: '%-.64s' dans %s est ambigu"
+	ger "Feld '%-.64s' in %-.64s ist nicht eindeutig"
+	greek "Ô ðï'%-.64s' ó-.64s ä Ýåêïó"
+	hun "A(z) '%-.64s' oszlop %-.64s-ben ketertelmu"
+	ita "Colonna: '%-.64s' di %-.64s e` ambigua"
+	jpn "Column: '%-.64s' in %-.64s is ambiguous"
+	kor "Ä·³: '%-.64s' in '%-.64s' À ¸ðÔ
+	nor "Felt: '%-.64s' i tabell %s er ikke entydig"
+	norwegian-ny "Kolonne: '%-.64s' i tabell %s er ikkje eintydig"
+	pol "Kolumna: '%-.64s' w  %s jest dwuznaczna"
+	por "Coluna '%-.64s' em '%-.64s' émbía"
+	rum "Coloana: '%-.64s' in %-.64s este ambigua"
+	rus "óÂÃ'%-.64s' ×%-.64s ÚÄÎÎÏÎÚÁÎ"
+	serbian "Kolona '%-.64s' u %-.64s nije jedinstvena u kontekstu"
+	slo "Pole: '%-.64s' v %-.64s je nejasné+	spa "La columna: '%-.64s' en %s es ambigua"
+	swe "Kolumn '%-.64s' i %s äinte unik"
+	ukr "óÂÃ '%-.64s' Õ%-.64s ×ÚÁÅÉ ÎÏÎÚÁÎ"
+ER_SERVER_SHUTDOWN 08S01 
+	cze "Prob-Bí ukonèá prá serveru"
+	dan "Database nedlukning er i gang"
+	nla "Bezig met het stoppen van de server"
+	eng "Server shutdown in progress"
+	jps "Server ‚ðutdown ’†...",
+	est "Serveri seiskamine kä"
+	fre "Arrêdu serveur en cours"
+	ger "Der Server wird heruntergefahren"
+	greek "Åáçééó áóåòõõåôserver shutdown)"
+	hun "A szerver leallitasa folyamatban"
+	ita "Shutdown del server in corso"
+	jpn "Server ¤òutdown Ã..."
+	kor "Server°¡ ¼ËÙîßÔÏÙ"
+	nor "Database nedkobling er i gang"
+	norwegian-ny "Tenar nedkopling er i gang"
+	pol "Trwa koñnie dzia³ania serwera"
+	por "'Shutdown' do servidor em andamento"
+	rum "Terminarea serverului este in desfasurare"
+	rus "óÅ ÎÈÄÔÑ×ÐÏÅÓ ÏÔÎ×É
+	serbian "Gašenje servera je u toku"
+	slo "Prebieha ukonèanie prá servera"
+	spa "Desconexion de servidor en proceso"
+	swe "Servern gånu ned"
+	ukr "úÛ¤ÔÓ ÒÂÔ ÓÒÅÁ
+ER_BAD_FIELD_ERROR 42S22 S0022
+	cze "Nezn-Bá sloupec '%-.64s' v %s"
+	dan "Ukendt kolonne '%-.64s' i tabel %s"
+	nla "Onbekende kolom '%-.64s' in %s"
+	eng "Unknown column '%-.64s' in '%-.64s'"
+	jps "'%-.64s' column ‚Í'%-.64s' ‚ÉÍ ‚肹‚ñ
+	est "Tundmatu tulp '%-.64s' '%-.64s'-s"
+	fre "Champ '%-.64s' inconnu dans %s"
+	ger "Unbekanntes Tabellenfeld '%-.64s' in %-.64s"
+	greek "Áíôåï%-.64s' ó%-.64s'"
+	hun "A(z) '%-.64s' oszlop ervenytelen '%-.64s'-ben"
+	ita "Colonna sconosciuta '%-.64s' in '%-.64s'"
+	jpn "'%-.64s' column ¤Ï'%-.64s' ¤ËÏ¢¤ê¤»¤ó+	kor "Unknown Ä·³ '%-.64s' in '%-.64s'"
+	nor "Ukjent kolonne '%-.64s' i tabell %s"
+	norwegian-ny "Ukjent felt '%-.64s' i tabell %s"
+	pol "Nieznana kolumna '%-.64s' w  %s"
+	por "Coluna '%-.64s' desconhecida em '%-.64s'"
+	rum "Coloana invalida '%-.64s' in '%-.64s'"
+	rus "îÚÅÔÙ ÓÏÂÃ'%-.64s' ×'%-.64s'"
+	serbian "Nepoznata kolona '%-.64s' u '%-.64s'"
+	slo "Nezná pole '%-.64s' v '%-.64s'"
+	spa "La columna '%-.64s' en %s es desconocida"
+	swe "Okä kolumn '%-.64s' i %s"
+	ukr "î¦ÄÍÊÓÏÂà '%-.64s' Õ'%-.64s'"
+ER_WRONG_FIELD_WITH_GROUP 42000 S1009
+	cze "Pou-B¾ité%-.64s' nebylo v group by"
+	dan "Brugte '%-.64s' som ikke var i group by"
+	nla "Opdracht gebruikt '%-.64s' dat niet in de GROUP BY voorkomt"
+	eng "'%-.64s' isn't in GROUP BY"
+	jps "'%-.64s' isn't in GROUP BY",
+	est "'%-.64s' puudub GROUP BY klauslis"
+	fre "'%-.64s' n'est pas dans 'group by'"
+	ger "'%-.64s' ist nicht in GROUP BY vorhanden"
+	greek "×çìïèå%-.64s' ðä õ÷ôroup by"
+	hun "Used '%-.64s' with wasn't in group by"
+	ita "Usato '%-.64s' che non e` nel GROUP BY"
+	kor "'%-.64s'À GROUP BY¼Ó¡ ¾ø+	nor "Brukte '%-.64s' som ikke var i group by"
+	norwegian-ny "Brukte '%-.64s' som ikkje var i group by"
+	pol "U¿yto '%-.64s' bez umieszczenia w group by"
+	por "'%-.64s' nãestám 'GROUP BY'"
+	rum "'%-.64s' nu exista in clauza GROUP BY"
+	rus "'%-.64s' Î ÐÉÕÓ×Å ×GROUP BY"
+	serbian "Entitet '%-.64s' nije naveden u komandi 'GROUP BY'"
+	slo "Pou¾ité%-.64s' nebolo v 'group by'"
+	spa "Usado '%-.64s' el cual no esta group by"
+	swe "'%-.64s' finns inte i GROUP BY"
+	ukr "'%-.64s' Î ¤ ÕGROUP BY"
+ER_WRONG_GROUP_FIELD 42000 S1009
+	cze "Nemohu pou-B¾ígroup na '%-.64s'"
+	dan "Kan ikke gruppere på%-.64s'"
+	nla "Kan '%-.64s' niet groeperen"
+	eng "Can't group on '%-.64s'"
+	est "Ei saa grupeerida '%-.64s' jäi"
+	fre "Ne peut regrouper '%-.64s'"
+	ger "Gruppierung ü%-.64s' nicht möch"
+	greek "Áýçìïßógroup on) '%-.64s'"
+	hun "A group nem hasznalhato: '%-.64s'"
+	ita "Impossibile raggruppare per '%-.64s'"
+	kor "'%-.64s'¸¦ ±×ì ¼öÀ"
+	nor "Kan ikke gruppere på%-.64s'"
+	norwegian-ny "Kan ikkje gruppere på%-.64s'"
+	pol "Nie mo¿na grupowaæo '%-.64s'"
+	por "Nãpode agrupar em '%-.64s'"
+	rum "Nu pot sa grupez pe (group on) '%-.64s'"
+	rus "îÏÍÖÏÐÏÚÅÔ ÇÕÐÒ×ÕÐ '%-.64s'"
+	serbian "Ne mogu da grupišem po '%-.64s'"
+	slo "Nemô pou¾i» 'group' na '%-.64s'"
+	spa "No puedo agrupar por '%-.64s'"
+	swe "Kan inte anväa GROUP BY med '%-.64s'"
+	ukr "îÍÖ ÇÕÕÁÉÐ '%-.64s'"
+ER_WRONG_SUM_SELECT 42000 S1009
+	cze "P-Bø obsahuje záveònkci sum a sloupce"
+	dan "Udtrykket har summer (sum) funktioner og kolonner i samme udtryk"
+	nla "Opdracht heeft totaliseer functies en kolommen in dezelfde opdracht"
+	eng "Statement has sum functions and columns in same statement"
+	est "Lauses on korraga nii tulbad kui summeerimisfunktsioonid"
+	fre "Vous demandez la fonction sum() et des champs dans la mê commande"
+	ger "Die Verwendung von Summierungsfunktionen und Spalten im selben Befehl ist nicht erlaubt"
+	greek "ÇäôóåÝåsum functions ê columns ó ßéäôó+	ita "Il comando ha una funzione SUM e una colonna non specificata nella GROUP BY"
+	kor "Statement °¡ sum±âÀ µ¿ÀÁÀ°í®·³µµ µ¿ÀÇ statementÀ´ÏÙ"
+	nor "Uttrykket har summer (sum) funksjoner og kolonner i samme uttrykk"
+	norwegian-ny "Uttrykket har summer (sum) funksjoner og kolonner i same uttrykk"
+	pol "Zapytanie ma funkcje sumuj?ce i kolumny w tym samym zapytaniu"
+	por "Cláula contéfunçs de soma e colunas juntas"
+	rum "Comanda are functii suma si coloane in aceeasi comanda"
+	rus "÷ÖÎÅÓÄÒÉ ÇÕÐ×ÅÆÎÃÉÉÓÏÂÙ Î Î ×ÌÞÅ GROUP BY. áÁ × ÕÕÒÌÓ ÐÌÞÔ ÜÏÓÏÝÎÅÏ ÏÉË?"
+	serbian "Izraz ima 'SUM' agregatnu funkciju i kolone u isto vreme"
+	slo "Príz obsahuje záveònkciu 'sum' a poµa"
+	spa "El estamento tiene funciones de suma y columnas en el mismo estamento"
+	swe "Kommandot har bå sum functions och enkla funktioner"
+	ukr "õÒÚ ×ËÒÓÁÏÐÄÕÏÕÞ ÆÎç ÐÒÄÚ¦ÍÎÍ ÓϦ×
+ER_WRONG_VALUE_COUNT 21S01 
+	cze "Po-Bè sloupcùdpoví zadanéodnotì+	dan "Kolonne tæer stemmer ikke med antallet af væier"
+	nla "Het aantal kolommen komt niet overeen met het aantal opgegeven waardes"
+	eng "Column count doesn't match value count"
+	est "Tulpade arv erineb vätuste arvust"
+	ger "Die Anzahl der Spalten entspricht nicht der Anzahl der Werte"
+	greek "Ô Column count ä ôéåìôalue count"
+	hun "Az oszlopban levo ertek nem egyezik meg a szamitott ertekkel"
+	ita "Il numero delle colonne non e` uguale al numero dei valori"
+	kor "Ä·³À Ä¿î°¡ °ªÀ Ä¿î¿ÍÀÄÇÁ ¾ÊÀÏÙ"
+	nor "Felt telling stemmer verdi telling"
+	norwegian-ny "Kolonne telling stemmer verdi telling"
+	pol "Liczba kolumn nie odpowiada liczbie warto?ci"
+	por "Contagem de colunas nãconfere com a contagem de valores"
+	rum "Numarul de coloane nu este acelasi cu numarul valoarei"
+	rus "ëÉÅÔÏÓÏÂÏ Î Ó×ÁÁÔÓËÌÞÓ×ÍÚÁÅÉ"
+	serbian "Broj kolona ne odgovara broju vrednosti"
+	slo "Poè políezodpovedáadanej hodnote"
+	spa "La columna con count no tiene valores para contar"
+	swe "Antalet kolumner motsvarar inte antalet väen"
+	ukr "ëئÓØÓϦ×Î Ó¦×ÁÁ ÚËÌËÓÀÚÁÅØ
+ER_TOO_LONG_IDENT 42000 S1009
+	cze "Jm-Bé identifikáru '%-.64s' je pø dlouhé+	dan "Navnet '%-.64s' er for langt"
+	nla "Naam voor herkenning '%-.64s' is te lang"
+	eng "Identifier name '%-.100s' is too long"
+	jps "Identifier name '%-.100s' ‚Í·‚·‚¬‚Ü·",
+	est "Identifikaatori '%-.100s' nimi on liiga pikk"
+	fre "Le nom de l'identificateur '%-.64s' est trop long"
+	ger "Name des Bezeichners '%-.100s' ist zu lang"
+	greek "Ô identifier name '%-.100s' åáð ìÜï+	hun "A(z) '%-.100s' azonositonev tul hosszu."
+	ita "Il nome dell'identificatore '%-.100s' e` troppo lungo"
+	jpn "Identifier name '%-.100s' ¤Ï¹¤¹¤®¤Þ¹"
+	kor "Identifier '%-.100s'´Â³Ê« ±æ¿ä
+	nor "Identifikator '%-.64s' er for lang"
+	norwegian-ny "Identifikator '%-.64s' er for lang"
+	pol "Nazwa identyfikatora '%-.64s' jest zbyt d³uga"
+	por "Nome identificador '%-.100s' éongo demais"
+	rum "Numele indentificatorului '%-.100s' este prea lung"
+	rus "óËÍÄÉÎÊÉÅÔÆËÔÒ'%-.100s'"
+	serbian "Ime '%-.100s' je predugaè"
+	slo "Meno identifikára '%-.100s' je prí¹ dlhé+	spa "El nombre del identificador '%-.64s' es demasiado grande"
+	swe "Kolumnnamn '%-.64s' äföåt"
+	ukr "¶ÍѦÄÎɦËÔÒ '%-.100s' ÚÄ×Å
+ER_DUP_FIELDNAME 42S21 S1009
+	cze "Zdvojen-Bémé sloupce '%-.64s'"
+	dan "Feltnavnet '%-.64s' findes allerede"
+	nla "Dubbele kolom naam '%-.64s'"
+	eng "Duplicate column name '%-.64s'"
+	jps "'%-.64s' ‚Æ¢‚¤ column –¼‚Íd•¡‚µ‚ÄÜ·",
+	est "Kattuv tulba nimi '%-.64s'"
+	fre "Nom du champ '%-.64s' dé utilisé+	ger "Doppelter Spaltenname: '%-.64s'"
+	greek "Åáëçolumn name '%-.64s'"
+	hun "Duplikalt oszlopazonosito: '%-.64s'"
+	ita "Nome colonna duplicato '%-.64s'"
+	jpn "'%-.64s' ¤È¤¤¦ column ̤ÏÅ£¤·¤ÆÞ¹"
+	kor "Áº¹µÈÄ·³ À¸§: '%-.64s'"
+	nor "Feltnavnet '%-.64s' eksisterte fra fø	norwegian-ny "Feltnamnet '%-.64s' eksisterte fråø	pol "Powtóna nazwa kolumny '%-.64s'"
+	por "Nome da coluna '%-.64s' duplicado"
+	rum "Numele coloanei '%-.64s' e duplicat"
+	rus "äÌÒÀÅÓ ÉÑÓÏÂÁ'%-.64s'"
+	serbian "Duplirano ime kolone '%-.64s'"
+	slo "Opakovanéeno poµa '%-.64s'"
+	spa "Nombre de columna duplicado '%-.64s'"
+	swe "Kolumnnamn '%-.64s finns flera gåer"
+	ukr "äÌÀŦÍÑÓÏÂÑ'%-.64s'"
+ER_DUP_KEYNAME 42000 S1009
+	cze "Zdvojen-Bémé klí '%-.64s'"
+	dan "Indeksnavnet '%-.64s' findes allerede"
+	nla "Dubbele zoeksleutel naam '%-.64s'"
+	eng "Duplicate key name '%-.64s'"
+	jps "'%-.64s' ‚Æ¢‚¤ key ‚̼‘O‚Íd•¡‚µ‚Ä¢‚Ü·",
+	est "Kattuv võ nimi '%-.64s'"
+	fre "Nom de clef '%-.64s' dé utilisé+	ger "Doppelter Name fülüvorhanden: '%-.64s'"
+	greek "Åáëçey name '%-.64s'"
+	hun "Duplikalt kulcsazonosito: '%-.64s'"
+	ita "Nome chiave duplicato '%-.64s'"
+	jpn "'%-.64s' ¤È¤¤¦ key ¤Î¾Á¤ÏÅ£¤·¤Æ¤¤Þ¹"
+	kor "Áº¹µÈÅ À¸§ : '%-.64s'"
+	nor "Nønavnet '%-.64s' eksisterte fra fø	norwegian-ny "Nønamnet '%-.64s' eksisterte fråø	pol "Powtóny nazwa klucza '%-.64s'"
+	por "Nome da chave '%-.64s' duplicado"
+	rum "Numele cheiei '%-.64s' e duplicat"
+	rus "äÌÒÀÅÓ ÉÑËÀÁ'%-.64s'"
+	serbian "Duplirano ime kljuè'%-.64s'"
+	slo "Opakovanéeno kµú%-.64s'"
+	spa "Nombre de clave duplicado '%-.64s'"
+	swe "Nyckelnamn '%-.64s' finns flera gåer"
+	ukr "äÌÀŦÍÑËÀÁ'%-.64s'"
+ER_DUP_ENTRY 23000 S1009
+	cze "Zvojen-Bý '%-.64s' (èlo klí %d)"
+	dan "Ens væier '%-.64s' for indeks %d"
+	nla "Dubbele ingang '%-.64s' voor zoeksleutel %d"
+	eng "Duplicate entry '%-.64s' for key %d"
+	jps "'%-.64s' ‚Íkey %d ‚ɨ‚¢‚Äd•¡‚µ‚Ä¢‚Ü·",
+	est "Kattuv vätus '%-.64s' võle %d"
+	fre "Duplicata du champ '%-.64s' pour la clef %d"
+	ger "Doppelter Eintrag '%-.64s' fülü%d"
+	greek "Äðåñ '%-.64s' ã ôëä%d"
+	hun "Duplikalt bejegyzes '%-.64s' a %d kulcs szerint."
+	ita "Valore duplicato '%-.64s' per la chiave %d"
+	jpn "'%-.64s' ¤Ïkey %d ¤Ëª¤¤¤ÆÅ£¤·¤Æ¤¤Þ¹"
+	kor "Áº¹µÈÀ·Â°ª '%-.64s': key %d"
+	nor "Like verdier '%-.64s' for nø %d"
+	norwegian-ny "Like verdiar '%-.64s' for nykkel %d"
+	pol "Powtóne wyst?pienie '%-.64s' dla klucza %d"
+	por "Entrada '%-.64s' duplicada para a chave %d"
+	rum "Cimpul '%-.64s' e duplicat pentru cheia %d"
+	rus "äÌÒÀÁÓ ÚÐÓ '%-.64s' Ð ËÀÕ%d"
+	serbian "Dupliran unos '%-.64s' za kljuè%d'"
+	slo "Opakovaný '%-.64s' (èlo kµúd)"
+	spa "Entrada duplicada '%-.64s' para la clave %d"
+	swe "Dubbel nyckel '%-.64s' föyckel %d"
+	ukr "äÌÀÉ ÚÐÓ'%-.64s' ÄÑËÀÁ%d"
+ER_WRONG_FIELD_SPEC 42000 S1009
+	cze "Chybn-Bápecifikace sloupce '%-.64s'"
+	dan "Forkert kolonnespecifikaton for felt '%-.64s'"
+	nla "Verkeerde kolom specificatie voor kolom '%-.64s'"
+	eng "Incorrect column specifier for column '%-.64s'"
+	est "Vigane tulba kirjeldus tulbale '%-.64s'"
+	fre "Mauvais paramèe de champ pour le champ '%-.64s'"
+	ger "Falsche Spezifikation füd '%-.64s'"
+	greek "ÅöÝïolumn specifier ã ôåï%-.64s'"
+	hun "Rossz oszlopazonosito: '%-.64s'"
+	ita "Specifica errata per la colonna '%-.64s'"
+	kor "Ä·³ '%-.64s'À ºÎ¤ÈÇ Ä·³ ÁÀÀ"
+	nor "Feil kolonne spesifikator for felt '%-.64s'"
+	norwegian-ny "Feil kolonne spesifikator for kolonne '%-.64s'"
+	pol "B³êa specyfikacja kolumny dla kolumny '%-.64s'"
+	por "Especificador de coluna incorreto para a coluna '%-.64s'"
+	rum "Specificandul coloanei '%-.64s' este incorect"
+	rus "îÏÒËÎÊÏÒÄÌÔÌ ÓÏÂÁÄÑÓÏÂÁ'%-.64s'"
+	serbian "Pogrešan naziv kolone za kolonu '%-.64s'"
+	slo "Chyba v ¹pecifikái poµa '%-.64s'"
+	spa "Especificador de columna erroneo para la columna '%-.64s'"
+	swe "Felaktigt kolumntyp föolumn '%-.64s'"
+	ukr "î¦ÒÉ ÓÅɦËÔÒÓÏÂÑ'%-.64s'"
+ER_PARSE_ERROR 42000 
+	cze "%s bl-Bío '%-.64s' na ø %d"
+	dan "%s næ'%-.64s' påinje %d"
+	nla "%s bij '%-.64s' in regel %d"
+	eng "%s near '%-.80s' at line %d"
+	jps "%s  : '%-.80s' •t‹ß : %d s–Ú,
+	est "%s '%-.80s' ligidal real %d"
+	fre "%s prède '%-.64s' àa ligne %d"
+	ger "%s bei '%-.80s' in Zeile %d"
+	greek "%s ðßí%-.80s' óãì %d"
+	hun "A %s a '%-.80s'-hez kozeli a %d sorban"
+	ita "%s vicino a '%-.80s' linea %d"
+	jpn "%s  : '%-.80s' ɶá: %d ¹ÔÜ
+	kor "'%-.64s' ¿¡·¯ °°À´ÏÙ ('%-.80s' ¸í¾îó%d)"
+	nor "%s næ'%-.64s' påinje %d"
+	norwegian-ny "%s attmed '%-.64s' påine %d"
+	pol "%s obok '%-.64s' w linii %d"
+	por "%s próo a '%-.80s' na linha %d"
+	rum "%s linga '%-.80s' pe linia %d"
+	rus "%s ÏÏÏ'%-.80s' Î ÓÒË %d"
+	serbian "'%s' u iskazu '%-.80s' na liniji %d"
+	slo "%s blío '%-.80s' na riadku %d"
+	spa "%s cerca '%-.64s' en la linea %d"
+	swe "%s nä '%-.64s' påad %d"
+	ukr "%s ÂÌ '%-.80s' ×ÓÒÃ %d"
+ER_EMPTY_QUERY 42000  
+	cze "V-Býk dotazu je pránýan "Forespøl var tom"
+	nla "Query was leeg"
+	eng "Query was empty"
+	jps "Query ‚ª‹ó·.",
+	est "Türing"
+	fre "Query est vide"
+	ger "Leere Abfrage"
+	greek "Ô åô (query) ðèá Þáêüun "Ures lekerdezes."
+	ita "La query e` vuota"
+	jpn "Query ¤¬¶õ¹."
+	kor "ĸ®°á°¡ ¾ø´Ù"
+	nor "Forespø var tom"
+	norwegian-ny "Føurnad var tom"
+	pol "Zapytanie by³o puste"
+	por "Consulta (query) estava vazia"
+	rum "Query-ul a fost gol"
+	rus "úÓÏÁÁÓ ÐÓÙ"
+	serbian "Upit je bio prazan"
+	slo "Výk po¾iadavky bol prány"
+	spa "La query estaba vacia"
+	swe "Från var tom"
+	ukr "ðÉ ÚÐÔ
+ER_NONUNIQ_TABLE 42000 S1009
+	cze "Nejednozna-Bè tabulka/alias: '%-.64s'"
+	dan "Tabellen/aliaset: '%-.64s' er ikke unikt"
+	nla "Niet unieke waarde tabel/alias: '%-.64s'"
+	eng "Not unique table/alias: '%-.64s'"
+	jps "'%-.64s' ‚Íê‚Ìtable/alias –¼‚ÅÍ ‚肹‚ñ+	est "Ei ole unikaalne tabel/alias '%-.64s'"
+	fre "Table/alias: '%-.64s' non unique"
+	ger "Tabellenname/Alias '%-.64s' nicht eindeutig"
+	greek "Áýçíñ unique table/alias: '%-.64s'"
+	hun "Nem egyedi tabla/alias: '%-.64s'"
+	ita "Tabella/alias non unico: '%-.64s'"
+	jpn "'%-.64s' ¤Ïì¤Îtable/alias ̤ÇÏ¢¤ê¤»¤ó	kor "Unique ÇÁ ¾Êº ÅÀºílias: '%-.64s'"
+	nor "Ikke unikt tabell/alias: '%-.64s'"
+	norwegian-ny "Ikkje unikt tabell/alias: '%-.64s'"
+	pol "Tabela/alias nie s? unikalne: '%-.64s'"
+	por "Tabela/alias '%-.64s' nãú"
+	rum "Tabela/alias: '%-.64s' nu este unic"
+	rus "ðÏÑÝÑÑÔÂÉÁÐÅÄÎÍ'%-.64s'"
+	serbian "Tabela ili alias nisu bili jedinstveni: '%-.64s'"
+	slo "Nie jednoznaè tabuµka/alias: '%-.64s'"
+	spa "Tabla/alias: '%-.64s' es no unica"
+	swe "Icke unikt tabell/alias: '%-.64s'"
+	ukr "îÎËÌÎ ÔÂÉÑÐÅÄÎÍ '%-.64s'"
+ER_INVALID_DEFAULT 42000 S1009
+	cze "Chybn-Báefaultníodnota pro '%-.64s'"
+	dan "Ugyldig standardvæi for '%-.64s'"
+	nla "Foutieve standaard waarde voor '%-.64s'"
+	eng "Invalid default value for '%-.64s'"
+	est "Vigane vaikevätus '%-.64s' jaoks"
+	fre "Valeur par déut invalide pour '%-.64s'"
+	ger "Fehlerhafter Vorgabewert (DEFAULT) fü.64s'"
+	greek "ÅöÝçñèéÝçé (default value) ã '%-.64s'"
+	hun "Ervenytelen ertek: '%-.64s'"
+	ita "Valore di default non valido per '%-.64s'"
+	kor "'%-.64s'À ÀÈÇÁ ¸øð® °ªÀ »çǼÌÀÏÙ"
+	nor "Ugyldig standardverdi for '%-.64s'"
+	norwegian-ny "Ugyldig standardverdi for '%-.64s'"
+	pol "Niew³a?ciwa warto?æomy?lna dla '%-.64s'"
+	por "Valor padrã(default) invádo para '%-.64s'"
+	rum "Valoarea de default este invalida pentru '%-.64s'"
+	rus "îÏÒËÎÅÚÁÅÉ Ð ÕÏÞÎÀÄÑ'%-.64s'"
+	serbian "Loša default vrednost za '%-.64s'"
+	slo "Chybnámplicitnáodnota pre '%-.64s'"
+	spa "Valor por defecto invalido para '%-.64s'"
+	swe "Ogiltigt DEFAULT väe fö%-.64s'"
+	ukr "î¦ÒÅÚÁÅÎ Ð ÚÍ×ÕÁÎ ÄÑ'%-.64s'"
+ER_MULTIPLE_PRI_KEY 42000 S1009
+	cze "Definov-Bá ví primáí klí"
+	dan "Flere primæø specificeret"
+	nla "Meerdere primaire zoeksleutels gedefinieerd"
+	eng "Multiple primary key defined"
+	jps "•¡”‚Ìprimary key ‚ª’肳‚ꂵ‚½",
+	est "Mitut primaarset võ ei saa olla"
+	fre "Plusieurs clefs primaires dénies"
+	ger "Mehrere Primächlü(PRIMARY KEY) definiert"
+	greek "Ðñüá Ýárimary key ïóá
+	hun "Tobbszoros elsodleges kulcs definialas."
+	ita "Definite piu` chiave primarie"
+	jpn "Ê¿ôprimary key ¤¬ÄµÁµ¤ì¤·¤¿"
+	kor "Multiple primary key°¡ ÁÀµÇîÖ¿"
+	nor "Fleire primæøspesifisert"
+	norwegian-ny "Fleire primæyklar spesifisert"
+	pol "Zdefiniowano wiele kluczy podstawowych"
+	por "Definida mais de uma chave primáa"
+	rum "Chei primare definite de mai multe ori"
+	rus "õÁÏÎÓÏØÏÐÒÉÎÈËÀÅ"
+	serbian "Definisani višestruki primarni kljuèi"
+	slo "Zadefinovanýac primáych kµú
+	spa "Multiples claves primarias definidas"
+	swe "Flera PRIMARY KEY anväa"
+	ukr "ðÉÎÇ ËÀÁ×ÚÁÅÏÎÏÎÒÚ×"
+ER_TOO_MANY_KEYS 42000 S1009
+	cze "Zad-Bá pø mnoho klí, je povoleno nejví %d klí"
+	dan "For mange nø specificeret. Kun %d nø måruges"
+	nla "Teveel zoeksleutels gedefinieerd. Maximaal zijn %d zoeksleutels toegestaan"
+	eng "Too many keys specified; max %d keys allowed"
+	jps "key ‚Ìw’葽‚·‚¬‚Ü·.  key ‚ÍÅåd ‚ÜÅÅ·",
+	est "Liiga palju võid. Maksimaalselt võolla %d võ"
+	fre "Trop de clefs sont dénies. Maximum de %d clefs alloué+	ger "Zu viele Schlüdefiniert. Maximal %d Schlüerlaubt"
+	greek "ÐñïÜkey ïóá Ô ð %d åôïá
+	hun "Tul sok kulcs. Maximum %d kulcs engedelyezett."
+	ita "Troppe chiavi. Sono ammesse max %d chiavi"
+	jpn "key ¤ÎØꤹ¤®¤Þ¹.  key ¤ÏÇçd ¤ÞÇǹ"
+	kor "³Ê« ¸¹À Å°¡ ÁÀµÇîÖ¾´ÏÙ. ôëdÀ Å°¡ °¡´ÉÔ
+	nor "For mange nø spesifisert. Maks %d nø tillatt"
+	norwegian-ny "For mange nykler spesifisert. Maks %d nyklar tillatt"
+	pol "Okre?lono zbyt wiele kluczy. Dostêych jest maksymalnie %d kluczy"
+	por "Especificadas chaves demais. O mámo permitido sã%d chaves"
+	rum "Prea multe chei. Numarul de chei maxim este %d"
+	rus "õÁÏÓÉËÍÍÏÏËÀÅ. òÅÁÔÑÕÁÙÁØÎ ÂÌÅ%d ËÀÅ"
+	serbian "Navedeno je previše kljuèa. Maksimum %d kljuèa je dozvoljeno"
+	slo "Zadanýli¹ veµa kµú Najviac %d kµúje povolený	spa "Demasiadas claves primarias declaradas. Un maximo de %d claves son permitidas"
+	swe "Föåa nycklar anväa. Man fåha hö %d nycklar"
+	ukr "úÁÏËÀ¦×ÚÚÁÅÏ ä×ÌÎ Î ÂÌÛ %d ËÀ¦×
+ER_TOO_MANY_KEY_PARTS 42000 S1009
+	cze "Zad-Bá pø mnoho èt klí, je povoleno nejví %d ètí+	dan "For mange nøele specificeret. Kun %d dele måruges"
+	nla "Teveel zoeksleutel onderdelen gespecificeerd. Maximaal %d onderdelen toegestaan"
+	eng "Too many key parts specified; max %d parts allowed"
+	est "Võkoosneb liiga paljudest osadest. Maksimaalselt võolla %d osa"
+	fre "Trop de parties specifié dans la clef. Maximum de %d parties"
+	ger "Zu viele Teilschlüdefiniert. Maximal %d Teilschlüerlaubt"
+	greek "ÐñïÜkey parts ïóá Ô ð %d åôïá
+	hun "Tul sok kulcsdarabot definialt. Maximum %d resz engedelyezett"
+	ita "Troppe parti di chiave specificate. Sono ammesse max %d parti"
+	kor "³Ê« ¸¹À Å ºÎÐparts)µé ÁÀµÇîÖ¾´ÏÙ. ôëd ºÎÐÌ°¡´ÉÔ
+	nor "For mange nødeler spesifisert. Maks %d deler tillatt"
+	norwegian-ny "For mange nykkeldelar spesifisert. Maks %d delar tillatt"
+	pol "Okre?lono zbyt wiele czêi klucza. Dostêych jest maksymalnie %d czêi"
+	por "Especificadas partes de chave demais. O mámo permitido sã%d partes"
+	rum "Prea multe chei. Numarul de chei maxim este %d"
+	rus "õÁÏÓÉËÍÍÏÏÞÓÅ ÓÓÁÎÇ ËÀÁ òÅÁÔÑÕÁÙÁØÎ ÂÌÅ%d ÞÓÅ"
+	serbian "Navedeno je previše delova kljuè Maksimum %d delova je dozvoljeno"
+	slo "Zadanýli¹ veµa ètíµú Je povolenýjviac %d ètí+	spa "Demasiadas partes de clave declaradas. Un maximo de %d partes son permitidas"
+	swe "Föåa nyckeldelar anväa. Man fåha hö %d nyckeldelar"
+	ukr "úÁÏÞÓÉ ËÀÁÚÚÁÅÏ ä×ÌÎ Î ÂÌÛ %d ÞÓÉ"
+ER_TOO_LONG_KEY 42000 S1009
+	cze "Zadan-Bý byl pø dlouhývìíéa klí je %d"
+	dan "Specificeret nøvar for lang. Maksimal nøæde er %d"
+	nla "Gespecificeerde zoeksleutel was te lang. De maximale lengte is %d"
+	eng "Specified key was too long; max key length is %d bytes"
+	jps "key ‚ª’·‚·‚¬‚Ü·. key ‚Ì·‚³‚ÍÅåd ‚Å·",
+	est "Võon liiga pikk. Maksimaalne võpikkus on %d"
+	fre "La clést trop longue. Longueur maximale: %d"
+	ger "Schlüist zu lang. Die maximale Schlüäe beträ %d"
+	greek "Ô êé ðïóåßáð ìÜïÔ ìéïÞïåá%d"
+	hun "A megadott kulcs tul hosszu. Maximalis kulcshosszusag: %d"
+	ita "La chiave specificata e` troppo lunga. La max lunghezza della chiave e` %d"
+	jpn "key ¤¬Ä¤¹¤®¤Þ¹. key ¤Î¹¤µ¤ÏÇçd ¤Ç¹"
+	kor "ÁÀµÈÅ°¡ ³Ê« ±é´Ù ôë°À ±æ´Â%dÀ´ÏÙ"
+	nor "Spesifisert nø var for lang. Maks nølengde er is %d"
+	norwegian-ny "Spesifisert nykkel var for lang. Maks nykkellengde er %d"
+	pol "Zdefinowany klucz jest zbyt d³ugi. Maksymaln? d³ugo?ci? klucza jest %d"
+	por "Chave especificada longa demais. O comprimento de chave mámo permitido éd"
+	rum "Cheia specificata este prea lunga. Marimea maxima a unei chei este de %d"
+	rus "õÁ ÓÉËÍÄÉÎÊËÀ. íÓÍÌÎÑÄÉÁËÀÁÓÓÁÌÅ %d ÂÊ"
+	serbian "Navedeni kljuèe predug. Maksimalna dužina kljuèje %d"
+	slo "Zadaný je prí¹ dlhýväia dåa kµúe %d"
+	spa "Declaracion de clave demasiado larga. La maxima longitud de clave es %d"
+	swe "Föå nyckel. Höa tillåa nyckelläd ä%d"
+	ukr "úÞÎÊËÀ ÚÄ×É. îÂÌÛ Ä×ÉÁËÀÁ%d Âʦ×
+ER_KEY_COLUMN_DOES_NOT_EXITS 42000 S1009
+	cze "Kl-Bívýpec '%-.64s' v tabulce neexistuje"
+	dan "Nøeltet '%-.64s' eksisterer ikke i tabellen"
+	nla "Zoeksleutel kolom '%-.64s' bestaat niet in tabel"
+	eng "Key column '%-.64s' doesn't exist in table"
+	jps "Key column '%-.64s' ‚ªƒe[ƒuƒ‹‚É ‚肹‚ñ
+	est "Võ tulp '%-.64s' puudub tabelis"
+	fre "La clé%-.64s' n'existe pas dans la table"
+	ger "In der Tabelle gibt es kein Schlüeld '%-.64s'"
+	greek "Ô ðïëä'%-.64s' ä õ÷ó ðê
+	hun "A(z) '%-.64s'kulcsoszlop nem letezik a tablaban"
+	ita "La colonna chiave '%-.64s' non esiste nella tabella"
+	jpn "Key column '%-.64s' ¤¬¥Æ¼¥Ö뤢¤ê¤»¤ó+	kor "Key Ä·³ '%-.64s'´ÂÅÀºí ÁÀÇÁ ¾ÊÀÏÙ"
+	nor "Nø felt '%-.64s' eksiterer ikke i tabellen"
+	norwegian-ny "Nykkel kolonne '%-.64s' eksiterar ikkje i tabellen"
+	pol "Kolumna '%-.64s' zdefiniowana w kluczu nie istnieje w tabeli"
+	por "Coluna chave '%-.64s' nãexiste na tabela"
+	rum "Coloana cheie '%-.64s' nu exista in tabela"
+	rus "ëÞ×ÊÓÏÂÃ'%-.64s' ×ÔÂÉÅÎ ÓÝÓ×Å"
+	serbian "Kljuè kolona '%-.64s' ne postoji u tabeli"
+	slo "Kµú ståc '%-.64s' v tabuµke neexistuje"
+	spa "La columna clave '%-.64s' no existe en la tabla"
+	swe "Nyckelkolumn '%-.64s' finns inte"
+	ukr "ëÞ×ÊÓÏÂà '%-.64s' Î ¦ÓÕ ÕÔÂɦ"
+ER_BLOB_USED_AS_KEY 42000 S1009
+	cze "Blob sloupec '%-.64s' nem-Bùý¾it jako klí
+	dan "BLOB feltet '%-.64s' kan ikke bruges ved specifikation af indeks"
+	nla "BLOB kolom '%-.64s' kan niet gebruikt worden bij zoeksleutel specificatie"
+	eng "BLOB column '%-.64s' can't be used in key specification with the used table type"
+	est "BLOB-tüulpa '%-.64s' ei saa kasutada võna"
+	fre "Champ BLOB '%-.64s' ne peut êe utiliséans une clé+	ger "BLOB-Feld '%-.64s' kann beim verwendeten Tabellentyp nicht als Schlüverwendet werden"
+	greek "Ðä ôõob '%-.64s' ä ìñí÷éðè ó ïóåòåé (key specification)"
+	hun "Blob objektum '%-.64s' nem hasznalhato kulcskent"
+	ita "La colonna BLOB '%-.64s' non puo` essere usata nella specifica della chiave"
+	kor "BLOB Ä·³ '%-.64s'´ÂÅ ÁÀ¿¡¼­ »çµÉ¼ö½ÀÏÙ"
+	nor "Blob felt '%-.64s' kan ikke brukes ved spesifikasjon av nø"
+	norwegian-ny "Blob kolonne '%-.64s' kan ikkje brukast ved spesifikasjon av nyklar"
+	pol "Kolumna typu Blob '%-.64s' nie mo¿e byæ¿yta w specyfikacji klucza"
+	por "Coluna BLOB '%-.64s' nãpode ser utilizada na especificaç de chave para o tipo de tabela usado"
+	rum "Coloana de tip BLOB '%-.64s' nu poate fi folosita in specificarea cheii cu tipul de tabla folosit"
+	rus "óÂÃÔÐ BLOB '%-.64s' Î ÍÖÔÂÔ ÉÐÌÚ×ÎËËÚÁÅÉ ËÀÁ×ÔÂÉÅÔËÇ ÔÐ"
+	serbian "BLOB kolona '%-.64s' ne može biti upotrebljena za navoðe kljuèsa tipom tabele koji se trenutno koristi"
+	slo "Blob pole '%-.64s' nemôby» pou¾itéko kµú	spa "La columna Blob '%-.64s' no puede ser usada en una declaracion de clave"
+	swe "En BLOB '%-.64s' kan inte vara nyckel med den anväa tabelltypen"
+	ukr "BLOB ÓÏÂà '%-.64s' Î ÍÖ ÂÔ ×ËÒÓÁÉ Õ×ÚÁÅÎ ËÀÁ×ÃÏÕÔÐ ÔÂɦ"
+ER_TOO_BIG_FIELDLENGTH 42000 S1009
+	cze "P-Bø velkáéa sloupce '%-.64s' (nejví %d). Pou¾ijte BLOB"
+	dan "For stor feltlæde for kolonne '%-.64s' (maks = %d). Brug BLOB i stedet"
+	nla "Te grote kolomlengte voor '%-.64s' (max = %d). Maak hiervoor gebruik van het type BLOB"
+	eng "Column length too big for column '%-.64s' (max = %d); use BLOB or TEXT instead"
+	jps "column '%-.64s' ‚ÍŠm•Û·‚éolumn ‚Ì傳‚ª‘½‚·‚¬‚Ü·. (Ååd ‚ÜÅ. BLOB ‚ðí‚Ég—p‚µ‚Ä­‚¾‚³‚¢.",
+	est "Tulba '%-.64s' pikkus on liiga pikk (maksimaalne pikkus: %d). Kasuta BLOB väatü+	fre "Champ '%-.64s' trop long (max = %d). Utilisez un BLOB"
+	ger "Feldläe füd '%-.64s' zu groß(maximal %d). BLOB- oder TEXT-Spaltentyp verwenden!"
+	greek "ÐëìÜïÞïã ôåï%-.64s' (max = %d). Ðñë÷éðßôïô BLOB"
+	hun "A(z) '%-.64s' oszlop tul hosszu. (maximum = %d). Hasznaljon BLOB tipust inkabb."
+	ita "La colonna '%-.64s' e` troppo grande (max=%d). Utilizza un BLOB."
+	jpn "column '%-.64s' ¤Ï³Îݹ¤ëolumn ¤Î礵¤¬Â¤¹¤®¤Þ¹. (ºÇçd ¤ÞÇ. BLOB ¤òï¤ËÈÑ·¤Æ¯¤Àµ¤¤."
+	kor "Ä·³ '%-.64s'À Ä·³ ±æ°¡ ³Ê« ±é´Ù(ôë %d). ´ë¿¡ BLOB¸¦ »çǼ¼¿ä
+	nor "For stor nølengde for kolonne '%-.64s' (maks = %d). Bruk BLOB istedenfor"
+	norwegian-ny "For stor nykkellengde for felt '%-.64s' (maks = %d). Bruk BLOB istadenfor"
+	pol "Zbyt du¿a d³ugo?æolumny '%-.64s' (maks. = %d). W zamian u¿yj typu BLOB"
+	por "Comprimento da coluna '%-.64s' grande demais (max = %d); use BLOB em seu lugar"
+	rum "Lungimea coloanei '%-.64s' este prea lunga (maximum = %d). Foloseste BLOB mai bine"
+	rus "óËÍÂÌÛÑÄÉÁÓÏÂÁ'%-.64s' (ÍËÉÕ = %d). éÏØÕÔ ÔÐBLOB ÉÉTEXT ×ÅÔ ÔËÝÇ"
+	serbian "Previše podataka za kolonu '%-.64s' (maksimum je %d). Upotrebite BLOB polje"
+	slo "Prí¹ veµkáåa pre pole '%-.64s' (maximum = %d). Pou¾ite BLOB"
+	spa "Longitud de columna demasiado grande para la columna '%-.64s' (maximo = %d).Usar BLOB en su lugar"
+	swe "Fötor kolumnläd angiven fö%-.64s' (max= %d). Anvä en BLOB instäet"
+	ukr "úÇ Ä×ÉÁÓÏÂÑ'%-.64s' (max = %d). ÷ÒÓÁÔ ÔÐBLOB"
+ER_WRONG_AUTO_KEY 42000 S1009
+	cze "M-Bù mípouze jedno AUTO pole a to musíýinová jako klí
+	dan "Der kan kun specificeres eet AUTO_INCREMENT-felt, og det skal væ indekseret"
+	nla "Er kan slechts 1 autofield zijn en deze moet als zoeksleutel worden gedefinieerd."
+	eng "Incorrect table definition; there can be only one auto column and it must be defined as a key"
+	jps "ƒe[ƒuƒ‹‚Ì肪ˆá‚Ü·; there can be only one auto column and it must be defined as a key",
+	est "Vigane tabelikirjeldus; Tabelis tohib olla üto_increment tüulp ning see peab olema defineeritud võna"
+	fre "Un seul champ automatique est permis et il doit êe indexé+	ger "Falsche Tabellendefinition. Es darf nur eine AUTO_INCREMENT-Spalte geben, und diese muss als Schlüdefiniert werden"
+	greek "Ìïßíõ÷ìïíauto field ê ðåíÝåïó ókey"
+	hun "Csak egy auto mezo lehetseges, es azt kulcskent kell definialni."
+	ita "Puo` esserci solo un campo AUTO e deve essere definito come chiave"
+	jpn "¥Æ¼¥ÖëĵÁ¬°ã¤Þ¹; there can be only one auto column and it must be defined as a key"
+	kor "ºÎ¤ÈÇ ÅÀºí¤À; ÅÀºí dzªÀ auto Ä·³À ÁÀÇ°í°·ÎÁÀµÇî¾ßÇ´ÏÙ"
+	nor "Bare ett auto felt kan væ definert som nø."
+	norwegian-ny "Bare eitt auto felt kan væ definert som nø."
+	pol "W tabeli mo¿e byæylko jedno pole auto i musi ono byædefiniowane jako klucz"
+	por "Definiç incorreta de tabela. Somente éermitido um ú campo auto-incrementado e ele tem que ser definido como chave"
+	rum "Definitia tabelei este incorecta; Nu pot fi mai mult de o singura coloana de tip auto si aceasta trebuie definita ca cheie"
+	rus "îÏÒËÎÅÏÒÄÌÎÅÔÂÉÙ ÍÖÔÓÝÓ××Ô ÔÌË ÏÉ ÁÔÉËÅÅÔÙ ÓÏÂà ÉÏ ÄÌÅ ÂÔ ÏÒÄÌÎËËËÀ"
+	serbian "Pogrešna definicija tabele; U tabeli može postojati samo jedna 'AUTO' kolona i ona mora biti istovremeno definisana kao kolona kljuè
+	slo "Môe ma» iba jedno AUTO pole a to musíy» definovanéko kµú	spa "Puede ser solamente un campo automatico y este debe ser definido como una clave"
+	swe "Det fåfinnas endast ett AUTO_INCREMENT-fä och detta måe vara en nyckel"
+	ukr "î¦ÒÅ×ÚÁÅÎ ÔÂɦ; íÅÂÔ ÌÛ ÏÉ ÁÔÍÔÞÉ ÓÏÂÃ, Ý Ð×ÎÎÂÔ ×ÚÁÅÉ Ñ ËÀ"
+ER_READY  
+	cze "%s: p-Bøven na spojení+	dan "%s: klar til tilslutninger"
+	nla "%s: klaar voor verbindingen"
+	eng "%s: ready for connections.\nVersion: '%s'  socket: '%s'  port: %d"
+	jps "%s: €”õ¹",
+	est "%s: ootab üsi"
+	fre "%s: Prêpour des connections"
+	ger "%-.64s: Bereit fübindungen.\nVersion: '%2'  Socket: '%s'  Port: %d"
+	greek "%s: óíï óÝå"
+	hun "%s: kapcsolatra kesz"
+	ita "%s: Pronto per le connessioni\n"
+	jpn "%s: ½à´°Î"
+	kor "%s: ¿¬°áØñÔÏÙ
+	nor "%s: klar for tilkoblinger"
+	norwegian-ny "%s: klar for tilkoblingar"
+	pol "%s: gotowe do po³?czenia"
+	por "%s: Pronto para conexõ
+	rum "%s: sint gata pentru conectii"
+	rus "%s: çÏ ÐÉÉÁØÓÅÉÅÉ.\n÷É: '%s'  ÓËÔ '%s'  ÐÒ: %d  %s"
+	serbian "%s: Spreman za konekcije\n"
+	slo "%s: pripravenýpojenie"
+	spa "%s: preparado para conexiones"
+	swe "%s: klar att ta emot klienter"
+	ukr "%s: çÏÉ ÄÑÚ¤ÄÁØ"
+ER_NORMAL_SHUTDOWN  
+	cze "%s: norm-Báíkonèí"
+	dan "%s: Normal nedlukning\n"
+	nla "%s: Normaal afgesloten \n"
+	eng "%s: Normal shutdown\n"
+	est "%s: MySQL lõas\n"
+	fre "%s: Arrênormal du serveur\n"
+	ger "%-.64s: Normal heruntergefahren\n"
+	greek "%s: Öóïêääááhutdown\n"
+	hun "%s: Normal leallitas\n"
+	ita "%s: Shutdown normale\n"
+	kor "%s: Á»óÎshutdown\n"
+	nor "%s: Normal avslutning\n"
+	norwegian-ny "%s: Normal nedkopling\n"
+	pol "%s: Standardowe zakoñnie dzia³ania\n"
+	por "%s: 'Shutdown' normal\n"
+	rum "%s: Terminare normala\n"
+	rus "%s: ëÒËÎÑÏÔÎ×Án"
+	serbian "%s: Normalno gašenje\n"
+	slo "%s: normáe ukonèie\n"
+	spa "%s: Apagado normal\n"
+	swe "%s: Normal avslutning\n"
+	ukr "%s: îÍÌÎ Ú×ÒÅÎ\n"
+ER_GOT_SIGNAL  
+	cze "%s: p-Bø signal %d, konè\n"
+	dan "%s: Fangede signal %d. Afslutter!!\n"
+	nla "%s: Signaal %d. Systeem breekt af!\n"
+	eng "%s: Got signal %d. Aborting!\n"
+	jps "%s: Got signal %d. ’†’f!\n",
+	est "%s: sain signaali %d. Lõan!\n"
+	fre "%s: Reçle signal %d. Abandonne!\n"
+	ger "%-.64s: Signal %d erhalten. Abbruch!\n"
+	greek "%s: ÅÞèôÞõ%d. Çääááãôßåén"
+	hun "%s: %d jelzes. Megszakitva!\n"
+	ita "%s: Ricevuto segnale %d. Interruzione!\n"
+	jpn "%s: Got signal %d. ÃÃ!\n"
+	kor "%s: %d ½Å£°¡ µé¿Ô½. ÁÁ!\n"
+	nor "%s: Oppdaget signal %d. Avslutter!\n"
+	norwegian-ny "%s: Oppdaga signal %d. Avsluttar!\n"
+	pol "%s: Otrzymano sygna³ %d. Koñnie dzia³ania!\n"
+	por "%s: Obteve sinal %d. Abortando!\n"
+	rum "%s: Semnal %d obtinut. Aborting!\n"
+	rus "%s: ðÞÎÓÇÁ %d. ðÒÝÅ!\n"
+	serbian "%s: Dobio signal %d. Prekidam!\n"
+	slo "%s: prijatýá%d, ukonèie (Abort)!\n"
+	spa "%s: Recibiendo signal %d. Abortando!\n"
+	swe "%s: Fick signal %d. Avslutar!\n"
+	ukr "%s: ïÉÁÏÓÇÁ %d. ðÒ×ÀØ\n"
+ER_SHUTDOWN_COMPLETE  
+	cze "%s: ukon-Bèírá hotovo\n"
+	dan "%s: Server lukket\n"
+	nla "%s: Afsluiten afgerond\n"
+	eng "%s: Shutdown complete\n"
+	jps "%s: Shutdown Š®—¹\n",
+	est "%s: Lõn"
+	fre "%s: Arrêdu serveur terminé"
+	ger "%-.64s: Herunterfahren beendet\n"
+	greek "%s: Çääááhutdown ïêñên"
+	hun "%s: A leallitas kesz\n"
+	ita "%s: Shutdown completato\n"
+	jpn "%s: Shutdown ´°Î\n"
+	kor "%s: Shutdown À ¿Ïá!\n"
+	nor "%s: Avslutning komplett\n"
+	norwegian-ny "%s: Nedkopling komplett\n"
+	pol "%s: Zakoñnie dzia³ania wykonane\n"
+	por "%s: 'Shutdown' completo\n"
+	rum "%s: Terminare completa\n"
+	rus "%s: ïÁÏË Ú×ÒÅÁn"
+	serbian "%s: Gašenje završeno\n"
+	slo "%s: prá ukonèá"
+	spa "%s: Apagado completado\n"
+	swe "%s: Avslutning klar\n"
+	ukr "%s: òÔ Ú×ÒÅÏn"
+ER_FORCING_CLOSE 08S01 
+	cze "%s: n-Bálnézavøthreadu %ld u¾ivatele '%-.64s'\n"
+	dan "%s: Forceret nedlukning af trå %ld  bruger: '%-.64s'\n"
+	nla "%s: Afsluiten afgedwongen van thread %ld  gebruiker: '%-.64s'\n"
+	eng "%s: Forcing close of thread %ld  user: '%-.32s'\n"
+	jps "%s: ƒXƒŒƒbƒh %ld ‹­§I—¹  user: '%-.64s'\n",
+	est "%s: Sulgen jõ lõ %ld  kasutaja: '%-.32s'\n"
+	fre "%s: Arrêforcée la tâe (thread) %ld  utilisateur: '%-.64s'\n"
+	ger "%s: Thread %ld zwangsweise beendet. Benutzer: '%-.32s'\n"
+	greek "%s: Ô thread èêßå%ld  user: '%-.64s'\n"
+	hun "%s: A(z) %ld thread kenyszeritett zarasa. Felhasznalo: '%-.64s'\n"
+	ita "%s: Forzata la chiusura del thread %ld utente: '%-.64s'\n"
+	jpn "%s: ¥¹¥ì¥É%ld ¶¯À½ªÎ  user: '%-.64s'\n"
+	kor "%s: thread %ldÀ °­Á Á·áser: '%-.64s'\n"
+	nor "%s: Påinget avslutning av trå%ld  bruker: '%-.64s'\n"
+	norwegian-ny "%s: Påinga avslutning av trå%ld  brukar: '%-.64s'\n"
+	pol "%s: Wymuszenie zamkniêa w?tku %ld  u¿ytkownik: '%-.64s'\n"
+	por "%s: Forçdo finalizaç da 'thread' %ld - usuáo '%-.32s'\n"
+	rum "%s: Terminare fortata a thread-ului %ld  utilizatorului: '%-.32s'\n"
+	rus "%s: ðÕÉÅØÏÚËÙÁÍÐÔË%ld  ÐÌÚ×ÔÌ: '%-.32s'\n"
+	serbian "%s: Usiljeno gašenje thread-a %ld koji pripada korisniku: '%-.32s'\n"
+	slo "%s: nálnékonèie vláa %ld u¾íteµa '%-.64s'\n"
+	spa "%s: Forzando a cerrar el thread %ld  usuario: '%-.64s'\n"
+	swe "%s: Stäer av trå%ld; anväare: '%-.64s'\n"
+	ukr "%s: ðËÒÀÚËÉÔ ÇÌÉ%ld ËÒÓÕÁÁ '%-.32s'\n"
+ER_IPSOCK_ERROR 08S01 
+	cze "Nemohu vytvo-BøP socket"
+	dan "Kan ikke oprette IP socket"
+	nla "Kan IP-socket niet openen"
+	eng "Can't create IP socket"
+	jps "IP socket ‚ªì‚ܹ‚ñ+	est "Ei suuda luua IP socketit"
+	fre "Ne peut cré la connection IP (socket)"
+	ger "Kann IP-Socket nicht erzeugen"
+	greek "Äíßáäá ççïã IP socket"
+	hun "Az IP socket nem hozhato letre"
+	ita "Impossibile creare il socket IP"
+	jpn "IP socket ¤¬ºî¤Þ»¤ó	kor "IP ¼ÒÏ» ¸¸µé ¸ø´ÏÙ"
+	nor "Kan ikke opprette IP socket"
+	norwegian-ny "Kan ikkje opprette IP socket"
+	pol "Nie mo¿na stworzyæocket'u IP"
+	por "Nãpode criar o soquete IP"
+	rum "Nu pot crea IP socket"
+	rus "îÏÍÖÏÓÚÁØIP-ÓËÔ
+	serbian "Ne mogu da kreiram IP socket"
+	slo "Nemô vytvori» IP socket"
+	spa "No puedo crear IP socket"
+	swe "Kan inte skapa IP-socket"
+	ukr "îÍÖ Ó×ÒÔ IP ÒÚ¤Í
+ER_NO_SUCH_INDEX 42S12 S1009
+	cze "Tabulka '%-.64s' nem-Bándex odpovíjí CREATE INDEX. Vytvoøabulku znovu"
+	dan "Tabellen '%-.64s' har ikke den nø som blev brugt i CREATE INDEX. Genopret tabellen"
+	nla "Tabel '%-.64s' heeft geen INDEX zoals deze gemaakt worden met CREATE INDEX. Maak de tabel opnieuw"
+	eng "Table '%-.64s' has no index like the one used in CREATE INDEX; recreate the table"
+	jps "Table '%-.64s' ‚Í»‚Ìæ‚Èindex ‚ðÁÄ¢‚ܹ‚ñEATE INDEX ŽÀsŽž‚Éw’è‚ê‚¢‚ܹ‚ñƒe[ƒuƒ‹‚ð肵‚Ä­‚¾‚³‚¢",
+	est "Tabelil '%-.64s' puuduvad võd. Loo tabel uuesti"
+	fre "La table '%-.64s' n'a pas d'index comme celle utilisédans CREATE INDEX. Recré la table"
+	ger "Tabelle '%-.64s' besitzt keinen wie den in CREATE INDEX verwendeten Index. Tabelle neu anlegen"
+	greek "Ïðê '%-.64s' ä Ýåååñ(index) óáü÷éðßåôCREATE INDEX. Ðñë îáìõóôðê
+	hun "A(z) '%-.64s' tablahoz nincs meg a CREATE INDEX altal hasznalt index. Alakitsa at a tablat"
+	ita "La tabella '%-.64s' non ha nessun indice come quello specificatato dalla CREATE INDEX. Ricrea la tabella"
+	jpn "Table '%-.64s' ¤Ï½¤Îè¤Êindex ¤òÃƤ¤Þ»¤óEATE INDEX ¼ÂÔþ¤ËØê¤ì¤¤¤Þ»¤ó¥Æ¼¥ÖëºîĤ·¤Æ¯¤Àµ¤¤"
+	kor "ÅÀºí%-.64s'´ÂÀµ¦½º¸¦ ¸¸µé ¾ÊÒÀÏÙ alter ÅÀºí·É» À¿ë¿© ÅÀºí ¼öϼ¿ä."
+	nor "Tabellen '%-.64s' har ingen index som den som er brukt i CREATE INDEX. Gjenopprett tabellen"
+	norwegian-ny "Tabellen '%-.64s' har ingen index som den som er brukt i CREATE INDEX. Oprett tabellen påytt"
+	pol "Tabela '%-.64s' nie ma indeksu takiego jak w CREATE INDEX. Stwótabelê+	por "Tabela '%-.64s' nãpossui um íice como o usado em CREATE INDEX. Recrie a tabela"
+	rum "Tabela '%-.64s' nu are un index ca acela folosit in CREATE INDEX. Re-creeaza tabela"
+	rus "÷ÂÉÅ'%-.64s' ÎÔÔËÇ ÉÄËÁ ËË×CREATE INDEX. óÁÔ ÔÂÉÕÚÎ×"
+	serbian "Tabela '%-.64s' nema isti indeks kao onaj upotrebljen pri komandi 'CREATE INDEX'. Napravite tabelu ponovo"
+	slo "Tabuµka '%-.64s' nemándex zodpovedajúREATE INDEX. Vytvorte tabulku znova"
+	spa "La tabla '%-.64s' no tiene indice como el usado en CREATE INDEX. Crea de nuevo la tabla"
+	swe "Tabellen '%-.64s' har inget index som motsvarar det angivna i CREATE INDEX. Skapa om tabellen"
+	ukr "ôÉÑ'%-.64s' ͤ ¦ÎÅÓ Ý Î Ó¦×ÁÁ Ú×ÁÁÎÍÕCREATE INDEX. óÒÔ ÔÂÉÀÚÏÕ
+ER_WRONG_FIELD_TERMINATORS 42000 S1009
+	cze "Argument separ-Báru polo¾ek nebyl oèán. Pøte si manuá
+	dan "Felt adskiller er ikke som forventet, se dokumentationen"
+	nla "De argumenten om velden te scheiden zijn anders dan verwacht. Raadpleeg de handleiding"
+	eng "Field separator argument is not what is expected; check the manual"
+	est "Väade eraldaja erineb oodatust. Tutvu kasutajajuhendiga"
+	fre "Sérateur de champs inconnu.  Véfiez dans le manuel"
+	ger "Feldbegrenzer-Argument ist nicht in der erwarteten Form. Bitte im Handbuch nachlesen"
+	greek "Ïä÷ó ðùíßááü áìüÐñëáôôôanual"
+	hun "A mezoelvalaszto argumentumok nem egyeznek meg a varttal. Nezze meg a kezikonyvben!"
+	ita "L'argomento 'Field separator' non e` quello atteso. Controlla il manuale"
+	kor "ǵ帺ÐÚÀ¼öÌ¿Ïü¾ÊÀÏÙ ¸Þº¾óþƺ¸¼¼¿ä
+	nor "Felt skiller argumentene er ikke som forventet, se dokumentasjonen"
+	norwegian-ny "Felt skiljer argumenta er ikkje som venta, sjåokumentasjonen"
+	pol "Nie oczekiwano separatora. Sprawd¥ podrênik"
+	por "Argumento separador de campos nãé esperado. Cheque o manual"
+	rum "Argumentul pentru separatorul de cimpuri este diferit de ce ma asteptam. Verifica manualul"
+	rus "áÕÅÔÒÚÅÉÅÑÐÌÊ- Î ÔÔ ËÔÒÊÏÉÁÓ. ïÁÁÔÓ ËÄËÍÎÁÉ"
+	serbian "Argument separatora polja nije ono što se oèivalo. Proverite uputstvo MySQL server-a"
+	slo "Argument oddeµovaèolíezodpovedáo¾iadavká Skontrolujte v manuá"
+	spa "Los separadores de argumentos del campo no son los especificados. Comprueba el manual"
+	swe "Fäseparatorerna ävad som föntades. Kontrollera mot manualen"
+	ukr "èÎÊÒÚ¦Ì×ÞÐÌ× ðÔÊÅÄËÍÎÁ¦À
+ER_BLOBS_AND_NO_TERMINATED 42000 S1009
+	cze "Nen-Bío¾néou¾ípevnýength s BLOBem. Pou¾ijte 'fields terminated by'."
+	dan "Man kan ikke bruge faste feltlæder med BLOB. Brug i stedet 'fields terminated by'."
+	nla "Bij het gebruik van BLOBs is het niet mogelijk om vaste rijlengte te gebruiken. Maak s.v.p. gebruik van 'fields terminated by'."
+	eng "You can't use fixed rowlength with BLOBs; please use 'fields terminated by'"
+	est "BLOB-tüäade olemasolul ei saa kasutada fikseeritud väapikkust. Vajalik 'fields terminated by' mäang."
+	fre "Vous ne pouvez utiliser des lignes de longueur fixe avec des BLOBs. Utiliser 'fields terminated by'."
+	ger "Eine feste Zeilenläe kann füB-Felder nicht verwendet werden. Bitte 'fields terminated by' verwenden"
+	greek "Äíðßåáñìïó fixed rowlength óLOBs. Ðñë÷éðßôfields terminated by'."
+	hun "Fix hosszusagu BLOB-ok nem hasznalhatok. Hasznalja a 'mezoelvalaszto jelet' ."
+	ita "Non possono essere usate righe a lunghezza fissa con i BLOB. Usa 'FIELDS TERMINATED BY'."
+	jpn "You can't use fixed rowlength with BLOBs; please use 'fields terminated by'."
+	kor "BLOB·Î°í±æÀ lowlength¸¦ »çÇ ¼ö½ÀÏÙ 'fields terminated by'¸¦ »çǼ¼¿ä
+	nor "En kan ikke bruke faste feltlengder med BLOB. Vennlisgt bruk 'fields terminated by'."
+	norwegian-ny "Ein kan ikkje bruke faste feltlengder med BLOB. Vennlisgt bruk 'fields terminated by'."
+	pol "Nie mo¿na u¿yæta³ej d³ugo?ci wiersza z polami typu BLOB. U¿yj 'fields terminated by'."
+	por "Vocêãpode usar comprimento de linha fixo com BLOBs. Por favor, use campos com comprimento limitado."
+	rum "Nu poti folosi lungime de cimp fix pentru BLOB-uri. Foloseste 'fields terminated by'."
+	rus "æÓÒ×ÎÙ ÒÚÅ ÚÐÓ ÓÐÌÍ ÔÐ BLOB ÉÐÌÚ×Ô ÎÌÚ, ÐÉÅÑÔ 'fields terminated by'"
+	serbian "Ne možete koristiti fiksnu velièu sloga kada imate BLOB polja. Molim koristite 'fields terminated by' opciju."
+	slo "Nie je mo¾néou¾i» fixnúku s BLOBom. Pou¾ite 'fields terminated by'."
+	spa "No puedes usar longitudes de filas fijos con BLOBs. Por favor usa 'campos terminados por '."
+	swe "Man kan inte anväa fast radläd med blobs. Anvä 'fields terminated by'"
+	ukr "îÍÖÁ×ËÒÓÏÕÁÉÓÁÕÄ×ÉÕÓÒË ÚBLOB. úÓÁÔÓ 'fields terminated by'"
+ER_TEXTFILE_NOT_READABLE  
+	cze "Soubor '%-.64s' mus-Bíýdresá databá nebo èelnýv¹echny"
+	dan "Filen '%-.64s' skal væ i database-folderen og kunne læs af alle"
+	nla "Het bestand '%-.64s' dient in de database directory voor the komen of leesbaar voor iedereen te zijn."
+	eng "The file '%-.64s' must be in the database directory or be readable by all"
+	jps "ƒtƒ ƒCƒ‹ '%-.64s' ‚Ídatabse ‚Ìdirectory ‚É ‚é‘S‚Ä̆[ƒU[‚ª“Çß邤‚É–‰Â³‚ê‚¢‚ȯ‚ê‚È肹‚ñ
+	est "Fail '%-.64s' peab asuma andmebaasi kataloogis võlema kõle loetav"
+	fre "Le fichier '%-.64s' doit êe dans le rértoire de la base et lisible par tous"
+	ger "Datei '%-.64s' muss im Datenbank-Verzeichnis vorhanden oder lesbar füe sein"
+	greek "Ô áå '%-.64s' ðåíõ÷ódatabase directory Þíìñíäâôá ü
+	hun "A(z) '%-.64s'-nak az adatbazis konyvtarban kell lennie, vagy mindenki szamara olvashatonak"
+	ita "Il file '%-.64s' deve essere nella directory del database e deve essere leggibile da tutti"
+	jpn "¥Õ¡¥¤¥ë%-.64s' ¤Ïdatabse ¤Îdirectory ¤Ë¢¤ëÁ¤ÆÎ楶¡¼¤¬Æ¤á¤è¤Ëöµ¤ì¤¤¤Ê±¤ì¤Êꤻ¤ó+	kor "'%-.64s' ÈÀ´Âµ¥Àź£À½º µðä¿¡ ÁÀǰŪ ¸ð¡°ÔÀ±â¡´ÉÏ©¾ßÇ´ÏÙ"
+	nor "Filen '%-.64s' måæ i database-katalogen for åæ lesbar for alle"
+	norwegian-ny "Filen '%-.64s' måæ i database-katalogen for åæ lesbar for alle"
+	pol "Plik '%-.64s' musi znajdowaæie w katalogu bazy danych lub mieærawa czytania przez wszystkich"
+	por "Arquivo '%-.64s' tem que estar no diretó do banco de dados ou ter leitura possíl para todos"
+	rum "Fisierul '%-.64s' trebuie sa fie in directorul bazei de data sau trebuie sa poata sa fie citit de catre toata lumea (verifica permisiile)"
+	rus "æÌ'%-.64s' ÄÌÅ ÎÈÄÔÓ ×ÔÍÖ ËÔÌÇ, ÞÏÉÂÚ ÄÎÙ, ÉÉÂÔ ÏÝÄÓÕÎÍÄÑÞÅÉ"
+	serbian "File '%-.64s' mora biti u direktorijumu gde su file-ovi baze i mora imati odgovarajuæprava pristupa"
+	slo "Sú'%-.64s' musíy» v adresá databá, alebo èateµnýv¹etký	spa "El archivo '%-.64s' debe estar en el directorio de la base de datos o ser de lectura por todos"
+	swe "Textfilen '%.64s' måe finnas i databasbiblioteket eller vara läar fölla"
+	ukr "æÌ'%-.64s' Ð×ÎÎÂÔ ÕÔà ÂÚ ÄÎÉ ÁÏÍÔ ×ÔÎ×ÅÅÐÁÏÎ ÞÔÎÑÄÑÕ¦È
+ER_FILE_EXISTS_ERROR  
+	cze "Soubor '%-.64s' ji-B¾ existuje"
+	dan "Filen '%-.64s' eksisterer allerede"
+	nla "Het bestand '%-.64s' bestaat reeds"
+	eng "File '%-.80s' already exists"
+	jps "File '%-.64s' ‚ÍùÝµ‚Ü·",
+	est "Fail '%-.80s' juba eksisteerib"
+	fre "Le fichier '%-.64s' existe dé"
+	ger "Datei '%-.80s' bereits vorhanden"
+	greek "Ô áå '%-.64s' õ÷Þç+	hun "A '%-.64s' file mar letezik."
+	ita "Il file '%-.64s' esiste gia`"
+	jpn "File '%-.64s' ¤Ïûºß·¤Þ¹"
+	kor "'%-.64s' ÈÀÀ À¹ÌÁÀÇ´ÏÙ"
+	nor "Filen '%-.64s' eksisterte allerede"
+	norwegian-ny "Filen '%-.64s' eksisterte allereide"
+	pol "Plik '%-.64s' ju¿ istnieje"
+	por "Arquivo '%-.80s' jáxiste"
+	rum "Fisierul '%-.80s' exista deja"
+	rus "æÌ'%-.80s' ÕÅÓÝÓ×Å"
+	serbian "File '%-.80s' veæostoji"
+	slo "Sú'%-.64s' u¾ existuje"
+	spa "El archivo '%-.64s' ya existe"
+	swe "Filen '%-.64s' existerar redan"
+	ukr "æÌ'%-.80s' ×ŦÓÕ"
+ER_LOAD_INFO  
+	cze "Z-Báamùd  Vymazá: %ld  Pøèo: %ld  Varová: %ld"
+	dan "Poster: %ld  Fjernet: %ld  Sprunget over: %ld  Advarsler: %ld"
+	nla "Records: %ld  Verwijderd: %ld  Overgeslagen: %ld  Waarschuwingen: %ld"
+	eng "Records: %ld  Deleted: %ld  Skipped: %ld  Warnings: %ld"
+	jps "ƒŒƒR[ƒh”: %ld  í: %ld  Skipped: %ld  Warnings: %ld",
+	est "Kirjeid: %ld  Kustutatud: %ld  Vahele jäud: %ld  Hoiatusi: %ld"
+	fre "Enregistrements: %ld  Effacé %ld  Non traité %ld  Avertissements: %ld"
+	ger "Datensäe: %ld  Gelöt: %ld  Ausgelassen: %ld  Warnungen: %ld"
+	greek "Åãö %ld  Äááòld  Ðñìçí%ld  Ðïäïó: %ld"
+	hun "Rekordok: %ld  Torolve: %ld  Skipped: %ld  Warnings: %ld"
+	ita "Records: %ld  Cancellati: %ld  Saltati: %ld  Avvertimenti: %ld"
+	jpn "¥ì¡¼¥Éôld  ºï: %ld  Skipped: %ld  Warnings: %ld"
+	kor "·¹Äµå%ld°³  »è: %ld°³  ½ºÅ: %ld°³  °æ: %ld°³"
+	nor "Poster: %ld  Fjernet: %ld  Hoppet over: %ld  Advarsler: %ld"
+	norwegian-ny "Poster: %ld  Fjerna: %ld  Hoppa over: %ld  Åvaringar: %ld"
+	pol "Recordó%ld  Usuniêch: %ld  Pominiêch: %ld  Ostrze¿eñld"
+	por "Registros: %ld - Deletados: %ld - Ignorados: %ld - Avisos: %ld"
+	rum "Recorduri: %ld  Sterse: %ld  Sarite (skipped): %ld  Atentionari (warnings): %ld"
+	rus "úÅ: %ld  õÅÏ %ld  ðÕÅÏ %ld  ðÕÒÖÅÉ: %ld"
+	serbian "Slogova: %ld  Izbrisano: %ld  Preskoèo: %ld  Upozorenja: %ld"
+	slo "Záamov: %ld  Zmazanýld  Preskoèýld  Varovania: %ld"
+	spa "Registros: %ld  Borrados: %ld  Saltados: %ld  Peligros: %ld"
+	swe "Rader: %ld  Bortagna: %ld  Dubletter: %ld  Varningar: %ld"
+	ukr "ú¦× %ld  ÷ÌÎ: %ld  ðÕÅÏ %ld  úÒÖÎ: %ld"
+ER_ALTER_INFO  
+	cze "Z-Báamùd  Zdvojenýld"
+	dan "Poster: %ld  Ens: %ld"
+	nla "Records: %ld  Dubbel: %ld"
+	eng "Records: %ld  Duplicates: %ld"
+	jps "ƒŒƒR[ƒh”: %ld  d•¡: %ld",
+	est "Kirjeid: %ld  Kattuvaid: %ld"
+	fre "Enregistrements: %ld  Doublons: %ld"
+	ger "Datensäe: %ld  Duplikate: %ld"
+	greek "Åãö %ld  Åáëå: %ld"
+	hun "Rekordok: %ld  Duplikalva: %ld"
+	ita "Records: %ld  Duplicati: %ld"
+	jpn "¥ì¡¼¥Éôld  ½Å£: %ld"
+	kor "·¹Äµå%ld°³  Áº¹: %ld°³"
+	nor "Poster: %ld  Like: %ld"
+	norwegian-ny "Poster: %ld  Like: %ld"
+	pol "Rekordó%ld  Duplikató%ld"
+	por "Registros: %ld - Duplicados: %ld"
+	rum "Recorduri: %ld  Duplicate: %ld"
+	rus "úÅ: %ld  äÌËÔ× %ld"
+	serbian "Slogova: %ld  Duplikata: %ld"
+	slo "Záamov: %ld  Opakovanýld"
+	spa "Registros: %ld  Duplicados: %ld"
+	swe "Rader: %ld  Dubletter: %ld"
+	ukr "ú¦× %ld  äÌËÔ× %ld"
+ER_WRONG_SUB_KEY  
+	cze "Chybn-Báodèt klí -- nenío øec nebo je del¹íe¾ déa èti klí"
+	dan "Forkert indeksdel. Den anvendte nøel er ikke en streng eller læden er støend nøæden"
+	nla "Foutief sub-gedeelte van de zoeksleutel. De gebruikte zoeksleutel is geen onderdeel van een string of of de gebruikte lengte is langer dan de zoeksleutel"
+	eng "Incorrect sub part key; the used key part isn't a string, the used length is longer than the key part, or the storage engine doesn't support unique sub keys"
+	est "Vigane võ osa. Kasutatud võosa ei ole string tümäatud pikkus on pikem kui võosa võabelihandler ei toeta seda tüõid"
+	fre "Mauvaise sous-clef. Ce n'est pas un 'string' ou la longueur désse celle dénie dans la clef"
+	ger "Falscher Unterteilschlü Der verwendete Schlüeil ist entweder kein String, die verwendete Läe ist läer als der Teilschlüoder die Speicher-Engine unterstüeine Unterteilschlü
+	greek "ÅöÝïub part key. Ô ÷éðýkey part ä åástring ÞôÞïôåáìáô"
+	hun "Rossz alkulcs. A hasznalt kulcsresz nem karaktersorozat vagy hosszabb, mint a kulcsresz"
+	ita "Sotto-parte della chiave errata. La parte di chiave utilizzata non e` una stringa o la lunghezza e` maggiore della parte di chiave."
+	jpn "Incorrect sub part key; the used key part isn't a string or the used length is longer than the key part"
+	kor "ºÎ¤ÈÇ ¼­¹öÆ Å. »çµÈÅ ÆÆ°¡ ½ºÆ¸µÀ ¾ÆÏŪ Å ÆÆÀ ±æ°¡ ³Ê« ±é´Ù"
+	nor "Feil delnø. Den brukte delnøen er ikke en streng eller den oppgitte lengde er lengre enn nø lengden"
+	norwegian-ny "Feil delnykkel. Den brukte delnykkelen er ikkje ein streng eller den oppgitte lengda er lengre enn nykkellengden"
+	pol "B³êa podczê klucza. U¿yta czê klucza nie jest ³añhem lub u¿yta d³ugo?æjest wiêza ni¿ czê klucza"
+	por "Sub parte da chave incorreta. A parte da chave usada nãéma 'string' ou o comprimento usado éaior que parte da chave ou o manipulador de tabelas nãsuporta sub chaves ús"
+	rum "Componentul cheii este incorrect. Componentul folosit al cheii nu este un sir sau lungimea folosita este mai lunga decit lungimea cheii"
+	rus "îÏÒËÎÑÞÓØËÀÁ éÏØÕÍÑÞÓØËÀÁÎ ÑÌÅÓ ÓÒËÊ ÕÁÁÎÑÄÉÁÂÌÛ, ÞÍÄÉÁÞÓÉËÀÁ ÉÉÏÒÂÔÉ ÔÂÉÙÎ ÐÄÅÖ×Å ÕÉÁØÙ ÞÓÉËÀÁ
+	serbian "Pogrešan pod-kljuèela kljuè Upotrebljeni deo kljuènije string, upotrebljena dužina je veæod dela kljuèili handler tabela ne podržava jedinstvene pod-kljuèe"
+	slo "Incorrect sub part key; the used key part isn't a string or the used length is longer than the key part"
+	spa "Parte de la clave es erronea. Una parte de la clave no es una cadena o la longitud usada es tan grande como la parte de la clave"
+	swe "Felaktig delnyckel. Nyckeldelen äinte en strä eller den angivna läden äläre äkolumnläden"
+	ukr "î¦ÒÁÞÓÉÁËÀÁ ÷ÒÓÁÁÞÓÉÁËÀÁÎ ¤ ÓÒËÀ ÚÄ×ÁÁÏ×Á¦×É ÔÂɦ Î ÐÄÒͤ Õ¦ËÌÎÈÞÓÉ ËÀÅ"
+ER_CANT_REMOVE_ALL_FIELDS 42000 
+	cze "Nen-Bío¾néymazat v¹echny polo¾ky s ALTER TABLE. Pou¾ijte DROP TABLE"
+	dan "Man kan ikke slette alle felter med ALTER TABLE. Brug DROP TABLE i stedet."
+	nla "Het is niet mogelijk alle velden te verwijderen met ALTER TABLE. Gebruik a.u.b. DROP TABLE hiervoor!"
+	eng "You can't delete all columns with ALTER TABLE; use DROP TABLE instead"
+	jps "ALTER TABLE ‚ÅS‚ÄÌcolumn ‚Íí‚Å«‚ܹ‚ñROP TABLE ‚ðp‚µ‚Ä­‚¾‚³‚¢",
+	est "ALTER TABLE kasutades ei saa kustutada kõ tulpasid. Kustuta tabel DROP TABLE abil"
+	fre "Vous ne pouvez effacer tous les champs avec ALTER TABLE. Utilisez DROP TABLE"
+	ger "Mit ALTER TABLE kön nicht alle Felder auf einmal gelöt werden. DafüP TABLE verwenden"
+	greek "Äíßáäá çéñ üùäíåLTER TABLE. Ðñë÷éðßôROP TABLE"
+	hun "Az osszes mezo nem torolheto az ALTER TABLE-lel. Hasznalja a DROP TABLE-t helyette"
+	ita "Non si possono cancellare tutti i campi con una ALTER TABLE. Utilizzare DROP TABLE"
+	jpn "ALTER TABLE ¤Ç´¤ÆÎcolumn ¤Ïï¤Ç­¤Þ»¤óROP TABLE ¤òÑ·¤Æ¯¤Àµ¤¤"
+	kor "ALTER TABLE ¸íÀ·Î¸ðÄ·³À Á¿ïö½ÀÏÙ DROP TABLE ¸íÀ À¿ë¼¼¿ä
+	nor "En kan ikke slette alle felt med ALTER TABLE. Bruk DROP TABLE isteden."
+	norwegian-ny "Ein kan ikkje slette alle felt med ALTER TABLE. Bruk DROP TABLE istadenfor."
+	pol "Nie mo¿na usun?æszystkich póykorzystuj?c ALTER TABLE. W zamian u¿yj DROP TABLE"
+	por "Vocêãpode deletar todas as colunas com ALTER TABLE; use DROP TABLE em seu lugar"
+	rum "Nu poti sterge toate coloanele cu ALTER TABLE. Foloseste DROP TABLE in schimb"
+	rus "îØÑÕÁÉØ×ÅÓÏÂÙÓÐÍÝÀALTER TABLE. éÏØÕÔ  DROP TABLE"
+	serbian "Ne možete da izbrišete sve kolone pomoækomande 'ALTER TABLE'. Upotrebite komandu 'DROP TABLE' ako želite to da uradite"
+	slo "One nemô zmaza» all fields with ALTER TABLE; use DROP TABLE instead"
+	spa "No puede borrar todos los campos con ALTER TABLE. Usa DROP TABLE para hacerlo"
+	swe "Man kan inte radera alla fä med ALTER TABLE. Anvä DROP TABLE istäet"
+	ukr "îÍÖÉÏ×ÄÌÔ ×¦ ÓϦ Ú ÄÐÍÇÀALTER TABLE. ä ÃÏÏÓÏÉÔÊÅÑDROP TABLE"
+ER_CANT_DROP_FIELD_OR_KEY 42000 
+	cze "Nemohu zru-B¹it '%-.64s' (prové DROP). Zkontrolujte, zda neexistujíáamy/klí"
+	dan "Kan ikke udføROP '%-.64s'. Undersø feltet/nø eksisterer."
+	nla "Kan '%-.64s' niet weggooien. Controleer of het veld of de zoeksleutel daadwerkelijk bestaat."
+	eng "Can't DROP '%-.64s'; check that column/key exists"
+	jps "'%-.64s' ‚ðüܹ‚ñµ‚½; check that column/key exists",
+	est "Ei suuda kustutada '%-.64s'. Kontrolli kas tulp/võeksisteerib"
+	fre "Ne peut effacer (DROP) '%-.64s'. Véfiez s'il existe"
+	ger "Kann '%-.64s' nicht löen. Existiert die Spalte oder der Schlü"
+	greek "Áýçéñ (DROP) '%-.64s'. Ðñëåãåíïåïëäõ÷
+	hun "A DROP '%-.64s' nem lehetseges. Ellenorizze, hogy a mezo/kulcs letezik-e"
+	ita "Impossibile cancellare '%-.64s'. Controllare che il campo chiave esista"
+	jpn "'%-.64s' ¤òþ¤Ç­¤Þ»¤ó·¤¿; check that column/key exists"
+	kor "'%-.64s'¸¦ DROPÇ ¼ö½ÀÏÙ Ä·³À³ª Å°¡ ÁÀÇ´ÂöÅǼ¼¿ä
+	nor "Kan ikke DROP '%-.64s'. Undersø felt/nø eksisterer."
+	norwegian-ny "Kan ikkje DROP '%-.64s'. Undersø felt/nø eksisterar."
+	pol "Nie mo¿na wykonaæperacji DROP '%-.64s'. Sprawd¥, czy to pole/klucz istnieje"
+	por "Nãse pode fazer DROP '%-.64s'. Confira se esta coluna/chave existe"
+	rum "Nu pot sa DROP '%-.64s'. Verifica daca coloana/cheia exista"
+	rus "îÏÍÖÏÕÁÉØ(DROP) '%-.64s'. õÉÅØÞÏÓÏÂÃËÀ ÄÊÔÉÅØÏÓÝÓ×Å"
+	serbian "Ne mogu da izvršim komandu drop 'DROP' na '%-.64s'. Proverite da li ta kolona (odnosno kljuèpostoji"
+	slo "Nemô zru¹i» (DROP) '%-.64s'. Skontrolujte, èneexistujúnamy/kµú+	spa "No puedo ELIMINAR '%-.64s'. compuebe que el campo/clave existe"
+	swe "Kan inte ta bort '%-.64s'. Kontrollera att fäet/nyckel finns"
+	ukr "îÍÖ DROP '%-.64s'. ð×ÒÅ Þ ÃÊÓÏÂÃ/ËÀ ¦ÓÕ"
+ER_INSERT_INFO  
+	cze "Z-Báamùd  Zdvojenýld  Varová: %ld"
+	dan "Poster: %ld  Ens: %ld  Advarsler: %ld"
+	nla "Records: %ld  Dubbel: %ld  Waarschuwing: %ld"
+	eng "Records: %ld  Duplicates: %ld  Warnings: %ld"
+	jps "ƒŒƒR[ƒh”: %ld  d•¡”: %ld  Warnings: %ld",
+	est "Kirjeid: %ld  Kattuvaid: %ld  Hoiatusi: %ld"
+	fre "Enregistrements: %ld  Doublons: %ld  Avertissements: %ld"
+	ger "Datensäe: %ld  Duplikate: %ld  Warnungen: %ld"
+	greek "Åãö %ld  Åáëå: %ld  Ðïäïó: %ld"
+	hun "Rekordok: %ld  Duplikalva: %ld  Warnings: %ld"
+	ita "Records: %ld  Duplicati: %ld  Avvertimenti: %ld"
+	jpn "¥ì¡¼¥Éôld  ½Å£¿ôld  Warnings: %ld"
+	kor "·¹Äµå%ld°³  Áº¹: %ld°³  °æ: %ld°³"
+	nor "Poster: %ld  Like: %ld  Advarsler: %ld"
+	norwegian-ny "Postar: %ld  Like: %ld  Åvaringar: %ld"
+	pol "Rekordó%ld  Duplikató%ld  Ostrze¿eñld"
+	por "Registros: %ld - Duplicados: %ld - Avisos: %ld"
+	rum "Recorduri: %ld  Duplicate: %ld  Atentionari (warnings): %ld"
+	rus "úÅ: %ld  äÌËÔ× %ld  ðÕÒÖÅÉ: %ld"
+	serbian "Slogova: %ld  Duplikata: %ld  Upozorenja: %ld"
+	slo "Záamov: %ld  Opakovanýld  Varovania: %ld"
+	spa "Registros: %ld  Duplicados: %ld  Peligros: %ld"
+	swe "Rader: %ld  Dubletter: %ld  Varningar: %ld"
+	ukr "ú¦× %ld  äÌËÔ× %ld  úÒÖÎ: %ld"
+ER_UPDATE_TABLE_USED  
+	eng "You can't specify target table '%-.64s' for update in FROM clause"
+	ger "Die Verwendung der zu aktualisierenden Zieltabelle '%-.64s' ist in der FROM-Klausel nicht zuläig."
+	rus "îÄÐÓÁÔÑÕÁÁÉ ÔÂÉÙ'%-.64s' ×ÓÉË ÔÂÉ FROM ÄÑ×ÅÅÉ ×ÎÅÉÍÎÎÊ
+	swe "INSERT-table '%-.64s' fåinte finnas i FROM tabell-listan"
+	ukr "ôÉÑ'%-.64s' Ý Ú¦Î¤ÔÓ Î ÄÚÏÅÁÕÐÒÌË ÔÂÉØFROM"
+ER_NO_SUCH_THREAD  
+	cze "Nezn-Bá identifikace threadu: %lu"
+	dan "Ukendt tråid: %lu"
+	nla "Onbekend thread id: %lu"
+	eng "Unknown thread id: %lu"
+	jps "thread id: %lu ‚Í ‚肹‚ñ+	est "Tundmatu lõ %lu"
+	fre "Numé de tâe inconnu: %lu"
+	ger "Unbekannte Thread-ID: %lu"
+	greek "Áíôhread id: %lu"
+	hun "Ervenytelen szal (thread) id: %lu"
+	ita "Thread id: %lu sconosciuto"
+	jpn "thread id: %lu ¤Ï¢¤ê¤»¤ó	kor "¾Ëö´Â¾²·¹µåd: %lu"
+	nor "Ukjent tråid: %lu"
+	norwegian-ny "Ukjent tråid: %lu"
+	pol "Nieznany identyfikator w?tku: %lu"
+	por "'Id' de 'thread' %lu desconhecido"
+	rum "Id-ul: %lu thread-ului este necunoscut"
+	rus "îÚÅÔÙ ÎÍÒÐÔË: %lu"
+	serbian "Nepoznat thread identifikator: %lu"
+	slo "Nezná identifikáa vláa: %lu"
+	spa "Identificador del thread: %lu  desconocido"
+	swe "Finns ingen tråmed id %lu"
+	ukr "î¦ÄÍʦÄÎɦËÔÒÇÌÉ %lu"
+ER_KILL_DENIED_ERROR  
+	cze "Nejste vlastn-Bím threadu %lu"
+	dan "Du er ikke ejer af trån %lu"
+	nla "U bent geen bezitter van thread %lu"
+	eng "You are not owner of thread %lu"
+	jps "thread %lu ‚ÌI[ƒi[‚ÅÍ ‚肹‚ñ+	est "Ei ole lõ %lu omanik"
+	fre "Vous n'ês pas propriéire de la tâe no: %lu"
+	ger "Sie sind nicht Eigentüon Thread %lu"
+	greek "Äíßèowner ôthread %lu"
+	hun "A %lu thread-nek mas a tulajdonosa"
+	ita "Utente non proprietario del thread %lu"
+	jpn "thread %lu ¤Îª¡¼¥Ê¼¤ÇÏ¢¤ê¤»¤ó	kor "¾²·¹µåhread) %luÀ ¼Ò¯À°¡ ¾ÆÕÏÙ"
+	nor "Du er ikke eier av trån %lu"
+	norwegian-ny "Du er ikkje eigar av trå%lu"
+	pol "Nie jeste? w³a?cicielem w?tku %lu"
+	por "Vocêãéroprietáo da 'thread' %lu"
+	rum "Nu sinteti proprietarul threadului %lu"
+	rus "÷ÅÑÌÅÅØ×ÁÅØÅ ÐÔË %lu"
+	serbian "Vi niste vlasnik thread-a %lu"
+	slo "Nie ste vlastním vláa %lu"
+	spa "Tu no eres el propietario del thread%lu"
+	swe "Du äinte äre till trå%lu"
+	ukr "÷Å×ÌÄÒÇÌÉ%lu"
+ER_NO_TABLES_USED  
+	cze "Nejsou pou-B¾ity ¾áéabulky"
+	dan "Ingen tabeller i brug"
+	nla "Geen tabellen gebruikt."
+	eng "No tables used"
+	est "Ütegi tabelit pole kasutusel"
+	fre "Aucune table utilisé
+	ger "Keine Tabellen verwendet"
+	greek "Äíñìïèáðê"
+	hun "Nincs hasznalt tabla"
+	ita "Nessuna tabella usata"
+	kor "¾î ÅÀºí »çµÇö¾ÒÀÏÙ"
+	nor "Ingen tabeller i bruk"
+	norwegian-ny "Ingen tabellar i bruk"
+	pol "Nie ma ¿adej u¿ytej tabeli"
+	por "Nenhuma tabela usada"
+	rum "Nici o tabela folosita"
+	rus "îÁÉ ÔÂÉÙÎ ÉÐÌÚ×Î"
+	serbian "Nema upotrebljenih tabela"
+	slo "Nie je pou¾itáiadna tabuµka"
+	spa "No ha tablas usadas"
+	swe "Inga tabeller angivna"
+	ukr "î×ËÒÓÁÏÔÂÉØ
+ER_TOO_BIG_SET  
+	cze "P-Bø mnoho øcù sloupec %s a SET"
+	dan "For mange tekststrenge til specifikationen af SET i kolonne %-.64s"
+	nla "Teveel strings voor kolom %s en SET"
+	eng "Too many strings for column %-.64s and SET"
+	est "Liiga palju string tulbale %-.64s tü SET"
+	fre "Trop de chaîs dans la colonne %s avec SET"
+	ger "Zu viele Strings füd %-.64s und SET angegeben"
+	greek "ÐñïÜstrings ã ôåï-.64s ê SET"
+	hun "Tul sok karakter: %-.64s es SET"
+	ita "Troppe stringhe per la colonna %-.64s e la SET"
+	kor "Ä·³ %-.64s¿ÍSET¿¡¼­ ½ºÆ¸µÀ ³Ê« ¸¹½ÀÏÙ"
+	nor "For mange tekststrenger kolonne %s og SET"
+	norwegian-ny "For mange tekststrengar felt %s og SET"
+	pol "Zbyt wiele ³añhóla kolumny %s i polecenia SET"
+	por "'Strings' demais para coluna '%-.64s' e SET"
+	rum "Prea multe siruri pentru coloana %-.64s si SET"
+	rus "óËÍÍÏÏÚÁÅÉ ÄÑÓÏÂÁ%-.64s ×SET"
+	serbian "Previše string-ova za kolonu '%-.64s' i komandu 'SET'"
+	slo "Prí¹ mnoho re»azcov pre pole %-.64s a SET"
+	spa "Muchas strings para columna %s y SET"
+	swe "Föåa alternativ till kolumn %s föET"
+	ukr "úÁÏÓÒËÄÑÓÏÂÑ%-.64s Ô SET"
+ER_NO_UNIQUE_LOGFILE  
+	cze "Nemohu vytvo-Bøednoznaè jmé logovací souboru %s.(1-999)\n"
+	dan "Kan ikke lave unikt log-filnavn %s.(1-999)\n"
+	nla "Het is niet mogelijk een unieke naam te maken voor de logfile %s.(1-999)\n"
+	eng "Can't generate a unique log-filename %-.64s.(1-999)\n"
+	est "Ei suuda luua unikaalset logifaili nime %-.64s.(1-999)\n"
+	fre "Ne peut gérer un unique nom de journal %s.(1-999)\n"
+	ger "Kann keinen eindeutigen Dateinamen fü Logdatei %-.64s(1-999) erzeugen\n"
+	greek "Áýççïã unique log-filename %-.64s.(1-999)\n"
+	hun "Egyedi log-filenev nem generalhato: %-.64s.(1-999)\n"
+	ita "Impossibile generare un nome del file log unico %-.64s.(1-999)\n"
+	kor "Unique ·Î×­À '%-.64s'¸¦ ¸¸µé ¾ø´Ù(1-999)\n"
+	nor "Kan ikke lage unikt loggfilnavn %s.(1-999)\n"
+	norwegian-ny "Kan ikkje lage unikt loggfilnavn %s.(1-999)\n"
+	pol "Nie mo¿na stworzyænikalnej nazwy pliku z logiem %s.(1-999)\n"
+	por "Nãpode gerar um nome de arquivo de 'log' ú '%-.64s'.(1-999)\n"
+	rum "Nu pot sa generez un nume de log unic %-.64s.(1-999)\n"
+	rus "îÏÍÖÏÓÚÁØÕÉÁØÏ ÉÑÆÊÁÖÒÁÁ%-.64s.(1-999)\n"
+	serbian "Ne mogu da generišem jedinstveno ime log-file-a: '%-.64s.(1-999)'\n"
+	slo "Nemô vytvori» unikáe meno log-sú %-.64s.(1-999)\n"
+	spa "No puede crear un unico archivo log %s.(1-999)\n"
+	swe "Kan inte generera ett unikt filnamn %s.(1-999)\n"
+	ukr "îÍÖ ÚÅÅÕÁÉÕ¦ËÌÎ ¦ÍÑlog-ÆÊÕ%-.64s.(1-999)\n"
+ER_TABLE_NOT_LOCKED_FOR_WRITE  
+	cze "Tabulka '%-.64s' byla zam-Bèa s READ a nemùýnì"
+	dan "Tabellen '%-.64s' var lå med READ låog kan ikke opdateres"
+	nla "Tabel '%-.64s' was gelocked met een lock om te lezen. Derhalve kunnen geen wijzigingen worden opgeslagen."
+	eng "Table '%-.64s' was locked with a READ lock and can't be updated"
+	jps "Table '%-.64s' ‚ÍREAD lock ‚ÉÈÁÄ¢‚ÄAXV‚ÍÅ«‚ܹ‚ñ+	est "Tabel '%-.64s' on lukustatud READ lukuga ning ei ole muudetav"
+	fre "Table '%-.64s' verrouillélecture (READ): modification impossible"
+	ger "Tabelle '%-.64s' ist mit Lesesperre versehen und kann nicht aktualisiert werden"
+	greek "Ïðê '%-.64s' Ýåêéè ìREAD lock ê ä åôïáááò	hun "A(z) '%-.64s' tabla zarolva lett (READ lock) es nem lehet frissiteni"
+	ita "La tabella '%-.64s' e` soggetta a lock in lettura e non puo` essere aggiornata"
+	jpn "Table '%-.64s' ¤ÏREAD lock ¤ËÊÃƤ¤Æ¢¹¹¿·¤ÏÇ­¤Þ»¤ó	kor "ÅÀºí%-.64s'´ÂREAD ¶ôÀ°ÜÖî °»½ÅÒ¼ö½ÀÏÙ"
+	nor "Tabellen '%-.64s' var lå med READ låog kan ikke oppdateres"
+	norwegian-ny "Tabellen '%-.64s' var lå med READ låog kan ikkje oppdaterast"
+	pol "Tabela '%-.64s' zosta³a zablokowana przez READ i nie mo¿e zostaæaktualizowana"
+	por "Tabela '%-.64s' foi travada com trava de leitura e nãpode ser atualizada"
+	rum "Tabela '%-.64s' a fost locked cu un READ lock si nu poate fi actualizata"
+	rus "ôÉÁ'%-.64s' ÚÂÏÉÏÁÁÕÏÎÍREAD lock ÉÎ ÍÖÔÂÔ ÉÍÎÎ"
+	serbian "Tabela '%-.64s' je zakljuèa READ lock-om; iz nje se može samo èati ali u nju se ne može pisati"
+	slo "Tabuµka '%-.64s' bola zamknutá READ a nemôby» zmenená+	spa "Tabla '%-.64s' fue trabada con un READ lock y no puede ser actualizada"
+	swe "Tabell '%-.64s' kan inte uppdateras emedan den älå föäing"
+	ukr "ôÉÀ'%-.64s' ÚÂÏÏÁÏÔÌË ÄÑÞÔÎÑ ÔÍ §§ Î ÍÖÁÏÏÉÉ
+ER_TABLE_NOT_LOCKED  
+	cze "Tabulka '%-.64s' nebyla zam-Bèa s LOCK TABLES"
+	dan "Tabellen '%-.64s' var ikke lå med LOCK TABLES"
+	nla "Tabel '%-.64s' was niet gelocked met LOCK TABLES"
+	eng "Table '%-.64s' was not locked with LOCK TABLES"
+	jps "Table '%-.64s' ‚ÍLOCK TABLES ‚Éæ‚čƒbƒN‚³‚ê‚¢‚ܹ‚ñ+	est "Tabel '%-.64s' ei ole lukustatud käga LOCK TABLES"
+	fre "Table '%-.64s' non verrouillé utilisez LOCK TABLES"
+	ger "Tabelle '%-.64s' wurde nicht mit LOCK TABLES gesperrt"
+	greek "Ïðê '%-.64s' ä Ýåêéè ìLOCK TABLES"
+	hun "A(z) '%-.64s' tabla nincs zarolva a LOCK TABLES-szel"
+	ita "Non e` stato impostato il lock per la tabella '%-.64s' con LOCK TABLES"
+	jpn "Table '%-.64s' ¤ÏLOCK TABLES ¤Ëè¤Æí¥¯¤µ¤ì¤¤¤Þ»¤ó	kor "ÅÀºí%-.64s'´ÂLOCK TABLES ¸íÀ·ÎÀ±â ¾ÊÒÀÏÙ"
+	nor "Tabellen '%-.64s' var ikke lå med LOCK TABLES"
+	norwegian-ny "Tabellen '%-.64s' var ikkje lå med LOCK TABLES"
+	pol "Tabela '%-.64s' nie zosta³a zablokowana poleceniem LOCK TABLES"
+	por "Tabela '%-.64s' nãfoi travada com LOCK TABLES"
+	rum "Tabela '%-.64s' nu a fost locked cu LOCK TABLES"
+	rus "ôÉÁ'%-.64s' Î ÂÌ ÚÂÏÉÏÁÁÓÐÍÝÀLOCK TABLES"
+	serbian "Tabela '%-.64s' nije bila zakljuèa komandom 'LOCK TABLES'"
+	slo "Tabuµka '%-.64s' nebola zamknutá LOCK TABLES"
+	spa "Tabla '%-.64s' no fue trabada con LOCK TABLES"
+	swe "Tabell '%-.64s' äinte lå med LOCK TABLES"
+	ukr "ôÉÀ'%-.64s' Î ÂÌ ÂÏÏÁÏÚLOCK TABLES"
+ER_BLOB_CANT_HAVE_DEFAULT 42000 
+	cze "Blob polo-B¾ka '%-.64s' nemùídefaultníodnotu"
+	dan "BLOB feltet '%-.64s' kan ikke have en standard væi"
+	nla "Blob veld '%-.64s' can geen standaardwaarde bevatten"
+	eng "BLOB/TEXT column '%-.64s' can't have a default value"
+	est "BLOB-tüulp '%-.64s' ei saa omada vaikevätust"
+	fre "BLOB '%-.64s' ne peut avoir de valeur par déut"
+	ger "BLOB/TEXT-Feld '%-.64s' darf keinen Vorgabewert (DEFAULT) haben"
+	greek "Ô Blob ðá%-.64s' ä ìñ íÝï ðáñÝåôòefault value)"
+	hun "A(z) '%-.64s' blob objektumnak nem lehet alapertelmezett erteke"
+	ita "Il campo BLOB '%-.64s' non puo` avere un valore di default"
+	jpn "BLOB column '%-.64s' can't have a default value"
+	kor "BLOB Ä·³ '%-.64s' ´Âµð® °ªÀ °¡Á ¼ö½ÀÏÙ"
+	nor "Blob feltet '%-.64s' kan ikke ha en standard verdi"
+	norwegian-ny "Blob feltet '%-.64s' kan ikkje ha ein standard verdi"
+	pol "Pole typu blob '%-.64s' nie mo¿e mieæomy?lnej warto?ci"
+	por "Coluna BLOB '%-.64s' nãpode ter um valor padrã(default)"
+	rum "Coloana BLOB '%-.64s' nu poate avea o valoare default"
+	rus "îÏÍÖÏÕÁÙÁØÚÁÅÉ Ð ÕÏÞÎÀÄÑÓÏÂÁBLOB '%-.64s'"
+	serbian "BLOB kolona '%-.64s' ne može imati default vrednost"
+	slo "Pole BLOB '%-.64s' nemôma» implicitnúnotu"
+	spa "Campo Blob '%-.64s' no puede tener valores patron"
+	swe "BLOB fä '%-.64s' kan inte ha ett DEFAULT-väe"
+	ukr "óÂà BLOB '%-.64s' Î ÍÖ ÍÔ ÚÁÅÎ Ð ÚÍ×ÕÁÎ"
+ER_WRONG_DB_NAME 42000 
+	cze "Nep-Bøtnémé databá '%-.64s'"
+	dan "Ugyldigt database navn '%-.64s'"
+	nla "Databasenaam '%-.64s' is niet getoegestaan"
+	eng "Incorrect database name '%-.100s'"
+	jps "Žw’肽 database –¼ '%-.100s' ‚ªŠÔá‚Ä¢‚Ü·",
+	est "Vigane andmebaasi nimi '%-.100s'"
+	fre "Nom de base de donnéillél: '%-.64s'"
+	ger "Unerlaubter Datenbankname '%-.100s'"
+	greek "Ëè üâçäïí '%-.100s'"
+	hun "Hibas adatbazisnev: '%-.100s'"
+	ita "Nome database errato '%-.100s'"
+	jpn "»Øꤿ database Ì '%-.100s' ¤¬´Öã¤Æ¤¤Þ¹"
+	kor "'%-.100s' µ¥Àź£À½ºÀ À¸§À ºÎ¤ÈÇ´ÏÙ"
+	nor "Ugyldig database navn '%-.64s'"
+	norwegian-ny "Ugyldig database namn '%-.64s'"
+	pol "Niedozwolona nazwa bazy danych '%-.64s'"
+	por "Nome de banco de dados '%-.100s' incorreto"
+	rum "Numele bazei de date este incorect '%-.100s'"
+	rus "îÏÒËÎÅÉÑÂÚ ÄÎÙ '%-.100s'"
+	serbian "Pogrešno ime baze '%-.100s'"
+	slo "Neprístnéeno databá '%-.100s'"
+	spa "Nombre de base de datos ilegal '%-.64s'"
+	swe "Felaktigt databasnamn '%-.64s'"
+	ukr "î¦ÒŦÍÑÂÚ ÄÎÉ '%-.100s'"
+ER_WRONG_TABLE_NAME 42000 
+	cze "Nep-Bøtnémé tabulky '%-.64s'"
+	dan "Ugyldigt tabel navn '%-.64s'"
+	nla "Niet toegestane tabelnaam '%-.64s'"
+	eng "Incorrect table name '%-.100s'"
+	jps "Žw’肽 table –¼ '%-.100s' ‚ÍÜ¿‚ª‚ÁÄ¢‚Ü·",
+	est "Vigane tabeli nimi '%-.100s'"
+	fre "Nom de table illél: '%-.64s'"
+	ger "Unerlaubter Tabellenname '%-.100s'"
+	greek "Ëè üðê'%-.100s'"
+	hun "Hibas tablanev: '%-.100s'"
+	ita "Nome tabella errato '%-.100s'"
+	jpn "»Øꤿ table Ì '%-.100s' ¤ÏÞÁ¬¤ÃƤ¤Þ¹"
+	kor "'%-.100s' ÅÀºí̧À ºÎ¤ÈÇ´ÏÙ"
+	nor "Ugyldig tabell navn '%-.64s'"
+	norwegian-ny "Ugyldig tabell namn '%-.64s'"
+	pol "Niedozwolona nazwa tabeli '%-.64s'..."
+	por "Nome de tabela '%-.100s' incorreto"
+	rum "Numele tabelei este incorect '%-.100s'"
+	rus "îÏÒËÎÅÉÑÔÂÉÙ'%-.100s'"
+	serbian "Pogrešno ime tabele '%-.100s'"
+	slo "Neprístnéeno tabuµky '%-.100s'"
+	spa "Nombre de tabla ilegal '%-.64s'"
+	swe "Felaktigt tabellnamn '%-.64s'"
+	ukr "î¦ÒŦÍÑÔÂɦ '%-.100s'"
+ER_TOO_BIG_SELECT 42000 
+	cze "Zadan-BýCT by prochál pø mnoho záamùrval velmi dlouho. Zkontrolujte tvar WHERE a je-li SELECT v poø, pou¾ijte SET SQL_BIG_SELECTS=1"
+	dan "SELECT ville undersøor mange poster og ville sandsynligvis tage meget lang tid. UndersøERE delen og brug SET SQL_BIG_SELECTS=1 hvis udtrykket er korrekt"
+	nla "Het SELECT-statement zou te veel records analyseren en dus veel tijd in beslagnemen. Kijk het WHERE-gedeelte van de query na en kies SET SQL_BIG_SELECTS=1 als het stament in orde is."
+	eng "The SELECT would examine more than MAX_JOIN_SIZE rows; check your WHERE and use SET SQL_BIG_SELECTS=1 or SET SQL_MAX_JOIN_SIZE=# if the SELECT is okay"
+	est "SELECT lause peab lä vaatama suure hulga kirjeid ja võs tõoliselt liiga kaua aega. Tasub kontrollida WHERE klauslit ja vajadusel kasutada käu SET SQL_BIG_SELECTS=1"
+	fre "SELECT va devoir examiner beaucoup d'enregistrements ce qui va prendre du temps. Véfiez la clause WHERE et utilisez SET SQL_BIG_SELECTS=1 si SELECT se passe bien"
+	ger "Die Ausfü des SELECT wüu viele Datensäe untersuchen und wahrscheinlich sehr lange dauern. Bitte WHERE-Klausel üünd gegebenenfalls SET SQL_BIG_SELECTS=1 oder SET SQL_MAX_JOIN_SIZE=# verwenden"
+	greek "Ô SELECT èåôéåëáè åñíáðí èêõñéÐñëåôåéðìñ ôWHERE ê ÷éðßôET SQL_BIG_SELECTS=1 áôELECT åáóüun "A SELECT tul sok rekordot fog megvizsgalni es nagyon sokaig fog tartani. Ellenorizze a WHERE-t es hasznalja a SET SQL_BIG_SELECTS=1 beallitast, ha a SELECT okay"
+	ita "La SELECT dovrebbe esaminare troppi record e usare troppo tempo. Controllare la WHERE e usa SET SQL_BIG_SELECTS=1 se e` tutto a posto."
+	kor "SELECT ¸í¿¡¼­ ³Ê« ¸¹À ·¹Äµå ñ⧹®¿¡ ¸¹À ½Ã£À ¼Òä´ÏÙ µû WHERE ¹®À Á°ËÏŪ, ¸¸¾àELECT°¡ okµÇéSET SQL_BIG_SELECTS=1 ¿ÉÇ» »çǼ¼¿ä
+	nor "SELECT ville undersøor mange poster og ville sannsynligvis ta veldig lang tid. UndersøERE klausulen og bruk SET SQL_BIG_SELECTS=1 om SELECTen er korrekt"
+	norwegian-ny "SELECT ville undersøfor mange postar og ville sannsynligvis ta veldig lang tid. UndersøERE klausulen og bruk SET SQL_BIG_SELECTS=1 om SELECTen er korrekt"
+	pol "Operacja SELECT bêie dotyczy³a zbyt wielu rekordó prawdopodobnie zajmie bardzo du¿o czasu. Sprawd¥ warunek WHERE i u¿yj SQL_OPTION BIG_SELECTS=1 je?li operacja SELECT jest poprawna"
+	por "O SELECT examinaria registros demais e provavelmente levaria muito tempo. Cheque sua cláula WHERE e use SET SQL_BIG_SELECTS=1, se o SELECT estiver correto"
+	rum "SELECT-ul ar examina prea multe cimpuri si probabil ar lua prea mult timp; verifica clauza WHERE si foloseste SET SQL_BIG_SELECTS=1 daca SELECT-ul e okay"
+	rus "ä ÔËÊ×ÂÒÉSELECT ÄÌÅ ÂÄÔÐÏÍÔÅØÓÉËÍÍÏÏÚÐÓÊÉ ×ÄÍ, ÜÏÚÊÅ ÏÅØÍÏÏ×ÅÅÉ ðÅØÅ×Û ÕÁÁÉ WHERE, É ÅÌ ×ÎÍ×Å×ÐÒÄÅ ÕÁÉÅSET SQL_BIG_SELECTS=1"
+	serbian "Komanda 'SELECT' æispitati previše slogova i potrošiti previše vremena. Proverite vaš 'WHERE' filter i upotrebite 'SET OPTION SQL_BIG_SELECTS=1' ako želite baš ovakvu komandu"
+	slo "Zadanáo¾iadavka SELECT by precháala prí¹ mnoho záamov a trvala by prí¹ dlho. Skontrolujte tvar WHERE a ak je v poriadku, pou¾ite SET SQL_BIG_SELECTS=1"
+	spa "El SELECT puede examinar muchos registros y probablemente con mucho tiempo. Verifique tu WHERE y usa SET SQL_BIG_SELECTS=1 si el SELECT esta correcto"
+	swe "Den angivna från skulle lä mer äMAX_JOIN_SIZE rader.  Kontrollera din WHERE och anvä SET SQL_BIG_SELECTS=1 eller SET MAX_JOIN_SIZE=# ifall du vill hantera stora joins"
+	ukr "úÕSELECT ÐÔ¦ÂÏÏÒÂÔ ÂÇÔ ÚÐÓ× Ý, Ð×Å ÚÊÅÄÖ ÂÇÔ ÞÓ. ð×ÒÅ×Û WHERE Ô ×ËÒÓÏÕÔ SET SQL_BIG_SELECTS=1, ÑÝ ÃÊÚÐÔSELECT ¤ ×ÒÉ"
+ER_UNKNOWN_ERROR  
+	cze "Nezn-Bá chyba"
+	dan "Ukendt fejl"
+	nla "Onbekende Fout"
+	eng "Unknown error"
+	est "Tundmatu viga"
+	fre "Erreur inconnue"
+	ger "Unbekannter Fehler"
+	greek "ÐïõÜíôÜï
+	hun "Ismeretlen hiba"
+	ita "Errore sconosciuto"
+	kor "¾Ëö´Â¿¡·¯À´ÏÙ"
+	nor "Ukjent feil"
+	norwegian-ny "Ukjend feil"
+	por "Erro desconhecido"
+	rum "Eroare unknown"
+	rus "îÚÅÔÁ ÏÉË"
+	serbian "Nepoznata greška"
+	slo "Nezná chyba"
+	spa "Error desconocido"
+	swe "Oidentifierat fel"
+	ukr "î¦ÄÍ ÐÍÌÁ
+ER_UNKNOWN_PROCEDURE 42000 
+	cze "Nezn-Bá procedura %s"
+	dan "Ukendt procedure %s"
+	nla "Onbekende procedure %s"
+	eng "Unknown procedure '%-.64s'"
+	est "Tundmatu protseduur '%-.64s'"
+	fre "Procére %s inconnue"
+	ger "Unbekannte Prozedur '%-.64s'"
+	greek "Áíôééó'%-.64s'"
+	hun "Ismeretlen eljaras: '%-.64s'"
+	ita "Procedura '%-.64s' sconosciuta"
+	kor "¾Ëö´Â¼ö® : '%-.64s'"
+	nor "Ukjent prosedyre %s"
+	norwegian-ny "Ukjend prosedyre %s"
+	pol "Unkown procedure %s"
+	por "'Procedure' '%-.64s' desconhecida"
+	rum "Procedura unknown '%-.64s'"
+	rus "îÚÅÔÁ ÐÏÅÕÁ'%-.64s'"
+	serbian "Nepoznata procedura '%-.64s'"
+	slo "Nezná procedú%-.64s'"
+	spa "Procedimiento desconocido %s"
+	swe "Okä procedur: %s"
+	ukr "î¦ÄÍ ÐÏÅÕÁ'%-.64s'"
+ER_WRONG_PARAMCOUNT_TO_PROCEDURE 42000 
+	cze "Chybn-Být parametrùcedury %s"
+	dan "Forkert antal  parametre til proceduren %s"
+	nla "Foutief aantal parameters doorgegeven aan procedure %s"
+	eng "Incorrect parameter count to procedure '%-.64s'"
+	est "Vale parameetrite hulk protseduurile '%-.64s'"
+	fre "Mauvais nombre de paramèes pour la procedure %s"
+	ger "Falsche Parameterzahl füzedur '%-.64s'"
+	greek "Ëè áèòñô óääáá%-.64s'"
+	hun "Rossz parameter a(z) '%-.64s'eljaras szamitasanal"
+	ita "Numero di parametri errato per la procedura '%-.64s'"
+	kor "'%-.64s' ¼ö®¿¡ ´ë ºÎ¤ÈÇ Æ¶óÍ
+	nor "Feil parameter antall til prosedyren %s"
+	norwegian-ny "Feil parameter tal til prosedyra %s"
+	pol "Incorrect parameter count to procedure %s"
+	por "Nú de parâtros incorreto para a 'procedure' '%-.64s'"
+	rum "Procedura '%-.64s' are un numar incorect de parametri"
+	rus "îÏÒËÎÅËÌÞÓ× ÐÒÍÔÏ ÄÑÐÏÅÕÙ'%-.64s'"
+	serbian "Pogrešan broj parametara za proceduru '%-.64s'"
+	slo "Chybnýt parametrov procedú%-.64s'"
+	spa "Equivocado parametro count para procedimiento %s"
+	swe "Felaktigt antal parametrar till procedur %s"
+	ukr "èÎ ËÌËÓØÐÒÍÔ¦×ÐÏÅÕÉ'%-.64s'"
+ER_WRONG_PARAMETERS_TO_PROCEDURE  
+	cze "Chybn-Béarametry procedury %s"
+	dan "Forkert(e) parametre til proceduren %s"
+	nla "Foutieve parameters voor procedure %s"
+	eng "Incorrect parameters to procedure '%-.64s'"
+	est "Vigased parameetrid protseduurile '%-.64s'"
+	fre "Paramèe erronéour la procedure %s"
+	ger "Falsche Parameter füzedur '%-.64s'"
+	greek "Ëè ðìñó ääáá%-.64s'"
+	hun "Rossz parameter a(z) '%-.64s' eljarasban"
+	ita "Parametri errati per la procedura '%-.64s'"
+	kor "'%-.64s' ¼ö®¿¡ ´ë ºÎ¤ÈÇ Æ¶óÍ
+	nor "Feil parametre til prosedyren %s"
+	norwegian-ny "Feil parameter til prosedyra %s"
+	pol "Incorrect parameters to procedure %s"
+	por "Parâtros incorretos para a 'procedure' '%-.64s'"
+	rum "Procedura '%-.64s' are parametrii incorecti"
+	rus "îÏÒËÎÅÐÒÍÔÙÄÑÐÏÅÕÙ'%-.64s'"
+	serbian "Pogrešni parametri prosleð proceduri '%-.64s'"
+	slo "Chybnéarametre procedú%-.64s'"
+	spa "Equivocados parametros para procedimiento %s"
+	swe "Felaktiga parametrar till procedur %s"
+	ukr "èÎÊÐÒÍÔÒÐÏÅÕÉ'%-.64s'"
+ER_UNKNOWN_TABLE 42S02 
+	cze "Nezn-Bá tabulka '%-.64s' v %s"
+	dan "Ukendt tabel '%-.64s' i %s"
+	nla "Onbekende tabel '%-.64s' in %s"
+	eng "Unknown table '%-.64s' in %-.32s"
+	est "Tundmatu tabel '%-.64s' %-.32s-s"
+	fre "Table inconnue '%-.64s' dans %s"
+	ger "Unbekannte Tabelle '%-.64s' in '%-.64s'"
+	greek "Áíôðê '%-.64s' ós"
+	hun "Ismeretlen tabla: '%-.64s' %s-ban"
+	ita "Tabella '%-.64s' sconosciuta in %s"
+	jpn "Unknown table '%-.64s' in %s"
+	kor "¾Ëö´ÂÅÀºí%-.64s' (µ¥Àź£À½º %s)"
+	nor "Ukjent tabell '%-.64s' i %s"
+	norwegian-ny "Ukjend tabell '%-.64s' i %s"
+	pol "Unknown table '%-.64s' in %s"
+	por "Tabela '%-.64s' desconhecida em '%-.32s'"
+	rum "Tabla '%-.64s' invalida in %-.32s"
+	rus "îÚÅÔÁ ÔÂÉÁ'%-.64s' ×%-.32s"
+	serbian "Nepoznata tabela '%-.64s' u '%-.32s'"
+	slo "Nezná tabuµka '%-.64s' v %s"
+	spa "Tabla desconocida '%-.64s' in %s"
+	swe "Okä tabell '%-.64s' i '%-.64s'"
+	ukr "î¦ÄÍ ÔÂÉÑ'%-.64s' Õ%-.32s"
+ER_FIELD_SPECIFIED_TWICE 42000 
+	cze "Polo-B¾ka '%-.64s' je zadá dvakrá
+	dan "Feltet '%-.64s' er anvendt to gange"
+	nla "Veld '%-.64s' is dubbel gespecificeerd"
+	eng "Column '%-.64s' specified twice"
+	est "Tulp '%-.64s' on mäatletud topelt"
+	fre "Champ '%-.64s' spéfiéeux fois"
+	ger "Feld '%-.64s' wurde zweimal angegeben"
+	greek "Ô ðï%-.64s' Ýåïó ä öò	hun "A(z) '%-.64s' mezot ketszer definialta"
+	ita "Campo '%-.64s' specificato 2 volte"
+	kor "Ä·³ '%-.64s'´ÂµÎøÇÇîÖ¾´ÏÙ"
+	nor "Feltet '%-.64s' er spesifisert to ganger"
+	norwegian-ny "Feltet '%-.64s' er spesifisert to gangar"
+	pol "Field '%-.64s' specified twice"
+	por "Coluna '%-.64s' especificada duas vezes"
+	rum "Coloana '%-.64s' specificata de doua ori"
+	rus "óÂÃ'%-.64s' ÕÁÁ ÄÁÄ"
+	serbian "Kolona '%-.64s' je navedena dva puta"
+	slo "Pole '%-.64s' je zadanévakrá
+	spa "Campo '%-.64s' especificado dos veces"
+	swe "Fä '%-.64s' äredan anvä"
+	ukr "óÂà '%-.64s' ÚÚÁÅÏĦÞ"
+ER_INVALID_GROUP_FUNC_USE  
+	cze "Nespr-Báéou¾itíunkce group"
+	dan "Forkert brug af grupperings-funktion"
+	nla "Ongeldig gebruik van GROUP-functie"
+	eng "Invalid use of group function"
+	est "Vigane grupeerimisfunktsiooni kasutus"
+	fre "Utilisation invalide de la clause GROUP"
+	ger "Falsche Verwendung einer Gruppierungsfunktion"
+	greek "ÅöÝçñ ôgroup function"
+	hun "A group funkcio ervenytelen hasznalata"
+	ita "Uso non valido di una funzione di raggruppamento"
+	kor "À¸ø×ìÔö»çÇ¿´½ÀÏÙ"
+	por "Uso invádo de funç de agrupamento (GROUP)"
+	rum "Folosire incorecta a functiei group"
+	rus "îÒ×ÌÎÅÉÐÌÚ×ÎÅÇÕÐ×ÈÆÎÃÊ
+	serbian "Pogrešna upotreba 'GROUP' funkcije"
+	slo "Nespráe pou¾itie funkcie GROUP"
+	spa "Invalido uso de función grupo"
+	swe "Felaktig använing av SQL grupp function"
+	ukr "èÎ ×ËÒÓÁÎ ÆÎç ÇÕÕÁÎ"
+ER_UNSUPPORTED_EXTENSION 42000 
+	cze "Tabulka '%-.64s' pou-B¾í roz¹íníkteré té verzi MySQL není+	dan "Tabellen '%-.64s' bruger et filtypenavn som ikke findes i denne MySQL version"
+	nla "Tabel '%-.64s' gebruikt een extensie, die niet in deze MySQL-versie voorkomt."
+	eng "Table '%-.64s' uses an extension that doesn't exist in this MySQL version"
+	est "Tabel '%-.64s' kasutab laiendust, mis ei eksisteeri antud MySQL versioonis"
+	fre "Table '%-.64s' : utilise une extension invalide pour cette version de MySQL"
+	ger "Tabelle '%-.64s' verwendet eine Erweiterung, die in dieser MySQL-Version nicht verfüist"
+	greek "Ïðê'%-.64s' ÷éðßêï extension ðä õ÷ó ÝäçõôMySQL"
+	hun "A(z) '%-.64s' tabla olyan bovitest hasznal, amely nem letezik ebben a MySQL versioban."
+	ita "La tabella '%-.64s' usa un'estensione che non esiste in questa versione di MySQL"
+	kor "ÅÀºí%-.64s'´ÂÈÀ¸íÀ À¿ëÁ¸¸ ÇÀÀ MySQL ¹ö¡¼­´ÂÁÀÇÁ ¾ÊÀÏÙ"
+	nor "Table '%-.64s' uses a extension that doesn't exist in this MySQL version"
+	norwegian-ny "Table '%-.64s' uses a extension that doesn't exist in this MySQL version"
+	pol "Table '%-.64s' uses a extension that doesn't exist in this MySQL version"
+	por "Tabela '%-.64s' usa uma extensãque nãexiste nesta versãdo MySQL"
+	rum "Tabela '%-.64s' foloseste o extensire inexistenta in versiunea curenta de MySQL"
+	rus "÷ÂÉÅ'%-.64s' ÉÐÌÚÀÓ ×ÚÏÎÓÉ Î ÐÄÅÖ×ÅÙ ×ÜÏ ×ÒÉ MySQL"
+	serbian "Tabela '%-.64s' koristi ekstenziju koje ne postoji u ovoj verziji MySQL-a"
+	slo "Tabuµka '%-.64s' pou¾í roz¹ínie, ktoré tejto verzii MySQL nie je"
+	spa "Tabla '%-.64s' usa una extensióue no existe en esta MySQL versió+	swe "Tabell '%-.64s' har en extension som inte finns i denna version av MySQL"
+	ukr "ôÉÑ'%-.64s' ×ËÒÓÏÕ ÒÚÉÅÎ, Ý Î ¦ÓÕ ÕÃÊ×Ò¦§ MySQL"
+ER_TABLE_MUST_HAVE_COLUMNS 42000 
+	cze "Tabulka mus-Bííalespoòden sloupec"
+	dan "En tabel skal have mindst een kolonne"
+	nla "Een tabel moet minstens 1 kolom bevatten"
+	eng "A table must have at least 1 column"
+	jps "ƒe[ƒuƒ‹‚ÍÅá ŒÂÌcolumn ‚ª•K—v‚Å·",
+	est "Tabelis peab olema vämalt ülp"
+	fre "Une table doit comporter au moins une colonne"
+	ger "Eine Tabelle muss mindestens eine Spalte besitzen"
+	greek "Åáðê ðåíÝåôÜéïÝáåï+	hun "A tablanak legalabb egy oszlopot tartalmazni kell"
+	ita "Una tabella deve avere almeno 1 colonna"
+	jpn "¥Æ¼¥ÖëºÇã ¸ÄÎcolumn ¤¬Éͤǹ"
+	kor "dzªÀ ÅÀºí¼­´ÂÀ¾î dzªÀ Ä·³À ÁÀÇ¿©¾ßÇ´ÏÙ"
+	por "Uma tabela tem que ter pelo menos uma (1) coluna"
+	rum "O tabela trebuie sa aiba cel putin o coloana"
+	rus "÷ÂÉÅÄÌÅ ÂÔ ËËÍÎÍÍÏÉ ÓÏÂÃ
+	serbian "Tabela mora imati najmanje jednu kolonu"
+	slo "Tabuµka musía» aspoòpole"
+	spa "Una tabla debe tener al menos 1 columna"
+	swe "Tabeller måe ha minst 1 kolumn"
+	ukr "ôÉÑÐ×ÎÁÍÔ ÈÞÂÏÉ ÓÏÂÃ"
+ER_RECORD_FILE_FULL  
+	cze "Tabulka '%-.64s' je pln-Bá+	dan "Tabellen '%-.64s' er fuld"
+	nla "De tabel '%-.64s' is vol"
+	eng "The table '%-.64s' is full"
+	jps "table '%-.64s' ‚Í¢‚ÁÏ¢‚Å·",
+	est "Tabel '%-.64s' on tä"
+	fre "La table '%-.64s' est pleine"
+	ger "Tabelle '%-.64s' ist voll"
+	greek "Ïðê '%-.64s' åáãÜï
+	hun "A '%-.64s' tabla megtelt"
+	ita "La tabella '%-.64s' e` piena"
+	jpn "table '%-.64s' ¤Ï¤¤ÃѤ¤Ç¹"
+	kor "ÅÀºí%-.64s'°¡ full³µ½ÀÏÙ "
+	por "Tabela '%-.64s' estáheia"
+	rum "Tabela '%-.64s' e plina"
+	rus "ôÉÁ'%-.64s' ÐÒÐÌÅÁ
+	serbian "Tabela '%-.64s' je popunjena do kraja"
+	slo "Tabuµka '%-.64s' je plná+	spa "La tabla '%-.64s' estálena"
+	swe "Tabellen '%-.64s' äfull"
+	ukr "ôÉÑ'%-.64s' ÚÐ×ÅÁ
+ER_UNKNOWN_CHARACTER_SET 42000 
+	cze "Nezn-Bá znakováada: '%-.64s'"
+	dan "Ukendt tegnsæ '%-.64s'"
+	nla "Onbekende character set: '%-.64s'"
+	eng "Unknown character set: '%-.64s'"
+	jps "character set '%-.64s' ‚ÍTƒ|[ƒg‚µ‚Ä¢‚ܹ‚ñ+	est "Vigane kooditabel '%-.64s'"
+	fre "Jeu de caractès inconnu: '%-.64s'"
+	ger "Unbekannter Zeichensatz: '%-.64s'"
+	greek "Áíôharacter set: '%-.64s'"
+	hun "Ervenytelen karakterkeszlet: '%-.64s'"
+	ita "Set di caratteri '%-.64s' sconosciuto"
+	jpn "character set '%-.64s' ¤Ïµ¥Ý¼¥È·¤Æ¤¤Þ»¤ó	kor "¾Ëö¾ðSet: '%-.64s'"
+	por "Conjunto de caracteres '%-.64s' desconhecido"
+	rum "Set de caractere invalid: '%-.64s'"
+	rus "îÚÅÔÁ ËÄÒ×Á'%-.64s'"
+	serbian "Nepoznati karakter-set: '%-.64s'"
+	slo "Nezná znakováada: '%-.64s'"
+	spa "Juego de caracteres desconocido: '%-.64s'"
+	swe "Okä teckenuppsäning: '%-.64s'"
+	ukr "î¦ÄÍ ËÄ× ÔÂÉÑ '%-.64s'"
+ER_TOO_MANY_TABLES  
+	cze "P-Bø mnoho tabulek, MySQL jich mùív joinu jen %d"
+	dan "For mange tabeller. MySQL kan kun bruge %d tabeller i et join"
+	nla "Teveel tabellen. MySQL kan slechts %d tabellen in een join bevatten"
+	eng "Too many tables; MySQL can only use %d tables in a join"
+	jps "ƒe[ƒuƒ‹‚ª‘½‚·‚¬‚Ü·; MySQL can only use %d tables in a join",
+	est "Liiga palju tabeleid. MySQL suudab JOINiga üda kuni %d tabelit"
+	fre "Trop de tables. MySQL ne peut utiliser que %d tables dans un JOIN"
+	ger "Zu viele Tabellen. MySQL kann in einem Join maximal %d Tabellen verwenden"
+	greek "ÐëìÜïáèòíù MySQL ìñí÷éðó%d ðê óééójoin"
+	hun "Tul sok tabla. A MySQL csak %d tablat tud kezelni osszefuzeskor"
+	ita "Troppe tabelle. MySQL puo` usare solo %d tabelle in una join"
+	jpn "¥Æ¼¥Ö뤹¤®¤Þ¹; MySQL can only use %d tables in a join"
+	kor "³Ê« ¸¹À ÅÀºí JoinµÇú´Ù MySQL¿¡¼­´ÂJOIN½Ã%d°³À ÅÀºí »çÇ ¼ö½ÀÏÙ"
+	por "Tabelas demais. O MySQL pode usar somente %d tabelas em uma junç (JOIN)"
+	rum "Prea multe tabele. MySQL nu poate folosi mai mult de %d tabele intr-un join"
+	rus "óËÍÍÏÏÔÂÉ. MySQL ÍÖÔÉÐÌÚ×Ô ÔÌË %d ÔÂÉ ×ÓÅÉÅÉ"
+	serbian "Previše tabela. MySQL može upotrebiti maksimum %d tabela pri 'JOIN' operaciji"
+	slo "Prí¹ mnoho tabuliek. MySQL môpou¾i» len %d v JOIN-e"
+	spa "Muchas tablas. MySQL solamente puede usar %d tablas en un join"
+	swe "Föåa tabeller. MySQL can ha hö %d tabeller i en och samma join"
+	ukr "úÁÏÔÂÉØ MySQL ÍÖ ×ËÒÓÏÕÁÉÌÛ %d ÔÂÉØÕÏ'¤ÄÁÎ"
+ER_TOO_MANY_FIELDS  
+	cze "P-Bø mnoho polo¾ek"
+	dan "For mange felter"
+	nla "Te veel velden"
+	eng "Too many columns"
+	jps "column ‚ª‘½‚·‚¬‚Ü·",
+	est "Liiga palju tulpasid"
+	fre "Trop de champs"
+	ger "Zu viele Felder"
+	greek "ÐëìÜïáèòäí+	hun "Tul sok mezo"
+	ita "Troppi campi"
+	jpn "column ¤¬Â¤¹¤®¤Þ¹"
+	kor "Ä·³À ³Ê« ¸¹½ÀÏÙ"
+	por "Colunas demais"
+	rum "Prea multe coloane"
+	rus "óËÍÍÏÏÓÏÂÏ"
+	serbian "Previše kolona"
+	slo "Prí¹ mnoho polí+	spa "Muchos campos"
+	swe "Föåa fä"
+	ukr "úÁÏÓϦ×
+ER_TOO_BIG_ROWSIZE 42000 
+	cze "-BØdek je pø velkýimáíelikost ø, nepoèaje polo¾ky blob, je %d. Musí zmìt nìeréolo¾ky na blob"
+	dan "For store poster. Max post støse, uden BLOB's, er %d. Du måave nogle felter til BLOB's"
+	nla "Rij-grootte is groter dan toegestaan. Maximale rij grootte, blobs niet meegeteld, is %d. U dient sommige velden in blobs te veranderen."
+	eng "Row size too large. The maximum row size for the used table type, not counting BLOBs, is %ld. You have to change some columns to TEXT or BLOBs"
+	jps "row size ‚ª‘å‚·‚¬‚Ü·. BLOB ‚ðÜÈ¢ê‚Ìrow size ‚ÌÅå %d ‚Å·. ‚¢‚­‚©‚Ìfield ‚ðOB ‚ÉϦ‚Ä­‚¾‚³‚¢.",
+	est "Liiga pikk kirje. Kirje maksimumpikkus arvestamata BLOB-tüäu on %d. Muuda mõ väad BLOB-tüäadeks"
+	fre "Ligne trop grande. Le taille maximale d'une ligne, sauf les BLOBs, est %d. Changez le type de quelques colonnes en BLOB"
+	ger "Zeilenläe zu groß Die maximale Zeilenläe fü verwendeten Tabellentyp (ohne BLOB-Felder) beträ %ld. Einige Felder müin BLOB oder TEXT umgewandelt werden"
+	greek "ÐëìÜïÝåòãö Ô ìéïÝåòãö ÷ò õïæôôlobs, åá%d. ÐÝåíïó êï ðááblobs"
+	hun "Tul nagy sormeret. A maximalis sormeret (nem szamolva a blob objektumokat) %d. Nehany mezot meg kell valtoztatnia"
+	ita "Riga troppo grande. La massima grandezza di una riga, non contando i BLOB, e` %d. Devi cambiare alcuni campi in BLOB"
+	jpn "row size ¤¬Â¤­¤¹¤®¤Þ¹. BLOB ¤òÞʤ¾ì¤Îrow size ¤ÎÇç %d ¤Ç¹. ¤¤¤¯¤Ä«¤Îfield ¤òOB ¤ËѨ¤Æ¯¤Àµ¤¤."
+	kor "³Ê« Å row »çÁÀ´ÏÙ BLOB¸¦ °èÇÁ ¾ÊíÖëow »çÁ´Â%dÀ´ÏÙ ¾ó£À ǵåÀ BLOB·Î¹ÙÙÅß°Úº¿ä"
+	por "Tamanho de linha grande demais. O mámo tamanho de linha, nãcontando BLOBs, éd. Vocêem que mudar alguns campos para BLOBs"
+	rum "Marimea liniei (row) prea mare. Marimea maxima a liniei, excluzind BLOB-urile este de %d. Trebuie sa schimbati unele cimpuri in BLOB-uri"
+	rus "óËÍÂÌÛÊÒÚÅ ÚÐÓ. íÓÍÌÎÊÒÚÅ ÓÒË, ÉËÀÁ ÐÌ BLOB, - %d. ÷ÏÎ, ×ÍÓÅÕÔÉÍÎÔ ÔÐÎËÔÒÈÐÌÊÎ BLOB"
+	serbian "Prevelik slog. Maksimalna velièa sloga, ne raèajuæBLOB polja, je %d. Trebali bi da promenite tip nekih polja u BLOB"
+	slo "Riadok je prí¹ veµkýimáa veµkos» riadku, okrem 'BLOB', je %d. Musí zmeni» niektoréolo¾ky na BLOB"
+	spa "Tamañe lía muy grande. Mámo tamañe lía, no contando blob, es %d. Tu tienes que cambiar algunos campos para blob"
+	swe "Fötor total radläd. Den hö tillåa radläden, föom BLOBs, ä%d. Ädra nåa av dina fä till BLOB"
+	ukr "úÇ ÓÒË. îÂÌÛÀÄ×ÉÏ ÓÒË, Î ÒÈÀÉBLOB, ¤ %d. ÷ÐÔ¦ÂÏÐÉÅÔ ÄѦ ÓϦ Ä ÔÐ BLOB"
+ER_STACK_OVERRUN  
+	cze "P-Bøeníábní threadu: pou¾ito %ld z %ld. Pou¾ijte 'mysqld -O thread_stack=#' k zadá vìí zábní"
+	dan "Thread stack brugt:  Brugt: %ld af en %ld stak.  Brug 'mysqld -O thread_stack=#' for at allokere en støstak om nødigt"
+	nla "Thread stapel overrun:  Gebruikte: %ld van een %ld stack. Gebruik 'mysqld -O thread_stack=#' om een grotere stapel te definieren (indien noodzakelijk)."
+	eng "Thread stack overrun:  Used: %ld of a %ld stack.  Use 'mysqld -O thread_stack=#' to specify a bigger stack if needed"
+	jps "Thread stack overrun:  Used: %ld of a %ld stack.  ƒXƒ^ƒbƒN—Ì摽‚­‚Æè‚¢êA'mysqld -O thread_stack=#' ‚Æw’è‚Ä­‚¾‚³‚¢",
+	fre "Dérdement de la pile des tâes (Thread stack). Utilisé: %ld pour une pile de %ld.  Essayez 'mysqld -O thread_stack=#' pour indiquer une plus grande valeur"
+	ger "Thread-Stack-Üerlauf. Benutzt: %ld von %ld Stack. 'mysqld -O thread_stack=#' verwenden, um bei Bedarf einen gröen Stack anzulegen"
+	greek "Stack overrun óthread:  Used: %ld of a %ld stack.  Ðñë÷éðßômysqld -O thread_stack=#' ã íïó Ýáåëå stack á÷Üåé+	hun "Thread verem tullepes:  Used: %ld of a %ld stack. Hasznalja a 'mysqld -O thread_stack=#' nagyobb verem definialasahoz"
+	ita "Thread stack overrun:  Usati: %ld di uno stack di %ld.  Usa 'mysqld -O thread_stack=#' per specificare uno stack piu` grande."
+	jpn "Thread stack overrun:  Used: %ld of a %ld stack.  ¥¹¥¿¥Ã¯Î°è¤¯¤Èꤤ¾ì¡¢'mysqld -O thread_stack=#' ¤ÈØê¤Æ¯¤Àµ¤¤"
+	kor "¾²·¹µåºÅÀ ³ÑÆÀÏÙ  »ç: %ld°³ ½ºÅ: %ld°³.  ¸¸¾àÊä ´õ½ºÅÀ ¿ø¿¡´Â'mysqld -O thread_stack=#' ¸¦ ÁÀǼ¼¿ä+	por "Estouro da pilha do 'thread'. Usados %ld de uma pilha de %ld. Use 'mysqld -O thread_stack=#' para especificar uma pilha maior, se necessáo"
+	rum "Stack-ul thread-ului a fost depasit (prea mic):  Folositi: %ld intr-un stack de %ld.  Folositi 'mysqld -O thread_stack=#' ca sa specifici un stack mai mare"
+	rus "ó ÐÔË×ÐÒÐÌÅ:  ÉÐÌÚ×Î: %ld É %ld ÓÅÁ  ðÅÑÔ 'mysqld -O thread_stack=#' ÄÑÕÁÁÉ ÂÌÛÇ ÒÚÅÁÓÅÁ ÅÌ ÎÏÈÄÍ"
+	serbian "Prepisivanje thread stack-a:  Upotrebljeno: %ld od %ld stack memorije.  Upotrebite 'mysqld -O thread_stack=#' da navedete veæstack ako je potrebno"
+	slo "Preteèie zábní vláa:  pou¾ité%ld z %ld.  Pou¾ite 'mysqld -O thread_stack=#' k zadaniu väieho zábní"
+	spa "Sobrecarga de la pila de thread:  Usada: %ld de una %ld pila.  Use 'mysqld -O thread_stack=#' para especificar una mayor pila si necesario"
+	swe "Tråtacken tog slut:  Har anvä %ld av %ld bytes.  Anvä 'mysqld -O thread_stack=#' ifall du behö en stö stack"
+	ukr "ó ÇÌËÐÒÐ×ÅÏ  ÷ÒÓÁÏ %ld Ú%ld. ÷ÒÓÏÕÔ 'mysqld -O thread_stack=#' ÁÉÚÚÁÉÉÂÌÛÊÓÅ, ÑÝ ÎÏÈÄÏ
+ER_WRONG_OUTER_JOIN 42000 
+	cze "V OUTER JOIN byl nalezen k-Bøýz. Provìe ON podmíy"
+	dan "Krydsreferencer fundet i OUTER JOIN; check dine ON conditions"
+	nla "Gekruiste afhankelijkheid gevonden in OUTER JOIN. Controleer uw ON-conditions"
+	eng "Cross dependency found in OUTER JOIN; examine your ON conditions"
+	est "Ristsõvus OUTER JOIN klauslis. Kontrolli oma ON tingimusi"
+	fre "Déndance croisédans une clause OUTER JOIN. Véfiez la condition ON"
+	ger "OUTER JOIN enthä fehlerhafte Abhäigkeiten. In ON verwendete Bedingungen üü+	greek "Cross dependency âèååUTER JOIN.  ÐñëåôåéóÞåðèá óON"
+	hun "Keresztfuggoseg van az OUTER JOIN-ban. Ellenorizze az ON felteteleket"
+	ita "Trovata una dipendenza incrociata nella OUTER JOIN. Controlla le condizioni ON"
+	por "Dependêia cruzada encontrada em junç externa (OUTER JOIN); examine as condiçs utilizadas nas cláulas 'ON'"
+	rum "Dependinta incrucisata (cross dependency) gasita in OUTER JOIN.  Examinati conditiile ON"
+	rus "÷TER JOIN ÏÎÒÖÎ ÐÒËÅÔÁ Ú×ÓÍÓØ ÷ÁÅØÏÐÏÎÌÚÒÊÅÓÏ ÕÌ×ÑON"
+	serbian "Unakrsna zavisnost pronað u komandi 'OUTER JOIN'. Istražite vaše 'ON' uslove"
+	slo "V OUTER JOIN bol náenýovýz.  Skontrolujte podmienky ON"
+	spa "Dependencia cruzada encontrada en OUTER JOIN.  Examine su condicióN"
+	swe "Felaktigt referens i OUTER JOIN.  Kontrollera ON-uttrycket"
+	ukr "ðÈÅÎ ÚÌÖ¦ÓØÕOUTER JOIN. ð×ÒÅÕÏÕON"
+ER_NULL_COLUMN_IN_INDEX 42000 
+	cze "Sloupec '%-.32s' je pou-B¾it s UNIQUE nebo INDEX, ale neníefinovájako NOT NULL"
+	dan "Kolonne '%-.32s' bruges som UNIQUE eller INDEX men er ikke defineret som NOT NULL"
+	nla "Kolom '%-.64s' wordt gebruikt met UNIQUE of INDEX maar is niet gedefinieerd als NOT NULL"
+	eng "Column '%-.64s' is used with UNIQUE or INDEX but is not defined as NOT NULL"
+	jps "Column '%-.64s' ‚ª UNIQUE ‚© INDEX ‚Åg—p‚³‚ꂵ‚½. ‚±‚ÌJƒ‰ƒ€‚ÍNOT NULL ‚Æ肳‚ê‚¢‚ܹ‚ñ
+	est "Tulp '%-.64s' on kasutusel indeksina, kuid ei ole mäatletud kui NOT NULL"
+	fre "La colonne '%-.32s' fait partie d'un index UNIQUE ou INDEX mais n'est pas dénie comme NOT NULL"
+	ger "Spalte '%-.64s' wurde mit UNIQUE oder INDEX benutzt, ist aber nicht als NOT NULL definiert"
+	greek "Ô ðï%-.64s' ÷éðßáóUNIQUE ÞINDEX áÜä Ýåïó óNOT NULL"
+	hun "A(z) '%-.64s' oszlop INDEX vagy UNIQUE (egyedi), de a definicioja szerint nem NOT NULL"
+	ita "La colonna '%-.64s' e` usata con UNIQUE o INDEX ma non e` definita come NOT NULL"
+	jpn "Column '%-.64s' ¤¬ UNIQUE ¤« INDEX ¤ÇÈѵ¤ì¤·¤¿. ¤³¤Î«¥é¤ÏNOT NULL ¤Èꤵ¤ì¤¤¤Þ»¤ó+	kor "'%-.64s' Ä·³À UNIQUE³ª INDEX¸¦ »çÇ¿´Á¸¸ NOT NULLÀ ÁÀµÇö¾Òº¿ä."
+	nor "Column '%-.32s' is used with UNIQUE or INDEX but is not defined as NOT NULL"
+	norwegian-ny "Column '%-.32s' is used with UNIQUE or INDEX but is not defined as NOT NULL"
+	pol "Column '%-.32s' is used with UNIQUE or INDEX but is not defined as NOT NULL"
+	por "Coluna '%-.64s' ésada com ú (UNIQUE) ou íice (INDEX), mas nãestáefinida como nãnula (NOT NULL)"
+	rum "Coloana '%-.64s' e folosita cu UNIQUE sau INDEX dar fara sa fie definita ca NOT NULL"
+	rus "óÂÃ'%-.64s' ÉÐÌÚÅÓ ×UNIQUE ÉÉ×INDEX, Î Î ÏÒÄÌÎËËNOT NULL"
+	serbian "Kolona '%-.64s' je upotrebljena kao 'UNIQUE' ili 'INDEX' ali nije definisana kao 'NOT NULL'"
+	slo "Pole '%-.64s' je pou¾ité UNIQUE alebo INDEX, ale nie je zadefinovanéko NOT NULL"
+	spa "Columna '%-.32s' es usada con UNIQUE o INDEX pero no estáefinida como NOT NULL"
+	swe "Kolumn '%-.32s' äanvä med UNIQUE eller INDEX men äinte definerad med NOT NULL"
+	ukr "óÂà '%-.64s' ×ËÒÓÏÕÔÓ ÚUNIQUE ÁÏINDEX, ÁÅÎ ×ÚÁÅÉ Ñ NOT NULL"
+ER_CANT_FIND_UDF  
+	cze "Nemohu na-Bèt funkci '%-.64s'"
+	dan "Kan ikke læ funktionen '%-.64s'"
+	nla "Kan functie '%-.64s' niet laden"
+	eng "Can't load function '%-.64s'"
+	jps "function '%-.64s' ‚ð[ƒh‚Å«‚ܹ‚ñ+	est "Ei suuda avada funktsiooni '%-.64s'"
+	fre "Imposible de charger la fonction '%-.64s'"
+	ger "Kann Funktion '%-.64s' nicht laden"
+	greek "Äíßáäá çééóload ã ôõô '%-.64s'"
+	hun "A(z) '%-.64s' fuggveny nem toltheto be"
+	ita "Impossibile caricare la funzione '%-.64s'"
+	jpn "function '%-.64s' ¤ò¡¼¥ÉÇ­¤Þ»¤ó	kor "'%-.64s' Ǽö·ÎåÁ ¸ø´ÏÙ"
+	por "Nãpode carregar a funç '%-.64s'"
+	rum "Nu pot incarca functia '%-.64s'"
+	rus "îÏÍÖÏÚÇÕÉØÆÎÃÀ'%-.64s'"
+	serbian "Ne mogu da uèam funkciju '%-.64s'"
+	slo "Nemô naèa» funkciu '%-.64s'"
+	spa "No puedo cargar funció%-.64s'"
+	swe "Kan inte ladda funktionen '%-.64s'"
+	ukr "îÍÖ Ú×ÎÁÉÉÆÎÃÀ'%-.64s'"
+ER_CANT_INITIALIZE_UDF  
+	cze "Nemohu inicializovat funkci '%-.64s'; %-.80s"
+	dan "Kan ikke starte funktionen '%-.64s'; %-.80s"
+	nla "Kan functie '%-.64s' niet initialiseren; %-.80s"
+	eng "Can't initialize function '%-.64s'; %-.80s"
+	jps "function '%-.64s' ‚ðú‚«‚ܹ‚ñ-.80s",
+	est "Ei suuda algvätustada funktsiooni '%-.64s'; %-.80s"
+	fre "Impossible d'initialiser la fonction '%-.64s'; %-.80s"
+	ger "Kann Funktion '%-.64s' nicht initialisieren: %-.80s"
+	greek "Äíßáäá çíîôóñç'%-.64s'; %-.80s"
+	hun "A(z) '%-.64s' fuggveny nem inicializalhato; %-.80s"
+	ita "Impossibile inizializzare la funzione '%-.64s'; %-.80s"
+	jpn "function '%-.64s' ¤òü­¤Þ»¤ó-.80s"
+	kor "'%-.64s' Ǽöñâ ÇÁ ¸ø´ÏÙ; %-.80s"
+	por "Nãpode inicializar a funç '%-.64s' - '%-.80s'"
+	rum "Nu pot initializa functia '%-.64s'; %-.80s"
+	rus "îÏÍÖÏÉÉÉÌÚÒ×Ô ÆÎÃÀ'%-.64s'; %-.80s"
+	serbian "Ne mogu da inicijalizujem funkciju '%-.64s'; %-.80s"
+	slo "Nemô inicializova» funkciu '%-.64s'; %-.80s"
+	spa "No puedo inicializar funció%-.64s'; %-.80s"
+	swe "Kan inte initialisera funktionen '%-.64s'; '%-.80s'"
+	ukr "îÍÖ ¦ÎÃÁ¦Ú×Ô ÆÎÃÀ'%-.64s'; %-.80s"
+ER_UDF_NO_PATHS  
+	cze "Pro sd-Bínou knihovnu nejsou povoleny cesty"
+	dan "Angivelse af sti ikke tilladt for delt bibliotek"
+	nla "Geen pad toegestaan voor shared library"
+	eng "No paths allowed for shared library"
+	jps "shared library ‚ÖÌpƒX‚ª’ÊÁÄ¢‚ܹ‚ñ+	est "Teegi nimes ei tohi olla kataloogi"
+	fre "Chemin interdit pour les bibliothèes partagé"
+	ger "Keine Pfade gestattet füred Library"
+	greek "Äíñê paths ã ôshared library"
+	hun "Nincs ut a megosztott konyvtarakhoz (shared library)"
+	ita "Non sono ammessi path per le librerie condivisa"
+	jpn "shared library ¤ØÎѹ¤¬Ä¤ÃƤ¤Þ»¤ó	kor "°øóö®¸¦ ÀÇ Æ½º°¡ ÁÀµÇîÖö½ÀÏÙ"
+	por "Nãháaminhos (paths) permitidos para biblioteca compartilhada"
+	rum "Nici un paths nu e permis pentru o librarie shared"
+	rus "îÏÕÔÍ ÕÁÙÁØÐÔ ÄÑÄÎÍÞÓÉ ÂÂÉÔË
+	serbian "Ne postoje dozvoljene putanje do share-ovane biblioteke"
+	slo "Neprístnéiadne cesty k zdieµanej kni¾nici"
+	spa "No pasos permitidos para librarias conjugadas"
+	swe "Man fåinte ange sög föynamiska bibliotek"
+	ukr "îÄÚÏÅÏ×ËÒÓÏÕÁÉÐÔ ÄÑÒÚ¦Ì×ÎȦÏÅ"
+ER_UDF_EXISTS  
+	cze "Funkce '%-.64s' ji-B¾ existuje"
+	dan "Funktionen '%-.64s' findes allerede"
+	nla "Functie '%-.64s' bestaat reeds"
+	eng "Function '%-.64s' already exists"
+	jps "Function '%-.64s' ‚Íù‹`‚³‚ê‚¢‚Ü·",
+	est "Funktsioon '%-.64s' juba eksisteerib"
+	fre "La fonction '%-.64s' existe dé"
+	ger "Funktion '%-.64s' existiert schon"
+	greek "Çóñç%-.64s' õ÷Þç+	hun "A '%-.64s' fuggveny mar letezik"
+	ita "La funzione '%-.64s' esiste gia`"
+	jpn "Function '%-.64s' ¤ÏûµÁµ¤ì¤¤¤Þ¹"
+	kor "'%-.64s' ǼöÀ¹ÌÁÀÇ´ÏÙ"
+	por "Funç '%-.64s' jáxiste"
+	rum "Functia '%-.64s' exista deja"
+	rus "æËÉ '%-.64s' ÕÅÓÝÓ×Å"
+	serbian "Funkcija '%-.64s' veæostoji"
+	slo "Funkcia '%-.64s' u¾ existuje"
+	spa "Funció%-.64s' ya existe"
+	swe "Funktionen '%-.64s' finns redan"
+	ukr "æ˦Ñ'%-.64s' ×ŦÓÕ"
+ER_CANT_OPEN_LIBRARY  
+	cze "Nemohu otev-Bødínou knihovnu '%-.64s' (errno: %d %-.128s)"
+	dan "Kan ikke åe delt bibliotek '%-.64s' (errno: %d %-.128s)"
+	nla "Kan shared library '%-.64s' niet openen (Errcode: %d %-.128s)"
+	eng "Can't open shared library '%-.64s' (errno: %d %-.128s)"
+	jps "shared library '%-.64s' ‚ð­Ž–‚ª‚Å«‚ܹ‚ñrrno: %d %-.128s)",
+	est "Ei suuda avada jagatud teeki '%-.64s' (veakood: %d %-.128s)"
+	fre "Impossible d'ouvrir la bibliothèe partagé'%-.64s' (errno: %d %-.128s)"
+	ger "Kann Shared Library '%-.64s' nicht öen (Fehler: %d %-.128s)"
+	greek "Äíßáäá çííççshared library '%-.64s' (êéòèòd %-.128s)"
+	hun "A(z) '%-.64s' megosztott konyvtar nem hasznalhato (hibakod: %d %-.128s)"
+	ita "Impossibile aprire la libreria condivisa '%-.64s' (errno: %d %-.128s)"
+	jpn "shared library '%-.64s' ¤ò¯»öÇ­¤Þ»¤órrno: %d %-.128s)"
+	kor "'%-.64s' °øóö®¸¦ ¿­¼ö½ÀÏÙ(¿¡·¯¹ø%d %-.128s)"
+	nor "Can't open shared library '%-.64s' (errno: %d %-.128s)"
+	norwegian-ny "Can't open shared library '%-.64s' (errno: %d %-.128s)"
+	pol "Can't open shared library '%-.64s' (errno: %d %-.128s)"
+	por "Nãpode abrir biblioteca compartilhada '%-.64s' (erro no. '%d' - '%-.128s')"
+	rum "Nu pot deschide libraria shared '%-.64s' (Eroare: %d %-.64s)"
+	rus "îÏÍÖÏÏËÙØÄÎÍÞÓÕ ÂÂÉÔË '%-.64s' (ÏÉË: %d %-.64s)"
+	serbian "Ne mogu da otvorim share-ovanu biblioteku '%-.64s' (errno: %d %-.64s)"
+	slo "Nemô otvori» zdieµanú¾nicu '%-.64s' (chybový %d %s)"
+	spa "No puedo abrir libraria conjugada '%-.64s' (errno: %d %s)"
+	swe "Kan inte öa det dynamiska biblioteket '%-.64s' (Felkod: %d %s)"
+	ukr "îÍÖ ×ÄÒÔ ÒÚ¦Ì×Π¦ÏÅÕ'%-.64s' (ÐÍÌÁ %d %-.64s)"
+ER_CANT_FIND_DL_ENTRY  
+	cze "Nemohu naj-Bífunkci '%-.128s' v knihovnì+	dan "Kan ikke finde funktionen '%-.128s' i bibliotek"
+	nla "Kan functie '%-.128s' niet in library vinden"
+	eng "Can't find function '%-.128s' in library"
+	jps "function '%-.128s' ‚ðCƒuƒ‰ƒŠ[’†‚É©•t‚¯‚邪‚Å«‚ܹ‚ñ+	est "Ei leia funktsiooni '%-.128s' antud teegis"
+	fre "Impossible de trouver la fonction '%-.128s' dans la bibliothèe"
+	ger "Kann Funktion '%-.128s' in der Library nicht finden"
+	greek "Äíßáäá çíñ ôóñç'%-.128s' ó âëèç+	hun "A(z) '%-.128s' fuggveny nem talalhato a konyvtarban"
+	ita "Impossibile trovare la funzione '%-.128s' nella libreria"
+	jpn "function '%-.128s' ¤ò¤¥Ö顼ä˫ɤ±¤ë¤¬¤Ç­¤Þ»¤ó	kor "¶óö®¿¡¼­ '%-.128s' ǼöÃÀ ¼ö½ÀÏÙ"
+	por "Nãpode encontrar a funç '%-.128s' na biblioteca"
+	rum "Nu pot gasi functia '%-.128s' in libraria"
+	rus "îÏÍÖÏÏÙËÔ ÆÎÃÀ'%-.128s' ×ÂÂÉÔË"
+	serbian "Ne mogu da pronadjem funkciju '%-.128s' u biblioteci"
+	slo "Nemô ná» funkciu '%-.128s' v kni¾nici"
+	spa "No puedo encontrar funció%-.128s' en libraria"
+	swe "Hittar inte funktionen '%-.128s' in det dynamiska biblioteket"
+	ukr "îÍÖ ÚÁÔ ÆÎÃÀ'%-.128s' Õ¦ÏŦ"
+ER_FUNCTION_NOT_DEFINED  
+	cze "Funkce '%-.64s' nen-Bíefinová"
+	dan "Funktionen '%-.64s' er ikke defineret"
+	nla "Functie '%-.64s' is niet gedefinieerd"
+	eng "Function '%-.64s' is not defined"
+	jps "Function '%-.64s' ‚Í肳‚ê‚¢‚ܹ‚ñ+	est "Funktsioon '%-.64s' ei ole defineeritud"
+	fre "La fonction '%-.64s' n'est pas dénie"
+	ger "Funktion '%-.64s' ist nicht definiert"
+	greek "Çóñç%-.64s' ä Ýåïó"
+	hun "A '%-.64s' fuggveny nem definialt"
+	ita "La funzione '%-.64s' non e` definita"
+	jpn "Function '%-.64s' ¤Ïꤵ¤ì¤¤¤Þ»¤ó	kor "'%-.64s' ǼöÁÀµÇîÖö½ÀÏÙ"
+	por "Funç '%-.64s' nãestáefinida"
+	rum "Functia '%-.64s' nu e definita"
+	rus "æËÉ '%-.64s' Î ÏÒÄÌÎ"
+	serbian "Funkcija '%-.64s' nije definisana"
+	slo "Funkcia '%-.64s' nie je definovaná+	spa "Funció%-.64s' no estáefinida"
+	swe "Funktionen '%-.64s' äinte definierad"
+	ukr "æ˦À'%-.64s' Î ×ÚÁÅÏ
+ER_HOST_IS_BLOCKED  
+	cze "Stroj '%-.64s' je zablokov-Bákvùnoha chybápøipojová. Odblokujete pou¾ití'mysqladmin flush-hosts'"
+	dan "Væen er blokeret pårund af mange fejlforespøer. Låop med 'mysqladmin flush-hosts'"
+	nla "Host '%-.64s' is geblokkeeerd vanwege te veel verbindings fouten. Deblokkeer met 'mysqladmin flush-hosts'"
+	eng "Host '%-.64s' is blocked because of many connection errors; unblock with 'mysqladmin flush-hosts'"
+	jps "Host '%-.64s' ‚Ímany connection error ‚̽‚ßA‹‘”Û³‚ꂵ‚½.  'mysqladmin flush-hosts' ‚Åðµ‚Ä­‚¾‚³‚¢",
+	est "Masin '%-.64s' on blokeeritud hulgaliste üsvigade tõ. Blokeeringu saab tüda 'mysqladmin flush-hosts' käga"
+	fre "L'hô'%-.64s' est bloqué cause d'un trop grand nombre d'erreur de connection. Déoquer le par 'mysqladmin flush-hosts'"
+	ger "Host '%-.64s' blockiert wegen zu vieler Verbindungsfehler. Aufheben der Blockierung mit 'mysqladmin flush-hosts'"
+	greek "Ïõïó Ýåáêéåëùëë ëþíýòñáóíäñôåmysqladmin flush-hosts'"
+	hun "A '%-.64s' host blokkolodott, tul sok kapcsolodasi hiba miatt. Hasznalja a 'mysqladmin flush-hosts' parancsot"
+	ita "Sistema '%-.64s' bloccato a causa di troppi errori di connessione. Per sbloccarlo: 'mysqladmin flush-hosts'"
+	jpn "Host '%-.64s' ¤Ïmany connection error ¤Î¿¤áµñµ¤ì¤·¤¿.  'mysqladmin flush-hosts' ¤Çò·¤Æ¯¤Àµ¤¤"
+	kor "³Ê« ¸¹À ¿¬°á·ùÎÏ© ȽºÆ '%-.64s'´ÂºíµÇú´Ù 'mysqladmin flush-hosts'¸¦ À¿ë¿© ºíÀ ÇÁǼ¼¿ä+	por "'Host' '%-.64s' estáloqueado devido a muitos erros de conexã Desbloqueie com 'mysqladmin flush-hosts'"
+	rum "Host-ul '%-.64s' e blocat din cauza multelor erori de conectie. Poti deploca folosind 'mysqladmin flush-hosts'"
+	rus "èÔ'%-.64s' ÚÂÏÉÏÁ É-Ú ÓÉËÍÂÌÛÇ ËÌÞÓ× ÏÉÏ ÓÅÉÅÉ. òÌËÒ×Ô ÅÏÍÖÏÓÐÍÝÀ'mysqladmin flush-hosts'"
+	serbian "Host '%-.64s' je blokiran zbog previše grešaka u konekciji.  Možete ga odblokirati pomoækomande 'mysqladmin flush-hosts'"
+	spa "Servidor '%-.64s' estáloqueado por muchos errores de conexió Desbloquear con 'mysqladmin flush-hosts'"
+	swe "Denna dator, '%-.64s', äblockerad pga måa felaktig paket. Gömysqladmin flush-hosts' fött ta bort alla blockeringarna"
+	ukr "èÔ'%-.64s' ÚÂÏÏÁÏÚÐÉÉÉ×Ì˧ ËÌËÓ¦ ÐÍÌËÚ¤ÄÁÎ. ä ÒÚÌË×ÎÑ×ËÒÓÏÕÔ 'mysqladmin flush-hosts'"
+ER_HOST_NOT_PRIVILEGED  
+	cze "Stroj '%-.64s' nem-Báovoleno se k tomuto MySQL serveru pøit"
+	dan "Væen '%-.64s' kan ikke tilkoble denne MySQL-server"
+	nla "Het is host '%-.64s' is niet toegestaan verbinding te maken met deze MySQL server"
+	eng "Host '%-.64s' is not allowed to connect to this MySQL server"
+	jps "Host '%-.64s' ‚ÍMySQL server ‚ÉÚ±‚ð³‚ê‚¢‚ܹ‚ñ+	est "Masinal '%-.64s' puudub ligipä sellele MySQL serverile"
+	fre "Le hô'%-.64s' n'est pas authorisé se connecter àe serveur MySQL"
+	ger "Host '%-.64s' hat keine Berechtigung, sich mit diesem MySQL-Server zu verbinden"
+	greek "Ïõïó ä Ýåäáìóåò ôMySQL server"
+	hun "A '%-.64s' host szamara nem engedelyezett a kapcsolodas ehhez a MySQL szerverhez"
+	ita "Al sistema '%-.64s' non e` consentita la connessione a questo server MySQL"
+	jpn "Host '%-.64s' ¤ÏMySQL server ¤Ëܳ¤òĵ¤ì¤¤¤Þ»¤ó	kor "'%-.64s' ȽºÆ´ÂÀ MySQL¼­¹öÁ¼ÓÒÇ°¡¸¦ ¹ÞöǽÀÏÙ"
+	por "'Host' '%-.64s' nãtem permissãpara se conectar com este servidor MySQL"
+	rum "Host-ul '%-.64s' nu este permis a se conecta la aceste server MySQL"
+	rus "èÔ '%-.64s' Î ÒÚÅÁÔÑÐÄÌÞÔÓ ËÜÏÕÓÒÅÕMySQL"
+	serbian "Host-u '%-.64s' nije dozvoljeno da se konektuje na ovaj MySQL server"
+	spa "Servidor '%-.64s' no estáermitido para conectar con este servidor MySQL"
+	swe "Denna dator, '%-.64s', har inte privileger att anväa denna MySQL server"
+	ukr "èÔ '%-.64s' Î Ä×ÌÎ Ú'ÑÕÁÉØÚÃÍÓÒÅÏ MySQL"
+ER_PASSWORD_ANONYMOUS_USER 42000 
+	cze "Pou-B¾íte MySQL jako anonymní¾ivatel a anonymní¾ivateléemajíovoleno mìt hesla"
+	dan "Du bruger MySQL som anonym bruger. Anonyme brugere måkke ære adgangskoder"
+	nla "U gebruikt MySQL als anonieme gebruiker en deze mogen geen wachtwoorden wijzigen"
+	eng "You are using MySQL as an anonymous user and anonymous users are not allowed to change passwords"
+	jps "MySQL ‚ðonymous users ‚Åg—p‚µ‚Ä¢‚é‘ÔÅÍAƒpƒXƒ[ƒh‚ÌÏX‚ÍÅ«‚ܹ‚ñ+	est "Te kasutate MySQL-i anonükasutajana, kelledel pole parooli muutmise õst"
+	fre "Vous utilisez un utilisateur anonyme et les utilisateurs anonymes ne sont pas autoriséàhanger les mots de passe"
+	ger "Sie benutzen MySQL als anonymer Benutzer und düdaher keine Passwör äern"
+	greek "×çìïßåçMySQL óanonymous user ê Ýóåìñåáëîåáasswords Üë ÷ô
+	hun "Nevtelen (anonymous) felhasznalokent nem negedelyezett a jelszovaltoztatas"
+	ita "Impossibile cambiare la password usando MySQL come utente anonimo"
+	jpn "MySQL ¤òonymous users ¤ÇÈÑ·¤Æ¤¤ë¤ÇÏ¢¥Ñ¹¥ï¥ÉÎѹ¤ÏÇ­¤Þ»¤ó	kor "´çÀ MySQL¼­¹öÀ¸í »çÀ·ÎÁ¼Ó» ǼÌÀÏÙÀ¸í »çÀ´Â¾Ï£¸¦ º¯°æ ¼ö½ÀÏÙ"
+	por "Vocêstásando o MySQL como usuáo anôo e usuáos anôos nãtêpermissãpara mudar senhas"
+	rum "Dumneavoastra folositi MySQL ca un utilizator anonim si utilizatorii anonimi nu au voie sa schime parolele"
+	rus "÷ÓÏØÕÔ MySQL Ï ÉÅÉÁÏÉÎÇ ÐÌÚ×ÔÌ, ÁÁÏÉÎÍÐÌÚ×ÔÌÍÎ ÒÚÅÁÔÑÍÎÔ ÐÒÌ"
+	serbian "Vi koristite MySQL kao anonimni korisnik a anonimnim korisnicima nije dozvoljeno da menjaju lozinke"
+	spa "Tu estáusando MySQL como un usuario anonimo y usuarios anonimos no tienen permiso para cambiar las claves"
+	swe "Du anväer MySQL som en anonym anväare och som sån fådu inte ära ditt löord"
+	ukr "÷ÉÏÉÔ×¤Ô MySQL Ñ ÁϦÍÉ ËÒÓÕÁ, ÔÍ ×ÍÎ ÄÚÏÅÏÚ¦Î×Ô ÐÒÌ"
+ER_PASSWORD_NOT_ALLOWED 42000 
+	cze "Na zm-Bì hesel ostatnímusí míprá prové update tabulek v databá mysql"
+	dan "Du skal have tilladelse til at opdatere tabeller i MySQL databasen for at ære andres adgangskoder"
+	nla "U moet tabel update priveleges hebben in de mysql database om wachtwoorden voor anderen te mogen wijzigen"
+	eng "You must have privileges to update tables in the mysql database to be able to change passwords for others"
+	jps "‘¼‚̆[ƒU[‚ÌpƒXƒ[ƒh‚ðX‚·‚é‚ßÉÍ mysql ƒf[ƒ^ƒx[ƒX‚Éε‚Äupdate ‚Ì–‰Âª‚ȯ‚ê‚È肹‚ñ
+	est "Teiste paroolide muutmiseks on nõv tabelite muutmisõs 'mysql' andmebaasis"
+	fre "Vous devez avoir le privilè update sur les tables de la base de donnémysql pour pouvoir changer les mots de passe des autres"
+	ger "Sie benöen die Berechtigung zum Aktualisieren von Tabellen in der Datenbank 'mysql', um die Passwör anderer Benutzer äern zu kön"
+	greek "ÐÝåíÝå äáìäñçðê (update) óâçåìùsql ã íìñåáëîåáasswords Üë ÷ô
+	hun "Onnek tabla-update joggal kell rendelkeznie a mysql adatbazisban masok jelszavanak megvaltoztatasahoz"
+	ita "E` necessario il privilegio di update sulle tabelle del database mysql per cambiare le password per gli altri utenti"
+	jpn "¤Î楶¡¼¤Îѹ¥ï¥Éò¹¤¹¤ë¤á¤Ï mysql ¥Ç¼¥¿¥Ù¼¥¹¤Ëз¤Æupdate ¤Îö¬¤Ê±¤ì¤Êꤻ¤ó+	kor "´çÀ ´Ù¥»çÀµé ¾Ï£¸¦ º¯°æ ¼öµµ·Ïµ¥Àź£À½º º¯°æÇÀ °¡Á¾ßÇ´ÏÙ"
+	por "Vocêeve ter priviléos para atualizar tabelas no banco de dados mysql para ser capaz de mudar a senha de outros"
+	rum "Trebuie sa aveti privilegii sa actualizati tabelele in bazele de date mysql ca sa puteti sa schimati parolele altora"
+	rus "ä ÔÇ ÞÏÙÉÍÎÔ ÐÒÌ ÄÕÉ ÐÌÚ×ÔÌÊ Õ×ÓÄÌÎ ÂÔ ÐÉÉÅÉ Î ÉÍÎÎÅÔÂÉ ×ÂÚ ÄÎÙ mysql"
+	serbian "Morate imati privilegije da možete da update-ujete odreð tabele ako želite da menjate lozinke za druge korisnike"
+	spa "Tu debes de tener permiso para actualizar tablas en la base de datos mysql para cambiar las claves para otros"
+	swe "Fött ära löord föndra måe du ha räigheter att uppdatera mysql-databasen"
+	ukr "÷Ïɦ ÍÔ ÐÁÏÎ ÏÏÌÎÑÔÂÉØÕÂÚ ÄÎÉ mysql, ÁÉÍÔ ÍÖɦÓØÚ¦Î×Ô ÐÒÌ ¦ÎÉ"
+ER_PASSWORD_NO_MATCH 42000 
+	cze "V tabulce user nen-Bíáývíjí ø"
+	dan "Kan ikke finde nogen tilsvarende poster i bruger tabellen"
+	nla "Kan geen enkele passende rij vinden in de gebruikers tabel"
+	eng "Can't find any matching row in the user table"
+	est "Ei leia vastavat kirjet kasutajate tabelis"
+	fre "Impossible de trouver un enregistrement correspondant dans la table user"
+	ger "Kann keinen passenden Datensatz in Tabelle 'user' finden"
+	greek "Äíßáäá çíñ ôáßôçåñòïðêô÷ô
+	hun "Nincs megegyezo sor a user tablaban"
+	ita "Impossibile trovare la riga corrispondente nella tabella user"
+	kor "»çÀ ÅÀºí¼­ ÀÄǴ°ͻ ÃÀ ¼öÀ´ÏÙ"
+	por "Nãpode encontrar nenhuma linha que combine na tabela usuáo (user table)"
+	rum "Nu pot gasi nici o linie corespunzatoare in tabela utilizatorului"
+	rus "îÏÍÖÏÏÙËÔ ÐÄÏÑÕ ÚÐÓ ×ÔÂÉÅÐÌÚ×ÔÌÊ
+	serbian "Ne mogu da pronaðodgovarajuæslog u 'user' tabeli"
+	spa "No puedo encontrar una lía correponsdiente en la tabla user"
+	swe "Hittade inte anväaren i 'user'-tabellen"
+	ukr "îÍÖ ÚÁÔ ×ÄϦÄÉ ÚÐÓ×ÕÔÂɦ ËÒÓÕÁÁ
+ER_UPDATE_INFO  
+	cze "Nalezen-Býdkùd  Zmìno: %ld  Varová: %ld"
+	dan "Poster fundet: %ld  Ædret: %ld  Advarsler: %ld"
+	nla "Passende rijen: %ld  Gewijzigd: %ld  Waarschuwingen: %ld"
+	eng "Rows matched: %ld  Changed: %ld  Warnings: %ld"
+	jps "ˆê”(Rows matched): %ld  •ÏX: %ld  Warnings: %ld",
+	est "Sobinud kirjeid: %ld  Muudetud: %ld  Hoiatusi: %ld"
+	fre "Enregistrements correspondants: %ld  Modifié %ld  Warnings: %ld"
+	ger "Datensäe gefunden: %ld  Geäert: %ld  Warnungen: %ld"
+	hun "Megegyezo sorok szama: %ld  Valtozott: %ld  Warnings: %ld"
+	ita "Rows riconosciute: %ld  Cambiate: %ld  Warnings: %ld"
+	jpn "°ì¿ôws matched): %ld  ʹ¹: %ld  Warnings: %ld"
+	kor "ÀÄÇ´ÂRows : %ld°³ º¯°æ: %ld°³  °æ: %ld°³"
+	por "Linhas que combinaram: %ld - Alteradas: %ld - Avisos: %ld"
+	rum "Linii identificate (matched): %ld  Schimbate: %ld  Atentionari (warnings): %ld"
+	rus "óÁÏÚÐÓÊ %ld  éÅÅÏ %ld  ðÕÒÖÅÉ: %ld"
+	serbian "Odgovarajuæ slogova: %ld  Promenjeno: %ld  Upozorenja: %ld"
+	spa "Lías correspondientes: %ld  Cambiadas: %ld  Avisos: %ld"
+	swe "Rader: %ld  Uppdaterade: %ld  Varningar: %ld"
+	ukr "ú¦××ÄϦĤ: %ld  úÎ: %ld  úÒÖÎ: %ld"
+ER_CANT_CREATE_THREAD  
+	cze "Nemohu vytvo-Bøovýad (errno %d). Pokud je je¹tììkáolnáamì podíjte se do manuá na èt o chybá specifickýo jednotlivéperaè systé"
+	dan "Kan ikke danne en ny trå(fejl nr. %d). Hvis computeren ikke er løtør hukommelse, kan du se i brugervejledningen for en mulig operativ-system - afhæig fejl"
+	nla "Kan geen nieuwe thread aanmaken (Errcode: %d). Indien er geen tekort aan geheugen is kunt u de handleiding consulteren over een mogelijke OS afhankelijke fout"
+	eng "Can't create a new thread (errno %d); if you are not out of available memory, you can consult the manual for a possible OS-dependent bug"
+	jps "V‹K‚ÉXƒŒƒbƒh‚ªì‚ܹ‚ñµ‚½ (errno %d). ‚àÅå—p‹–‰Âƒ‚ƒŠ[”‚ð¦‚Ä¢‚È¢‚ÌÉGƒ‰[‚ª”­¶‚µ‚Ä¢‚é‚çƒ}ƒjƒ…ƒAƒ‹‚̆‚©‚çpossible OS-dependent bug' ‚Æ¢‚¤•¶Žš‚ðµ‚Ä­‚Ýľ‚³‚¢.",
+	est "Ei suuda luua uut lõ (veakood %d). Kui mä ei ole otsas, on tõoliselt tegemist operatsioonisüispetsiifilise veaga"
+	fre "Impossible de cré une nouvelle tâe (errno %d). S'il reste de la méire libre, consultez le manual pour trouver un éntuel bug déndant de l'OS"
+	ger "Kann keinen neuen Thread erzeugen (Fehler: %d). Sollte noch Speicher verfüsein, bitte im Handbuch wegen möcher Fehler im Betriebssystem nachschlagen"
+	hun "Uj thread letrehozasa nem lehetseges (Hibakod: %d). Amenyiben van meg szabad memoria, olvassa el a kezikonyv operacios rendszerfuggo hibalehetosegekrol szolo reszet"
+	ita "Impossibile creare un nuovo thread (errno %d). Se non ci sono problemi di memoria disponibile puoi consultare il manuale per controllare possibili problemi dipendenti dal SO"
+	jpn "¿·µ¬¤Ë¹¥ì¥É¬ºî¤Þ»¤ó·¤¿ (errno %d). ¤âºÇç͵öá¥ê¿ôÛ¨¤Æ¤¤Ê¤¤Î˨¥é¤¬ÈÀ¤·¤Æ¤¤ë¤é¥ÞËå¥ë䫤épossible OS-dependent bug' ¤È¤¤¦Ê»ú¤·¤Æ¯¤ßÆÀµ¤¤."
+	kor "»õî²·¹µå ¸¸µéö½ÀÏÙ(¿¡·¯¹ød). ¸¸¾à©À¸Þð¡ À´ÙéS-dependent¹öÀ ¸Þº¾óºÐ» þƸ½ÃÀ"
+	nor "Can't create a new thread (errno %d); if you are not out of available memory you can consult the manual for any possible OS dependent bug"
+	norwegian-ny "Can't create a new thread (errno %d); if you are not out of available memory you can consult the manual for any possible OS dependent bug"
+	pol "Can't create a new thread (errno %d); if you are not out of available memory you can consult the manual for any possible OS dependent bug"
+	por "Nãpode criar uma nova 'thread' (erro no. %d). Se vocêãestiver sem memó disponíl, vocêode consultar o manual sobre um possíl 'bug' dependente do sistema operacional"
+	rum "Nu pot crea un thread nou (Eroare %d). Daca mai aveti memorie disponibila in sistem, puteti consulta manualul - ar putea exista un potential bug in legatura cu sistemul de operare"
+	rus "îÏÍÖÏÓÚÁØÎ×ÊÐÔË(ÏÉË %d). åÉÜÏÎ ÓÔÁÉ, ÓÑÁÎÑÓÎÈÁËÊÐÍÔ, Ô ×ÍÓÅÕÔÉÕÉØÄËÍÎÁÉ Î ÐÅÍÔÏÉÁÉ ×ÚÏÎÊÏÉË ÒÂÔ ×ËÎÒÔÏ ï
+	serbian "Ne mogu da kreiram novi thread (errno %d). Ako imate još slobodne memorije, trebali biste da pogledate u priruèku da li je ovo specifiè greška vašeg operativnog sistema"
+	spa "No puedo crear un nuevo thread (errno %d). Si tu estáon falta de memoria disponible, tu puedes consultar el Manual para posibles problemas con SO"
+	swe "Kan inte skapa en ny trå(errno %d)"
+	ukr "îÍÖ Ó×ÒÔ Î× ÇÌÕ(ÐÍÌÁ%d). ñ × Î ×ËÒÓÁÉÕÀÐÍÑØ Ô ÐÏÉÁÔ ÄËÍÎÁ¦ÀÄ ×Û§ ï- ÍÖÉÏà ÐÍÌÁï
+ER_WRONG_VALUE_COUNT_ON_ROW 21S01 
+	cze "Po-Bè sloupcùdpoví poè hodnot na ø %ld"
+	dan "Kolonne antallet stemmer ikke overens med antallet af væier i post %ld"
+	nla "Kolom aantal komt niet overeen met waarde aantal in rij %ld"
+	eng "Column count doesn't match value count at row %ld"
+	est "Tulpade hulk erineb vätuste hulgast real %ld"
+	ger "Anzahl der Felder stimmt nicht mit der Anzahl der Werte in Zeile %ld ün"
+	hun "Az oszlopban talalhato ertek nem egyezik meg a %ld sorban szamitott ertekkel"
+	ita "Il numero delle colonne non corrisponde al conteggio alla riga %ld"
+	kor "Row %ld¿¡¼­ Ä·³ Ä¿î¿Ívalue Ä¿î¿ÍÀÄÇÁ ¾ÊÀÏÙ"
+	por "Contagem de colunas nãconfere com a contagem de valores na linha %ld"
+	rum "Numarul de coloane nu corespunde cu numarul de valori la linia %ld"
+	rus "ëÉÅÔÏÓÏÂÏ Î Ó×ÁÁÔÓËÌÞÓ×ÍÚÁÅÉ ×ÚÐÓ %ld"
+	serbian "Broj kolona ne odgovara broju vrednosti u slogu %ld"
+	spa "El nú de columnas no corresponde al nú en la lía %ld"
+	swe "Antalet kolumner motsvarar inte antalet väen påad: %ld"
+	ukr "ëئÓØÓϦ×Î Ó¦×ÁÁ ÚËÌËÓÀÚÁÅØÕÓÒà %ld"
+ER_CANT_REOPEN_TABLE  
+	cze "Nemohu znovuotev-Bøabulku: '%-.64s"
+	dan "Kan ikke genåe tabel '%-.64s"
+	nla "Kan tabel niet opnieuw openen: '%-.64s"
+	eng "Can't reopen table: '%-.64s'"
+	est "Ei suuda taasavada tabelit '%-.64s'"
+	fre "Impossible de révrir la table: '%-.64s"
+	ger "Kann Tabelle'%-.64s' nicht erneut öen"
+	hun "Nem lehet ujra-megnyitni a tablat: '%-.64s"
+	ita "Impossibile riaprire la tabella: '%-.64s'"
+	kor "ÅÀºí ´Ùÿ­¼ö±º¿ä'%-.64s"
+	nor "Can't reopen table: '%-.64s"
+	norwegian-ny "Can't reopen table: '%-.64s"
+	pol "Can't reopen table: '%-.64s"
+	por "Nãpode reabrir a tabela '%-.64s"
+	rum "Nu pot redeschide tabela: '%-.64s'"
+	rus "îÏÍÖÏÚÎ× ÏËÙØÔÂÉÕ'%-.64s'"
+	serbian "Ne mogu da ponovo otvorim tabelu '%-.64s'"
+	slo "Can't reopen table: '%-.64s"
+	spa "No puedo reabrir tabla: '%-.64s"
+	swe "Kunde inte stäa och öa tabell '%-.64s"
+	ukr "îÍÖ ÐÒ×ÄÒÔ ÔÂÉÀ '%-.64s'"
+ER_INVALID_USE_OF_NULL 22004 
+	cze "Neplatn-Bé¾itíodnoty NULL"
+	dan "Forkert brug af nulvæi (NULL)"
+	nla "Foutief gebruik van de NULL waarde"
+	eng "Invalid use of NULL value"
+	jps "NULL ’l‚Ìg—p•û•s“KØÅ·",
+	est "NULL vätuse väkasutus"
+	fre "Utilisation incorrecte de la valeur NULL"
+	ger "Unerlaubte Verwendung eines NULL-Werts"
+	hun "A NULL ervenytelen hasznalata"
+	ita "Uso scorretto del valore NULL"
+	jpn "NULL äÎÈÑýÔ¬À¤Ç¹"
+	kor "NULL °ªÀ À¸øë¼Ìº¿ä."
+	por "Uso invádo do valor NULL"
+	rum "Folosirea unei value NULL e invalida"
+	rus "îÒ×ÌÎÅÉÐÌÚ×ÎÅ×ÌÞÎ NULL"
+	serbian "Pogrešna upotreba vrednosti NULL"
+	spa "Invalido uso de valor NULL"
+	swe "Felaktig anväing av NULL"
+	ukr "èÎ ×ËÒÓÁÎ ÚÁÅÎ NULL"
+ER_REGEXP_ERROR 42000 
+	cze "Regul-Báíýrál chybu '%-.64s'"
+	dan "Fik fejl '%-.64s' fra regexp"
+	nla "Fout '%-.64s' ontvangen van regexp"
+	eng "Got error '%-.64s' from regexp"
+	est "regexp tagastas vea '%-.64s'"
+	fre "Erreur '%-.64s' provenant de regexp"
+	ger "regexp lieferte Fehler '%-.64s'"
+	hun "'%-.64s' hiba a regularis kifejezes hasznalata soran (regexp)"
+	ita "Errore '%-.64s' da regexp"
+	kor "regexp¿¡¼­ '%-.64s'°¡ ³µ½ÀÏÙ"
+	por "Obteve erro '%-.64s' em regexp"
+	rum "Eroarea '%-.64s' obtinuta din expresia regulara (regexp)"
+	rus "ðÞÎ ÏÉË '%-.64s' Ï ÒÇÌÒÏÏ×ÒÖÎÑ
+	serbian "Funkcija regexp je vratila grešku '%-.64s'"
+	spa "Obtenido error '%-.64s' de regexp"
+	swe "Fick fel '%-.64s' fråREGEXP"
+	ukr "ïÉÁÏÐÍÌÕ'%-.64s' ×ÄÒÇÌÒÏÏ×ÒÚ"
+ER_MIX_OF_GROUP_FUNC_AND_FIELDS 42000 
+	cze "Pokud nen-BíááROUP BY klauzule, neníovoleno souènéou¾itíROUP polo¾ek (MIN(),MAX(),COUNT()...) s ne GROUP polo¾kami"
+	dan "Sammenblanding af GROUP kolonner (MIN(),MAX(),COUNT()...) uden GROUP kolonner er ikke tilladt, hvis der ikke er noget GROUP BY prækat"
+	nla "Het mixen van GROUP kolommen (MIN(),MAX(),COUNT()...) met no-GROUP kolommen is foutief indien er geen GROUP BY clausule is"
+	eng "Mixing of GROUP columns (MIN(),MAX(),COUNT(),...) with no GROUP columns is illegal if there is no GROUP BY clause"
+	est "GROUP tulpade (MIN(),MAX(),COUNT()...) kooskasutamine tavaliste tulpadega ilma GROUP BY klauslita ei ole lubatud"
+	fre "Ménger les colonnes GROUP (MIN(),MAX(),COUNT()...) avec des colonnes normales est interdit s'il n'y a pas de clause GROUP BY"
+	ger "Das Vermischen von GROUP-Feldern (MIN(),MAX(),COUNT()...) mit Nicht-GROUP-Feldern ist nicht zuläig, wenn keine GROUP-BY-Klausel vorhanden ist"
+	hun "A GROUP mezok (MIN(),MAX(),COUNT()...) kevert hasznalata nem lehetseges GROUP BY hivatkozas nelkul"
+	ita "Il mescolare funzioni di aggregazione (MIN(),MAX(),COUNT()...) e non e` illegale se non c'e` una clausula GROUP BY"
+	kor "Mixing of GROUP Ä·³s (MIN(),MAX(),COUNT(),...) with no GROUP Ä·³s is illegal if there is no GROUP BY clause"
+	por "Mistura de colunas agrupadas (com MIN(), MAX(), COUNT(), ...) com colunas nãagrupadas élegal, se nãexistir uma cláula de agrupamento (cláula GROUP BY)"
+	rum "Amestecarea de coloane GROUP (MIN(),MAX(),COUNT()...) fara coloane GROUP este ilegala daca nu exista o clauza GROUP BY"
+	rus "ïÏÒÍÎÏ ÉÐÌÚ×ÎÅÓÒÐÉÏÁÎÈ(GROUP) ÓÏÂÏ (MIN(),MAX(),COUNT(),...) ÓÎÓÒÐÉÏÁÎÍ ÓÏÂÁÉÑÌÅÓ ÎËÒÅÔÙ, ÅÌ ××ÒÖÎÉÅÔ GROUP BY"
+	serbian "Upotreba agregatnih funkcija (MIN(),MAX(),COUNT()...) bez 'GROUP' kolona je pogrešna ako ne postoji 'GROUP BY' iskaz"
+	spa "Mezcla de columnas GROUP (MIN(),MAX(),COUNT()...) con no GROUP columnas es ilegal si no hat la clausula GROUP BY"
+	swe "Man fåha bå GROUP-kolumner (MIN(),MAX(),COUNT()...) och fä i en frå om man inte har en GROUP BY-del"
+	ukr "ú×ÎÑGROUP ÓϦ×(MIN(),MAX(),COUNT()...) ÚÎ GROUP ÓÏÂÑɤ ÚÂÒÎÎÍ ÑÝ Î Í¤ GROUP BY"
+ER_NONEXISTING_GRANT 42000 
+	cze "Neexistuje odpov-Bíjí grant pro u¾ivatele '%-.32s' na stroji '%-.64s'"
+	dan "Denne tilladelse findes ikke for brugeren '%-.32s' påæ '%-.64s'"
+	nla "Deze toegang (GRANT) is niet toegekend voor gebruiker '%-.32s' op host '%-.64s'"
+	eng "There is no such grant defined for user '%-.32s' on host '%-.64s'"
+	jps "ƒ†[ƒU[ '%-.32s' (ƒzƒXƒg '%-.64s' ‚̆[ƒU[) ‚Í–‰Â³‚ê‚¢‚ܹ‚ñ+	est "Sellist õst ei ole defineeritud kasutajale '%-.32s' masinast '%-.64s'"
+	fre "Un tel droit n'est pas déni pour l'utilisateur '%-.32s' sur l'hô'%-.64s'"
+	ger "Füutzer '%-.32s' auf Host '%-.64s' gibt es keine solche Berechtigung"
+	hun "A '%-.32s' felhasznalonak nincs ilyen joga a '%-.64s' host-on"
+	ita "GRANT non definita per l'utente '%-.32s' dalla macchina '%-.64s'"
+	jpn "¥æ¥¶¡¼ '%-.32s' (¥Û¹¥È'%-.64s' ¤Î楶¡¼) ¤Ïöµ¤ì¤¤¤Þ»¤ó	kor "»çÀ '%-.32s' (ȽºÆ '%-.64s')¸¦ ÀÇ¿© ÁÀµÈ±×± ½Âκ ¾ø´Ù"
+	por "Nãexiste tal permissã(grant) definida para o usuáo '%-.32s' no 'host' '%-.64s'"
+	rum "Nu exista un astfel de grant definit pentru utilzatorul '%-.32s' de pe host-ul '%-.64s'"
+	rus "ôÅÐÁÁÎ ÏÒÄÌÎ ÄÑÐÌÚ×ÔÌ '%-.32s' Î ÈÓÅ'%-.64s'"
+	serbian "Ne postoji odobrenje za pristup korisniku '%-.32s' na host-u '%-.64s'"
+	spa "No existe permiso definido para usuario '%-.32s' en el servidor '%-.64s'"
+	swe "Det finns inget privilegium definierat fönväare '%-.32s' på%-.64s'"
+	ukr "ðÏÁÅØÎ ×ÚÁÅÏÄÑËÒÓÕÁÁ'%-.32s' ÚÈÓÕ'%-.64s'"
+ER_TABLEACCESS_DENIED_ERROR 42000 
+	cze "%-.16s p-Bø nepøpnýu¾ivatele: '%-.32s'@'%-.64s' pro tabulku '%-.64s'"
+	dan "%-.16s-kommandoen er ikke tilladt for brugeren '%-.32s'@'%-.64s' for tabellen '%-.64s'"
+	nla "%-.16s commando geweigerd voor gebruiker: '%-.32s'@'%-.64s' voor tabel '%-.64s'"
+	eng "%-.16s command denied to user '%-.32s'@'%-.64s' for table '%-.64s'"
+	jps "ƒRƒ}ƒ“ƒh %-.16s ‚̓†[ƒU[ '%-.32s'@'%-.64s' ,ƒe[ƒuƒ‹ '%-.64s' ‚Éε‚Ä–‰Â³‚ê‚¢‚ܹ‚ñ+	est "%-.16s kä ei ole lubatud kasutajale '%-.32s'@'%-.64s' tabelis '%-.64s'"
+	fre "La commande '%-.16s' est interdite à'utilisateur: '%-.32s'@'@%-.64s' sur la table '%-.64s'"
+	ger "%-.16s Befehl nicht erlaubt füutzer '%-.32s'@'%-.64s' auf Tabelle '%-.64s'"
+	hun "%-.16s parancs a '%-.32s'@'%-.64s' felhasznalo szamara nem engedelyezett a '%-.64s' tablaban"
+	ita "Comando %-.16s negato per l'utente: '%-.32s'@'%-.64s' sulla tabella '%-.64s'"
+	jpn "¥³¥Þó%-.16s ¤Ï¥æ¥¶¡¼ '%-.32s'@'%-.64s' ,¥Æ¼¥Öë%-.64s' ¤Ëз¤Æöµ¤ì¤¤¤Þ»¤ó	kor "'%-.16s' ¸íÀ ´Ù½ »çÀ¿¡°Ô°ÅÎÇú´Ù : '%-.32s'@'%-.64s' for ÅÀºí%-.64s'"
+	por "Comando '%-.16s' negado para o usuáo '%-.32s'@'%-.64s' na tabela '%-.64s'"
+	rum "Comanda %-.16s interzisa utilizatorului: '%-.32s'@'%-.64s' pentru tabela '%-.64s'"
+	rus "ëÁÄ %-.16s ÚÐÅÅÁÐÌÚ×ÔÌ '%-.32s'@'%-.64s' ÄÑÔÂÉÙ'%-.64s'"
+	serbian "%-.16s komanda zabranjena za korisnika '%-.32s'@'%-.64s' za tabelu '%-.64s'"
+	spa "%-.16s comando negado para usuario: '%-.32s'@'%-.64s' para tabla '%-.64s'"
+	swe "%-.16s ej tillåt fö%-.32s'@'%-.64s' föabell '%-.64s'"
+	ukr "%-.16s ËÍÎÁÚÂÒÎÎ ËÒÓÕÁÕ '%-.32s'@'%-.64s' ÕÔÂɦ '%-.64s'"
+ER_COLUMNACCESS_DENIED_ERROR 42000 
+	cze "%-.16s p-Bø nepøpnýu¾ivatele: '%-.32s'@'%-.64s' pro sloupec '%-.64s' v tabulce '%-.64s'"
+	dan "%-.16s-kommandoen er ikke tilladt for brugeren '%-.32s'@'%-.64s' for kolonne '%-.64s' in tabellen '%-.64s'"
+	nla "%-.16s commando geweigerd voor gebruiker: '%-.32s'@'%-.64s' voor kolom '%-.64s' in tabel '%-.64s'"
+	eng "%-.16s command denied to user '%-.32s'@'%-.64s' for column '%-.64s' in table '%-.64s'"
+	jps "ƒRƒ}ƒ“ƒh %-.16s ‚̓†[ƒU[ '%-.32s'@'%-.64s'\n ƒJƒ‰ƒ€ '%-.64s' ƒe[ƒuƒ‹ '%-.64s' ‚Éε‚Ä–‰Â³‚ê‚¢‚ܹ‚ñ+	est "%-.16s kä ei ole lubatud kasutajale '%-.32s'@'%-.64s' tulbale '%-.64s' tabelis '%-.64s'"
+	fre "La commande '%-.16s' est interdite à'utilisateur: '%-.32s'@'@%-.64s' sur la colonne '%-.64s' de la table '%-.64s'"
+	ger "%-.16s Befehl nicht erlaubt füutzer '%-.32s'@'%-.64s' und Feld '%-.64s' in Tabelle '%-.64s'"
+	hun "%-.16s parancs a '%-.32s'@'%-.64s' felhasznalo szamara nem engedelyezett a '%-.64s' mezo eseten a '%-.64s' tablaban"
+	ita "Comando %-.16s negato per l'utente: '%-.32s'@'%-.64s' sulla colonna '%-.64s' della tabella '%-.64s'"
+	jpn "¥³¥Þó%-.16s ¤Ï¥æ¥¶¡¼ '%-.32s'@'%-.64s'\n ¥«¥é '%-.64s' ¥Æ¼¥Öë%-.64s' ¤Ëз¤Æöµ¤ì¤¤¤Þ»¤ó	kor "'%-.16s' ¸íÀ ´Ù½ »çÀ¿¡°Ô°ÅÎÇú´Ù : '%-.32s'@'%-.64s' for Ä·³ '%-.64s' in ÅÀºí%-.64s'"
+	por "Comando '%-.16s' negado para o usuáo '%-.32s'@'%-.64s' na coluna '%-.64s', na tabela '%-.64s'"
+	rum "Comanda %-.16s interzisa utilizatorului: '%-.32s'@'%-.64s' pentru coloana '%-.64s' in tabela '%-.64s'"
+	rus "ëÁÄ %-.16s ÚÐÅÅÁÐÌÚ×ÔÌ '%-.32s'@'%-.64s' ÄÑÓÏÂÁ'%-.64s' ×ÔÂÉÅ'%-.64s'"
+	serbian "%-.16s komanda zabranjena za korisnika '%-.32s'@'%-.64s' za kolonu '%-.64s' iz tabele '%-.64s'"
+	spa "%-.16s comando negado para usuario: '%-.32s'@'%-.64s' para columna '%-.64s' en la tabla '%-.64s'"
+	swe "%-.16s ej tillåt fö%-.32s'@'%-.64s' föolumn '%-.64s' i tabell '%-.64s'"
+	ukr "%-.16s ËÍÎÁÚÂÒÎÎ ËÒÓÕÁÕ '%-.32s'@'%-.64s' ÄÑÓÏÂÑ'%-.64s' ÕÔÂɦ '%-.64s'"
+ER_ILLEGAL_GRANT_FOR_TABLE 42000 
+	cze "Neplatn-Býaz GRANT/REVOKE. Prosí pøte si v manuá, jakárivilegia je mo¾néou¾í"
+	dan "Forkert GRANT/REVOKE kommando. Se i brugervejledningen hvilke privilegier der kan specificeres."
+	nla "Foutief GRANT/REVOKE commando. Raadpleeg de handleiding welke priveleges gebruikt kunnen worden."
+	eng "Illegal GRANT/REVOKE command; please consult the manual to see which privileges can be used"
+	est "Vigane GRANT/REVOKE kä. Tutvu kasutajajuhendiga"
+	fre "Commande GRANT/REVOKE incorrecte. Consultez le manuel."
+	ger "Unzuläiger GRANT- oder REVOKE-Befehl. Verfü Berechtigungen sind im Handbuch aufgefü+	greek "Illegal GRANT/REVOKE command; please consult the manual to see which privileges can be used."
+	hun "Ervenytelen GRANT/REVOKE parancs. Kerem, nezze meg a kezikonyvben, milyen jogok lehetsegesek"
+	ita "Comando GRANT/REVOKE illegale. Prego consultare il manuale per sapere quali privilegi possono essere usati."
+	jpn "Illegal GRANT/REVOKE command; please consult the manual to see which privleges can be used."
+	kor "À¸øRANT/REVOKE ¸í. ¾î ±Ç®¿Í½ÂÎÌ»çµÇîúÀ´Âö´º¾óº¸½ÃÀ"
+	nor "Illegal GRANT/REVOKE command; please consult the manual to see which privleges can be used."
+	norwegian-ny "Illegal GRANT/REVOKE command; please consult the manual to see which privleges can be used."
+	pol "Illegal GRANT/REVOKE command; please consult the manual to see which privleges can be used."
+	por "Comando GRANT/REVOKE ilegal. Por favor consulte no manual quais priviléos podem ser usados."
+	rum "Comanda GRANT/REVOKE ilegala. Consultati manualul in privinta privilegiilor ce pot fi folosite."
+	rus "îÅÎÑËÍÎÁGRANT ÉÉREVOKE. ïÁÉÅØËÄËÍÎÁÉ, ÞÏÙ×ÑÎÔ, ËËÅÐÉÉÅÉ ÍÖÏÉÐÌÚ×Ô"
+	serbian "Pogrešna 'GRANT' odnosno 'REVOKE' komanda. Molim Vas pogledajte u priruèku koje vrednosti mogu biti upotrebljene."
+	slo "Illegal GRANT/REVOKE command; please consult the manual to see which privleges can be used."
+	spa "Ilegal comando GRANT/REVOKE. Por favor consulte el manual para cuales permisos pueden ser usados."
+	swe "Felaktigt GRANT-privilegium anvä"
+	ukr "èÎ GRANT/REVOKE ËÍÎÁ ÐÏÉÁÔ ÄËÍÎÁ¦ÀÓÏÏÎ ÔÇ, Ѧ ÐÁÁÍÖÁ×ËÒÓÏÕÁÉ
+ER_GRANT_WRONG_HOST_OR_USER 42000 
+	cze "Argument p-Bøu GRANT u¾ivatel nebo stroj je pø dlouhýan "Væs- eller brugernavn for langt til GRANT"
+	nla "De host of gebruiker parameter voor GRANT is te lang"
+	eng "The host or user argument to GRANT is too long"
+	est "Masina võasutaja nimi GRANT lauses on liiga pikk"
+	fre "L'hôou l'utilisateur donnén argument àRANT est trop long"
+	ger "Das Host- oder User-Argument füNT ist zu lang"
+	hun "A host vagy felhasznalo argumentuma tul hosszu a GRANT parancsban"
+	ita "L'argomento host o utente per la GRANT e` troppo lungo"
+	kor "½ÂÎGRANT)À ÀÇ¿© »çÇ »çÀ³ª ȽºÆÀ °ªµé ³Ê« ±é´Ù"
+	por "Argumento de 'host' ou de usuáo para o GRANT éongo demais"
+	rum "Argumentul host-ului sau utilizatorului pentru GRANT e prea lung"
+	rus "óËÍÄÉÎÅÉÑÐÌÚ×ÔÌ/ÈÓÁÄÑGRANT"
+	serbian "Argument 'host' ili 'korisnik' prosleðkomandi 'GRANT' je predugaè"
+	spa "El argumento para servidor o usuario para GRANT es demasiado grande"
+	swe "Felaktigt maskinnamn eller anväarnamn anvä med GRANT"
+	ukr "áÕÅÔhost ÁÏuser ÄÑGRANT ÚÄ×É"
+ER_NO_SUCH_TABLE 42S02 
+	cze "Tabulka '%-.64s.%s' neexistuje"
+	dan "Tabellen '%-.64s.%-.64s' eksisterer ikke"
+	nla "Tabel '%-.64s.%s' bestaat niet"
+	eng "Table '%-.64s.%-.64s' doesn't exist"
+	est "Tabelit '%-.64s.%-.64s' ei eksisteeri"
+	fre "La table '%-.64s.%s' n'existe pas"
+	ger "Tabelle '%-.64s.%-.64s' existiert nicht"
+	hun "A '%-.64s.%s' tabla nem letezik"
+	ita "La tabella '%-.64s.%s' non esiste"
+	jpn "Table '%-.64s.%s' doesn't exist"
+	kor "ÅÀºí%-.64s.%s' ´ÂÁÀÇÁ ¾ÊÀÏÙ"
+	nor "Table '%-.64s.%s' doesn't exist"
+	norwegian-ny "Table '%-.64s.%s' doesn't exist"
+	pol "Table '%-.64s.%s' doesn't exist"
+	por "Tabela '%-.64s.%-.64s' nãexiste"
+	rum "Tabela '%-.64s.%-.64s' nu exista"
+	rus "ôÉÁ'%-.64s.%-.64s' Î ÓÝÓ×Å"
+	serbian "Tabela '%-.64s.%-.64s' ne postoji"
+	slo "Table '%-.64s.%s' doesn't exist"
+	spa "Tabla '%-.64s.%s' no existe"
+	swe "Det finns ingen tabell som heter '%-.64s.%s'"
+	ukr "ôÉÑ'%-.64s.%-.64s' Î ¦ÓÕ"
+ER_NONEXISTING_TABLE_GRANT 42000 
+	cze "Neexistuje odpov-Bíjí grant pro u¾ivatele '%-.32s' na stroji '%-.64s' pro tabulku '%-.64s'"
+	dan "Denne tilladelse eksisterer ikke for brugeren '%-.32s' påæ '%-.64s' for tabellen '%-.64s'"
+	nla "Deze toegang (GRANT) is niet toegekend voor gebruiker '%-.32s' op host '%-.64s' op tabel '%-.64s'"
+	eng "There is no such grant defined for user '%-.32s' on host '%-.64s' on table '%-.64s'"
+	est "Sellist õst ei ole defineeritud kasutajale '%-.32s' masinast '%-.64s' tabelile '%-.64s'"
+	fre "Un tel droit n'est pas déni pour l'utilisateur '%-.32s' sur l'hô'%-.64s' sur la table '%-.64s'"
+	ger "Eine solche Berechtigung ist für '%-.32s' auf Host '%-.64s' an Tabelle '%-.64s' nicht definiert"
+	hun "A '%-.32s' felhasznalo szamara a '%-.64s' host '%-.64s' tablajaban ez a parancs nem engedelyezett"
+	ita "GRANT non definita per l'utente '%-.32s' dalla macchina '%-.64s' sulla tabella '%-.64s'"
+	kor "»çÀ '%-.32s'(ȽºÆ '%-.64s')´ÂÅÀºí%-.64s'¸¦ »çDZâ§Ç¿© ÁÀµÈ½Âκ ¾ø´Ù "
+	por "Nãexiste tal permissã(grant) definido para o usuáo '%-.32s' no 'host' '%-.64s', na tabela '%-.64s'"
+	rum "Nu exista un astfel de privilegiu (grant) definit pentru utilizatorul '%-.32s' de pe host-ul '%-.64s' pentru tabela '%-.64s'"
+	rus "ôÅÐÁÁÎ ÏÒÄÌÎ ÄÑÐÌÚ×ÔÌ '%-.32s' Î ËÍØÔÒ '%-.64s' ÄÑÔÂÉÙ'%-.64s'"
+	serbian "Ne postoji odobrenje za pristup korisniku '%-.32s' na host-u '%-.64s' tabeli '%-.64s'"
+	spa "No existe tal permiso definido para usuario '%-.32s' en el servidor '%-.64s' en la tabla '%-.64s'"
+	swe "Det finns inget privilegium definierat fönväare '%-.32s' på%-.64s' föabell '%-.64s'"
+	ukr "ðÏÁÅØÎ ×ÚÁÅÏÄÑËÒÓÕÁÁ'%-.32s' ÚÈÓÕ'%-.64s' ÄÑÔÂɦ '%-.64s'"
+ER_NOT_ALLOWED_COMMAND 42000 
+	cze "Pou-B¾itýaz není té verzi MySQL povolen"
+	dan "Den brugte kommando er ikke tilladt med denne udgave af MySQL"
+	nla "Het used commando is niet toegestaan in deze MySQL versie"
+	eng "The used command is not allowed with this MySQL version"
+	est "Antud kä ei ole lubatud käolevas MySQL versioonis"
+	fre "Cette commande n'existe pas dans cette version de MySQL"
+	ger "Der verwendete Befehl ist in dieser MySQL-Version nicht zuläig"
+	hun "A hasznalt parancs nem engedelyezett ebben a MySQL verzioban"
+	ita "Il comando utilizzato non e` supportato in questa versione di MySQL"
+	kor "»çµÈ¸íÀ ÇÀÀ MySQL ¹ö¡¼­´ÂÀ¿ëÁ ¾ÊÀÏÙ"
+	por "Comando usado nãéermitido para esta versãdo MySQL"
+	rum "Comanda folosita nu este permisa pentru aceasta versiune de MySQL"
+	rus "üÍÎÁÎ ÄÐÓÁÔÑ×ÄÎÏ  ×ÒÉ MySQL"
+	serbian "Upotrebljena komanda nije dozvoljena sa ovom verzijom MySQL servera"
+	spa "El comando usado no es permitido con esta versióe MySQL"
+	swe "Du kan inte anväa detta kommando med denna MySQL version"
+	ukr "÷ÒÓÏÕÁÁËÍÎÁÎ ÄÚÏÅÁÕÃÊ×Ò¦§ MySQL"
+ER_SYNTAX_ERROR 42000 
+	cze "Va-B¹e syntaxe je nìkáivná+	dan "Der er en fejl i SQL syntaksen"
+	nla "Er is iets fout in de gebruikte syntax"
+	eng "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use"
+	est "Viga SQL süis"
+	fre "Erreur de syntaxe"
+	ger "Fehler in der SQL-Syntax. Bitte die korrekte Syntax im Handbuch nachschlagen"
+	greek "You have an error in your SQL syntax"
+	hun "Szintaktikai hiba"
+	ita "Errore di sintassi nella query SQL"
+	jpn "Something is wrong in your syntax"
+	kor "SQL ±¸¹®¿¡ ¿ÀùÖÀÏÙ"
+	nor "Something is wrong in your syntax"
+	norwegian-ny "Something is wrong in your syntax"
+	pol "Something is wrong in your syntax"
+	por "Vocêem um erro de sintaxe no seu SQL"
+	rum "Aveti o eroare in sintaxa RSQL"
+	rus "õÓÏÉË ×ÚÐÏÅ éÞÔ ÄËÍÎÁÉ Ð ÉÐÌÚÅÏ ×ÒÉ MySQL Î ÐÅÍÔËÒÅÔÏÏÓÎÁÓÓ"
+	serbian "Imate grešku u vašoj SQL sintaksi"
+	slo "Something is wrong in your syntax"
+	spa "Algo estáquivocado en su sintax"
+	swe "Du har nåt fel i din syntax"
+	ukr "õÓÐÍÌÁÕÓÎÁÓÓ SQL"
+ER_DELAYED_CANT_CHANGE_LOCK  
+	cze "Zpo-B¾dì insert threadu nebyl schopen zíat po¾adovanýk pro tabulku %-.64s"
+	dan "Forsinket indsæelse trån (delayed insert thread) kunne ikke opnååpåabellen %-.64s"
+	nla "'Delayed insert' thread kon de aangevraagde 'lock' niet krijgen voor tabel %-.64s"
+	eng "Delayed insert thread couldn't get requested lock for table %-.64s"
+	est "INSERT DELAYED lõei suutnud saada soovitud lukku tabelile %-.64s"
+	fre "La tâe 'delayed insert' n'a pas pu obtenir le verrou déndéur la table %-.64s"
+	ger "Verzöter (DELAYED) Einfüread konnte die angeforderte Sperre füelle '%-.64s' nicht erhalten"
+	hun "A kesleltetett beillesztes (delayed insert) thread nem kapott zatolast a %-.64s tablahoz"
+	ita "Il thread di inserimento ritardato non riesce ad ottenere il lock per la tabella %-.64s"
+	kor "Á¿¬µÈinsert ¾²·¹µå ÅÀºí-.64sÀ ¿äµÈ¶ô» ø®Ç ¼ö¾ú´Ù"
+	por "'Thread' de inserç retardada (atrasada) pois nãconseguiu obter a trava solicitada para tabela '%-.64s'"
+	rum "Thread-ul pentru inserarea aminata nu a putut obtine lacatul (lock) pentru tabela %-.64s"
+	rus "ðË ÏÓÕÉÁÝÊÏÌÖÎÕ ×Ô×Õ(delayed insert), Î ÓÏ ÐÌÞÔ ÚÐÁÉÁÍÀÂÏÉÏË Î ÔÂÉÕ%-.64s"
+	serbian "Prolongirani 'INSERT' thread nije mogao da dobije traženo zakljuèanje tabele '%-.64s'"
+	spa "Thread de insercióetarda no pudiendo bloquear para la tabla %-.64s"
+	swe "DELAYED INSERT-trån kunde inte lå tabell '%-.64s'"
+	ukr "çË ÄÑINSERT DELAYED Î ÍÖ ÏÒÍÔ ÂÏÕÁÎ ÄÑÔÂɦ %-.64s"
+ER_TOO_MANY_DELAYED_THREADS  
+	cze "P-Bø mnoho zpo¾dìch threadùdan "For mange slettede trå (threads) i brug"
+	nla "Te veel 'delayed' threads in gebruik"
+	eng "Too many delayed threads in use"
+	est "Liiga palju DELAYED lõsid kasutusel"
+	fre "Trop de tâe 'delayed' en cours"
+	ger "Zu viele verzöte (DELAYED) Threads in Verwendung"
+	hun "Tul sok kesletetett thread (delayed)"
+	ita "Troppi threads ritardati in uso"
+	kor "³Ê« ¸¹À Á¿¬ ¾²·¹µå »çÇ°íÖÀÏÙ"
+	por "Excesso de 'threads' retardadas (atrasadas) em uso"
+	rum "Prea multe threaduri aminate care sint in uz"
+	rus "óËÍÍÏÏÐÔË× ÏÓÕÉÁÝÈÏÌÖÎÕ ×Ô×Õ(delayed insert)"
+	serbian "Previše prolongiranih thread-ova je u upotrebi"
+	spa "Muchos threads retardados en uso"
+	swe "Det finns redan 'max_delayed_threads' trår i anväing"
+	ukr "úÁÏÚÔÉÁÉ ÇÌË×ËÒÓÏÕÔÓ"
+ER_ABORTING_CONNECTION 08S01 
+	cze "Zru-B¹eno spojeníld do databá: '%-.64s' u¾ivatel: '%-.64s' (%s)"
+	dan "Afbrudt forbindelse %ld til database: '%-.64s' bruger: '%-.64s' (%-.64s)"
+	nla "Afgebroken verbinding %ld naar db: '%-.64s' gebruiker: '%-.64s' (%s)"
+	eng "Aborted connection %ld to db: '%-.64s' user: '%-.32s' (%-.64s)"
+	est "Üendus katkestatud %ld andmebaasile: '%-.64s' kasutajale: '%-.32s' (%-.64s)"
+	fre "Connection %ld avortévers la bd: '%-.64s' utilisateur: '%-.64s' (%s)"
+	ger "Abbruch der Verbindung %ld zur Datenbank '%-.64s'. Benutzer: '%-.64s' (%-.64s)"
+	hun "Megszakitott kapcsolat %ld db: '%-.64s' adatbazishoz, felhasznalo: '%-.64s' (%s)"
+	ita "Interrotta la connessione %ld al db: '%-.64s' utente: '%-.64s' (%s)"
+	jpn "Aborted connection %ld to db: '%-.64s' user: '%-.64s' (%s)"
+	kor "µ¥Àź£À½º Á¼Ó» ÀÇ ¿¬°áld°¡ Á´ÜÊ: '%-.64s' »çÀ: '%-.64s' (%s)"
+	nor "Aborted connection %ld to db: '%-.64s' user: '%-.64s' (%s)"
+	norwegian-ny "Aborted connection %ld to db: '%-.64s' user: '%-.64s' (%s)"
+	pol "Aborted connection %ld to db: '%-.64s' user: '%-.64s' (%s)"
+	por "Conexã%ld abortou para o banco de dados '%-.64s' - usuáo '%-.32s' (%-.64s)"
+	rum "Conectie terminata %ld la baza de date: '%-.64s' utilizator: '%-.32s' (%-.64s)"
+	rus "ð×Î ÓÅÉÅÉ %ld ËÂÚ ÄÎÙ '%-.64s' ÐÌÚ×ÔÌ '%-.32s' (%-.64s)"
+	serbian "Prekinuta konekcija broj %ld ka bazi: '%-.64s' korisnik je bio: '%-.32s' (%-.64s)"
+	slo "Aborted connection %ld to db: '%-.64s' user: '%-.64s' (%s)"
+	spa "Conexióbortada %ld para db: '%-.64s' usuario: '%-.64s' (%s)"
+	swe "Avbröäen förå%ld till db '%-.64s', anväare '%-.64s' (%s)"
+	ukr "ðÒÁÏÚ¤ÄÁÎ %ld Ä ÂÚ ÄÎÉ: '%-.64s' ËÒÓÕÁÁ '%-.32s' (%-.64s)"
+ER_NET_PACKET_TOO_LARGE 08S01 
+	cze "Zji-B¹tìpøzíacket del¹íe¾ 'max_allowed_packet'"
+	dan "Modtog en datapakke som var støend 'max_allowed_packet'"
+	nla "Groter pakket ontvangen dan 'max_allowed_packet'"
+	eng "Got a packet bigger than 'max_allowed_packet' bytes"
+	est "Saabus suurem pakett kui lubatud 'max_allowed_packet' muutujaga"
+	fre "Paquet plus grand que 'max_allowed_packet' reç
+	ger "Empfangenes Paket ist grö als 'max_allowed_packet' Bytes"
+	hun "A kapott csomag nagyobb, mint a maximalisan engedelyezett: 'max_allowed_packet'"
+	ita "Ricevuto un pacchetto piu` grande di 'max_allowed_packet'"
+	kor "'max_allowed_packet'º¸´Ù´õÆÅÀ ¹ÞÒÀÏÙ"
+	por "Obteve um pacote maior do que a taxa máma de pacotes definida (max_allowed_packet)"
+	rum "Un packet mai mare decit 'max_allowed_packet' a fost primit"
+	rus "ðÞÎÙ ÐËÔÂÌÛ, ÞÍ'max_allowed_packet'"
+	serbian "Primio sam mrežni paket veæod definisane vrednosti 'max_allowed_packet'"
+	spa "Obtenido un paquete mayor que 'max_allowed_packet'"
+	swe "Kommunkationspaketet ästö ä'max_allowed_packet'"
+	ukr "ïÉÁÏÐËÔÂÌÛÊÎÖmax_allowed_packet"
+ER_NET_READ_ERROR_FROM_PIPE 08S01 
+	cze "Zji-B¹tì chyba pøení roury spojení+	dan "Fik læfejl fra forbindelse (connection pipe)"
+	nla "Kreeg leesfout van de verbindings pipe"
+	eng "Got a read error from the connection pipe"
+	est "Viga üstoru lugemisel"
+	fre "Erreur de lecture reç du pipe de connection"
+	ger "Lese-Fehler bei einer Verbindungs-Pipe"
+	hun "Olvasasi hiba a kapcsolat soran"
+	ita "Rilevato un errore di lettura dalla pipe di connessione"
+	kor "¿¬°áÄÌÁÎÎÍ¿¡·¯°¡ ¹ßýÀÏÙ"
+	por "Obteve um erro de leitura no 'pipe' da conexã
+	rum "Eroare la citire din cauza lui 'connection pipe'"
+	rus "ðÞÎ ÏÉË ÞÅÉ Ï ÐÔË ÓÅÉÅÉ (connection pipe)"
+	serbian "Greška pri èanju podataka sa pipe-a"
+	spa "Obtenido un error de lectura de la conexióipe"
+	swe "Fick läel fråklienten vid läing frå'PIPE'"
+	ukr "ïÉÁÏÐÍÌÕÞÔÎÑÚËÍÎËÃÊÏÏËÎÌ"
+ER_NET_FCNTL_ERROR 08S01 
+	cze "Zji-B¹tì chyba fcntl()"
+	dan "Fik fejlmeddelelse fra fcntl()"
+	nla "Kreeg fout van fcntl()"
+	eng "Got an error from fcntl()"
+	est "fcntl() tagastas vea"
+	fre "Erreur reç de fcntl() "
+	ger "fcntl() lieferte einen Fehler"
+	hun "Hiba a fcntl() fuggvenyben"
+	ita "Rilevato un errore da fcntl()"
+	kor "fcntl() ǼöÎÍ¿¡·¯°¡ ¹ßýÀÏÙ"
+	por "Obteve um erro em fcntl()"
+	rum "Eroare obtinuta de la fcntl()"
+	rus "ðÞÎ ÏÉË Ï fcntl()"
+	serbian "Greška pri izvršavanju funkcije fcntl()"
+	spa "Obtenido un error de fcntl()"
+	swe "Fick fatalt fel frå'fcntl()'"
+	ukr "ïÉÁÏÐÍÌË ×Äfcntl()"
+ER_NET_PACKETS_OUT_OF_ORDER 08S01 
+	cze "P-Bøzíackety v chybnépoø
+	dan "Modtog ikke datapakker i korrekt ræefø
+	nla "Pakketten in verkeerde volgorde ontvangen"
+	eng "Got packets out of order"
+	est "Paketid saabusid vales jäekorras"
+	fre "Paquets reç dans le dérdre"
+	ger "Pakete nicht in der richtigen Reihenfolge empfangen"
+	hun "Helytelen sorrendben erkezett adatcsomagok"
+	ita "Ricevuti pacchetti non in ordine"
+	kor "¼ø ¸ÂöÂÆÅÀ ¹ÞÒÀÏÙ"
+	por "Obteve pacotes fora de ordem"
+	rum "Packets care nu sint ordonati au fost gasiti"
+	rus "ðÔ ÐÌÞÎ ×Î×ÒÏ ÐÒÄÅ
+	serbian "Primio sam mrežne pakete van reda"
+	spa "Obtenido paquetes desordenados"
+	swe "Kommunikationspaketen kom i fel ordning"
+	ukr "ïÉÁÏÐËÔ ÕÎÎÌÖÏÕÐÒÄÕ
+ER_NET_UNCOMPRESS_ERROR 08S01 
+	cze "Nemohu rozkomprimovat komunika-Bè packet"
+	dan "Kunne ikke dekomprimere kommunikations-pakke (communication packet)"
+	nla "Communicatiepakket kon niet worden gedecomprimeerd"
+	eng "Couldn't uncompress communication packet"
+	est "Viga andmepaketi lahtipakkimisel"
+	fre "Impossible de démpresser le paquet reç
+	ger "Kommunikationspaket lät sich nicht entpacken"
+	hun "A kommunikacios adatcsomagok nem tomorithetok ki"
+	ita "Impossibile scompattare i pacchetti di comunicazione"
+	kor "ŽÅÆÅÀ ¾ÐàÁ¸¦ Ç ¼ö¾ú´Ù"
+	por "Nãconseguiu descomprimir pacote de comunicaç"
+	rum "Nu s-a putut decompresa pachetul de comunicatie (communication packet)"
+	rus "îÏÍÖÏÒÓÁÏÁØÐËÔ ÐÌÞÎÙ ÞÒÚËÍÕÉÁÉÎÙ ÐÏÏÏ"
+	serbian "Ne mogu da dekompresujem mrežne pakete"
+	spa "No puedo descomprimir paquetes de comunicació+	swe "Kunde inte packa up kommunikationspaketet"
+	ukr "îÍÖ ÄËÍÒÓ×Ô ËÍÎËÃÊÉ ÐËÔ
+ER_NET_READ_ERROR 08S01 
+	cze "Zji-B¹tì chyba pøeníomunikaèho packetu"
+	dan "Fik fejlmeddelelse ved læing af kommunikations-pakker (communication packets)"
+	nla "Fout bij het lezen van communicatiepakketten"
+	eng "Got an error reading communication packets"
+	est "Viga andmepaketi lugemisel"
+	fre "Erreur de lecture des paquets reç"
+	ger "Fehler beim Lesen eines Kommunikationspakets"
+	hun "HIba a kommunikacios adatcsomagok olvasasa soran"
+	ita "Rilevato un errore ricevendo i pacchetti di comunicazione"
+	kor "ŽÅÆÅÀ À´ÂÁ ¿ÀùßýÀÏÙ"
+	por "Obteve um erro na leitura de pacotes de comunicaç"
+	rum "Eroare obtinuta citind pachetele de comunicatie (communication packets)"
+	rus "ðÞÎ ÏÉË ×ÐÏÅÓ ÐÌÞÎÑÐËÔ ÞÒÚËÍÕÉÁÉÎÙ ÐÏÏÏ "
+	serbian "Greška pri primanju mrežnih paketa"
+	spa "Obtenido un error leyendo paquetes de comunicació+	swe "Fick ett fel vid läing fråklienten"
+	ukr "ïÉÁÏÐÍÌÕÞÔÎÑËÍÎËÃÊÉ ÐËÔ×
+ER_NET_READ_INTERRUPTED 08S01 
+	cze "Zji-B¹tìtimeout pøeníomunikaèho packetu"
+	dan "Timeout-fejl ved læing af kommunukations-pakker (communication packets)"
+	nla "Timeout bij het lezen van communicatiepakketten"
+	eng "Got timeout reading communication packets"
+	est "Kontrollaja üine andmepakettide lugemisel"
+	fre "Timeout en lecture des paquets reç"
+	ger "Zeitühreitung beim Lesen eines Kommunikationspakets"
+	hun "Idotullepes a kommunikacios adatcsomagok olvasasa soran"
+	ita "Rilevato un timeout ricevendo i pacchetti di comunicazione"
+	kor "ŽÅÆÅÀ À´ÂÁ timeoutÀ ¹ßýÀÏÙ"
+	por "Obteve expiraç de tempo (timeout) na leitura de pacotes de comunicaç"
+	rum "Timeout obtinut citind pachetele de comunicatie (communication packets)"
+	rus "ðÞÎÔÊÁÔÏÉÁÉ ÐËÔ ÞÒÚËÍÕÉÁÉÎÙ ÐÏÏÏ "
+	serbian "Vremenski limit za èanje mrežnih paketa je istekao"
+	spa "Obtenido timeout leyendo paquetes de comunicació+	swe "Fick 'timeout' vid läing fråklienten"
+	ukr "ïÉÁÏÚÔÉË ÞÔÎÑËÍÎËÃÊÉ ÐËÔ×
+ER_NET_ERROR_ON_WRITE 08S01 
+	cze "Zji-B¹tì chyba pøpisu komunikaèho packetu"
+	dan "Fik fejlmeddelelse ved skrivning af kommunukations-pakker (communication packets)"
+	nla "Fout bij het schrijven van communicatiepakketten"
+	eng "Got an error writing communication packets"
+	est "Viga andmepaketi kirjutamisel"
+	fre "Erreur d'éiture des paquets envoyé
+	ger "Fehler beim Schreiben eines Kommunikationspakets"
+	hun "Hiba a kommunikacios csomagok irasa soran"
+	ita "Rilevato un errore inviando i pacchetti di comunicazione"
+	kor "ŽÅÆÅÀ ±âÇ´ÂÁ ¿ÀùßýÀÏÙ"
+	por "Obteve um erro na escrita de pacotes de comunicaç"
+	rum "Eroare in scrierea pachetelor de comunicatie (communication packets)"
+	rus "ðÞÎ ÏÉË ÐÉÐÒÄÞ ÐËÔ ÞÒÚËÍÕÉÁÉÎÙ ÐÏÏÏ "
+	serbian "Greška pri slanju mrežnih paketa"
+	spa "Obtenido un error de escribiendo paquetes de comunicació+	swe "Fick ett fel vid skrivning till klienten"
+	ukr "ïÉÁÏÐÍÌÕÚÐÓ ËÍÎËÃÊÉ ÐËÔ×
+ER_NET_WRITE_INTERRUPTED 08S01 
+	cze "Zji-B¹tìtimeout pøpisu komunikaèho packetu"
+	dan "Timeout-fejl ved skrivning af kommunukations-pakker (communication packets)"
+	nla "Timeout bij het schrijven van communicatiepakketten"
+	eng "Got timeout writing communication packets"
+	est "Kontrollaja üine andmepakettide kirjutamisel"
+	fre "Timeout d'éiture des paquets envoyé
+	ger "Zeitühreitung beim Schreiben eines Kommunikationspakets"
+	hun "Idotullepes a kommunikacios csomagok irasa soran"
+	ita "Rilevato un timeout inviando i pacchetti di comunicazione"
+	kor "ŽÅÆÆÀ ±âÇ´ÂÁ timeoutÀ ¹ßýÀÏÙ"
+	por "Obteve expiraç de tempo ('timeout') na escrita de pacotes de comunicaç"
+	rum "Timeout obtinut scriind pachetele de comunicatie (communication packets)"
+	rus "ðÞÎÔÊÁÔ×ÐÏÅÓ ÐÒÄÞ ÐËÔ ÞÒÚËÍÕÉÁÉÎÙ ÐÏÏÏ "
+	serbian "Vremenski limit za slanje mrežnih paketa je istekao"
+	spa "Obtenido timeout escribiendo paquetes de comunicació+	swe "Fick 'timeout' vid skrivning till klienten"
+	ukr "ïÉÁÏÚÔÉË ÚÐÓ ËÍÎËÃÊÉ ÐËÔ×
+ER_TOO_LONG_STRING 42000 
+	cze "V-Býýzec je del¹íe¾ 'max_allowed_packet'"
+	dan "Strengen med resultater er støend 'max_allowed_packet'"
+	nla "Resultaat string is langer dan 'max_allowed_packet'"
+	eng "Result string is longer than 'max_allowed_packet' bytes"
+	est "Tulemus on pikem kui lubatud 'max_allowed_packet' muutujaga"
+	fre "La chaî réltat est plus grande que 'max_allowed_packet'"
+	ger "Ergebnis-String ist läer als 'max_allowed_packet' Bytes"
+	hun "Ez eredmeny sztring nagyobb, mint a lehetseges maximum: 'max_allowed_packet'"
+	ita "La stringa di risposta e` piu` lunga di 'max_allowed_packet'"
+	por "'String' resultante éais longa do que 'max_allowed_packet'"
+	rum "Sirul rezultat este mai lung decit 'max_allowed_packet'"
+	rus "òÌÔÒÀÁ ÓÒË ÂÌÛ, ÞÍ'max_allowed_packet'"
+	serbian "Rezultujuèstring je duži nego što to dozvoljava parametar servera 'max_allowed_packet'"
+	spa "La string resultante es mayor que max_allowed_packet"
+	swe "Resultatsträen äläre ämax_allowed_packet"
+	ukr "óË ÒÚÌÔÔ Ä×ÁÎÖmax_allowed_packet"
+ER_TABLE_CANT_HANDLE_BLOB 42000 
+	cze "Typ pou-B¾itéabulky nepodporuje BLOB/TEXT sloupce"
+	dan "Denne tabeltype understø ikke brug af BLOB og TEXT kolonner"
+	nla "Het gebruikte tabel type ondersteunt geen BLOB/TEXT kolommen"
+	eng "The used table type doesn't support BLOB/TEXT columns"
+	est "Valitud tabelitü toeta BLOB/TEXT tüäu"
+	fre "Ce type de table ne supporte pas les colonnes BLOB/TEXT"
+	ger "Der verwendete Tabellentyp unterstüeine BLOB- und TEXT-Felder"
+	hun "A hasznalt tabla tipus nem tamogatja a BLOB/TEXT mezoket"
+	ita "Il tipo di tabella usata non supporta colonne di tipo BLOB/TEXT"
+	por "Tipo de tabela usado nãpermite colunas BLOB/TEXT"
+	rum "Tipul de tabela folosit nu suporta coloane de tip BLOB/TEXT"
+	rus "éÏØÕÍÑÔÂÉÁÎ ÐÄÅÖ×Å ÔÐ BLOB/TEXT"
+	serbian "Iskorišteni tip tabele ne podržava kolone tipa 'BLOB' odnosno 'TEXT'"
+	spa "El tipo de tabla usada no permite soporte para columnas BLOB/TEXT"
+	swe "Den anväa tabelltypen kan inte hantera BLOB/TEXT-kolumner"
+	ukr "÷ÒÓÁÉ ÔÐÔÂɦ Î ÐÄÒͤ BLOB/TEXT ÓϦ"
+ER_TABLE_CANT_HANDLE_AUTO_INCREMENT 42000 
+	cze "Typ pou-B¾itéabulky nepodporuje AUTO_INCREMENT sloupce"
+	dan "Denne tabeltype understø ikke brug af AUTO_INCREMENT kolonner"
+	nla "Het gebruikte tabel type ondersteunt geen AUTO_INCREMENT kolommen"
+	eng "The used table type doesn't support AUTO_INCREMENT columns"
+	est "Valitud tabelitü toeta AUTO_INCREMENT tüäu"
+	fre "Ce type de table ne supporte pas les colonnes AUTO_INCREMENT"
+	ger "Der verwendete Tabellentyp unterstüeine AUTO_INCREMENT-Felder"
+	hun "A hasznalt tabla tipus nem tamogatja az AUTO_INCREMENT tipusu mezoket"
+	ita "Il tipo di tabella usata non supporta colonne di tipo AUTO_INCREMENT"
+	por "Tipo de tabela usado nãpermite colunas AUTO_INCREMENT"
+	rum "Tipul de tabela folosit nu suporta coloane de tip AUTO_INCREMENT"
+	rus "éÏØÕÍÑÔÂÉÁÎ ÐÄÅÖ×Å ÁÔÉËÅÅÔÙ ÓÏÂÙ
+	serbian "Iskorišteni tip tabele ne podržava kolone tipa 'AUTO_INCREMENT'"
+	spa "El tipo de tabla usada no permite soporte para columnas AUTO_INCREMENT"
+	swe "Den anväa tabelltypen kan inte hantera AUTO_INCREMENT-kolumner"
+	ukr "÷ÒÓÁÉ ÔÐÔÂɦ Î ÐÄÒͤ AUTO_INCREMENT ÓϦ"
+ER_DELAYED_INSERT_TABLE_LOCKED  
+	cze "INSERT DELAYED nen-Bío¾no s tabulkou '%-.64s' pou¾í proto¾e je zamèáomocíOCK TABLES"
+	dan "INSERT DELAYED kan ikke bruges med tabellen '%-.64s', fordi tabellen er lå med LOCK TABLES"
+	nla "INSERT DELAYED kan niet worden gebruikt bij table '%-.64s', vanwege een 'lock met LOCK TABLES"
+	eng "INSERT DELAYED can't be used with table '%-.64s' because it is locked with LOCK TABLES"
+	est "INSERT DELAYED ei saa kasutada tabeli '%-.64s' peal, kuna see on lukustatud LOCK TABLES käga"
+	fre "INSERT DELAYED ne peut êe utilisévec la table '%-.64s', car elle est verrouéavec LOCK TABLES"
+	ger "INSERT DELAYED kann füelle '%-.64s' nicht verwendet werden, da sie mit LOCK TABLES gesperrt ist"
+	greek "INSERT DELAYED can't be used with table '%-.64s', because it is locked with LOCK TABLES"
+	hun "Az INSERT DELAYED nem hasznalhato a '%-.64s' tablahoz, mert a tabla zarolt (LOCK TABLES)"
+	ita "L'inserimento ritardato (INSERT DELAYED) non puo` essere usato con la tabella '%-.64s', perche` soggetta a lock da 'LOCK TABLES'"
+	jpn "INSERT DELAYED can't be used with table '%-.64s', because it is locked with LOCK TABLES"
+	kor "INSERT DELAYED can't be used with table '%-.64s', because it is locked with LOCK TABLES"
+	nor "INSERT DELAYED can't be used with table '%-.64s', because it is locked with LOCK TABLES"
+	norwegian-ny "INSERT DELAYED can't be used with table '%-.64s', because it is locked with LOCK TABLES"
+	pol "INSERT DELAYED can't be used with table '%-.64s', because it is locked with LOCK TABLES"
+	por "INSERT DELAYED nãpode ser usado com a tabela '%-.64s', porque ela estáravada com LOCK TABLES"
+	rum "INSERT DELAYED nu poate fi folosit cu tabela '%-.64s', deoarece este locked folosing LOCK TABLES"
+	rus "îØÑÉÐÌÚ×Ô INSERT DELAYED ÄÑÔÂÉÙ'%-.64s', ÐÔÍ ÞÏÏÁÚÂÏÉÏÁÁÓÐÍÝÀLOCK TABLES"
+	serbian "Komanda 'INSERT DELAYED' ne može biti iskorištena u tabeli '%-.64s', zbog toga što je zakljuèa komandom 'LOCK TABLES'"
+	slo "INSERT DELAYED can't be used with table '%-.64s', because it is locked with LOCK TABLES"
+	spa "INSERT DELAYED no puede ser usado con tablas '%-.64s', porque esta bloqueada con LOCK TABLES"
+	swe "INSERT DELAYED kan inte anväas med tabell '%-.64s', emedan den älå med LOCK TABLES"
+	ukr "INSERT DELAYED Î ÍÖ ÂÔ ×ËÒÓÁÏÚÔÂÉÅ '%-.64s', ÔÍ Ý §§ ÚÂÏÏÁÏÚLOCK TABLES"
+ER_WRONG_COLUMN_NAME 42000 
+	cze "Nespr-Báémé sloupce '%-.100s'"
+	dan "Forkert kolonnenavn '%-.100s'"
+	nla "Incorrecte kolom naam '%-.100s'"
+	eng "Incorrect column name '%-.100s'"
+	est "Vigane tulba nimi '%-.100s'"
+	fre "Nom de colonne '%-.100s' incorrect"
+	ger "Falscher Spaltenname '%-.100s'"
+	hun "Ervenytelen mezonev: '%-.100s'"
+	ita "Nome colonna '%-.100s' non corretto"
+	por "Nome de coluna '%-.100s' incorreto"
+	rum "Nume increct de coloana '%-.100s'"
+	rus "îÅÎÅÉÑÓÏÂÁ'%-.100s'"
+	serbian "Pogrešno ime kolone '%-.100s'"
+	spa "Incorrecto nombre de columna '%-.100s'"
+	swe "Felaktigt kolumnnamn '%-.100s'"
+	ukr "î¦ÒŦÍÑÓÏÂÑ'%-.100s'"
+ER_WRONG_KEY_COLUMN 42000 
+	cze "Handler pou-B¾itéabulky neumíndexovat sloupce '%-.64s'"
+	dan "Den brugte tabeltype kan ikke indeksere kolonnen '%-.64s'"
+	nla "De gebruikte tabel 'handler' kan kolom '%-.64s' niet indexeren"
+	eng "The used storage engine can't index column '%-.64s'"
+	est "Tabelihandler ei oska indekseerida tulpa '%-.64s'"
+	fre "Le handler de la table ne peut indexéa colonne '%-.64s'"
+	ger "Die verwendete Speicher-Engine kann die Spalte '%-.64s' nicht indizieren"
+	greek "The used table handler can't index column '%-.64s'"
+	hun "A hasznalt tablakezelo nem tudja a '%-.64s' mezot indexelni"
+	ita "Il gestore delle tabelle non puo` indicizzare la colonna '%-.64s'"
+	jpn "The used table handler can't index column '%-.64s'"
+	kor "The used table handler can't index column '%-.64s'"
+	nor "The used table handler can't index column '%-.64s'"
+	norwegian-ny "The used table handler can't index column '%-.64s'"
+	pol "The used table handler can't index column '%-.64s'"
+	por "O manipulador de tabela usado nãpode indexar a coluna '%-.64s'"
+	rum "Handler-ul tabelei folosite nu poate indexa coloana '%-.64s'"
+	rus "éÏØÏÁÎÊÏÒÂÔÉ ÔÂÉÙÎ ÍÖÔÐÏÎÅÓÒ×Ô ÓÏÂÃ'%-.64s'"
+	serbian "Handler tabele ne može da indeksira kolonu '%-.64s'"
+	slo "The used table handler can't index column '%-.64s'"
+	spa "El manipulador de tabla usado no puede indexar columna '%-.64s'"
+	swe "Den anväa tabelltypen kan inte indexera kolumn '%-.64s'"
+	ukr "÷ÒÓÁÉ ×Á¦×É ÔÂɦ Î ÍÖ ¦ÎÅÓ×Ô ÓÏÂà '%-.64s'"
+ER_WRONG_MRG_TABLE  
+	cze "V-B¹echny tabulky v MERGE tabulce nejsou definová stejnì+	dan "Tabellerne i MERGE er ikke defineret ens"
+	nla "Niet alle tabellen in de MERGE tabel hebben identieke gedefinities"
+	eng "All tables in the MERGE table are not identically defined"
+	est "Kõtabelid MERGE tabeli mäatluses ei ole identsed"
+	fre "Toutes les tables de la table de type MERGE n'ont pas la mê dénition"
+	ger "Nicht alle Tabellen in der MERGE-Tabelle sind gleich definiert"
+	hun "A MERGE tablaban talalhato tablak definicioja nem azonos"
+	ita "Non tutte le tabelle nella tabella di MERGE sono definite in maniera identica"
+	jpn "All tables in the MERGE table are not defined identically"
+	kor "All tables in the MERGE table are not defined identically"
+	nor "All tables in the MERGE table are not defined identically"
+	norwegian-ny "All tables in the MERGE table are not defined identically"
+	pol "All tables in the MERGE table are not defined identically"
+	por "Todas as tabelas contidas na tabela fundida (MERGE) nãestãdefinidas identicamente"
+	rum "Toate tabelele din tabela MERGE nu sint definite identic"
+	rus "î×ÅÔÂÉÙ×MERGE ÏÒÄÌÎ ÏÉÁÏÏ
+	serbian "Tabele iskorištene u 'MERGE' tabeli nisu definisane na isti naè"
+	slo "All tables in the MERGE table are not defined identically"
+	spa "Todas las tablas en la MERGE tabla no estan definidas identicamente"
+	swe "Tabellerna i MERGE-tabellen äinte identiskt definierade"
+	ukr "ôɦ ÕMERGE TABLE ÍÀØÒÚÕÓÒËÕÕ
+ER_DUP_UNIQUE 23000 
+	cze "Kv-Bùnique constraintu nemozu zapsat do tabulky '%-.64s'"
+	dan "Kan ikke skrive til tabellen '%-.64s' fordi det vil bryde CONSTRAINT regler"
+	nla "Kan niet opslaan naar table '%-.64s' vanwege 'unique' beperking"
+	eng "Can't write, because of unique constraint, to table '%-.64s'"
+	est "Ei suuda kirjutada tabelisse '%-.64s', kuna see rikub üe kitsendust"
+	fre "Ériture impossible àause d'un index UNIQUE sur la table '%-.64s'"
+	ger "Schreiben in Tabelle '%-.64s' nicht möch wegen einer Eindeutigkeitsbeschräung (unique constraint)"
+	hun "A '%-.64s' nem irhato, az egyedi mezok miatt"
+	ita "Impossibile scrivere nella tabella '%-.64s' per limitazione di unicita`"
+	por "Nãpode gravar, devido àestriç UNIQUE, na tabela '%-.64s'"
+	rum "Nu pot scrie pe hard-drive, din cauza constraintului unic (unique constraint) pentru tabela '%-.64s'"
+	rus "îÏÍÖÏÚÐÓÔ ×ÔÂÉÕ'%-.64s' É-Ú ÏÒÎÞÎÊÕÉÁØÏÏËÀÁ
+	serbian "Zbog provere jedinstvenosti ne mogu da upišem podatke u tabelu '%-.64s'"
+	spa "No puedo escribir, debido al ú constraint, para tabla '%-.64s'"
+	swe "Kan inte skriva till tabell '%-.64s'; UNIQUE-test"
+	ukr "îÍÖ ÚÐÓÔ Ä ÔÂɦ '%-.64s', ÚÐÉÉÉ×ÍÇÕ¦ËÌÎÓ¦"
+ER_BLOB_KEY_WITHOUT_LENGTH 42000 
+	cze "BLOB sloupec '%-.64s' je pou-B¾it ve specifikaci klí bez déy"
+	dan "BLOB kolonnen '%-.64s' brugt i nøpecifikation uden nøæde"
+	nla "BLOB kolom '%-.64s' gebruikt in zoeksleutel specificatie zonder zoeksleutel lengte"
+	eng "BLOB/TEXT column '%-.64s' used in key specification without a key length"
+	est "BLOB-tüulp '%-.64s' on kasutusel võs ilma pikkust mäatlemata"
+	fre "La colonne '%-.64s' de type BLOB est utilisédans une dénition d'index sans longueur d'index"
+	ger "BLOB- oder TEXT-Spalte '%-.64s' wird in der Schlüefinition ohne Schlüäenangabe verwendet"
+	greek "BLOB column '%-.64s' used in key specification without a key length"
+	hun "BLOB mezo '%-.64s' hasznalt a mezo specifikacioban, a mezohossz megadasa nelkul"
+	ita "La colonna '%-.64s' di tipo BLOB e` usata in una chiave senza specificarne la lunghezza"
+	jpn "BLOB column '%-.64s' used in key specification without a key length"
+	kor "BLOB column '%-.64s' used in key specification without a key length"
+	nor "BLOB column '%-.64s' used in key specification without a key length"
+	norwegian-ny "BLOB column '%-.64s' used in key specification without a key length"
+	pol "BLOB column '%-.64s' used in key specification without a key length"
+	por "Coluna BLOB '%-.64s' usada na especificaç de chave sem o comprimento da chave"
+	rum "Coloana BLOB '%-.64s' este folosita in specificarea unei chei fara ca o lungime de cheie sa fie folosita"
+	rus "óÂÃÔÐ BLOB '%-.64s' ÂÌÕÁÁ ×ÏÒÄÌÎÉËÀÁÂÚÕÁÁÉ ÄÉÙËÀÁ
+	serbian "BLOB kolona '%-.64s' je upotrebljena u specifikaciji kljuèbez navoða dužine kljuè
+	slo "BLOB column '%-.64s' used in key specification without a key length"
+	spa "Columna BLOB column '%-.64s' usada en especificacióe clave sin tamañe la clave"
+	swe "Du har inte angett nån nyckelläd föLOB '%-.64s'"
+	ukr "óÂà BLOB '%-.64s' ×ËÒÓÁÏÕ×ÚÁÅÎ ËÀÁÂÚ×ÁÁÎ Ä×ÉÉËÀÁ
+ER_PRIMARY_CANT_HAVE_NULL 42000 
+	cze "V-B¹echny èti primáí klí musejíý NULL; pokud potøete NULL, pou¾ijte UNIQUE"
+	dan "Alle dele af en PRIMARY KEY skal væ NOT NULL;  Hvis du skal bruge NULL i nø, brug UNIQUE istedet"
+	nla "Alle delen van een PRIMARY KEY moeten NOT NULL zijn; Indien u NULL in een zoeksleutel nodig heeft kunt u UNIQUE gebruiken"
+	eng "All parts of a PRIMARY KEY must be NOT NULL; if you need NULL in a key, use UNIQUE instead"
+	est "KõPRIMARY KEY peavad olema mäatletud NOT NULL piiranguga; vajadusel kasuta UNIQUE tüõ"
+	fre "Toutes les parties d'un index PRIMARY KEY doivent êe NOT NULL; Si vous avez besoin d'un NULL dans l'index, utilisez un index UNIQUE"
+	ger "Alle Teile eines PRIMARY KEY müals NOT NULL definiert sein. Wenn NULL in einem Schlübenöt wird, muss ein UNIQUE-Schlüverwendet werden"
+	hun "Az elsodleges kulcs teljes egeszeben csak NOT NULL tipusu lehet; Ha NULL mezot szeretne a kulcskent, hasznalja inkabb a UNIQUE-ot"
+	ita "Tutte le parti di una chiave primaria devono essere dichiarate NOT NULL; se necessitano valori NULL nelle chiavi utilizzare UNIQUE"
+	por "Todas as partes de uma chave primáa devem ser nãnulas. Se vocêrecisou usar um valor nulo (NULL) em uma chave, use a cláula UNIQUE em seu lugar"
+	rum "Toate partile unei chei primare (PRIMARY KEY) trebuie sa fie NOT NULL; Daca aveti nevoie de NULL in vreo cheie, folositi UNIQUE in schimb"
+	rus "÷ÞÓÉÐÒÉÎÇ ËÀÁ(PRIMARY KEY) ÄÌÎ ÂÔ ÏÒÄÌÎ ËËNOT NULL; åÉ×ÍÎÖÁÐÄÅÖÁ×ÌÞÎNULL ×ËÀÅ ×ÓÏØÕÔÓ ÉÄËÏ UNIQUE"
+	serbian "Svi delovi primarnog kljuèmoraju biti razlièi od NULL;  Ako Vam ipak treba NULL vrednost u kljuè upotrebite 'UNIQUE'"
+	spa "Todas las partes de un PRIMARY KEY deben ser NOT NULL;  Si necesitas NULL en una clave, use UNIQUE"
+	swe "Alla delar av en PRIMARY KEY måe vara NOT NULL;  Om du vill ha en nyckel med NULL, anvä UNIQUE istäet"
+	ukr "õÞÓÉÉPRIMARY KEY Ð×Φ ÂÔ NOT NULL; ñ × ÐÔÅÕÔ NULL ÕËÀ¦, ÓÏÉÔÊÅÑUNIQUE"
+ER_TOO_MANY_ROWS 42000 
+	cze "V-Býk obsahuje ví ne¾ jeden ø"
+	dan "Resultatet bestod af mere end een ræe"
+	nla "Resultaat bevatte meer dan een rij"
+	eng "Result consisted of more than one row"
+	est "Tulemis oli rohkem kui ürje"
+	fre "Le réltat contient plus d'un enregistrement"
+	ger "Ergebnis besteht aus mehr als einer Zeile"
+	hun "Az eredmeny tobb, mint egy sort tartalmaz"
+	ita "Il risultato consiste di piu` di una riga"
+	por "O resultado consistiu em mais do que uma linha"
+	rum "Resultatul constista din mai multe linii"
+	rus "÷ÚÌÔÔ ×ÚÒÝÎ ÂÌÅÞÍÏÎ ÓÒË"
+	serbian "Rezultat je saèjen od više slogova"
+	spa "Resultado compuesto de mas que una lía"
+	swe "Resultet bestod av mera äen rad"
+	ukr "òÌÔÔÚÁÏÉØÑÕÂÌÛ ÎÖÏÎÊÓÒÃ"
+ER_REQUIRES_PRIMARY_KEY 42000 
+	cze "Tento typ tabulky vy-B¾aduje primáílí
+	dan "Denne tabeltype krær en primæø
+	nla "Dit tabel type heeft een primaire zoeksleutel nodig"
+	eng "This table type requires a primary key"
+	est "Antud tabelitüuab primaarset võ"
+	fre "Ce type de table néssite une clérimaire (PRIMARY KEY)"
+	ger "Dieser Tabellentyp benöt einen Primächlü(PRIMARY KEY)"
+	hun "Az adott tablatipushoz elsodleges kulcs hasznalata kotelezo"
+	ita "Questo tipo di tabella richiede una chiave primaria"
+	por "Este tipo de tabela requer uma chave primáa"
+	rum "Aceast tip de tabela are nevoie de o cheie primara"
+	rus "üÉ ÔÂÉÙÔÅÕÔÏÒÄÌÎÑÐÒÉÎÇ ËÀÁ
+	serbian "Ovaj tip tabele zahteva da imate definisan primarni kljuè+	spa "Este tipo de tabla necesita de una primary key"
+	swe "Denna tabelltyp krär en PRIMARY KEY"
+	ukr "ã ÔÐÔÂɦ ÐÔÅÕ ÐÒÉÎÇ ËÀÁ
+ER_NO_RAID_COMPILED  
+	cze "Tato verze MySQL nen-Bíkompilová s podporou RAID"
+	dan "Denne udgave af MySQL er ikke oversat med understøse af RAID"
+	nla "Deze versie van MySQL is niet gecompileerd met RAID ondersteuning"
+	eng "This version of MySQL is not compiled with RAID support"
+	est "Antud MySQL versioon on kompileeritud ilma RAID toeta"
+	fre "Cette version de MySQL n'est pas compiléavec le support RAID"
+	ger "Diese MySQL-Version ist nicht mit RAID-Unterstü kompiliert"
+	hun "Ezen leforditott MySQL verzio nem tartalmaz RAID support-ot"
+	ita "Questa versione di MYSQL non e` compilata con il supporto RAID"
+	por "Esta versãdo MySQL nãfoi compilada com suporte a RAID"
+	rum "Aceasta versiune de MySQL, nu a fost compilata cu suport pentru RAID"
+	rus "üÒÉ MySQL ÓÏÐÌÒ×Î ÂÚÐÄÅÖÉRAID"
+	serbian "Ova verzija MySQL servera nije kompajlirana sa podrškom za RAID ureð"
+	spa "Esta versióe MySQL no es compilada con soporte RAID"
+	swe "Denna version av MySQL äinte kompilerad med RAID"
+	ukr "ã×Ò¦ÑMySQL Î ÚÏÐÌÏÁÁÚÐÄÒÍÏ RAID"
+ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE  
+	cze "Update tabulky bez WHERE s kl-Bím není móbezpeèch update dovoleno"
+	dan "Du bruger sikker opdaterings modus ('safe update mode') og du forsøat opdatere en tabel uden en WHERE klausul, der gøug af et KEY felt"
+	nla "U gebruikt 'safe update mode' en u probeerde een tabel te updaten zonder een WHERE met een KEY kolom"
+	eng "You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column"
+	est "Katse muuta tabelit turvalises rezhiimis ilma WHERE klauslita"
+	fre "Vous ês en mode 'safe update' et vous essayez de faire un UPDATE sans clause WHERE utilisant un index"
+	ger "MySQL lät im sicheren Aktualisierungsmodus (safe update mode). Sie haben versucht, eine Tabelle zu aktualisieren, ohne in der WHERE-Klausel ein KEY-Feld anzugeben"
+	hun "On a biztonsagos update modot hasznalja, es        WHERE that uses a KEY column"
+	ita "In modalita` 'safe update' si e` cercato di aggiornare una tabella senza clausola WHERE su una chiave"
+	por "Vocêstásando modo de atualizaç seguro e tentou atualizar uma tabela sem uma cláula WHERE que use uma coluna chave"
+	rus "÷ÁÏÁÔ ×ÒÖÍ ÂÚÐÓÙ ÏÎ×ÅÉ (safe update mode) ÉÐÐÏÏÁÉÉÍÎÔ ÔÂÉÕÂÚÉÐÌÚ×ÎÑËÀÅÏÏÓÏÂÁ×ÞÓÉWHERE"
+	serbian "Vi koristite safe update mod servera, a probali ste da promenite podatke bez 'WHERE' komande koja koristi kolonu kljuè
+	spa "Tu estáusando modo de actualizacióegura y tentado actualizar una tabla sin un WHERE que usa una KEY columna"
+	swe "Du anväer 'sär uppdateringsmod' och fökte uppdatera en tabell utan en WHERE-sats som anväer sig av en nyckel"
+	ukr "÷ ÒÖÍ ÂÚÅÎÇ ÏÏÌÎÑÔ ÎÍǤÔÓ ÏÏÉÉÔÂÉÀÂÚÏÅÁÏÁWHERE, Ý ×ËÒÓÏÕ KEY ÓÏÂÃ"
+ER_KEY_DOES_NOT_EXITS  
+	cze "Kl-Bí'%-.64s' v tabulce '%-.64s' neexistuje"
+	dan "Nø '%-.64s' eksisterer ikke i tabellen '%-.64s'"
+	nla "Zoeksleutel '%-.64s' bestaat niet in tabel '%-.64s'"
+	eng "Key '%-.64s' doesn't exist in table '%-.64s'"
+	est "Võ'%-.64s' ei eksisteeri tabelis '%-.64s'"
+	fre "L'index '%-.64s' n'existe pas sur la table '%-.64s'"
+	ger "Schlü'%-.64s' existiert in der Tabelle '%-.64s' nicht"
+	hun "A '%-.64s' kulcs nem letezik a '%-.64s' tablaban"
+	ita "La chiave '%-.64s' non esiste nella tabella '%-.64s'"
+	por "Chave '%-.64s' nãexiste na tabela '%-.64s'"
+	rus "ëÞ'%-.64s' Î ÓÝÓ×Å ×ÔÂÉÅ'%-.64s'"
+	serbian "Kljuè%-.64s' ne postoji u tabeli '%-.64s'"
+	spa "Clave '%-.64s' no existe en la tabla '%-.64s'"
+	swe "Nyckel '%-.64s' finns inte in tabell '%-.64s'"
+	ukr "ëÞ'%-.64s' Î ¦ÓÕ ×ÔÂɦ '%-.64s'"
+ER_CHECK_NO_SUCH_TABLE 42000 
+	cze "Nemohu otev-Bøabulku"
+	dan "Kan ikke åe tabellen"
+	nla "Kan tabel niet openen"
+	eng "Can't open table"
+	est "Ei suuda avada tabelit"
+	fre "Impossible d'ouvrir la table"
+	ger "Kann Tabelle nicht öen"
+	hun "Nem tudom megnyitni a tablat"
+	ita "Impossibile aprire la tabella"
+	por "Nãpode abrir a tabela"
+	rus "îÏÍÖÏÏËÙØÔÂÉÕ
+	serbian "Ne mogu da otvorim tabelu"
+	spa "No puedo abrir tabla"
+	swe "Kan inte öa tabellen"
+	ukr "îÍÖ ×ÄÒÔ ÔÂÉÀ
+ER_CHECK_NOT_IMPLEMENTED 42000 
+	cze "Handler tabulky nepodporuje %s"
+	dan "Denne tabeltype understø ikke %s"
+	nla "De 'handler' voor de tabel ondersteund geen %s"
+	eng "The storage engine for the table doesn't support %s"
+	est "Antud tabelitü toeta %s käe"
+	fre "Ce type de table ne supporte pas les %s"
+	ger "Die Speicher-Engine füse Tabelle unterstüein %s"
+	greek "The handler for the table doesn't support %s"
+	hun "A tabla kezeloje (handler) nem tamogatja az %s"
+	ita "Il gestore per la tabella non supporta il %s"
+	jpn "The handler for the table doesn't support %s"
+	kor "The handler for the table doesn't support %s"
+	nor "The handler for the table doesn't support %s"
+	norwegian-ny "The handler for the table doesn't support %s"
+	pol "The handler for the table doesn't support %s"
+	por "O manipulador de tabela nãsuporta %s"
+	rum "The handler for the table doesn't support %s"
+	rus "ïÁÏÞËÔÂÉÙÎ ÐÄÅÖ×Å ÜÏÏ %s"
+	serbian "Handler za ovu tabelu ne dozvoljava 'check' odnosno 'repair' komande"
+	slo "The handler for the table doesn't support %s"
+	spa "El manipulador de la tabla no permite soporte para %s"
+	swe "Tabellhanteraren föenna tabell kan inte gö%s"
+	ukr "÷¦×É ÔÂɦ Î ÐÄÒÍÅ%s"
+ER_CANT_DO_THIS_DURING_AN_TRANSACTION 25000 
+	cze "Proveden-Bíohoto pøu není transakci dovoleno"
+	dan "Du måkke bruge denne kommando i en transaktion"
+	nla "Het is u niet toegestaan dit commando uit te voeren binnen een transactie"
+	eng "You are not allowed to execute this command in a transaction"
+	est "Seda käu ei saa kasutada transaktsiooni sees"
+	fre "Vous n'ês pas autorisé exéte cette commande dans une transaction"
+	ger "Sie düdiesen Befehl nicht in einer Transaktion ausfü
+	hun "Az On szamara nem engedelyezett a parancs vegrehajtasa a tranzakcioban"
+	ita "Non puoi eseguire questo comando in una transazione"
+	por "Nãlhe éermitido executar este comando em uma transaç"
+	rus "÷Î ÒÚÅÅÏ×ÐÌÑØÜÕËÍÎÕ×ÔÁÚËÉ"
+	serbian "Nije Vam dozvoljeno da izvršite ovu komandu u transakciji"
+	spa "No tienes el permiso para ejecutar este comando en una transició+	swe "Du fåinte utfödetta kommando i en transaktion"
+	ukr "÷Î ÄÚÏÅÏ×ËÎ×Ô Ã ËÍÎÕ×ÔÁÚ˦§"
+ER_ERROR_DURING_COMMIT  
+	cze "Chyba %d p-BøMMIT"
+	dan "Modtog fejl %d mens kommandoen COMMIT blev udfø+	nla "Kreeg fout %d tijdens COMMIT"
+	eng "Got error %d during COMMIT"
+	est "Viga %d kä COMMIT tämisel"
+	fre "Erreur %d lors du COMMIT"
+	ger "Fehler %d beim COMMIT"
+	hun "%d hiba a COMMIT vegrehajtasa soran"
+	ita "Rilevato l'errore %d durante il COMMIT"
+	por "Obteve erro %d durante COMMIT"
+	rus "ðÞÎ ÏÉË %d ×ÐÏÅÓ COMMIT"
+	serbian "Greška %d za vreme izvršavanja komande 'COMMIT'"
+	spa "Obtenido error %d durante COMMIT"
+	swe "Fick fel %d vid COMMIT"
+	ukr "ïÉÁÏÐÍÌÕ%d ÐÄÞÓCOMMIT"
+ER_ERROR_DURING_ROLLBACK  
+	cze "Chyba %d p-BøLLBACK"
+	dan "Modtog fejl %d mens kommandoen ROLLBACK blev udfø+	nla "Kreeg fout %d tijdens ROLLBACK"
+	eng "Got error %d during ROLLBACK"
+	est "Viga %d kä ROLLBACK tämisel"
+	fre "Erreur %d lors du ROLLBACK"
+	ger "Fehler %d beim ROLLBACK"
+	hun "%d hiba a ROLLBACK vegrehajtasa soran"
+	ita "Rilevato l'errore %d durante il ROLLBACK"
+	por "Obteve erro %d durante ROLLBACK"
+	rus "ðÞÎ ÏÉË %d ×ÐÏÅÓ ROLLBACK"
+	serbian "Greška %d za vreme izvršavanja komande 'ROLLBACK'"
+	spa "Obtenido error %d durante ROLLBACK"
+	swe "Fick fel %d vid ROLLBACK"
+	ukr "ïÉÁÏÐÍÌÕ%d ÐÄÞÓROLLBACK"
+ER_ERROR_DURING_FLUSH_LOGS  
+	cze "Chyba %d p-BøUSH_LOGS"
+	dan "Modtog fejl %d mens kommandoen FLUSH_LOGS blev udfø+	nla "Kreeg fout %d tijdens FLUSH_LOGS"
+	eng "Got error %d during FLUSH_LOGS"
+	est "Viga %d kä FLUSH_LOGS tämisel"
+	fre "Erreur %d lors du FLUSH_LOGS"
+	ger "Fehler %d bei FLUSH_LOGS"
+	hun "%d hiba a FLUSH_LOGS vegrehajtasa soran"
+	ita "Rilevato l'errore %d durante il FLUSH_LOGS"
+	por "Obteve erro %d durante FLUSH_LOGS"
+	rus "ðÞÎ ÏÉË %d ×ÐÏÅÓ FLUSH_LOGS"
+	serbian "Greška %d za vreme izvršavanja komande 'FLUSH_LOGS'"
+	spa "Obtenido error %d durante FLUSH_LOGS"
+	swe "Fick fel %d vid FLUSH_LOGS"
+	ukr "ïÉÁÏÐÍÌÕ%d ÐÄÞÓFLUSH_LOGS"
+ER_ERROR_DURING_CHECKPOINT  
+	cze "Chyba %d p-BøECKPOINT"
+	dan "Modtog fejl %d mens kommandoen CHECKPOINT blev udfø+	nla "Kreeg fout %d tijdens CHECKPOINT"
+	eng "Got error %d during CHECKPOINT"
+	est "Viga %d kä CHECKPOINT tämisel"
+	fre "Erreur %d lors du CHECKPOINT"
+	ger "Fehler %d bei CHECKPOINT"
+	hun "%d hiba a CHECKPOINT vegrehajtasa soran"
+	ita "Rilevato l'errore %d durante il CHECKPOINT"
+	por "Obteve erro %d durante CHECKPOINT"
+	rus "ðÞÎ ÏÉË %d ×ÐÏÅÓ CHECKPOINT"
+	serbian "Greška %d za vreme izvršavanja komande 'CHECKPOINT'"
+	spa "Obtenido error %d durante CHECKPOINT"
+	swe "Fick fel %d vid CHECKPOINT"
+	ukr "ïÉÁÏÐÍÌÕ%d ÐÄÞÓCHECKPOINT"
+ER_NEW_ABORTING_CONNECTION 08S01 
+	cze "Spojen-Bíld do databá: '%-.64s' u¾ivatel: '%-.32s' stroj: '%-.64s' (%-.64s) bylo pøeno"
+	dan "Afbrørbindelsen %ld til databasen '%-.64s' bruger: '%-.32s' væ: '%-.64s' (%-.64s)"
+	nla "Afgebroken verbinding %ld naar db: '%-.64s' gebruiker: '%-.32s' host: '%-.64s' (%-.64s)"
+	eng "Aborted connection %ld to db: '%-.64s' user: '%-.32s' host: '%-.64s' (%-.64s)"
+	est "Üendus katkestatud %ld andmebaas: '%-.64s' kasutaja: '%-.32s' masin: '%-.64s' (%-.64s)"
+	fre "Connection %ld avortévers la bd: '%-.64s' utilisateur: '%-.32s' hô '%-.64s' (%-.64s)"
+	ger "Abbruch der Verbindung %ld zur Datenbank '%-.64s'. Benutzer: '%-.32s', Host: '%-.64s' (%-.64s)"
+	ita "Interrotta la connessione %ld al db: ''%-.64s' utente: '%-.32s' host: '%-.64s' (%-.64s)"
+	por "Conexã%ld abortada para banco de dados '%-.64s' - usuáo '%-.32s' - 'host' '%-.64s' ('%-.64s')"
+	rus "ð×Î ÓÅÉÅÉ %ld ËÂÚ ÄÎÙ '%-.64s' ÐÌÚ×ÔÌ '%-.32s' ÓÈÓÁ'%-.64s' (%-.64s)"
+	serbian "Prekinuta konekcija broj %ld ka bazi: '%-.64s' korisnik je bio: '%-.32s' a host: '%-.64s' (%-.64s)"
+	spa "Abortada conexióld para db: '%-.64s' usuario: '%-.32s' servidor: '%-.64s' (%-.64s)"
+	swe "Avbröäen förå%ld till db '%-.64s', anväare '%-.32s', host '%-.64s' (%-.64s)"
+	ukr "ðÒÁÏÚ¤ÄÁÎ %ld Ä ÂÚ ÄÎÉ: '%-.64s' ËÒÓÕÁ: '%-.32s' ÈÓ: '%-.64s' (%-.64s)"
+ER_DUMP_NOT_IMPLEMENTED  
+	cze "Handler tabulky nepodporuje bin-Báíump"
+	dan "Denne tabeltype unserstø ikke binæ tabeldump"
+	nla "De 'handler' voor de tabel ondersteund geen binaire tabel dump"
+	eng "The storage engine for the table does not support binary table dump"
+	fre "Ce type de table ne supporte pas les copies binaires"
+	ger "Die Speicher-Engine fü Tabelle unterstüeinen binän Tabellen-Dump"
+	ita "Il gestore per la tabella non supporta il dump binario"
+	jpn "The handler for the table does not support binary table dump"
+	por "O manipulador de tabela nãsuporta 'dump' bináo de tabela"
+	rum "The handler for the table does not support binary table dump"
+	rus "ïÁÏÞËÜÏ ÔÂÉÙÎ ÐÄÅÖ×Å ÄÏÞÏÏÓÈÁÅÉ ÏÒÚ ÔÂÉÙ(dump)"
+	serbian "Handler tabele ne podržava binarni dump tabele"
+	spa "El manipulador de tabla no soporta dump para tabla binaria"
+	swe "Tabellhanteraren klarar inte en binäkopiering av tabellen"
+	ukr "ã ÔÐÔÂɦ Î ÐÄÒͤ ÂÎÒÕÐÒÄÞ ÔÂɦ"
+ER_FLUSH_MASTER_BINLOG_CLOSED  
+	eng "Binlog closed, cannot RESET MASTER"
+	ger "Binlog geschlossen. Kann RESET MASTER nicht ausfü
+	por "Binlog fechado. Nãpode fazer RESET MASTER"
+	rus "äÉÎÊÖÒÁ ÏÎ×ÅÉ ÚËÙ, Î×ÚÏÎ ×ÐÌÉØRESET MASTER"
+	serbian "Binarni log file zatvoren, ne mogu da izvršim komandu 'RESET MASTER'"
+	ukr "ò¦ËÃÊÉ ÌÇÚËÉÏ Î ÍÖ ×ËÎÔ RESET MASTER"
+ER_INDEX_REBUILD  
+	cze "P-Bøová indexu dumpnutéabulky '%-.64s' nebylo úné+	dan "Kunne ikke genopbygge indekset for den dumpede tabel '%-.64s'"
+	nla "Gefaald tijdens heropbouw index van gedumpte tabel '%-.64s'"
+	eng "Failed rebuilding the index of  dumped table '%-.64s'"
+	fre "La reconstruction de l'index de la table copié'%-.64s' a éoué+	ger "Neuerstellung des Index der Dump-Tabelle '%-.64s' fehlgeschlagen"
+	greek "Failed rebuilding the index of dumped table '%-.64s'"
+	hun "Failed rebuilding the index of dumped table '%-.64s'"
+	ita "Fallita la ricostruzione dell'indice della tabella copiata '%-.64s'"
+	por "Falhou na reconstruç do íice da tabela 'dumped' '%-.64s'"
+	rus "ïÂÁÐÒÓÒÊÉÉÄËÁÓÈÁÅÎÊÔÂÉÙ'%-.64s'"
+	serbian "Izgradnja indeksa dump-ovane tabele '%-.64s' nije uspela"
+	spa "Falla reconstruyendo el indice de la tabla dumped '%-.64s'"
+	ukr "îÄÌ ×ÄÏÌÎѦÎÅÓ ÐÒÄΧ ÔÂɦ '%-.64s'"
+ER_MASTER  
+	cze "Chyba masteru: '%-.64s'"
+	dan "Fejl fra master: '%-.64s'"
+	nla "Fout van master: '%-.64s'"
+	eng "Error from master: '%-.64s'"
+	fre "Erreur reç du maîe: '%-.64s'"
+	ger "Fehler vom Master: '%-.64s'"
+	ita "Errore dal master: '%-.64s"
+	por "Erro no 'master' '%-.64s'"
+	rus "ïÂÁÏ ÇÌ×ÏÏÓÒÅÁ '%-.64s'"
+	serbian "Greška iz glavnog servera '%-.64s' u klasteru"
+	spa "Error del master: '%-.64s'"
+	swe "Fick en master: '%-.64s'"
+	ukr "ðÌÁ×ÄÇÌ×ÏÏ '%-.64s'"
+ER_MASTER_NET_READ 08S01 
+	cze "S-Bíváhyba pøení masteru"
+	dan "Netvæsfejl ved læing fra master"
+	nla "Net fout tijdens lezen van master"
+	eng "Net error reading from master"
+	fre "Erreur de lecture réau reç du maîe"
+	ger "Netzfehler beim Lesen vom Master"
+	ita "Errore di rete durante la ricezione dal master"
+	por "Erro de rede lendo do 'master'"
+	rus "÷ÉÌ ÏÉË ÞÅÉ ×ÐÏÅÓ ËÍÕÉÁÉ ÓÇÌ×Ù ÓÒÅÏ"
+	serbian "Greška u primanju mrežnih paketa sa glavnog servera u klasteru"
+	spa "Error de red leyendo del master"
+	swe "Fick näerksfel vid läing fråmaster"
+	ukr "íÅÅÁÐÍÌÁÞÔÎÑ×ÄÇÌ×ÏÏ
+ER_MASTER_NET_WRITE 08S01 
+	cze "S-Bíváhyba pøpisu na master"
+	dan "Netvæsfejl ved skrivning til master"
+	nla "Net fout tijdens schrijven naar master"
+	eng "Net error writing to master"
+	fre "Erreur d'éiture réau reç du maîe"
+	ger "Netzfehler beim Schreiben zum Master"
+	ita "Errore di rete durante l'invio al master"
+	por "Erro de rede gravando no 'master'"
+	rus "÷ÉÌ ÏÉË ÚÐÓ ×ÐÏÅÓ ËÍÕÉÁÉ ÓÇÌ×Ù ÓÒÅÏ"
+	serbian "Greška u slanju mrežnih paketa na glavni server u klasteru"
+	spa "Error de red escribiendo para el master"
+	swe "Fick näerksfel vid skrivning till master"
+	ukr "íÅÅÁÐÍÌÁÚÐÓ Ä ÇÌ×ÏÏ
+ER_FT_MATCHING_KEY_NOT_FOUND  
+	cze "-B®áýpec nemáytvoøulltextovýx"
+	dan "Kan ikke finde en FULLTEXT nøsom svarer til kolonne listen"
+	nla "Kan geen FULLTEXT index vinden passend bij de kolom lijst"
+	eng "Can't find FULLTEXT index matching the column list"
+	est "Ei suutnud leida FULLTEXT indeksit, mis kattuks kasutatud tulpadega"
+	fre "Impossible de trouver un index FULLTEXT correspondant àette liste de colonnes"
+	ger "Kann keinen FULLTEXT-Index finden, der der Feldliste entspricht"
+	ita "Impossibile trovare un indice FULLTEXT che corrisponda all'elenco delle colonne"
+	por "Nãpode encontrar um íice para o texto todo que combine com a lista de colunas"
+	rus "îÏÍÖÏÏÙËÔ ÐÌÏÅÓÏÙ (FULLTEXT) ÉÄË, ÓÏ×ÔÔÕÝÊÓÉË ÓÏÂÏ"
+	serbian "Ne mogu da pronað'FULLTEXT' indeks koli odgovara listi kolona"
+	spa "No puedo encontrar íice FULLTEXT correspondiendo a la lista de columnas"
+	swe "Hittar inte ett FULLTEXT-index i kolumnlistan"
+	ukr "îÍÖ ÚÁÔ FULLTEXT ¦ÎÅÓ Ý ×ÄϦĤ ÐÒÌË ÓϦ×
+ER_LOCK_OR_ACTIVE_TRANSACTION  
+	cze "Nemohu prov-Bé zadanýaz, proto¾e existujíktivníamèéabulky nebo aktivníransakce"
+	dan "Kan ikke udføen givne kommando fordi der findes aktive, låe tabeller eller fordi der udføen transaktion"
+	nla "Kan het gegeven commando niet uitvoeren, want u heeft actieve gelockte tabellen of een actieve transactie"
+	eng "Can't execute the given command because you have active locked tables or an active transaction"
+	est "Ei suuda täa antud käu kuna on aktiivseid lukke võäasolev transaktsioon"
+	fre "Impossible d'exéter la commande car vous avez des tables verrouillé ou une transaction active"
+	ger "Kann den angegebenen Befehl wegen einer aktiven Tabellensperre oder einer aktiven Transaktion nicht ausfü
+	ita "Impossibile eseguire il comando richiesto: tabelle sotto lock o transazione in atto"
+	por "Nãpode executar o comando dado porque vocêem tabelas ativas travadas ou uma transaç ativa"
+	rus "îÏÍÖÏ×ÐÌÉØÕÁÁÎÀËÍÎÕ ÐÓÏØÕÕ×ÓÐÉÕÓ×À ÁÔ×ÏÚÂÏÉÏÁÎÅÔÂÉÁÉÉÏËÙÁ ÔÁÚËÉ"
+	serbian "Ne mogu da izvršim datu komandu zbog toga što su tabele zakljuèe ili je transakcija u toku"
+	spa "No puedo ejecutar el comando dado porque tienes tablas bloqueadas o una transicióctiva"
+	swe "Kan inte utfökommandot emedan du har en lå tabell eller an aktiv transaktion"
+	ukr "îÍÖ ×ËÎÔ ÐÄÎ ËÍÎÕÔÍ, Ý ÔÂÉÑÚÂÏÏÁÁÁÏ×ËΤÔÓ ÔÁÚ˦Ñ
+ER_UNKNOWN_SYSTEM_VARIABLE  
+	cze "Nezn-Bá systéváromìá%-.64s'"
+	dan "Ukendt systemvariabel '%-.64s'"
+	nla "Onbekende systeem variabele '%-.64s'"
+	eng "Unknown system variable '%-.64s'"
+	est "Tundmatu süne muutuja '%-.64s'"
+	fre "Variable systè '%-.64s' inconnue"
+	ger "Unbekannte Systemvariable '%-.64s'"
+	ita "Variabile di sistema '%-.64s' sconosciuta"
+	por "Variál de sistema '%-.64s' desconhecida"
+	rus "îÚÅÔÁ ÓÓÅÎÑÐÒÍÎÁ '%-.64s'"
+	serbian "Nepoznata sistemska promenljiva '%-.64s'"
+	spa "Desconocida variable de sistema '%-.64s'"
+	swe "Okä systemvariabel: '%-.64s'"
+	ukr "î¦ÄÍ ÓÓÅÎ Ú¦ÎÁ'%-.64s'"
+ER_CRASHED_ON_USAGE  
+	cze "Tabulka '%-.64s' je ozna-Bèa jako poru¹ená mì by býavena"
+	dan "Tabellen '%-.64s' er markeret med fejl og bøpareres"
+	nla "Tabel '%-.64s' staat als gecrashed gemarkeerd en dient te worden gerepareerd"
+	eng "Table '%-.64s' is marked as crashed and should be repaired"
+	est "Tabel '%-.64s' on mäitud vigaseks ja tuleb parandada"
+	fre "La table '%-.64s' est marqué'crashed' et devrait êe réré
+	ger "Tabelle '%-.64s' ist als defekt markiert und sollte repariert werden"
+	ita "La tabella '%-.64s' e` segnalata come corrotta e deve essere riparata"
+	por "Tabela '%-.64s' estáarcada como danificada e deve ser reparada"
+	rus "ôÉÁ'%-.64s' ÐÍÞÎ ËËÉÐÒÅÎÑÉÄÌÎ ÐÏÔ ÐÏÅË ÉÒÍÎ"
+	serbian "Tabela '%-.64s' je markirana kao ošteæa i trebala bi biti popravljena"
+	spa "Tabla '%-.64s' estáarcada como crashed y debe ser reparada"
+	swe "Tabell '%-.64s' ätrasig och böepareras med REPAIR TABLE"
+	ukr "ôÉÀ'%-.64s' ÍÒÏÁÏÑ ÚÐÏÁÕÔ §§ ÐÔ¦ÂÏ×ÄÏÉÉ
+ER_CRASHED_ON_REPAIR  
+	cze "Tabulka '%-.64s' je ozna-Bèa jako poru¹ená posledníautomatická oprava se nezdaø
+	dan "Tabellen '%-.64s' er markeret med fejl og sidste (automatiske?) REPAIR fejlede"
+	nla "Tabel '%-.64s' staat als gecrashed gemarkeerd en de laatste (automatische?) reparatie poging mislukte"
+	eng "Table '%-.64s' is marked as crashed and last (automatic?) repair failed"
+	est "Tabel '%-.64s' on mäitud vigaseks ja viimane (automaatne?) parandus ebaõstus"
+	fre "La table '%-.64s' est marqué'crashed' et le dernier 'repair' a éoué+	ger "Tabelle '%-.64s' ist als defekt markiert und der letzte (automatische?) Reparaturversuch schlug fehl"
+	ita "La tabella '%-.64s' e` segnalata come corrotta e l'ultima ricostruzione (automatica?) e` fallita"
+	por "Tabela '%-.64s' estáarcada como danificada e a úa reparaç (automáca?) falhou"
+	rus "ôÉÁ'%-.64s' ÐÍÞÎ ËËÉÐÒÅÎÑÉÐÓÅÎÊ(ÁÔÍÔÞÓÉ?) ÒÍÎ Î ÂÌÕÐÛÙ"
+	serbian "Tabela '%-.64s' je markirana kao ošteæa, a zadnja (automatska?) popravka je bila neuspela"
+	spa "Tabla '%-.64s' estáarcada como crashed y la úa reparacióautomactica?) falló	swe "Tabell '%-.64s' ätrasig och senast (automatiska?) reparation misslyckades"
+	ukr "ôÉÀ'%-.64s' ÍÒÏÁÏÑ ÚÐÏÁÕÔ ÏÔΤ (ÁÔÍÔÞÅ) ×ÄÏÌÎÑÎ ×ÁÏÑ
+ER_WARNING_NOT_COMPLETE_ROLLBACK  
+	dan "Advarsel: Visse data i tabeller der ikke understø transaktioner kunne ikke tilbagestilles"
+	nla "Waarschuwing: Roll back mislukt voor sommige buiten transacties gewijzigde tabellen"
+	eng "Some non-transactional changed tables couldn't be rolled back"
+	est "Hoiatus: mõid transaktsioone mittetoetavaid tabeleid ei suudetud tagasi kerida"
+	fre "Attention: certaines tables ne supportant pas les transactions ont é changé et elles ne pourront pas êe restitué"
+	ger "Äderungen an einigen nicht transaktionalen Tabellen konnten nicht zurüollt werden"
+	ita "Attenzione: Alcune delle modifiche alle tabelle non transazionali non possono essere ripristinate (roll back impossibile)"
+	por "Aviso: Algumas tabelas nãtransacionais alteradas nãpuderam ser reconstituís (rolled back)"
+	rus "÷ÁÉ: Ð ÎËÔÒÍÉÍÎÎÙ ÎÔÁÚËÉÎÙ ÔÂÉÁ Î×ÚÏÎ ÂÄÔÐÏÚÅÔ ÏËÔÔÁÚËÉ"
+	serbian "Upozorenje: Neke izmenjene tabele ne podržavaju komandu 'ROLLBACK'"
+	spa "Aviso:  Algunas tablas no transancionales no pueden tener rolled back"
+	swe "Warning:  Nåa icke transaktionella tabeller kunde inte årstäas vid ROLLBACK"
+	ukr "úÒÖÎÑ äË ÎÔÁÚ˦ʦ ڦΠÔÂÉØÎ ÍÖÁÂÄ Ð×ÒÕÉ
+ER_TRANS_CACHE_FULL  
+	dan "Fler-udtryks transaktion kræde mere plads en 'max_binlog_cache_size' bytes. Forhørdien af denne variabel og prøen"
+	nla "Multi-statement transactie vereist meer dan 'max_binlog_cache_size' bytes opslag. Verhoog deze mysqld variabele en probeer opnieuw"
+	eng "Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage; increase this mysqld variable and try again"
+	est "Mitme lausendiga transaktsioon nõs rohkem ruumi kui lubatud 'max_binlog_cache_size' muutujaga. Suurenda muutuja vätust ja proovi uuesti"
+	fre "Cette transaction àommandes multiples néssite plus de 'max_binlog_cache_size' octets de stockage, augmentez cette variable de mysqld et résayez"
+	ger "Transaktionen, die aus mehreren Befehlen bestehen, benöten mehr als 'max_binlog_cache_size' Bytes an Speicher. Btte vergrörn Sie diese Server-Variable versuchen Sie es noch einmal"
+	ita "La transazione a comandi multipli (multi-statement) ha richiesto piu` di 'max_binlog_cache_size' bytes di disco: aumentare questa variabile di mysqld e riprovare"
+	por "Transaçs multi-declaradas (multi-statement transactions) requeriram mais do que o valor limite (max_binlog_cache_size) de bytes para armazenagem. Aumente o valor desta variál do mysqld e tente novamente"
+	rus "ôÚËÉ, ×ÌÞÀÅ ÂÌÛÅËÌÞÓ× ËÍÎ, ÐÔÅÏÁÏØÂÌÅÞÍ'max_binlog_cache_size' ÂÊ. õÉØÅÜÕÐÒÍÎÕ ÓÒÅÁmysqld ÉÐÐÏÕÔ ÅÅÒÚ
+	spa "Multipla transicióecesita mas que 'max_binlog_cache_size' bytes de almacenamiento. Aumente esta variable mysqld y tente de nuevo"
+	swe "Transaktionen kräe mera ä'max_binlog_cache_size' minne. Öa denna mysqld-variabel och fök påytt"
+	ukr "ôÚ˦ÑÚÂÇÔÍ ×ÒÚÍ ×ÍǤ ÂÌÛ ÎÖ'max_binlog_cache_size' Âʦ×ÄÑÚŦÇÎÑ úÛÅà ڦÎÕmysqld Ô ÓÒÂÊÅÚÏÕ
+ER_SLAVE_MUST_STOP  
+	dan "Denne handling kunne ikke udfømed køe slave, brug føkommandoen STOP SLAVE"
+	nla "Deze operatie kan niet worden uitgevoerd met een actieve slave, doe eerst STOP SLAVE"
+	eng "This operation cannot be performed with a running slave; run STOP SLAVE first"
+	fre "Cette opétion ne peut êe réiséavec un esclave actif, faites STOP SLAVE d'abord"
+	ger "Diese Operation kann bei einem aktiven Slave nicht durchgefüerden. Bitte zuerst STOP SLAVE ausfü
+	ita "Questa operazione non puo' essere eseguita con un database 'slave' che gira, lanciare prima STOP SLAVE"
+	por "Esta operaç nãpode ser realizada com um 'slave' em execuç. Execute STOP SLAVE primeiro"
+	rus "üÅÁÉ Î×ÚÏÎ ×ÐÌÉØÐÉÒÂÔÀÅ ÐÔË ÐÄÉÅÎÇ ÓÒÅÁ óÁÁ×ÐÌÉÅSTOP SLAVE"
+	serbian "Ova operacija ne može biti izvršena dok je aktivan podreð server. Zadajte prvo komandu 'STOP SLAVE' da zaustavite podreð server."
+	spa "Esta operacióo puede ser hecha con el esclavo funcionando, primero use STOP SLAVE"
+	swe "Denna operation kan inte gö under replikering; GöTOP SLAVE fö"
+	ukr "ïÒÃÑÎ ÍÖ ÂÔ ×ËÎÎ ÚÚÐÝÎÍÐÄÅÌÍ ÓÏÁË ×ËÎÊÅSTOP SLAVE"
+ER_SLAVE_NOT_RUNNING  
+	dan "Denne handling krær en køe slave. Konfigurer en slave og brug kommandoen START SLAVE"
+	nla "Deze operatie vereist een actieve slave, configureer slave en doe dan START SLAVE"
+	eng "This operation requires a running slave; configure slave and do START SLAVE"
+	fre "Cette opétion néssite un esclave actif, configurez les esclaves et faites START SLAVE"
+	ger "Diese Operation benöt einen aktiven Slave. Bitte Slave konfigurieren und mittels START SLAVE aktivieren"
+	ita "Questa operaione richiede un database 'slave', configurarlo ed eseguire START SLAVE"
+	por "Esta operaç requer um 'slave' em execuç. Configure  o 'slave' e execute START SLAVE"
+	rus "ä ÜÏ ÏÅÁÉ ÔÅÕÔÑÒÂÔÀÉ ÐÄÉÅÎÊÓÒÅ. óÁÁ×ÐÌÉÅSTART SLAVE"
+	serbian "Ova operacija zahteva da je aktivan podreð server. Konfigurišite prvo podreð server i onda izvršite komandu 'START SLAVE'"
+	spa "Esta operacióecesita el esclavo funcionando, configure esclavo y haga el START SLAVE"
+	swe "Denna operation kan endast gö under replikering; Konfigurera slaven och göTART SLAVE"
+	ukr "ïÒÃÑ×ÍǤ ÚÐÝÎÇ ÐÄÅÌÇ, ÚÏÆÇÒÊÅÐÄÅÌÇ Ô ×ËÎÊÅSTART SLAVE"
+ER_BAD_SLAVE  
+	dan "Denne server er ikke konfigureret som slave. Ret in config-filen eller brug kommandoen CHANGE MASTER TO"
+	nla "De server is niet geconfigureerd als slave, fix in configuratie bestand of met CHANGE MASTER TO"
+	eng "The server is not configured as slave; fix in config file or with CHANGE MASTER TO"
+	fre "Le server n'est pas configuréomme un esclave, changez le fichier de configuration ou utilisez CHANGE MASTER TO"
+	ger "Der Server ist nicht als Slave konfiguriert. Bitte in der Konfigurationsdatei oder mittels CHANGE MASTER TO beheben"
+	ita "Il server non e' configurato come 'slave', correggere il file di configurazione cambiando CHANGE MASTER TO"
+	por "O servidor nãestáonfigurado como 'slave'. Acerte o arquivo de configuraç ou use CHANGE MASTER TO"
+	rus "üÅ×ÒÎ ÎÓÒÅ ËËÐÄÉÅÎÊ ÷ÉÅÉÐÁÌÎÑ×ËÎÉÕÁÉÎÏ ÆÊÅÉÉÓÐÍÝÀCHANGE MASTER TO"
+	serbian "Server nije konfigurisan kao podreð server, ispravite konfiguracioni file ili na njemu izvršite komandu 'CHANGE MASTER TO'"
+	spa "El servidor no estáonfigurado como esclavo, edite el archivo config file o con CHANGE MASTER TO"
+	swe "Servern äinte konfigurerade som en replikationsslav. Ädra konfigurationsfilen eller göHANGE MASTER TO"
+	ukr "óÅ Î ÚÏÆÇÒ×Î Ñ ÐÄÅÌÊ ×ÐÁÔ Ã ÕÆʦ ËΦÇÒç ÁÏÚCHANGE MASTER TO"
+ER_MASTER_INFO  
+	eng "Could not initialize master info structure; more error messages can be found in the MySQL error log"
+	fre "Impossible d'initialiser les structures d'information de maîe, vous trouverez des messages d'erreur suppléntaires dans le journal des erreurs de MySQL"
+	ger "Konnte Master-Info-Struktur nicht initialisieren. Weitere Fehlermeldungen kön im MySQL-Error-Log eingesehen werden"
+	serbian "Nisam mogao da inicijalizujem informacionu strukturu glavnog servera, proverite da li imam privilegije potrebne za pristup file-u 'master.info'"
+	swe "Kunde inte initialisera replikationsstrukturerna. See MySQL fel fil föera information"
+ER_SLAVE_THREAD  
+	dan "Kunne ikke danne en slave-trå check systemressourcerne"
+	nla "Kon slave thread niet aanmaken, controleer systeem resources"
+	eng "Could not create slave thread; check system resources"
+	fre "Impossible de cré une tâe esclave, véfiez les ressources systè"
+	ger "Konnte Slave-Thread nicht starten. Bitte System-Ressourcen üü+	ita "Impossibile creare il thread 'slave', controllare le risorse di sistema"
+	por "Nãconseguiu criar 'thread' de 'slave'. Verifique os recursos do sistema"
+	rus "îÏÍÖÏÓÚÁØÐÔËÐÄÉÅÎÇ ÓÒÅÁ ðÅØÅÓÓÅÎÅÒÓÒÙ
+	serbian "Nisam mogao da startujem thread za podreð server, proverite sistemske resurse"
+	spa "No puedo crear el thread esclavo, verifique recursos del sistema"
+	swe "Kunde inte starta en tråföeplikering"
+	ukr "îÍÖ Ó×ÒÔ ÐÄÅÌ ÇÌÕ ÐÒ×ÒÅÓÓÅÎ ÒÓÒÉ
+ER_TOO_MANY_USER_CONNECTIONS 42000 
+	dan "Brugeren %-.64s har allerede mere end 'max_user_connections' aktive forbindelser"
+	nla "Gebruiker %-.64s heeft reeds meer dan 'max_user_connections' actieve verbindingen"
+	eng "User %-.64s already has more than 'max_user_connections' active connections"
+	est "Kasutajal %-.64s on juba rohkem üsi kui lubatud 'max_user_connections' muutujaga"
+	fre "L'utilisateur %-.64s possè dé plus de 'max_user_connections' connections actives"
+	ger "Benutzer '%-.64s' hat mehr als 'max_user_connections' aktive Verbindungen"
+	ita "L'utente %-.64s ha gia' piu' di 'max_user_connections' connessioni attive"
+	por "Usuáo '%-.64s' jáossui mais que o valor mámo de conexõ(max_user_connections) ativas"
+	rus "õÌÚ×ÔÌ %-.64s ÕÅÂÌÛ ÞÍ'max_user_connections' ÁÔ×Ù ÓÅÉÅÉ"
+	serbian "Korisnik %-.64s veæma više aktivnih konekcija nego što je to odreð 'max_user_connections' promenljivom"
+	spa "Usario %-.64s ya tiene mas que 'max_user_connections' conexiones activas"
+	swe "Anväare '%-.64s' har redan 'max_user_connections' aktiva inloggningar"
+	ukr "ëÉÔ×Þ%-.64s ×Åͤ ÂÌÛ ÎÖ'max_user_connections' ÁÔ×É Ú¤ÄÁØ
+ER_SET_CONSTANTS_ONLY  
+	dan "Du måun bruge konstantudtryk med SET"
+	nla "U mag alleen constante expressies gebruiken bij SET"
+	eng "You may only use constant expressions with SET"
+	est "Ainult konstantsed suurused on lubatud SET klauslis"
+	fre "Seules les expressions constantes sont autorisé avec SET"
+	ger "Bei SET dünur konstante Ausdrüerwendet werden"
+	ita "Si possono usare solo espressioni costanti con SET"
+	por "Vocêode usar apenas expressõconstantes com SET"
+	rus "÷ÏÅÅÉÐÌÚ×Ô ×SET ÔÌË ËÎÔÎÎÅ×ÒÖÎÑ
+	serbian "Možete upotrebiti samo konstantan iskaz sa komandom 'SET'"
+	spa "Tu solo debes usar expresiones constantes con SET"
+	swe "Man kan endast anväa konstantuttryck med SET"
+	ukr "íÎ ×ËÒÓÏÕÁÉÌÛ ×ÒÚ Ú ÓÁÉÉÕSET"
+ER_LOCK_WAIT_TIMEOUT  
+	dan "Lock wait timeout overskredet"
+	nla "Lock wacht tijd overschreden"
+	eng "Lock wait timeout exceeded; try restarting transaction"
+	est "Kontrollaeg üud luku jäl ootamisel; Proovi transaktsiooni otsast alata"
+	fre "Timeout sur l'obtention du verrou"
+	ger "Beim Warten auf eine Sperre wurde die zuläige Wartezeit ühritten. Bitte versuchen Sie, die Transaktion neu zu starten"
+	ita "E' scaduto il timeout per l'attesa del lock"
+	por "Tempo de espera (timeout) de travamento excedido. Tente reiniciar a transaç."
+	rus "ôÁÔÏÉÁÉ ÂÏÉÏË ÉÔË ÐÐÏÕÔ ÐÒÚÐÓÉØÔÁÚËÉ"
+	serbian "Vremenski limit za zakljuèanje tabele je istekao; Probajte da ponovo startujete transakciju"
+	spa "Tiempo de bloqueo de espera excedido"
+	swe "Fick inte ett låi tid ; Fök att starta om transaktionen"
+	ukr "úÍÕϦË×ÎÑÂÏÕÁÎ ×ÞÒÁÏ
+ER_LOCK_TABLE_FULL  
+	dan "Det totale antal lå overstiger støsen påå-tabellen"
+	nla "Het totale aantal locks overschrijdt de lock tabel grootte"
+	eng "The total number of locks exceeds the lock table size"
+	est "Lukkude koguarv ü lukutabeli suuruse"
+	fre "Le nombre total de verrou désse la taille de la table des verrous"
+	ger "Die Gesamtzahl der Sperren ühreitet die Gröder Sperrtabelle"
+	ita "Il numero totale di lock e' maggiore della grandezza della tabella di lock"
+	por "O nú total de travamentos excede o tamanho da tabela de travamentos"
+	rus "ïÅ ËÌÞÓ× ÂÏÉÏÏ ÐÅÙÉÏÒÚÅÙÔÂÉÙÂÏÉÏÏ"
+	serbian "Broj totalnih zakljuèanja tabele premašuje velièu tabele zakljuèanja"
+	spa "El nú total de bloqueos excede el tamañe bloqueo de la tabla"
+	swe "Antal låöskrider antalet reserverade lå
+	ukr "úØÁËÌËÓØÂÏÕÁØÐÒ×ÝÌ ÒÚ¦ÒÂÏÕÁØÄÑÔÂɦ"
+ER_READ_ONLY_TRANSACTION 25000 
+	dan "Update låkan ikke opnåunder en READ UNCOMMITTED transaktion"
+	nla "Update locks kunnen niet worden verkregen tijdens een READ UNCOMMITTED transactie"
+	eng "Update locks cannot be acquired during a READ UNCOMMITTED transaction"
+	est "Uuenduslukke ei saa kasutada READ UNCOMMITTED transaktsiooni käus"
+	fre "Un verrou en update ne peut êe acquit pendant une transaction READ UNCOMMITTED"
+	ger "Wäend einer READ-UNCOMMITTED-Transaktion kön keine UPDATE-Sperren angefordert werden"
+	ita "I lock di aggiornamento non possono essere acquisiti durante una transazione 'READ UNCOMMITTED'"
+	por "Travamentos de atualizaç nãpodem ser obtidos durante uma transaç de tipo READ UNCOMMITTED"
+	rus "âËÒ×ÉÏÎ×ÅÉ ÎÌÚ ÐÌÞÔ ×ÐÏÅÓ ÞÅÉ Î ÐÉÑÏ (×ÒÖÍ READ UNCOMMITTED) ÔÁÚËÉ"
+	serbian "Zakljuèanja izmena ne mogu biti realizovana sve dok traje 'READ UNCOMMITTED' transakcija"
+	spa "Bloqueos de actualizacióo pueden ser adqueridos durante una transicióEAD UNCOMMITTED"
+	swe "Updateringslåkan inte gö näman anväer READ UNCOMMITTED"
+	ukr "ï×Ô ÂÏÕÁÎ Î ÍÖÉÏÎ ÐÏѦ ÔÁÚ˦§ READ UNCOMMITTED"
+ER_DROP_DB_WITH_READ_LOCK  
+	dan "DROP DATABASE er ikke tilladt mens en tråholder pålobalt read lock"
+	nla "DROP DATABASE niet toegestaan terwijl thread een globale 'read lock' bezit"
+	eng "DROP DATABASE not allowed while thread is holding global read lock"
+	est "DROP DATABASE ei ole lubatud kui lõomab globaalset READ lukku"
+	fre "DROP DATABASE n'est pas autorisépendant qu'une tâe possè un verrou global en lecture"
+	ger "DROP DATABASE ist nicht erlaubt, solange der Thread eine globale Lesesperre hä"
+	ita "DROP DATABASE non e' permesso mentre il thread ha un lock globale di lettura"
+	por "DROP DATABASE nãpermitido enquanto uma 'thread' estáantendo um travamento global de leitura"
+	rus "îÄÐÓÁÔÑDROP DATABASE, ÐË ÐÔËÄÒÉ ÇÏÁØÕ ÂÏÉÏË ÞÅÉ"
+	serbian "Komanda 'DROP DATABASE' nije dozvoljena dok thread globalno zakljuèa èanje podataka"
+	spa "DROP DATABASE no permitido mientras un thread estájerciendo un bloqueo de lectura global"
+	swe "DROP DATABASE äinte tillåt näman har ett globalt läå
+	ukr "DROP DATABASE Î ÄÚÏÅÏÄË ÇÌÁÐÒÂפ ÐÄÚÇÌÎÍÂÏÕÁÎÍÞÔÎÑ
+ER_CREATE_DB_WITH_READ_LOCK  
+	dan "CREATE DATABASE er ikke tilladt mens en tråholder pålobalt read lock"
+	nla "CREATE DATABASE niet toegestaan terwijl thread een globale 'read lock' bezit"
+	eng "CREATE DATABASE not allowed while thread is holding global read lock"
+	est "CREATE DATABASE ei ole lubatud kui lõomab globaalset READ lukku"
+	fre "CREATE DATABASE n'est pas autorisépendant qu'une tâe possè un verrou global en lecture"
+	ger "CREATE DATABASE ist nicht erlaubt, solange der Thread eine globale Lesesperre hä"
+	ita "CREATE DATABASE non e' permesso mentre il thread ha un lock globale di lettura"
+	por "CREATE DATABASE nãpermitido enquanto uma 'thread' estáantendo um travamento global de leitura"
+	rus "îÄÐÓÁÔÑCREATE DATABASE, ÐË ÐÔËÄÒÉ ÇÏÁØÕ ÂÏÉÏË ÞÅÉ"
+	serbian "Komanda 'CREATE DATABASE' nije dozvoljena dok thread globalno zakljuèa èanje podataka"
+	spa "CREATE DATABASE no permitido mientras un thread estájerciendo un bloqueo de lectura global"
+	swe "CREATE DATABASE äinte tillåt näman har ett globalt läå
+	ukr "CREATE DATABASE Î ÄÚÏÅÏÄË ÇÌÁÐÒÂפ ÐÄÚÇÌÎÍÂÏÕÁÎÍÞÔÎÑ
+ER_WRONG_ARGUMENTS  
+	nla "Foutieve parameters voor %s"
+	eng "Incorrect arguments to %s"
+	est "Vigased parameetrid %s-le"
+	fre "Mauvais arguments às"
+	ger "Falsche Argumente fü
+	ita "Argomenti errati a %s"
+	por "Argumentos errados para %s"
+	rus "îÅÎÅÐÒÍÔÙÄÑ%s"
+	serbian "Pogrešni argumenti prosleð na %s"
+	spa "Argumentos errados para %s"
+	swe "Felaktiga argument till %s"
+	ukr "èÎÊÁÇÍÎ ÄÑ%s"
+ER_NO_PERMISSION_TO_CREATE_USER 42000 
+	nla "'%-.32s'@'%-.64s' mag geen nieuwe gebruikers creeren"
+	eng "'%-.32s'@'%-.64s' is not allowed to create new users"
+	est "Kasutajal '%-.32s'@'%-.64s' ei ole lubatud luua uusi kasutajaid"
+	fre "'%-.32s'@'%-.64s' n'est pas autorisé cré de nouveaux utilisateurs"
+	ger "'%-.32s'@'%-.64s' ist nicht berechtigt, neue Benutzer hinzuzufü+	ita "A '%-.32s'@'%-.64s' non e' permesso creare nuovi utenti"
+	por "Nãéermitido a '%-.32s'@'%-.64s' criar novos usuáos"
+	rus "'%-.32s'@'%-.64s' Î ÒÚÅÁÔÑÓÚÁÁØÎ×ÈÐÌÚ×ÔÌÊ
+	serbian "Korisniku '%-.32s'@'%-.64s' nije dozvoljeno da kreira nove korisnike"
+	spa "'%- 32s` `%- 64s` no es permitido para crear nuevos usuarios"
+	swe "'%-.32s'@'%-.64s' har inte räighet att skapa nya anväare"
+	ukr "ëÉÔ×Þ '%-.32s'@'%-.64s' Î ÄÚÏÅÏÓ×Ò×Ô Î×ÈËÒÓÕÁ¦×
+ER_UNION_TABLES_IN_DIFFERENT_DIR  
+	nla "Incorrecte tabel definitie; alle MERGE tabellen moeten tot dezelfde database behoren"
+	eng "Incorrect table definition; all MERGE tables must be in the same database"
+	est "Vigane tabelimäatlus; kõMERGE tabeli liikmed peavad asuma samas andmebaasis"
+	fre "Dénition de table incorrecte; toutes les tables MERGE doivent êe dans la mê base de donné
+	ger "Falsche Tabellendefinition. Alle MERGE-Tabellen müsich in derselben Datenbank befinden"
+	ita "Definizione della tabella errata; tutte le tabelle di tipo MERGE devono essere nello stesso database"
+	por "Definiç incorreta da tabela. Todas as tabelas contidas na junç devem estar no mesmo banco de dados."
+	rus "îÅÎÅÏÒÄÌÎÅÔÂÉÙ ÷ÔÂÉÙ×MERGE ÄÌÎ ÐÉÁÌÖÔ ÏÎÊÉÔÊÖ ÂÚ ÄÎÙ"
+	serbian "Pogrešna definicija tabele; sve 'MERGE' tabele moraju biti u istoj bazi podataka"
+	spa "Incorrecta definicióe la tabla; Todas las tablas MERGE deben estar en el mismo banco de datos"
+	swe "Felaktig tabelldefinition; alla tabeller i en MERGE-tabell måe vara i samma databas"
+ER_LOCK_DEADLOCK 40001 
+	nla "Deadlock gevonden tijdens lock-aanvraag poging; Probeer herstart van de transactie"
+	eng "Deadlock found when trying to get lock; try restarting transaction"
+	est "Lukustamisel tekkis tupik (deadlock); alusta transaktsiooni otsast"
+	fre "Deadlock déuvert en essayant d'obtenir les verrous : essayez de redérrer la transaction"
+	ger "Beim Versuch, eine Sperre anzufordern, ist ein Deadlock aufgetreten. Versuchen Sie, die Transaktion neu zu starten"
+	ita "Trovato deadlock durante il lock; Provare a far ripartire la transazione"
+	por "Encontrado um travamento fatal (deadlock) quando tentava obter uma trava. Tente reiniciar a transaç."
+	rus "÷ÉÌ ÔÐË×ÑÓÔÁÉ ×ÐÏÅÓ ÐÌÞÎÑÂÏÉÏË; ðÏÕÔ ÐÒÚÐÓÉØÔÁÚËÉ"
+	serbian "Unakrsno zakljuèanje pronað kada sam pokušao da dobijem pravo na zakljuèanje; Probajte da restartujete transakciju"
+	spa "Encontrado deadlock cuando tentando obtener el bloqueo; Tente recomenzar la transició+	swe "Fick 'DEADLOCK' vid låök av block/rad. Fök att starta om transaktionen"
+ER_TABLE_CANT_HANDLE_FT  
+	nla "Het gebruikte tabel type ondersteund geen FULLTEXT indexen"
+	eng "The used table type doesn't support FULLTEXT indexes"
+	est "Antud tabelitü toeta FULLTEXT indekseid"
+	fre "Le type de table utilisée supporte pas les index FULLTEXT"
+	ger "Der verwendete Tabellentyp unterstüeine FULLTEXT-Indizes"
+	ita "La tabella usata non supporta gli indici FULLTEXT"
+	por "O tipo de tabela utilizado nãsuporta íices de texto completo (fulltext indexes)"
+	rus "éÏØÕÍÊÔÐÔÂÉ Î ÐÄÅÖ×Å ÐÌÏÅÓÏÙ ÉÄËÏ"
+	serbian "Upotrebljeni tip tabele ne podržava 'FULLTEXT' indekse"
+	spa "El tipo de tabla usada no soporta íices FULLTEXT"
+	swe "Tabelltypen har inte hantering av FULLTEXT-index"
+	ukr "÷ÒÓÁÉ ÔÐÔÂɦ Î ÐÄÒͤ FULLTEXT ¦ÎÅÓ×
+ER_CANNOT_ADD_FOREIGN  
+	nla "Kan foreign key beperking niet toevoegen"
+	eng "Cannot add foreign key constraint"
+	fre "Impossible d'ajouter des contraintes d'index externe"
+	ger "FremdschlüBeschräung kann nicht hinzugefürden"
+	ita "Impossibile aggiungere il vincolo di integrita' referenziale (foreign key constraint)"
+	por "Nãpode acrescentar uma restriç de chave estrangeira"
+	rus "îÏÍÖÏÄÂ×Ô ÏÒÎÞÎÑ×ÅÎÇ ËÀÁ
+	serbian "Ne mogu da dodam proveru spoljnog kljuè
+	spa "No puede adicionar clave extranjera constraint"
+	swe "Kan inte läa till 'FOREIGN KEY constraint'"
+ER_NO_REFERENCED_ROW 23000 
+	nla "Kan onderliggende rij niet toevoegen: foreign key beperking gefaald"
+	eng "Cannot add or update a child row: a foreign key constraint fails"
+	fre "Impossible d'ajouter un enregistrement fils : une constrainte externe l'empèe"
+	ger "Hinzufüder Aktualisieren eines Kind-Datensatzes schlug aufgrund einer FremdschlüBeschräung fehl"
+	greek "Cannot add a child row: a foreign key constraint fails"
+	hun "Cannot add a child row: a foreign key constraint fails"
+	ita "Impossibile aggiungere la riga: un vincolo d'integrita' referenziale non e' soddisfatto"
+	norwegian-ny "Cannot add a child row: a foreign key constraint fails"
+	por "Nãpode acrescentar uma linha filha: uma restriç de chave estrangeira falhou"
+	rus "îÏÍÖÏÄÂ×Ô ÉÉÏÎ×Ô ÄÞÒÀ ÓÒË: ÐÏÅË ÏÒÎÞÎÊ×ÅÎÇ ËÀÁÎ ×ÐÌÑÔÑ
+	spa "No puede adicionar una lía hijo: falla de clave extranjera constraint"
+	swe "FOREIGN KEY-konflikt:  Kan inte skriva barn"
+ER_ROW_IS_REFERENCED 23000 
+	eng "Cannot delete or update a parent row: a foreign key constraint fails"
+	fre "Impossible de supprimer un enregistrement pè : une constrainte externe l'empèe"
+	ger "Löen oder Aktualisieren eines Eltern-Datensatzes schlug aufgrund einer FremdschlüBeschräung fehl"
+	greek "Cannot delete a parent row: a foreign key constraint fails"
+	hun "Cannot delete a parent row: a foreign key constraint fails"
+	ita "Impossibile cancellare la riga: un vincolo d'integrita' referenziale non e' soddisfatto"
+	por "Nãpode apagar uma linha pai: uma restriç de chave estrangeira falhou"
+	rus "îÏÍÖÏÕÁÉØÉÉÏÎ×Ô ÒÄÔÌÓÕ ÓÒË: ÐÏÅË ÏÒÎÞÎÊ×ÅÎÇ ËÀÁÎ ×ÐÌÑÔÑ
+	serbian "Ne mogu da izbrišem roditeljski slog: provera spoljnog kljuèje neuspela"
+	spa "No puede deletar una lía padre: falla de clave extranjera constraint"
+	swe "FOREIGN KEY-konflikt:  Kan inte radera fader"
+ER_CONNECT_TO_MASTER 08S01 
+	nla "Fout bij opbouwen verbinding naar master: %-.128s"
+	eng "Error connecting to master: %-.128s"
+	ger "Fehler bei der Verbindung zum Master: %-.128s"
+	ita "Errore durante la connessione al master: %-.128s"
+	por "Erro conectando com o master: %-.128s"
+	rus "ïÂÁÓÅÉÅÉ ÓÇÌ×Ù ÓÒÅÏ: %-.128s"
+	spa "Error de coneccion a master: %-.128s"
+	swe "Fick fel vid anslutning till master: %-.128s"
+ER_QUERY_ON_MASTER  
+	nla "Fout bij uitvoeren query op master: %-.128s"
+	eng "Error running query on master: %-.128s"
+	ger "Beim Ausfüeiner Abfrage auf dem Master trat ein Fehler auf: %-.128s"
+	ita "Errore eseguendo una query sul master: %-.128s"
+	por "Erro rodando consulta no master: %-.128s"
+	rus "ïÂÁ×ÐÌÅÉ ÚÐÏÁÎ ÇÌ×Ï ÓÒÅÅ %-.128s"
+	spa "Error executando el query en master: %-.128s"
+	swe "Fick fel vid utföde av command påastern: %-.128s"
+ER_ERROR_WHEN_EXECUTING_COMMAND  
+	nla "Fout tijdens uitvoeren van commando %s: %-.128s"
+	eng "Error when executing command %s: %-.128s"
+	est "Viga kä %s tämisel: %-.128s"
+	ger "Fehler beim Ausfüdes Befehls %s: %-.128s"
+	ita "Errore durante l'esecuzione del comando %s: %-.128s"
+	por "Erro quando executando comando %s: %-.128s"
+	rus "ïÂÁÐÉ×ÐÌÅÉ ËÍÎÙ%s: %-.128s"
+	serbian "Greška pri izvršavanju komande %s: %-.128s"
+	spa "Error de %s: %-.128s"
+	swe "Fick fel vid utföde av %s: %-.128s"
+ER_WRONG_USAGE  
+	nla "Foutief gebruik van %s en %s"
+	eng "Incorrect usage of %s and %s"
+	est "Vigane %s ja %s kasutus"
+	ger "Falsche Verwendung von %s und %s"
+	ita "Uso errato di %s e %s"
+	por "Uso errado de %s e %s"
+	rus "îÅÎÅÉÐÌÚ×ÎÅ%s É%s"
+	serbian "Pogrešna upotreba %s i %s"
+	spa "Equivocado uso de %s y  %s"
+	swe "Felaktig anväing av %s and %s"
+	ukr "Wrong usage of %s and %s"
+ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT 21000 
+	nla "De gebruikte SELECT commando's hebben een verschillend aantal kolommen"
+	eng "The used SELECT statements have a different number of columns"
+	est "Tulpade arv kasutatud SELECT lausetes ei kattu"
+	ger "Die verwendeten SELECT-Befehle liefern unterschiedliche Anzahlen von Feldern zurü	ita "La SELECT utilizzata ha un numero di colonne differente"
+	por "Os comandos SELECT usados têdiferente nú de colunas"
+	rus "éÏØÏÁÎÅÏÅÁÏÙ×ÂÒÉ(SELECT) ÄÀ ÒÚÏ ËÌÞÓ× ÓÏÂÏ"
+	serbian "Upotrebljene 'SELECT' komande adresiraju razliè broj kolona"
+	spa "El comando SELECT usado tiene diferente nú de columnas"
+	swe "SELECT-kommandona har olika antal kolumner"
+ER_CANT_UPDATE_WITH_READLOCK  
+	nla "Kan de query niet uitvoeren vanwege een conflicterende read lock"
+	eng "Can't execute the query because you have a conflicting read lock"
+	est "Ei suuda täa pängut konfliktse luku tõ"
+	ger "Augrund eines READ-LOCK-Konflikts kann die Abfrage nicht ausgefüerden"
+	ita "Impossibile eseguire la query perche' c'e' un conflitto con in lock di lettura"
+	por "Nãposso executar a consulta porque vocêem um conflito de travamento de leitura"
+	rus "îÏÍÖÏÉÐÌÉØÚÐÏ, ÐÓÏØÕÕ×ÓÕÔÎ×ÅÙËÎÌËÕÝÅÂÏÉÏË ÞÅÉ"
+	serbian "Ne mogu da izvršim upit zbog toga što imate zakljuèanja èanja podataka u konfliktu"
+	spa "No puedo ejecutar el query  porque usted tiene conflicto de traba de lectura"
+	swe "Kan inte utfökommandot emedan du har ett READ-lå
+ER_MIXING_NOT_ALLOWED  
+	nla "Het combineren van transactionele en niet-transactionele tabellen is uitgeschakeld."
+	eng "Mixing of transactional and non-transactional tables is disabled"
+	est "Transaktsioone toetavate ning mittetoetavate tabelite kooskasutamine ei ole lubatud"
+	ger "Die gleichzeitige Verwendung von Tabellen mit und ohne Transaktionsunterstü ist deaktiviert"
+	ita "E' disabilitata la possibilita' di mischiare tabelle transazionali e non-transazionali"
+	por "Mistura de tabelas transacional e nãtransacional estáesabilitada"
+	rus "éÏØÏÁÉ ÔÁÚËÉÎÙ ÔÂÉ ÎÒÄ ÓÎÔÁÚËÉÎÙÉÚÐÅÅÏ
+	serbian "Mešanje tabela koje podržavaju transakcije i onih koje ne podržavaju transakcije je iskljuèo"
+	spa "Mezla de transancional y no-transancional tablas estáeshabilitada"
+	swe "Blandning av transaktionella och icke-transaktionella tabeller äinaktiverat"
+ER_DUP_ARGUMENT  
+	nla "Optie '%s' tweemaal gebruikt in opdracht"
+	eng "Option '%s' used twice in statement"
+	est "Mäangut '%s' on lauses kasutatud topelt"
+	ger "Option '%s' wird im Befehl zweimal verwendet"
+	ita "L'opzione '%s' e' stata usata due volte nel comando"
+	por "Opç '%s' usada duas vezes no comando"
+	rus "ïÉ '%s' ÄÁÄ ÉÐÌÚ×Î ××ÒÖÎÉ
+	spa "Opció%s' usada dos veces en el comando"
+	swe "Option '%s' anväes tvååer"
+ER_USER_LIMIT_REACHED 42000 
+	nla "Gebruiker '%-.64s' heeft het maximale gebruik van de '%s' faciliteit overschreden (huidige waarde: %ld)"
+	eng "User '%-.64s' has exceeded the '%s' resource (current value: %ld)"
+	ger "Benutzer '%-.64s' hat die Ressourcenbeschräung '%s' ühritten (aktueller Wert: %ld)"
+	ita "L'utente '%-.64s' ha ecceduto la risorsa '%s' (valore corrente: %ld)"
+	por "Usuáo '%-.64s' tem excedido o '%s' recurso (atual valor: %ld)"
+	rus "ðÚ×ÔÌ '%-.64s' ÐÅÙÉ ÉÐÌÚ×ÎÅÒÓÒÁ'%s' (ÔËÝÅÚÁÅÉ: %ld)"
+	spa "Usuario '%-.64s' ha excedido el recurso '%s' (actual valor: %ld)"
+	swe "Anväare '%-.64s' har öskridit '%s' (nuvarande väe: %ld)"
+ER_SPECIFIC_ACCESS_DENIED_ERROR 42000 
+	nla "Toegang geweigerd. U moet het %-.128s privilege hebben voor deze operatie"
+	eng "Access denied; you need the %-.128s privilege for this operation"
+	ger "Kein Zugriff. Hierfüd die Berechtigung %-.128s benöt"
+	ita "Accesso non consentito. Serve il privilegio %-.128s per questa operazione"
+	por "Acesso negado. Vocêrecisa o priviléo %-.128s para essa operaç"
+	rus "÷ÓÕÅÏËÚÎ. ÷ÎÖÙÐÉÉÅÉ %-.128s ÄÑÜÏ ÏÅÁÉ"
+	spa "Acceso negado. Usted necesita el privilegio %-.128s para esta operació+	swe "Du har inte privlegiet '%-.128s' som behöföenna operation"
+	ukr "Access denied. You need the %-.128s privilege for this operation"
+ER_LOCAL_VARIABLE  
+	nla "Variabele '%-.64s' is SESSION en kan niet worden gebruikt met SET GLOBAL"
+	eng "Variable '%-.64s' is a SESSION variable and can't be used with SET GLOBAL"
+	ger "Variable '%-.64s' ist eine lokale Variable und kann nicht mit SET GLOBAL veräert werden"
+	ita "La variabile '%-.64s' e' una variabile locale ( SESSION ) e non puo' essere cambiata usando SET GLOBAL"
+	por "Variál '%-.64s' éma SESSION variál e nãpode ser usada com SET GLOBAL"
+	rus "ðÍÎÁ '%-.64s' ÑÌÅÓ ÐÔË×Ê(SESSION) ÐÒÍÎÏ ÉÎ ÍÖÔÂÔ ÉÍÎÎ ÓÐÍÝÀSET GLOBAL"
+	spa "Variable '%-.64s' es una SESSION variable y no puede ser usada con SET GLOBAL"
+	swe "Variabel '%-.64s' äen SESSION variabel och kan inte ärad med SET GLOBAL"
+ER_GLOBAL_VARIABLE  
+	nla "Variabele '%-.64s' is GLOBAL en dient te worden gewijzigd met SET GLOBAL"
+	eng "Variable '%-.64s' is a GLOBAL variable and should be set with SET GLOBAL"
+	ger "Variable '%-.64s' ist eine globale Variable und muss mit SET GLOBAL veräert werden"
+	ita "La variabile '%-.64s' e' una variabile globale ( GLOBAL ) e deve essere cambiata usando SET GLOBAL"
+	por "Variál '%-.64s' éma GLOBAL variál e deve ser configurada com SET GLOBAL"
+	rus "ðÍÎÁ '%-.64s' ÑÌÅÓ ÇÏÁØÏ (GLOBAL) ÐÒÍÎÏ, ÉÅ ÓÅÕÔÉÍÎÔ ÓÐÍÝÀSET GLOBAL"
+	spa "Variable '%-.64s' es una GLOBAL variable y no puede ser configurada con SET GLOBAL"
+	swe "Variabel '%-.64s' äen GLOBAL variabel och böäas med SET GLOBAL"
+ER_NO_DEFAULT 42000 
+	nla "Variabele '%-.64s' heeft geen standaard waarde"
+	eng "Variable '%-.64s' doesn't have a default value"
+	ger "Variable '%-.64s' hat keinen Vorgabewert"
+	ita "La variabile '%-.64s' non ha un valore di default"
+	por "Variál '%-.64s' nãtem um valor padrã
+	rus "ðÍÎÁ '%-.64s' Î ÉÅÔÚÁÅÉ Ð ÕÏÞÎÀ
+	spa "Variable '%-.64s' no tiene un valor patró+	swe "Variabel '%-.64s' har inte ett DEFAULT-väe"
+ER_WRONG_VALUE_FOR_VAR 42000 
+	nla "Variabele '%-.64s' kan niet worden gewijzigd naar de waarde '%-.64s'"
+	eng "Variable '%-.64s' can't be set to the value of '%-.64s'"
+	ger "Variable '%-.64s' kann nicht auf '%-.64s' gesetzt werden"
+	ita "Alla variabile '%-.64s' non puo' essere assegato il valore '%-.64s'"
+	por "Variál '%-.64s' nãpode ser configurada para o valor de '%-.64s'"
+	rus "ðÍÎÁ '%-.64s' Î ÍÖÔÂÔ ÕÔÎ×ÅÁ×ÚÁÅÉ '%-.64s'"
+	spa "Variable '%-.64s' no puede ser configurada para el valor de '%-.64s'"
+	swe "Variabel '%-.64s' kan inte säas till '%-.64s'"
+ER_WRONG_TYPE_FOR_VAR 42000 
+	nla "Foutief argumenttype voor variabele '%-.64s'"
+	eng "Incorrect argument type to variable '%-.64s'"
+	ger "Falscher Argumenttyp füiable '%-.64s'"
+	ita "Tipo di valore errato per la variabile '%-.64s'"
+	por "Tipo errado de argumento para variál '%-.64s'"
+	rus "îÅÎÊÔÐÁÇÍÎÁÄÑÐÒÍÎÏ '%-.64s'"
+	spa "Tipo de argumento equivocado para variable '%-.64s'"
+	swe "Fel typ av argument till variabel '%-.64s'"
+ER_VAR_CANT_BE_READ  
+	nla "Variabele '%-.64s' kan alleen worden gewijzigd, niet gelezen"
+	eng "Variable '%-.64s' can only be set, not read"
+	ger "Variable '%-.64s' kann nur veräert, nicht gelesen werden"
+	ita "Alla variabile '%-.64s' e' di sola scrittura quindi puo' essere solo assegnato un valore, non letto"
+	por "Variál '%-.64s' somente pode ser configurada, nãlida"
+	rus "ðÍÎÁ '%-.64s' ÍÖÔÂÔ ÔÌË ÕÔÎ×ÅÁ Î Î ÓÉÁÁ
+	spa "Variable '%-.64s' solamente puede ser configurada, no leí"
+	swe "Variabeln '%-.64s' kan endast säas, inte läs"
+ER_CANT_USE_OPTION_HERE 42000 
+	nla "Foutieve toepassing/plaatsing van '%s'"
+	eng "Incorrect usage/placement of '%s'"
+	ger "Falsche Verwendung oder Platzierung von '%s'"
+	ita "Uso/posizione di '%s' sbagliato"
+	por "Errado uso/colocaç de '%s'"
+	rus "îÅÎÅÉÐÌÚ×ÎÅÉÉ×Î×ÒÏ ÍÓÅÕÁÁ '%s'"
+	spa "Equivocado uso/colocacióe '%s'"
+	swe "Fel anväing/placering av '%s'"
+ER_NOT_SUPPORTED_YET 42000 
+	nla "Deze versie van MySQL ondersteunt nog geen '%s'"
+	eng "This version of MySQL doesn't yet support '%s'"
+	ger "Diese MySQL-Version unterstü%s' nicht"
+	ita "Questa versione di MySQL non supporta ancora '%s'"
+	por "Esta versãde MySQL nãsuporta ainda '%s'"
+	rus "üÒÉ MySQL ÐË ÅÅÎ ÐÄÅÖ×Å '%s'"
+	spa "Esta versióe MySQL no soporta todavia '%s'"
+	swe "Denna version av MySQL kan äu inte utfö'%s'"
+ER_MASTER_FATAL_ERROR_READING_BINLOG  
+	nla "Kreeg fatale fout %d: '%-.128s' van master tijdens lezen van data uit binaire log"
+	eng "Got fatal error %d: '%-.128s' from master when reading data from binary log"
+	ger "Schwerer Fehler %d: '%-.128s vom Master beim Lesen des binän Logs"
+	ita "Errore fatale %d: '%-.128s' dal master leggendo i dati dal log binario"
+	por "Obteve fatal erro %d: '%-.128s' do master quando lendo dados do binary log"
+	rus "ðÞÎ ÎÉÐÁÉÁ ÏÉË %d: '%-.128s' Ï ÇÌ×ÏÏÓÒÅÁ×ÐÏÅÓ ×ÂÒÉÄÎÙ É ÄÏÞÏÏÖÒÁÁ
+	spa "Recibiótal error %d: '%-.128s' del master cuando leyendo datos del binary log"
+	swe "Fick fatalt fel %d: '%-.128s' fråmaster vid läing av binäoggen"
+ER_SLAVE_IGNORED_TABLE  
+	eng "Slave SQL thread ignored the query because of replicate-*-table rules"
+	ger "Slave-SQL-Thread hat die Abfrage aufgrund von replicate-*-table-Regeln ignoriert"
+	por "Slave SQL thread ignorado a consulta devido ànormas de replicaç-*-tabela"
+	spa "Slave SQL thread ignorado el query debido a las reglas de replicació-tabla"
+	swe "Slav SQL trån ignorerade från pga en replicate-*-table regel"
+ER_INCORRECT_GLOBAL_LOCAL_VAR  
+	eng "Variable '%-.64s' is a %s variable"
+	serbian "Incorrect foreign key definition for '%-.64s': %s"
+	ger "Variable '%-.64s' ist eine %s-Variable"
+	spa "Variable '%-.64s' es una %s variable"
+	swe "Variabel '%-.64s' äav typ %s"
+ER_WRONG_FK_DEF 42000 
+	eng "Incorrect foreign key definition for '%-.64s': %s"
+	ger "Falsche FremdschlüDefinition fü.64s': %s"
+	por "Definiç errada da chave estrangeira para '%-.64s': %s"
+	spa "Equivocada definicióe llave extranjera para '%-.64s': %s"
+	swe "Felaktig FOREIGN KEY-definition fö%-.64s': %s"
+ER_KEY_REF_DO_NOT_MATCH_TABLE_REF  
+	eng "Key reference and table reference don't match"
+	ger "Schlü und Tabellenverweis passen nicht zusammen"
+	por "Referêia da chave e referêia da tabela nãcoincidem"
+	spa "Referencia de llave y referencia de tabla no coinciden"
+	swe "Nyckelreferensen och tabellreferensen stäer inte öens"
+ER_OPERAND_COLUMNS 21000 
+	eng "Operand should contain %d column(s)"
+	ger "Operand sollte %d Spalte(n) enthalten"
+	rus "ïÒÎ ÄÌÅ ÓÄÒÁØ%d ËÌÎË
+	spa "Operando debe tener %d columna(s)"
+	ukr "ïÒΠͤ ÓÌÄÔÓ Ú%d ÓϦ×
+ER_SUBQUERY_NO_1_ROW 21000 
+	eng "Subquery returns more than 1 row"
+	ger "Unterabfrage lieferte mehr als einen Datensatz zurü	por "Subconsulta retorna mais que 1 registro"
+	rus "ðÁÒÓ×ÚÒÝÅ ÂÌÅÏÎÊÚÐÓ"
+	spa "Subconsulta retorna mas que 1 lía"
+	swe "Subquery returnerade mer ä1 rad"
+	ukr "ðÁÉ Ð×ÒÁ ÂÌÛÎÖ1 ÚÐÓ
+ER_UNKNOWN_STMT_HANDLER  
+	dan "Unknown prepared statement handler (%.*s) given to %s"
+	eng "Unknown prepared statement handler (%.*s) given to %s"
+	ger "Unbekannter Prepared-Statement-Handler (%.*s) füangegeben"
+	por "Desconhecido manipulador de declaraç preparado (%.*s) determinado para %s"
+	spa "Desconocido preparado comando handler (%.*s) dado para %s"
+	swe "Okä PREPARED STATEMENT id (%.*s) var given till %s"
+	ukr "Unknown prepared statement handler (%.*s) given to %s"
+ER_CORRUPT_HELP_DB  
+	eng "Help database is corrupt or does not exist"
+	ger "Die Hilfe-Datenbank ist beschägt oder existiert nicht"
+	por "Banco de dado de ajuda corrupto ou nãexistente"
+	spa "Base de datos Help estáorrupto o no existe"
+	swe "Hjädatabasen finns inte eller äskadad"
+ER_CYCLIC_REFERENCE  
+	eng "Cyclic reference on subqueries"
+	ger "Zyklischer Verweis in Unterabfragen"
+	por "Referêia cíica em subconsultas"
+	rus "ãÌÞÓÁ ÓÙË Î ÐÄÁÒÓ
+	spa "Cíica referencia en subconsultas"
+	swe "Cyklisk referens i subqueries"
+	ukr "ãÌÞÅÐÓÌÎÑÎ ÐÄÁÉ"
+ER_AUTO_CONVERT  
+	eng "Converting column '%s' from %s to %s"
+	ger "Feld '%s' wird von %s nach %s umgewandelt"
+	por "Convertendo coluna '%s' de %s para %s"
+	rus "ðÂÁÏÁÉ ÐÌ '%s' É %s ×%s"
+	spa "Convirtiendo columna '%s' de %s para %s"
+	swe "Konvertar kolumn '%s' frå%s till %s"
+	ukr "ðÔÏÅÎ ÓÏÂÁ'%s' Ú%s Õ%s"
+ER_ILLEGAL_REFERENCE 42S22 
+	eng "Reference '%-.64s' not supported (%s)"
+	ger "Verweis '%-.64s' wird nicht unterstü%s)"
+	por "Referêia '%-.64s' nãsuportada (%s)"
+	rus "óË '%-.64s' Î ÐÄÅÖ×ÅÓ (%s)"
+	spa "Referencia '%-.64s' no soportada (%s)"
+	swe "Referens '%-.64s' stöinte (%s)"
+	ukr "ðÌÎÑ'%-.64s' Î ÐÄÒÍÅÓ (%s)"
+ER_DERIVED_MUST_HAVE_ALIAS 42000 
+	eng "Every derived table must have its own alias"
+	ger "Füe abgeleitete Tabelle muss ein eigener Alias angegeben werden"
+	por "Cada tabela derivada deve ter seu próo alias"
+	spa "Cada tabla derivada debe tener su propio alias"
+	swe "Varje 'derived table' måe ha sitt eget alias"
+ER_SELECT_REDUCED 01000 
+	eng "Select %u was reduced during optimization"
+	ger "Select %u wurde wäend der Optimierung reduziert"
+	por "Select %u foi reduzido durante otimizaç"
+	rus "Select %u ÂÌÕÒÚÎÎ×ÐÏÅÓ ÏÔÍÚÃÉ
+	spa "Select %u fuéeducido durante optimizació+	swe "Select %u reducerades vid optimiering"
+	ukr "Select %u was ÓÁÏÁÏÐÉÏÔÍÚÃi"
+ER_TABLENAME_NOT_ALLOWED_HERE 42000 
+	eng "Table '%-.64s' from one of the SELECTs cannot be used in %-.32s"
+	ger "Tabelle '%-.64s', die in einem der SELECT-Befehle verwendet wurde, kann nicht in %-.32s verwendet werden"
+	por "Tabela '%-.64s' de um dos SELECTs nãpode ser usada em %-.32s"
+	spa "Tabla '%-.64s' de uno de los SELECT no puede ser usada en %-.32s"
+	swe "Tabell '%-.64s' fråen SELECT kan inte anväas i %-.32s"
+ER_NOT_SUPPORTED_AUTH_MODE 08004 
+	eng "Client does not support authentication protocol requested by server; consider upgrading MySQL client"
+	ger "Client unterstüas vom Server erwartete Authentifizierungsprotokoll nicht. Bitte aktualisieren Sie Ihren MySQL-Client"
+	por "Cliente nãsuporta o protocolo de autenticaç exigido pelo servidor; considere a atualizaç do cliente MySQL"
+	spa "Cliente no soporta protocolo de autenticacióolicitado por el servidor; considere actualizar el cliente MySQL"
+	swe "Klienten stö inte autentiseringsprotokollet som begäs av servern; öväuppgradering av klientprogrammet."
+ER_SPATIAL_CANT_HAVE_NULL 42000 
+	eng "All parts of a SPATIAL index must be NOT NULL"
+	ger "Alle Teile eines SPATIAL-Index müals NOT NULL deklariert sein"
+	por "Todas as partes de uma SPATIAL index devem ser NOT NULL"
+	spa "Todas las partes de una SPATIAL index deben ser NOT NULL"
+	swe "Alla delar av en SPATIAL index måe vara NOT NULL"
+ER_COLLATION_CHARSET_MISMATCH 42000 
+	eng "COLLATION '%s' is not valid for CHARACTER SET '%s'"
+	ger "COLLATION '%s' ist füRACTER SET '%s' ungü
+	por "COLLATION '%s' nãéáda para CHARACTER SET '%s'"
+	spa "COLLATION '%s' no es vádo para CHARACTER SET '%s'"
+	swe "COLLATION '%s' äinte tillåt föHARACTER SET '%s'"
+ER_SLAVE_WAS_RUNNING  
+	eng "Slave is already running"
+	ger "Slave lät bereits"
+	por "O slave jástáodando"
+	spa "Slave ya estáuncionando"
+	swe "Slaven har redan startat"
+ER_SLAVE_WAS_NOT_RUNNING  
+	eng "Slave already has been stopped"
+	ger "Slave wurde bereits angehalten"
+	por "O slave jástáarado"
+	spa "Slave ya fuéarado"
+	swe "Slaven har redan stoppat"
+ER_TOO_BIG_FOR_UNCOMPRESS  
+	eng "Uncompressed data size too large; the maximum size is %d (probably, length of uncompressed data was corrupted)"
+	ger "Unkomprimierte Daten sind zu groß Die maximale Gröbeträ %d (wahrscheinlich wurde die Läe der unkomprimierten Daten beschägt)"
+	por "Tamanho muito grande dos dados des comprimidos. O mámo tamanho éd. (provavelmente, o comprimento dos dados descomprimidos estáorrupto)"
+	spa "Tamañemasiado grande para datos descomprimidos. El mámo tamañs %d. (probablemente, extensióe datos descomprimidos fuéorrompida)"
+ER_ZLIB_Z_MEM_ERROR  
+	eng "ZLIB: Not enough memory"
+	ger "ZLIB: Nicht genug Speicher"
+	por "ZLIB: Nãsuficiente memó disponíl"
+	spa "Z_MEM_ERROR: No suficiente memoria para zlib"
+ER_ZLIB_Z_BUF_ERROR  
+	eng "ZLIB: Not enough room in the output buffer (probably, length of uncompressed data was corrupted)"
+	ger "ZLIB: Im Ausgabepuffer ist nicht genug Platz vorhanden (wahrscheinlich wurde die Läe der unkomprimierten Daten beschägt)"
+	por "ZLIB: Nãsuficiente espaçno buffer emissor (provavelmente, o comprimento dos dados descomprimidos estáorrupto)"
+	spa "Z_BUF_ERROR: No suficiente espacio en el búde salida para zlib (probablemente, extensióe datos descomprimidos fuéorrompida)"
+ER_ZLIB_Z_DATA_ERROR  
+	eng "ZLIB: Input data corrupted"
+	ger "ZLIB: Eingabedaten beschägt"
+	por "ZLIB: Dados de entrada estáorrupto"
+	spa "ZLIB: Dato de entrada fuéorrompido para zlib"
+ER_CUT_VALUE_GROUP_CONCAT  
+	eng "%d line(s) were cut by GROUP_CONCAT()"
+	ger "%d Zeile(n) durch GROUP_CONCAT() abgeschnitten"
+	por "%d linha(s) foram cortada(s) por GROUP_CONCAT()"
+	spa "%d lía(s) fue(fueron) cortadas por group_concat()"
+	swe "%d rad(er) kapades av group_concat()"
+	ukr "%d line(s) was(were) cut by group_concat()"
+ER_WARN_TOO_FEW_RECORDS 01000 
+	eng "Row %ld doesn't contain data for all columns"
+	ger "Zeile %ld enthä nicht füe Felder Daten"
+	por "Conta de registro éenor que a conta de coluna na linha %ld"
+	spa "Lía %ld no contiene datos para todas las columnas"
+ER_WARN_TOO_MANY_RECORDS 01000 
+	eng "Row %ld was truncated; it contained more data than there were input columns"
+	ger "Zeile %ld geküdie Zeile enthielt mehr Daten, als es Eingabefelder gibt"
+	por "Conta de registro éaior que a conta de coluna na linha %ld"
+	spa "Lía %ld fuéruncada; La misma contine mas datos que las que existen en las columnas de entrada"
+ER_WARN_NULL_TO_NOTNULL 22004 
+	eng "Column set to default value; NULL supplied to NOT NULL column '%s' at row %ld"
+	ger "Feld auf Vorgabewert gesetzt, da NULL fü-NULL-Feld '%s' in Zeile %ld angegeben"
+	por "Dado truncado, NULL fornecido para NOT NULL coluna '%s' na linha %ld"
+	spa "Datos truncado, NULL suministrado para NOT NULL columna '%s' en la lía %ld"
+ER_WARN_DATA_OUT_OF_RANGE 22003 
+	eng "Out of range value adjusted for column '%s' at row %ld"
+	ger "Daten abgeschnitten, außrhalb des Wertebereichs füd '%s' in Zeile %ld"
+	por "Dado truncado, fora de alcance para coluna '%s' na linha %ld"
+	spa "Datos truncados, fuera de gama para columna '%s' en la lía %ld"
+WARN_DATA_TRUNCATED 01000 
+	eng "Data truncated for column '%s' at row %ld"
+	ger "Daten abgeschnitten füd '%s' in Zeile %ld"
+	por "Dado truncado para coluna '%s' na linha %ld"
+	spa "Datos truncados para columna '%s' en la lía %ld"
+ER_WARN_USING_OTHER_HANDLER  
+	eng "Using storage engine %s for table '%s'"
+	ger "Füelle '%s' wird Speicher-Engine %s benutzt"
+	por "Usando engine de armazenamento %s para tabela '%s'"
+	spa "Usando motor de almacenamiento %s para tabla '%s'"
+	swe "Anväer handler %s föabell '%s'"
+ER_CANT_AGGREGATE_2COLLATIONS  
+	eng "Illegal mix of collations (%s,%s) and (%s,%s) for operation '%s'"
+	ger "Unerlaubte Mischung von Sortierreihenfolgen (%s, %s) und (%s, %s) füration '%s'"
+	por "Combinaç ilegal de collations (%s,%s) e (%s,%s) para operaç '%s'"
+	spa "Ilegal mezcla de collations (%s,%s) y (%s,%s) para operació%s'"
+ER_DROP_USER  
+	eng "Cannot drop one or more of the requested users"
+	ger "Kann einen oder mehrere der angegebenen Benutzer nicht löen"
+ER_REVOKE_GRANTS  
+	eng "Can't revoke all privileges for one or more of the requested users"
+	ger "Kann nicht alle Berechtigungen widerrufen, die füen oder mehrere Benutzer gewät wurden"
+	por "Nãpode revocar todos os priviléos, grant para um ou mais dos usuáos pedidos"
+	spa "No puede revocar todos los privilegios, derecho para uno o mas de los usuarios solicitados"
+ER_CANT_AGGREGATE_3COLLATIONS  
+	eng "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'"
+	ger "Unerlaubte Mischung von Sortierreihenfolgen (%s, %s), (%s, %s), (%s, %s) füration '%s'"
+	por "Ilegal combinaç de collations (%s,%s), (%s,%s), (%s,%s) para operaç '%s'"
+	spa "Ilegal mezcla de collations (%s,%s), (%s,%s), (%s,%s) para operació%s'"
+ER_CANT_AGGREGATE_NCOLLATIONS  
+	eng "Illegal mix of collations for operation '%s'"
+	ger "Unerlaubte Mischung von Sortierreihenfolgen füration '%s'"
+	por "Ilegal combinaç de collations para operaç '%s'"
+	spa "Ilegal mezcla de collations para operació%s'"
+ER_VARIABLE_IS_NOT_STRUCT  
+	eng "Variable '%-.64s' is not a variable component (can't be used as XXXX.variable_name)"
+	ger "Variable '%-.64s' ist keine Variablen-Komponente (kann nicht als XXXX.variablen_name verwendet werden)"
+	por "Variál '%-.64s' nãéma variál componente (Nãpode ser usada como XXXX.variál_nome)"
+	spa "Variable '%-.64s' no es una variable componente (No puede ser usada como XXXX.variable_name)"
+ER_UNKNOWN_COLLATION  
+	eng "Unknown collation: '%-.64s'"
+	ger "Unbekannte Sortierreihenfolge: '%-.64s'"
+	por "Collation desconhecida: '%-.64s'"
+	spa "Collation desconocida: '%-.64s'"
+ER_SLAVE_IGNORED_SSL_PARAMS  
+	eng "SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support; they can be used later if MySQL slave with SSL is started"
+	ger "SSL-Parameter in CHANGE MASTER werden ignoriert, weil dieser MySQL-Slave ohne SSL-Unterstü kompiliert wurde. Sie kön aber spär verwendet werden, wenn ein MySQL-Slave mit SSL gestartet wird"
+	por "SSL parâtros em CHANGE MASTER sãignorados porque este escravo MySQL foi compilado sem o SSL suporte. Os mesmos podem ser usados mais tarde quando o escravo MySQL com SSL seja iniciado."
+	spa "Parametros SSL en CHANGE MASTER son ignorados porque este slave MySQL fue compilado sin soporte SSL; pueden ser usados despues cuando el slave MySQL con SSL sea inicializado"
+ER_SERVER_IS_IN_SECURE_AUTH_MODE  
+	eng "Server is running in --secure-auth mode, but '%s'@'%s' has a password in the old format; please change the password to the new format"
+	ger "Server lät im Modus --secure-auth, aber '%s'@'%s' hat ein Passwort im alten Format. Bitte Passwort ins neue Format äern"
+	por "Servidor estáodando em --secure-auth modo, porê'%s'@'%s' tem senha no formato antigo; por favor troque a senha para o novo formato"
+	rus "óÅ ÚÐÝÎ×ÒÖÍ --secure-auth (ÂÚÐÓÏ ÁÔÒÚÃÉ, Î ÄÑÐÌÚ×ÔÌ '%s'@'%s' ÐÒÌ ÓÈÁ£Î×ÓÁÏ ÆÒÁÅ ÎÏÈÄÍ ÏÎ×Ô ÆÒÁ ÐÒÌ"
+	spa "Servidor estáodando en modo --secure-auth, pero '%s'@'%s' tiene clave en el antiguo formato; por favor cambie la clave para el nuevo formato"
+ER_WARN_FIELD_RESOLVED  
+	eng "Field or reference '%-.64s%s%-.64s%s%-.64s' of SELECT #%d was resolved in SELECT #%d"
+	ger "Feld oder Verweis '%-.64s%s%-.64s%s%-.64s' im SELECT-Befehl Nr. %d wurde im SELECT-Befehl Nr. %d aufgelö
+	por "Campo ou referêia '%-.64s%s%-.64s%s%-.64s' de SELECT #%d foi resolvido em SELECT #%d"
+	rus "ð ÉÉÓÙË '%-.64s%s%-.64s%s%-.64s' É SELECTÁ#%d ÂÌ ÎÊÅÁ×SELECTÅ#%d"
+	spa "Campo o referencia '%-.64s%s%-.64s%s%-.64s' de SELECT #%d fue resolvido en SELECT #%d"
+	ukr "óÂÃ ÁÏÐÓÌÎÑ'%-.64s%s%-.64s%s%-.64s' ¦ÚSELECTÕ#%d ÂÌ ÚÁÄÎ ÕSELECT¦ #%d"
+ER_BAD_SLAVE_UNTIL_COND  
+	eng "Incorrect parameter or combination of parameters for START SLAVE UNTIL"
+	ger "Falscher Parameter oder falsche Kombination von Parametern füRT SLAVE UNTIL"
+	por "Parâtro ou combinaç de parâtros errado para START SLAVE UNTIL"
+	spa "Parametro equivocado o combinacióe parametros para START SLAVE UNTIL"
+ER_MISSING_SKIP_SLAVE  
+	eng "It is recommended to use --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL; otherwise, you will get problems if you get an unexpected slave's mysqld restart"
+	ger "Es wird empfohlen, mit --skip-slave-start zu starten, wenn mit START SLAVE UNTIL eine Schritt-füritt-Replikation ausgefüird. Ansonsten gibt es Probleme, wenn ein Slave-Server unerwartet neu startet"
+	por "Érecomendado para rodar com --skip-slave-start quando fazendo replicaç passo-por-passo com START SLAVE UNTIL, de outra forma vocêãestáeguro em caso de inesperada reinicialiç do mysqld escravo"
+	spa "Es recomendado rodar con --skip-slave-start cuando haciendo replicaciótep-by-step con START SLAVE UNTIL, a menos que usted no estéeguro en caso de inesperada reinicializacióel mysqld slave"
+ER_UNTIL_COND_IGNORED  
+	eng "SQL thread is not to be started so UNTIL options are ignored"
+	ger "SQL-Thread soll nicht gestartet werden. Daher werden UNTIL-Optionen ignoriert"
+	por "Thread SQL nãpode ser inicializado tal que opçs UNTIL sãignoradas"
+	spa "SQL thread no es inicializado tal que opciones UNTIL son ignoradas"
+ER_WRONG_NAME_FOR_INDEX 42000 
+	eng "Incorrect index name '%-.100s'"
+	ger "Falscher Indexname '%-.100s'"
+	por "Incorreto nome de íice '%-.100s'"
+	spa "Nombre de íice incorrecto '%-.100s'"
+	swe "Felaktigt index namn '%-.100s'"
+ER_WRONG_NAME_FOR_CATALOG 42000 
+	eng "Incorrect catalog name '%-.100s'"
+	ger "Falscher Katalogname '%-.100s'"
+	por "Incorreto nome de catágo '%-.100s'"
+	spa "Nombre de catalog incorrecto '%-.100s'"
+	swe "Felaktigt katalog namn '%-.100s'"
+ER_WARN_QC_RESIZE  
+	eng "Query cache failed to set size %lu; new query cache size is %lu"
+	ger "Äderung der Query-Cache-Gröauf %lu fehlgeschlagen; neue Query-Cache-Gröist %lu"
+	por "Falha em Query cache para configurar tamanho %lu, novo tamanho de query cache élu"
+	rus "ë ÚÐÏÏ Î ÍÖÔÕÔÎ×Ô ÒÚÅ %lu, Î×ÊÒÚÅ ËÛ ÚÒÓ×- %lu"
+	spa "Query cache fallada para configurar tamañlu, nuevo tamañe query cache es %lu"
+	swe "Storleken av "Query cache" kunde inte säas till %lu, ny storlek ä%lu"
+	ukr "ë ÚÐÔ×ÎÓÒÍÖÎ×ÔÎ×Ô ÒÚ¦Ò%lu, Î×ÊÒÚ¦ÒËÛ ÚÐÔ×- %lu"
+ER_BAD_FT_COLUMN  
+	eng "Column '%-.64s' cannot be part of FULLTEXT index"
+	ger "Feld '%-.64s' kann nicht Teil eines FULLTEXT-Index sein"
+	por "Coluna '%-.64s' nãpode ser parte de íice FULLTEXT"
+	spa "Columna '%-.64s' no puede ser parte de FULLTEXT index"
+	swe "Kolumn '%-.64s' kan inte vara del av ett FULLTEXT index"
+ER_UNKNOWN_KEY_CACHE  
+	eng "Unknown key cache '%-.100s'"
+	ger "Unbekannter SchlüCache '%-.100s'"
+	por "Key cache desconhecida '%-.100s'"
+	spa "Desconocida key cache '%-.100s'"
+	swe "Okä nyckel cache '%-.100s'"
+ER_WARN_HOSTNAME_WONT_WORK  
+	eng "MySQL is started in --skip-name-resolve mode; you must restart it without this switch for this grant to work"
+	ger "MySQL wurde mit --skip-name-resolve gestartet. Diese Option darf nicht verwendet werden, damit diese Rechtevergabe möch ist"
+	por "MySQL foi inicializado em modo --skip-name-resolve. Vocêecesita reincializáo sem esta opç para este grant funcionar"
+	spa "MySQL esta inicializado en modo --skip-name-resolve. Usted necesita reinicializarlo sin esta opcióara este derecho funcionar"
+ER_UNKNOWN_STORAGE_ENGINE 42000 
+	eng "Unknown table engine '%s'"
+	ger "Unbekannte Speicher-Engine '%s'"
+	por "Motor de tabela desconhecido '%s'"
+	spa "Desconocido motor de tabla '%s'"
+ER_WARN_DEPRECATED_SYNTAX  
+	eng "'%s' is deprecated; use '%s' instead"
+	ger "'%s' ist veraltet. Bitte benutzen Sie '%s'"
+	por "'%s' éesatualizado. Use '%s' em seu lugar"
+	spa "'%s' estáesaprobado, use '%s' en su lugar"
+ER_NON_UPDATABLE_TABLE  
+	eng "The target table %-.100s of the %s is not updatable"
+	ger "Die Zieltabelle %-.100s von %s ist nicht aktualisierbar"
+	por "A tabela destino %-.100s do %s nãétualizál"
+	rus "ôÉÁ%-.100s ×%s Î ÍÖÔÉÍÎÔÑ
+	spa "La tabla destino %-.100s del %s no es actualizable"
+	swe "Tabell %-.100s anvä med '%s' äinte uppdateringsbar"
+	ukr "ôÉÑ%-.100s Õ%s Î ÍÖ ÏÏÌ×ÔÓ"
+ER_FEATURE_DISABLED  
+	eng "The '%s' feature is disabled; you need MySQL built with '%s' to have it working"
+	ger "Das Feature '%s' ist ausgeschaltet, Sie müMySQL mit '%s' ützen, damit es verfüist"
+	por "O recurso '%s' foi desativado; vocêecessita MySQL construí com '%s' para ter isto funcionando"
+	spa "El recurso '%s' fue deshabilitado; usted necesita construir MySQL con '%s' para tener eso funcionando"
+	swe "'%s' äinte aktiverad; Fött aktivera detta måe du bygga om MySQL med '%s' definerad"
+ER_OPTION_PREVENTS_STATEMENT  
+	eng "The MySQL server is running with the %s option so it cannot execute this statement"
+	ger "Der MySQL-Server lät mit der Option %s und kann diese Anweisung deswegen nicht ausfü
+	por "O servidor MySQL estáodando com a opç %s razãpela qual nãpode executar esse commando"
+	spa "El servidor MySQL estáodando con la opciós tal que no puede ejecutar este comando"
+	swe "MySQL ästartad med --skip-grant-tables. Pga av detta kan du inte anväa detta kommando"
+ER_DUPLICATED_VALUE_IN_TYPE  
+	eng "Column '%-.100s' has duplicated value '%-.64s' in %s"
+	ger "Feld '%-.100s' hat doppelten Wert '%-.64s' in %s"
+	por "Coluna '%-.100s' tem valor duplicado '%-.64s' em %s"
+	spa "Columna '%-.100s' tiene valor doblado '%-.64s' en %s"
+ER_TRUNCATED_WRONG_VALUE 22007 
+	eng "Truncated incorrect %-.32s value: '%-.128s'"
+	ger "Falscher %-.32s-Wert gekü'%-.128s'"
+	por "Truncado errado %-.32s valor: '%-.128s'"
+	spa "Equivocado truncado %-.32s valor: '%-.128s'"
+ER_TOO_MUCH_AUTO_TIMESTAMP_COLS  
+	eng "Incorrect table definition; there can be only one TIMESTAMP column with CURRENT_TIMESTAMP in DEFAULT or ON UPDATE clause"
+	ger "Fehlerhafte Tabellendefinition. Es kann nur eine einzige TIMESTAMP-Spalte mit CURRENT_TIMESTAMP als DEFAULT oder in einer ON-UPDATE-Klausel geben"
+	por "Incorreta definiç de tabela; Pode ter somente uma coluna TIMESTAMP com CURRENT_TIMESTAMP em DEFAULT ou ON UPDATE cláula"
+	spa "Incorrecta definicióe tabla; Solamente debe haber una columna TIMESTAMP con CURRENT_TIMESTAMP en DEFAULT o ON UPDATE cláula"
+ER_INVALID_ON_UPDATE  
+	eng "Invalid ON UPDATE clause for '%-.64s' column"
+	ger "Ungü ON-UPDATE-Klausel fülte '%-.64s'"
+	por "Inváda cláula ON UPDATE para campo '%-.64s'"
+	spa "Invádo ON UPDATE cláula para campo '%-.64s'"
+ER_UNSUPPORTED_PS  
+	eng "This command is not supported in the prepared statement protocol yet"
+	ger "Dieser Befehl wird im Protokoll fübereitete Anweisungen noch nicht unterstü+ER_GET_ERRMSG  
+	dan "Modtog fejl %d '%-.100s' fra %s"
+	eng "Got error %d '%-.100s' from %s"
+	ger "Fehler %d '%-.100s' von %s"
+	nor "Mottok feil %d '%-.100s' fa %s"
+	norwegian-ny "Mottok feil %d '%-.100s' fra %s"
+ER_GET_TEMPORARY_ERRMSG  
+	dan "Modtog temporary fejl %d '%-.100s' fra %s"
+	eng "Got temporary error %d '%-.100s' from %s"
+	ger "Temporär Fehler %d '%-.100s' von %s"
+	nor "Mottok temporary feil %d '%-.100s' fra %s"
+	norwegian-ny "Mottok temporary feil %d '%-.100s' fra %s"
+ER_UNKNOWN_TIME_ZONE  
+	eng "Unknown or incorrect time zone: '%-.64s'"
+	ger "Unbekannte oder falsche Zeitzone: '%-.64s'"
+ER_WARN_INVALID_TIMESTAMP  
+	eng "Invalid TIMESTAMP value in column '%s' at row %ld"
+	ger "Ungür TIMESTAMP-Wert in Feld '%s', Zeile %ld"
+ER_INVALID_CHARACTER_STRING  
+	eng "Invalid %s character string: '%.64s'"
+	ger "Ungür %s-Zeichen-String: '%.64s'"
+ER_WARN_ALLOWED_PACKET_OVERFLOWED  
+	eng "Result of %s() was larger than max_allowed_packet (%ld) - truncated"
+	ger "Ergebnis von %s() war grö als max_allowed_packet (%ld) Bytes und wurde deshalb gekü+ER_CONFLICTING_DECLARATIONS  
+	eng "Conflicting declarations: '%s%s' and '%s%s'"
+	ger "Widersprühe Deklarationen: '%s%s' und '%s%s'"
+ER_SP_NO_RECURSIVE_CREATE 2F003 
+	eng "Can't create a %s from within another stored routine"
+	ger "Kann kein %s innerhalb einer anderen gespeicherten Routine erzeugen"
+ER_SP_ALREADY_EXISTS 42000 
+	eng "%s %s already exists"
+	ger "%s %s existiert bereits"
+ER_SP_DOES_NOT_EXIST 42000 
+	eng "%s %s does not exist"
+	ger "%s %s existiert nicht"
+ER_SP_DROP_FAILED  
+	eng "Failed to DROP %s %s"
+	ger "DROP %s %s ist fehlgeschlagen"
+ER_SP_STORE_FAILED  
+	eng "Failed to CREATE %s %s"
+	ger "CREATE %s %s ist fehlgeschlagen"
+ER_SP_LILABEL_MISMATCH 42000 
+	eng "%s with no matching label: %s"
+	ger "%s ohne passende Marke: %s"
+ER_SP_LABEL_REDEFINE 42000 
+	eng "Redefining label %s"
+	ger "Neudefinition der Marke %s"
+ER_SP_LABEL_MISMATCH 42000 
+	eng "End-label %s without match"
+	ger "Ende-Marke %s ohne zugehöen Anfang"
+ER_SP_UNINIT_VAR 01000 
+	eng "Referring to uninitialized variable %s"
+	ger "Zugriff auf nichtinitialisierte Variable %s"
+ER_SP_BADSELECT 0A000 
+	eng "PROCEDURE %s can't return a result set in the given context"
+	ger "PROCEDURE %s kann im gegebenen Kontext keine Ergebnismenge zurüen"
+ER_SP_BADRETURN 42000 
+	eng "RETURN is only allowed in a FUNCTION"
+	ger "RETURN ist nur innerhalb einer FUNCTION erlaubt"
+ER_SP_BADSTATEMENT 0A000 
+	eng "%s is not allowed in stored procedures"
+	ger "%s ist in gespeicherten Prozeduren nicht erlaubt"
+ER_UPDATE_LOG_DEPRECATED_IGNORED 42000 
+	eng "The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been ignored"
+	ger "Das Update-Log ist veraltet und wurde durch das BinäLog ersetzt. SET SQL_LOG_UPDATE wird ignoriert"
+ER_UPDATE_LOG_DEPRECATED_TRANSLATED 42000 
+	eng "The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN"
+	ger "Das Update-Log ist veraltet und wurde durch das BinäLog ersetzt. SET SQL_LOG_UPDATE wurde in SET SQL_LOG_BIN ützt"
+ER_QUERY_INTERRUPTED 70100 
+	eng "Query execution was interrupted"
+	ger "Ausfü der Abfrage wurde unterbrochen"
+ER_SP_WRONG_NO_OF_ARGS 42000 
+	eng "Incorrect number of arguments for %s %s; expected %u, got %u"
+	ger "Falsche Anzahl von Argumenten fü%s; erwarte %u, erhalte %u"
+ER_SP_COND_MISMATCH 42000 
+	eng "Undefined CONDITION: %s"
+	ger "Undefinierte CONDITION: %s"
+ER_SP_NORETURN 42000 
+	eng "No RETURN found in FUNCTION %s"
+	ger "Kein RETURN in FUNCTION %s gefunden"
+ER_SP_NORETURNEND 2F005 
+	eng "FUNCTION %s ended without RETURN"
+	ger "FUNCTION %s endete ohne RETURN"
+ER_SP_BAD_CURSOR_QUERY 42000 
+	eng "Cursor statement must be a SELECT"
+	ger "Cursor-Anweisung muss ein SELECT sein"
+ER_SP_BAD_CURSOR_SELECT 42000 
+	eng "Cursor SELECT must not have INTO"
+	ger "Cursor-SELECT darf kein INTO haben"
+ER_SP_CURSOR_MISMATCH 42000 
+	eng "Undefined CURSOR: %s"
+	ger "Undefinierter CURSOR: %s"
+ER_SP_CURSOR_ALREADY_OPEN 24000 
+	eng "Cursor is already open"
+	ger "Cursor ist schon geöet"
+ER_SP_CURSOR_NOT_OPEN 24000 
+	eng "Cursor is not open"
+	ger "Cursor ist nicht geöet"
+ER_SP_UNDECLARED_VAR 42000 
+	eng "Undeclared variable: %s"
+	ger "Nicht deklarierte Variable: %s"
+ER_SP_WRONG_NO_OF_FETCH_ARGS  
+	eng "Incorrect number of FETCH variables"
+	ger "Falsche Anzahl von FETCH-Variablen"
+ER_SP_FETCH_NO_DATA 02000 
+	eng "No data - zero rows fetched, selected, or processed"
+	ger "Keine Daten - null Zeilen geholt (fetch), ausgewät oder verarbeitet"
+ER_SP_DUP_PARAM 42000 
+	eng "Duplicate parameter: %s"
+	ger "Doppelter Parameter: %s"
+ER_SP_DUP_VAR 42000 
+	eng "Duplicate variable: %s"
+	ger "Doppelte Variable: %s"
+ER_SP_DUP_COND 42000 
+	eng "Duplicate condition: %s"
+	ger "Doppelte Bedingung: %s"
+ER_SP_DUP_CURS 42000 
+	eng "Duplicate cursor: %s"
+	ger "Doppelter Cursor: %s"
+ER_SP_CANT_ALTER  
+	eng "Failed to ALTER %s %s"
+	ger "ALTER %s %s fehlgeschlagen"
+ER_SP_SUBSELECT_NYI 0A000 
+	eng "Subselect value not supported"
+	ger "Subselect-Wert wird nicht unterstü+ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG 0A000
+        eng "%s is not allowed in stored function or trigger"
+	ger "%s ist in gespeicherten Funktionen und in Triggern nicht erlaubt"
+ER_SP_VARCOND_AFTER_CURSHNDLR 42000 
+	eng "Variable or condition declaration after cursor or handler declaration"
+	ger "Deklaration einer Variablen oder einer Bedingung nach der Deklaration eines Cursors oder eines Handlers"
+ER_SP_CURSOR_AFTER_HANDLER 42000 
+	eng "Cursor declaration after handler declaration"
+	ger "Deklaration eines Cursors nach der Deklaration eines Handlers"
+ER_SP_CASE_NOT_FOUND 20000 
+	eng "Case not found for CASE statement"
+	ger "Fall füE-Anweisung nicht gefunden"
+ER_FPARSER_TOO_BIG_FILE  
+	eng "Configuration file '%-.64s' is too big"
+	ger "Konfigurationsdatei '%-.64s' ist zu groß
+	rus "óËÍÂÌÛÊËÎÉÕÁÉÎÙ ÆÊ '%-.64s'"
+	ukr "úÔ ×ÌËÊËΦÇÒÃÊÉ ÆÊ '%-.64s'"
+ER_FPARSER_BAD_HEADER  
+	eng "Malformed file type header in file '%-.64s'"
+	ger "Nicht wohlgeformter Dateityp-Header in Datei '%-.64s'"
+	rus "îÅÎÊÚÇÌ×ËÔÐ ÆÊÁ'%-.64s'"
+	ukr "î¦ÒÉ ÚÇÌ×ËÔÐ ÕÆʦ '%-.64s'"
+ER_FPARSER_EOF_IN_COMMENT  
+	eng "Unexpected end of file while parsing comment '%-.64s'"
+	ger "Unerwartetes Dateiende beim Parsen des Kommentars '%-.64s'"
+	rus "îÖÄÎÙ ËÎÃÆÊÁ×ËÍÎÁÉ '%-.64s'"
+	ukr "îÐÄ×ÎÉ ËÎà ÆÊÕÕËÍÎÁ¦ '%-.64s'"
+ER_FPARSER_ERROR_IN_PARAMETER  
+	eng "Error while parsing parameter '%-.64s' (line: '%-.64s')"
+	ger "Fehler beim Parsen des Parameters '%-.64s' (Zeile: '%-.64s')"
+	rus "ïÂÁÐÉÒÓÏÎ×ÎÉÐÒÍÔÁ'%-.64s' (ÓÒË: '%-.64s')"
+	ukr "ðÌÁ×ÒÓ¦ÚÁÁÎ ÐÒÍÔÕ'%-.64s' (ÒÄË '%-.64s')"
+ER_FPARSER_EOF_IN_UNKNOWN_PARAMETER  
+	eng "Unexpected end of file while skipping unknown parameter '%-.64s'"
+	ger "Unerwartetes Dateiende beim Üerspringen des unbekannten Parameters '%-.64s'"
+	rus "îÖÄÎÙ ËÎÃÆÊÁÐÉÐÏÕË ÎÉ×ÓÎÇ ÐÒÍÔÁ'%-.64s'"
+	ukr "îÐÄ×ÎÉ ËÎà ÆÊÕÕÓÒ ÐÏÉÕÉÎ×ÄÍÊÐÒÍÔ '%-.64s'"
+ER_VIEW_NO_EXPLAIN  
+	eng "EXPLAIN/SHOW can not be issued; lacking privileges for underlying table"
+	ger "EXPLAIN/SHOW kann nicht verlangt werden. Rechte fürunde liegende Tabelle fehlen"
+	rus "EXPLAIN/SHOW Î ÍÖÔÂÔ ×ÐÌÅÎ; ÎÄÓÁÏÎ ÐÁ Î ÔËÌà ÚÐÏÁ
+	ukr "EXPLAIN/SHOW Î ÍÖ ÂÔ ×ËÎÎ; Îͤ ÐÁ Î ÔÂɦ ÚÐÔ"
+ER_FRM_UNKNOWN_TYPE  
+	eng "File '%-.64s' has unknown type '%-.64s' in its header"
+	ger "Datei '%-.64s' hat unbekannten Typ '%-.64s' im Header"
+	rus "æÌ'%-.64s' ÓÄÒÉ ÎÉ×ÓÎÊÔÐ'%-.64s' ×ÚÇÌ×Å
+	ukr "æÌ'%-.64s' ͤ Î×ÄÍÊÔÐ'%-.64s' ÕÚÇÌ×Õ
+ER_WRONG_OBJECT  
+	eng "'%-.64s.%-.64s' is not %s"
+	ger "'%-.64s.%-.64s' ist nicht %s"
+	rus "'%-.64s.%-.64s' - Î %s"
+	ukr "'%-.64s.%-.64s' Î ¤ %s"
+ER_NONUPDATEABLE_COLUMN  
+	eng "Column '%-.64s' is not updatable"
+	ger "Feld '%-.64s' ist nicht aktualisierbar"
+	rus "óÂÃ'%-.64s' Î ÏÎ×ÑÍÊ
+	ukr "óÂÃ '%-.64s' Î ÍÖ ÂÔ ÚÉÅÉ"
+ER_VIEW_SELECT_DERIVED  
+	eng "View's SELECT contains a subquery in the FROM clause"
+	ger "SELECT der View enthä eine Subquery in der FROM-Klausel"
+	rus "View SELECT ÓÄÒÉ ÐÄÁÒÓ×ËÎÔÕÃÉFROM"
+	ukr "View SELECT ͤ ÐÄÁÉ ÕËÎÔÕç FROM"
+ER_VIEW_SELECT_CLAUSE  
+	eng "View's SELECT contains a '%s' clause"
+	ger "SELECT der View enthä eine '%s'-Klausel"
+	rus "View SELECT ÓÄÒÉ ËÎÔÕÃÀ'%s'"
+	ukr "View SELECT ͤ ËÎÔÕÃÀ'%s'"
+ER_VIEW_SELECT_VARIABLE  
+	eng "View's SELECT contains a variable or parameter"
+	ger "SELECT der View enthä eine Variable oder einen Parameter"
+	rus "View SELECT ÓÄÒÉ ÐÒÍÎÕ ÉÉÐÒÍÔ"
+	ukr "View SELECT ͤ ÚÉÎ ÁÏÐÒÍÔÒ
+ER_VIEW_SELECT_TMPTABLE  
+	eng "View's SELECT refers to a temporary table '%-.64s'"
+	ger "SELECT der View verweist auf eine temporä Tabelle '%-.64s'"
+	rus "View SELECT ÓÄÒÉ ÓÙË Î ×ÅÅÎÀÔÂÉÕ'%-.64s'"
+	ukr "View SELECT ×ËÒÓÏÕ ÔÍÁÏÕÔÂÉÀ'%-.64s'"
+ER_VIEW_WRONG_LIST  
+	eng "View's SELECT and view's field list have different column counts"
+	ger "SELECT- und Feldliste der Views haben unterschiedliche Anzahlen von Spalten"
+	rus "View SELECT ÉÓÉÏ ÐÌÊview ÉÅÔÒÚÏ ËÌÞÓ× ÓÏÂÏ"
+	ukr "View SELECT ¦ ÐÒÌËÓϦ×view ÍÀØÒÚÕËÌËÓØÓϦ×
+ER_WARN_VIEW_MERGE  
+	eng "View merge algorithm can't be used here for now (assumed undefined algorithm)"
+	ger "View-Merge-Algorithmus kann hier momentan nicht verwendet werden (undefinierter Algorithmus wird angenommen)"
+	rus "áÏÉÍÓÉÎÑview Î ÍÖÔÂÔ ÉÐÌÚ×ÎÓÊÁ (ÁÇÒÔ ÂÄÔÎÏÅÅÅÅÎÍ"
+	ukr "áÏÉÍÚÉÁÎ view Î ÍÖ ÂÔ ×ËÒÓÁÉ ÚÒÚ(ÁÇÒÔ ÂÄ Î×ÚÁÅÉ)"
+ER_WARN_VIEW_WITHOUT_KEY  
+	eng "View being updated does not have complete key of underlying table in it"
+	ger "Die aktualisierte View enthä nicht den vollstäigen Schlüder zugrunde liegenden Tabelle"
+	rus "ïÏÌÅÙ view Î ÓÄÒÉ ËÀÁÉÐÌÚ×ÎÙ(Ï) ×ÎÍÔÂÉ(Ù"
+	ukr "View, Ý ÏÏÌÅØÑ Î ÍÓÉØÐ×ÏÏËÀÁÔÂɦ(Ø, Ý ×ËÒÓÁÁ×ÎÀÍ"
+ER_VIEW_INVALID  
+	eng "View '%-.64s.%-.64s' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them"
+ER_SP_NO_DROP_SP  
+	eng "Can't drop or alter a %s from within another stored routine"
+	ger "Kann eine %s nicht von innerhalb einer anderen gespeicherten Routine löen oder äern"
+ER_SP_GOTO_IN_HNDLR  
+	eng "GOTO is not allowed in a stored procedure handler"
+	ger "GOTO ist im Handler einer gespeicherten Prozedur nicht erlaubt"
+ER_TRG_ALREADY_EXISTS  
+	eng "Trigger already exists"
+	ger "Trigger existiert bereits"
+ER_TRG_DOES_NOT_EXIST  
+	eng "Trigger does not exist"
+	ger "Trigger existiert nicht"
+ER_TRG_ON_VIEW_OR_TEMP_TABLE  
+	eng "Trigger's '%-.64s' is view or temporary table"
+	ger "'%-.64s' des Triggers ist View oder temporä Tabelle"
+ER_TRG_CANT_CHANGE_ROW  
+	eng "Updating of %s row is not allowed in %strigger"
+	ger "Aktualisieren einer %s-Zeile ist in einem %-Trigger nicht erlaubt"
+ER_TRG_NO_SUCH_ROW_IN_TRG  
+	eng "There is no %s row in %s trigger"
+	ger "Es gibt keine %s-Zeile im %s-Trigger"
+ER_NO_DEFAULT_FOR_FIELD  
+	eng "Field '%-.64s' doesn't have a default value"
+	ger "Feld '%-.64s' hat keinen Vorgabewert"
+ER_DIVISION_BY_ZERO 22012 
+	eng "Division by 0"
+	ger "Division durch 0"
+ER_TRUNCATED_WRONG_VALUE_FOR_FIELD  
+	eng "Incorrect %-.32s value: '%-.128s' for column '%.64s' at row %ld"
+	ger "Falscher %-.32s-Wert: '%-.128s' füd '%.64s' in Zeile %ld"
+ER_ILLEGAL_VALUE_FOR_TYPE 22007 
+	eng "Illegal %s '%-.64s' value found during parsing"
+	ger "Nicht zuläiger %s-Wert '%-.64s' beim Parsen gefunden"
+ER_VIEW_NONUPD_CHECK  
+	eng "CHECK OPTION on non-updatable view '%-.64s.%-.64s'"
+	ger "CHECK OPTION auf nicht-aktualisierbarem View '%-.64s.%-.64s'"
+	rus "CHECK OPTION ÄÑÎÏÎ×ÑÍÇ VIEW '%-.64s.%-.64s'"
+	ukr "CHECK OPTION ÄÑVIEW '%-.64s.%-.64s' Ý Î ÍÖ ÂÔ ÏÏÌÎÉ"
+ER_VIEW_CHECK_FAILED  
+	eng "CHECK OPTION failed '%-.64s.%-.64s'"
+	ger "CHECK OPTION fehlgeschlagen: '%-.64s.%-.64s'"
+	rus "ÐÏÅË CHECK OPTION ÄÑVIEW '%-.64s.%-.64s' ÐÏÁÉÁØ
+	ukr "ð×ÒÁCHECK OPTION ÄÑVIEW '%-.64s.%-.64s' Î ÐÏÛÁ
+ER_PROCACCESS_DENIED_ERROR 42000 
+	eng "%-.16s command denied to user '%-.32s'@'%-.64s' for routine '%-.64s'"
+	ger "Befehl %-.16s nicht zuläig füutzer '%-.32s'@'%-.64s' in Routine '%-.64s'"
+ER_RELAY_LOG_FAIL  
+	eng "Failed purging old relay logs: %s"
+	ger "Bereinigen alter Relais-Logs fehlgeschlagen: %s"
+ER_PASSWD_LENGTH  
+	eng "Password hash should be a %d-digit hexadecimal number"
+	ger "Passwort-Hash sollte eine Hexdaezimalzahl mit %d Stellen sein"
+ER_UNKNOWN_TARGET_BINLOG  
+	eng "Target log not found in binlog index"
+	ger "Ziel-Log im Binlog-Index nicht gefunden"
+ER_IO_ERR_LOG_INDEX_READ  
+	eng "I/O error reading log index file"
+	ger "Fehler beim Lesen der Log-Index-Datei"
+ER_BINLOG_PURGE_PROHIBITED  
+	eng "Server configuration does not permit binlog purge"
+	ger "Server-Konfiguration erlaubt keine Binlog-Bereinigung"
+ER_FSEEK_FAIL  
+	eng "Failed on fseek()"
+	ger "fseek() fehlgeschlagen"
+ER_BINLOG_PURGE_FATAL_ERR  
+	eng "Fatal error during log purge"
+	ger "Schwerwiegender Fehler bei der Log-Bereinigung"
+ER_LOG_IN_USE  
+	eng "A purgeable log is in use, will not purge"
+	ger "Ein zu bereinigendes Log wird gerade benutzt, daher keine Bereinigung"
+ER_LOG_PURGE_UNKNOWN_ERR  
+	eng "Unknown error during log purge"
+	ger "Unbekannter Fehler bei Log-Bereinigung"
+ER_RELAY_LOG_INIT  
+	eng "Failed initializing relay log position: %s"
+	ger "Initialisierung der Relais-Log-Position fehlgeschlagen: %s"
+ER_NO_BINARY_LOGGING  
+	eng "You are not using binary logging"
+	ger "Sie verwenden keine Binäogs"
+ER_RESERVED_SYNTAX  
+	eng "The '%-.64s' syntax is reserved for purposes internal to the MySQL server"
+	ger "Die Schreibweise '%-.64s' ist füerne Zwecke des MySQL-Servers reserviert"
+ER_WSAS_FAILED  
+	eng "WSAStartup Failed"
+	ger "WSAStartup fehlgeschlagen"
+ER_DIFF_GROUPS_PROC  
+	eng "Can't handle procedures with different groups yet"
+	ger "Kann Prozeduren mit unterschiedlichen Gruppen noch nicht verarbeiten"
+ER_NO_GROUP_FOR_PROC  
+	eng "Select must have a group with this procedure"
+	ger "SELECT muss bei dieser Prozedur ein GROUP BY haben"
+ER_ORDER_WITH_PROC  
+	eng "Can't use ORDER clause with this procedure"
+	ger "Kann bei dieser Prozedur keine ORDER-BY-Klausel verwenden"
+ER_LOGGING_PROHIBIT_CHANGING_OF  
+	eng "Binary logging and replication forbid changing the global server %s"
+	ger "Binäogs und Replikation verhindern Wechsel des globalen Servers %s"
+ER_NO_FILE_MAPPING  
+	eng "Can't map file: %-.64s, errno: %d"
+	ger "Kann Datei nicht abbilden: %-.64s, Fehler: %d"
+ER_WRONG_MAGIC  
+	eng "Wrong magic in %-.64s"
+	ger "Falsche magische Zahlen in %-.64s"
+ER_PS_MANY_PARAM  
+	eng "Prepared statement contains too many placeholders"
+	ger "Vorbereitete Anweisung enthä zu viele Platzhalter"
+ER_KEY_PART_0  
+	eng "Key part '%-.64s' length cannot be 0"
+	ger "Läe des Schlüeils '%-.64s' kann nicht 0 sein"
+ER_VIEW_CHECKSUM  
+	eng "View text checksum failed"
+	ger "View-Text-Prüe fehlgeschlagen"
+	rus "ðÅË ËÎÒÌÎÊÓÍÙÔËÔ VIEW ÐÏÁÉÁØ
+	ukr "ð×ÒÁËÎÒÌΧ ÓÍ ÔËÔ VIEW Î ÐÏÛÁ
+ER_VIEW_MULTIUPDATE  
+	eng "Can not modify more than one base table through a join view '%-.64s.%-.64s'"
+	ger "Kann nicht mehr als eine Basistabelle üoin-View '%-.64s.%-.64s' äern"
+	rus "îØÑÉÍÎÔ ÂÌÛ ÞÍÏÎ ÂÚ×ÀÔÂÉÕÉÐÌÚÑÍÏÏÁÌÞÙ VIEW '%-.64s.%-.64s'"
+	ukr "îÏÌ× ÏÏÉÉÂÌÛÎÖÏÎ ÂÚ× ÔÂÉÀ×ËÒÓÏÕÞ VIEW '%-.64s.%-.64s', Ý ÍÓ¦Ô ÄËÌË ÔÂÉØ
+ER_VIEW_NO_INSERT_FIELD_LIST  
+	eng "Can not insert into join view '%-.64s.%-.64s' without fields list"
+	ger "Kann nicht ohne Feldliste in Join-View '%-.64s.%-.64s' einfü+	rus "îØÑ×Ô×ÑØÚÐÓ ×ÍÏÏÁÌÞÙ VIEW '%-.64s.%-.64s' ÂÚÓÉË ÐÌÊ
+	ukr "îÏÌ× ÕÔ×Ô ÒÄÉÕVIEW '%-.64s.%-.64s', Ý ÍÓÉØÄËÌË ÔÂÉØ ÂÚÓÉË ÓϦ×
+ER_VIEW_DELETE_MERGE_VIEW  
+	eng "Can not delete from join view '%-.64s.%-.64s'"
+	ger "Kann nicht aus Join-View '%-.64s.%-.64s' löen"
+	rus "îØÑÕÁÑØÉ ÍÏÏÁÌÞÏÏVIEW '%-.64s.%-.64s'"
+	ukr "îÏÌ× ×ÄÌÔ ÒÄÉÕVIEW '%-.64s.%-.64s', Ý ÍÓÉØÄËÌË ÔÂÉØ
+ER_CANNOT_USER  
+	eng "Operation %s failed for %.256s"
+	ger "Operation %s schlug fehl fü56s"
+	norwegian-ny "Operation %s failed for '%.256s'"
+ER_XAER_NOTA XAE04
+        eng "XAER_NOTA: Unknown XID"
+	ger "XAER_NOTA: Unbekannte XID"
+ER_XAER_INVAL XAE05
+        eng "XAER_INVAL: Invalid arguments (or unsupported command)"
+	ger "XAER_INVAL: Ungü Argumente (oder nicht unterstü Befehl)"
+ER_XAER_RMFAIL XAE07
+        eng "XAER_RMFAIL: The command cannot be executed when global transaction is in the  %.64s state"
+        ger "XAER_RMFAIL: DEr Befehl kann nicht ausgefüerden, wenn die globale Transaktion im Zustand %.64s ist"
+        rus "XAER_RMFAIL: ÜÕËÍÎÕÎÌÚ ×ÐÌÑØËÇÁÇÏÁØÁ ÔÁÚËÉ ÎÈÄÔÑ×ÓÓÏÎÉ'%.64s'"
+ER_XAER_OUTSIDE XAE09
+        eng "XAER_OUTSIDE: Some work is done outside global transaction"
+	ger "XAER_OUTSIDE: Einige Arbeiten werden außrhalb der globalen Transaktion verrichtet"
+ER_XAER_RMERR XAE03
+        eng "XAER_RMERR: Fatal error occurred in the transaction branch - check your data for consistency"
+	ger "XAER_RMERR: Schwerwiegender Fehler im Transaktionszweig - prüie Ihre Daten auf Konsistenz"
+ER_XA_RBROLLBACK XA100
+        eng "XA_RBROLLBACK: Transaction branch was rolled back"
+	ger "XA_RBROLLBACK: Transaktionszweig wurde zurüollt"
+ER_NONEXISTING_PROC_GRANT 42000 
+	eng "There is no such grant defined for user '%-.32s' on host '%-.64s' on routine '%-.64s'"
+	ger "Es gibt diese Berechtigung füutzer '%-.32s' auf Host '%-.64s' fütine '%-.64s' nicht"
+ER_PROC_AUTO_GRANT_FAIL
+	eng "Failed to grant EXECUTE and ALTER ROUTINE privileges"
+	ger "Gewäung von EXECUTE- und ALTER-ROUTINE-Rechten fehlgeschlagen"
+ER_PROC_AUTO_REVOKE_FAIL
+	eng "Failed to revoke all privileges to dropped routine"
+	ger "Rüme aller Rechte fü gelöte Routine fehlgeschlagen"
+ER_DATA_TOO_LONG 22001
+	eng "Data too long for column '%s' at row %ld"
+	ger "Daten zu lang füd '%s' in Zeile %ld"
+ER_SP_BAD_SQLSTATE 42000
+	eng "Bad SQLSTATE: '%s'"
+	ger "Ungür SQLSTATE: '%s'"
+ER_STARTUP
+	eng "%s: ready for connections.\nVersion: '%s'  socket: '%s'  port: %d  %s"
+	ger "%s: bereit fübindungen.\nVersion: '%s'  Socket: '%s'  Port: %d  %s"
+ER_LOAD_FROM_FIXED_SIZE_ROWS_TO_VAR
+        eng "Can't load value from file with fixed size rows to variable"
+	ger "Kann Wert aus Datei mit Zeilen fester Grönicht in Variable laden"
+ER_CANT_CREATE_USER_WITH_GRANT 42000
+	eng "You are not allowed to create a user with GRANT"
+	ger "Sie dükeinen Benutzer mit GRANT anlegen"
+ER_WRONG_VALUE_FOR_TYPE  
+	eng "Incorrect %-.32s value: '%-.128s' for function %-.32s"
+	ger "Falscher %-.32s-Wert: '%-.128s' füktion %-.32s"
+ER_TABLE_DEF_CHANGED
+	eng "Table definition has changed, please retry transaction"
+	ger "Tabellendefinition wurde geäert, bitte starten Sie die Transaktion neu"
+ER_SP_DUP_HANDLER 42000
+	eng "Duplicate handler declared in the same block"
+	ger "Doppelter Handler im selben Block deklariert"
+ER_SP_NOT_VAR_ARG 42000
+	eng "OUT or INOUT argument %d for routine %s is not a variable or NEW pseudo-variable in BEFORE trigger"
+	ger "OUT- oder INOUT-Argument %d fütine %s ist keine Variable"
+ER_SP_NO_RETSET 0A000
+	eng "Not allowed to return a result set from a %s"
+	ger "Rüe einer Ergebnismenge aus einer %s ist nicht erlaubt"
+ER_CANT_CREATE_GEOMETRY_OBJECT 22003 
+	eng "Cannot get geometry object from data you send to the GEOMETRY field"
+	ger "Kann kein Geometrieobjekt aus den Daten machen, die Sie dem GEOMETRY-Feld üben haben"
+ER_FAILED_ROUTINE_BREAK_BINLOG
+	eng "A routine failed and has neither NO SQL nor READS SQL DATA in its declaration and binary logging is enabled; if non-transactional tables were updated, the binary log will miss their changes"
+	ger "Eine Routine, die weder NO SQL noch READS SQL DATA in der Deklaration hat, schlug fehl und Binäogging ist aktiv. Wenn Nicht-Transaktions-Tabellen aktualisiert wurden, enthä das Binäog ihre Äderungen nicht"
+ER_BINLOG_UNSAFE_ROUTINE
+	eng "This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable)"
+	ger "Diese Routine hat weder DETERMINISTIC, NO SQL noch READS SQL DATA in der Deklaration und Binäogging ist aktiv (*vielleicht* sollten Sie die weniger sichere Variable log_bin_trust_routine_creators verwenden)"
+ER_BINLOG_CREATE_ROUTINE_NEED_SUPER
+	eng "You do not have the SUPER privilege and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable)"
+	ger "Sie haben keine SUPER-Berechtigung und Binäogging ist aktiv (*vielleicht* sollten Sie die weniger sichere Variable log_bin_trust_routine_creators verwenden)"
+ER_EXEC_STMT_WITH_OPEN_CURSOR
+	eng "You can't execute a prepared statement which has an open cursor associated with it. Reset the statement to re-execute it."
+	ger "Sie kön keine vorbereitete Anweisung ausfü die mit einem geöeten Cursor verknüst. Setzen Sie die Anweisung zurüm sie neu auszufü
+ER_STMT_HAS_NO_OPEN_CURSOR
+	eng "The statement (%lu) has no open cursor."
+	ger "Die Anweisung (%lu) hat keinen geöeten Cursor"
+ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG
+        eng "Explicit or implicit commit is not allowed in stored function or trigger."
+	ger "Explizites oder implizites Commit ist in gespeicherten Funktionen und in Triggern nicht erlaubt"
+ER_NO_DEFAULT_FOR_VIEW_FIELD
+        eng "Field of view '%-.64s.%-.64s' underlying table doesn't have a default value"
+	ger "Ein Feld der dem View '%-.64s.%-.64s' zugrundeliegenden Tabelle hat keinen Vorgabewert"
+ER_SP_NO_RECURSION
+        eng "Recursive stored functions and triggers are not allowed."
+	ger "Rekursive gespeicherte Routinen und Triggers sind nicht erlaubt"
+ER_TOO_BIG_SCALE 42000 S1009
+        eng "Too big scale %d specified for column '%-.64s'. Maximum is %d."
+	ger "Zu großr Skalierungsfaktor %d füd '%-.64s' angegeben. Maximum ist %d"
+ER_TOO_BIG_PRECISION 42000 S1009
+        eng "Too big precision %d specified for column '%-.64s'. Maximum is %d."
+	ger "Zu groß Genauigkeit %d füd '%-.64s' angegeben. Maximum ist %d"
+ER_M_BIGGER_THAN_D 42000 S1009
+        eng "For float(M,D), double(M,D) or decimal(M,D), M must be >= D (column '%-.64s')."
+	ger "FüAT(M,D), DOUBLE(M,D) oder DECIMAL(M,D) muss M >= D sein (Feld '%-.64s')"
+ER_WRONG_LOCK_OF_SYSTEM_TABLE
+        eng "You can't combine write-locking of system '%-.64s.%-.64s' table with other tables"
+	ger "Sie kön Schreibsperren auf der Systemtabelle '%-.64s.%-.64s' nicht mit anderen Tabellen kombinieren"
+ER_CONNECT_TO_FOREIGN_DATA_SOURCE
+        eng "Unable to connect to foreign data source: %.64s"
+	ger "Kann nicht mit Fremddatenquelle verbinden: %.64s"
+ER_QUERY_ON_FOREIGN_DATA_SOURCE
+        eng "There was a problem processing the query on the foreign data source. Data source error: %-.64"
+	ger "Bei der Verarbeitung der Abfrage ist in der Fremddatenquelle ein Problem aufgetreten. Datenquellenfehlermeldung: %-.64s"
+ER_FOREIGN_DATA_SOURCE_DOESNT_EXIST
+        eng "The foreign data source you are trying to reference does not exist. Data source error:  %-.64s"
+	ger "Die Fremddatenquelle, auf die Sie zugreifen wollen, existiert nicht. Datenquellenfehlermeldung:  %-.64s"
+ER_FOREIGN_DATA_STRING_INVALID_CANT_CREATE
+        eng "Can't create federated table. The data source connection string '%-.64s' is not in the correct format"
+        ger "Kann föierte Tabelle nicht erzeugen. Der Datenquellen-Verbindungsstring '%-.64s' hat kein korrektes Format"
+ER_FOREIGN_DATA_STRING_INVALID
+        eng "The data source connection string '%-.64s' is not in the correct format"
+	ger "Der Datenquellen-Verbindungsstring '%-.64s' hat kein korrektes Format"
+ER_CANT_CREATE_FEDERATED_TABLE  
+	eng "Can't create federated table. Foreign data src error:  %-.64s"
+	ger "Kann föierte Tabelle nicht erzeugen. Fremddatenquellenfehlermeldung:  %-.64s"
+ER_TRG_IN_WRONG_SCHEMA  
+	eng "Trigger in wrong schema"
+	ger "Trigger im falschen Schema"
+ER_STACK_OVERRUN_NEED_MORE
+	eng "Thread stack overrun:  %ld bytes used of a %ld byte stack, and %ld bytes needed.  Use 'mysqld -O thread_stack=#' to specify a bigger stack."
+	ger "Thread-Stack-Üerlauf: %ld Bytes eines %ld-Byte-Stacks in Verwendung, und %ld Bytes benöt. Verwenden Sie 'mysqld -O thread_stack=#', um einen gröen Stack anzugeben"
+ER_TOO_LONG_BODY 42000 S1009
+	eng "Routine body for '%-.100s' is too long"
+	ger "Routinen-Body fü.100s' ist zu lang"
+ER_WARN_CANT_DROP_DEFAULT_KEYCACHE
+	eng "Cannot drop default keycache"
+        ger "Der vorgabemäge SchlüCache kann nicht gelöt werden"
+ER_TOO_BIG_DISPLAYWIDTH 42000 S1009
+	eng "Display width out of range for column '%-.64s' (max = %d)"
+	ger "Anzeigebreite außrhalb des zuläigen Bereichs fülte '%-.64s' (Maximum: %d)"
+ER_XAER_DUPID XAE08
+        eng "XAER_DUPID: The XID already exists"
+	ger "XAER_DUPID: Die XID existiert bereits"
+ER_DATETIME_FUNCTION_OVERFLOW 22008
+        eng "Datetime function: %-.32s field overflow"
+	ger "Datetime-Funktion: %-.32s Feldüuf"
+ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG
+        eng "Can't update table '%-.64s' in stored function/trigger because it is already used by statement which invoked this stored function/trigger."
+	ger "Kann Tabelle '%-.64s' in gespeicherter Funktion oder Trigger nicht aktualisieren, weil sie bereits von der Anweisung verwendet wird, die diese gespeicherte Funktion oder den Trigger aufrief"
+ER_VIEW_PREVENT_UPDATE
+        eng "The definition of table '%-.64s' prevents operation %.64s on table '%-.64s'."
+	ger "Die Definition der Tabelle '%-.64s' verhindert die Operation %.64s auf Tabelle '%-.64s'"
+ER_PS_NO_RECURSION
+        eng "The prepared statement contains a stored routine call that refers to that same statement. It's not allowed to execute a prepared statement in such a recursive manner"
+	ger "Die vorbereitete Anweisung enthä einen Aufruf einer gespeicherten Routine, die auf eben dieselbe Anweisung verweist. Es ist nicht erlaubt, eine vorbereitete Anweisung in solch rekursiver Weise auszufü
+ER_SP_CANT_SET_AUTOCOMMIT
+	eng "Not allowed to set autocommit from a stored function or trigger"
+        ger "Es ist nicht erlaubt, innerhalb einer gespeicherten Funktion oder eines Triggers AUTOCOMMIT zu setzen"
+ER_MALFORMED_DEFINER
+	eng "Definer is not fully qualified"
+	ger "Definierer des View ist nicht vollstäig spezifiziert"
+ER_VIEW_FRM_NO_USER
+        eng "View '%-.64s'.'%-.64s' has no definer information (old table format). Current user is used as definer. Please recreate the view!"
+	ger "View '%-.64s'.'%-.64s' hat keine Definierer-Information (altes Tabellenformat). Der aktuelle Benutzer wird als Definierer verwendet. Bitte erstellen Sie den View neu"
+ER_VIEW_OTHER_USER
+	eng "You need the SUPER privilege for creation view with '%-.64s'@'%-.64s' definer"
+	ger "Sie brauchen die SUPER-Berechtigung, um einen View mit dem Definierer '%-.64s'@'%-.64s' zu erzeugen"
+ER_NO_SUCH_USER
+        eng "There is no '%-.64s'@'%-.64s' registered"
+	ger "'%-.64s'@'%-.64s' ist nicht registriert"
+ER_FORBID_SCHEMA_CHANGE
+	eng "Changing schema from '%-.64s' to '%-.64s' is not allowed."
+	ger "Wechsel des Schemas von '%-.64s' auf '%-.64s' ist nicht erlaubt"
+ER_ROW_IS_REFERENCED_2 23000
+	eng "Cannot delete or update a parent row: a foreign key constraint fails (%.192s)"
+	ger "Kann Eltern-Zeile nicht löen oder aktualisieren: eine Fremdschlüedingung schlä fehl (%.192s)"
+ER_NO_REFERENCED_ROW_2 23000
+	eng "Cannot add or update a child row: a foreign key constraint fails (%.192s)"
+	ger "Kann Kind-Zeile nicht hinzufüder aktualisieren: eine Fremdschlüedingung schlä fehl (%.192s)"
+ER_SP_BAD_VAR_SHADOW 42000
+	eng "Variable '%-.64s' must be quoted with `...`, or renamed"
+        ger "Variable '%-.64s' muss mit `...` geschüder aber umbenannt werden"
+ER_TRG_NO_DEFINER
+        eng "No definer attribute for trigger '%-.64s'.'%-.64s'. The trigger will be activated under the authorization of the invoker, which may have insufficient privileges. Please recreate the trigger."
+        ger "Kein Definierer-Attribut függer '%-.64s'.'%-.64s'. Der Trigger wird mit der Autorisierung des Aufrufers aktiviert, der möcherweise keine zureichenden Berechtigungen hat. Bitte legen Sie den Trigger neu an."
+ER_OLD_FILE_FORMAT
+        eng "'%-.64s' has an old format, you should re-create the '%s' object(s)"
+        ger "'%-.64s' hat altes Format, Sie sollten die '%s'-Objekt(e) neu erzeugen"
+ER_SP_RECURSION_LIMIT
+        eng "Recursive limit %d (as set by the max_sp_recursion_depth variable) was exceeded for routine %.64s"
+        ger "Rekursionsgrenze %d (durch Variable max_sp_recursion_depth gegeben) wurde fütine %.64s ühritten"
+ER_SP_PROC_TABLE_CORRUPT
+	eng "Failed to load routine %s. The table mysql.proc is missing, corrupt, or contains bad data (internal code %d)"
+ER_SP_WRONG_NAME 42000
+	eng "Incorrect routine name '%-.64s'"
+ER_TABLE_NEEDS_UPGRADE
+         eng "Table upgrade required. Please do \"REPAIR TABLE `%-.32s`\" to fix it!"
+ER_SP_NO_AGGREGATE 42000
+	eng "AGGREGATE is not supported for stored functions"
+ER_MAX_PREPARED_STMT_COUNT_REACHED 42000
+        eng "Can't create more than max_prepared_stmt_count statements (current value: %lu)"
+ER_VIEW_RECURSIVE
+        eng "`%-.64s`.`%-.64s` contains view recursion"
+ER_NON_GROUPING_FIELD_USED 42000
+	eng "non-grouping field '%-.64s' is used in %-.64s clause"
+ER_TABLE_CANT_HANDLE_SPKEYS
+        eng "The used table type doesn't support SPATIAL indexes"
+ER_NO_TRIGGERS_ON_SYSTEM_SCHEMA
+	eng "Triggers can not be created on system tables"

Added: trunk/data/icons/16x16/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/data/icons/16x16/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,6 @@
+include $(top_srcdir)/Makefile.decl
+
+icondir = $(datadir)/icons/hicolor/16x16/apps
+icon_DATA = tracker.png
+
+EXTRA_DIST = $(icon_DATA)

Added: trunk/data/icons/16x16/tracker.png
==============================================================================
Binary file. No diff available.

Added: trunk/data/icons/22x22/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/data/icons/22x22/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,6 @@
+include $(top_srcdir)/Makefile.decl
+
+icondir = $(datadir)/icons/hicolor/22x22/apps
+icon_DATA = tracker.png
+
+EXTRA_DIST = $(icon_DATA)

Added: trunk/data/icons/22x22/tracker.png
==============================================================================
Binary file. No diff available.

Added: trunk/data/icons/24x24/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/data/icons/24x24/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,6 @@
+include $(top_srcdir)/Makefile.decl
+
+icondir = $(datadir)/icons/hicolor/24x24/apps
+icon_DATA = tracker.png
+
+EXTRA_DIST = $(icon_DATA)

Added: trunk/data/icons/24x24/tracker.png
==============================================================================
Binary file. No diff available.

Added: trunk/data/icons/32x32/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/data/icons/32x32/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,6 @@
+include $(top_srcdir)/Makefile.decl
+
+icondir = $(datadir)/icons/hicolor/32x32/apps
+icon_DATA = tracker.png
+
+EXTRA_DIST = $(icon_DATA)

Added: trunk/data/icons/32x32/tracker.png
==============================================================================
Binary file. No diff available.

Added: trunk/data/icons/48x48/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/data/icons/48x48/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,6 @@
+include $(top_srcdir)/Makefile.decl
+
+icondir = $(datadir)/icons/hicolor/48x48/apps
+icon_DATA = tracker.png
+
+EXTRA_DIST = $(icon_DATA)

Added: trunk/data/icons/48x48/tracker.png
==============================================================================
Binary file. No diff available.

Added: trunk/data/icons/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/data/icons/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,16 @@
+include $(top_srcdir)/Makefile.decl
+
+SUBDIRS = 16x16 22x22 24x24 32x32 48x48 scalable
+
+gtk_update_icon_cache = gtk-update-icon-cache -f -t $(datadir)/icons/hicolor
+
+install-data-hook: update-icon-cache
+uninstall-hook: update-icon-cache
+update-icon-cache:
+	@-if test -z "$(DESTDIR)"; then \
+		echo "Updating Gtk icon cache."; \
+		$(gtk_update_icon_cache); \
+	else \
+		echo "*** Icon cache not updated.  After (un)install, run this:"; \
+		echo "***   $(gtk_update_icon_cache)"; \
+	fi

Added: trunk/data/icons/scalable/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/data/icons/scalable/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,6 @@
+include $(top_srcdir)/Makefile.decl
+
+icondir = $(datadir)/icons/hicolor/scalable/apps
+icon_DATA = tracker.svg
+
+EXTRA_DIST = $(icon_DATA)

Added: trunk/data/icons/scalable/tracker.svg
==============================================================================
--- (empty file)
+++ trunk/data/icons/scalable/tracker.svg	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,1347 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:i="http://ns.adobe.com/AdobeIllustrator/10.0/";
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://web.resource.org/cc/";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:xlink="http://www.w3.org/1999/xlink";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="48"
+   height="48"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.44.1"
+   version="1.0"
+   sodipodi:docbase="/home/baze"
+   sodipodi:docname="tracker.svg"
+   inkscape:export-filename="/home/baze/tracker.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs4">
+    <radialGradient
+       r="45"
+       fy="106.49996"
+       fx="-25"
+       cy="106.49996"
+       cx="-25"
+       gradientTransform="matrix(3.466166,-6.346783e-2,7.070491e-2,3.695342,32.7241,-321.4409)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3293"
+       xlink:href="#linearGradient3075"
+       inkscape:collect="always" />
+    <radialGradient
+       r="46"
+       fy="99.774971"
+       fx="-24.85253"
+       cy="99.774971"
+       cx="-24.85253"
+       gradientTransform="matrix(0.934777,-0.846006,0.891541,0.985091,-120.2744,-50.59553)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3290"
+       xlink:href="#linearGradient3139"
+       inkscape:collect="always" />
+    <linearGradient
+       gradientTransform="matrix(1.2,0,0,1.2,-18.8,-66.7005)"
+       y2="97.761848"
+       x2="-44.159863"
+       y1="103.68449"
+       x1="-39.098888"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3287"
+       xlink:href="#linearGradient3149"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="97.761848"
+       x2="-44.159863"
+       y1="103.68449"
+       x1="-39.098888"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3283"
+       xlink:href="#linearGradient3149"
+       inkscape:collect="always" />
+    <radialGradient
+       r="46"
+       fy="99.774971"
+       fx="-24.85253"
+       cy="99.774971"
+       cx="-24.85253"
+       gradientTransform="matrix(0.778981,-0.705005,0.742951,0.820909,-84.56197,13.42081)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3281"
+       xlink:href="#linearGradient3139"
+       inkscape:collect="always" />
+    <radialGradient
+       r="45"
+       fy="106.49996"
+       fx="-25"
+       cy="106.49996"
+       cx="-25"
+       gradientTransform="matrix(2.888472,-5.288986e-2,5.892076e-2,3.079452,42.93675,-212.2837)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3279"
+       xlink:href="#linearGradient3075"
+       inkscape:collect="always" />
+    <radialGradient
+       r="64.122231"
+       fy="123.41074"
+       fx="31.487459"
+       cy="123.41074"
+       cx="64.37719"
+       gradientTransform="matrix(1,0,0,8.747514e-2,0,112.6154)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2348"
+       xlink:href="#linearGradient2345"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2215">
+      <stop
+         style="stop-color:#8e8e8e;stop-opacity:1;"
+         offset="0"
+         id="stop2217" />
+      <stop
+         style="stop-color:#f8f8f8;stop-opacity:1;"
+         offset="1"
+         id="stop2219" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2221">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop2223" />
+      <stop
+         id="stop2225"
+         offset="0.40659341"
+         style="stop-color:#000000;stop-opacity:0.68235294;" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0.48736462;"
+         offset="0.45329672"
+         id="stop2227" />
+      <stop
+         id="stop2229"
+         offset="0.5"
+         style="stop-color:#000000;stop-opacity:0.36101082;" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0.15162455;"
+         offset="0.70604396"
+         id="stop2231" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop2233" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2235">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop2237" />
+      <stop
+         id="stop2239"
+         offset="0.42597079"
+         style="stop-color:#ffffff;stop-opacity:1;" />
+      <stop
+         id="stop2241"
+         offset="0.5892781"
+         style="stop-color:#f1f1f1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#eaeaea;stop-opacity:1;"
+         offset="0.80219781"
+         id="stop2243" />
+      <stop
+         style="stop-color:#dfdfdf;stop-opacity:1;"
+         offset="1"
+         id="stop2245" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3075"
+       id="radialGradient1458"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.888472,-5.288986e-2,5.892076e-2,3.079452,42.93675,-212.2837)"
+       cx="-25"
+       cy="106.49996"
+       fx="-25"
+       fy="106.49996"
+       r="45" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3139"
+       id="radialGradient1460"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.778981,-0.705005,0.742951,0.820909,-84.56197,13.42081)"
+       cx="-24.85253"
+       cy="99.774971"
+       fx="-24.85253"
+       fy="99.774971"
+       r="46" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3149"
+       id="linearGradient1462"
+       gradientUnits="userSpaceOnUse"
+       x1="-39.098888"
+       y1="103.68449"
+       x2="-44.159863"
+       y2="97.761848" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2345"
+       id="radialGradient2250"
+       cx="64.37719"
+       cy="123.41074"
+       fx="31.487459"
+       fy="123.41074"
+       r="64.122231"
+       gradientTransform="matrix(1,0,0,8.747514e-2,0,112.6154)"
+       gradientUnits="userSpaceOnUse" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2257"
+       id="radialGradient1495"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.520175,8.839467e-2,-0.843351,13.788,109.1206,-1545.323)"
+       cx="42.617531"
+       cy="120.64188"
+       fx="42.617531"
+       fy="120.64188"
+       r="3.406888" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3311"
+       id="radialGradient1497"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(6.228741e-2,-3.825032e-4,4.90218e-3,0.798611,68.90433,5.49306)"
+       cx="95.505852"
+       cy="59.591507"
+       fx="95.505852"
+       fy="59.591507"
+       r="47.746403" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3225"
+       id="radialGradient1499"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.297068,-1.880044e-3,7.085819e-4,0.48867,6.806484,-3.45491)"
+       cx="49.009884"
+       cy="8.4953117"
+       fx="47.370888"
+       fy="6.7701697"
+       r="3.9750404" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3217"
+       id="linearGradient1501"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.29707,0,0,1.29707,6.81152,-10.31269)"
+       x1="48.914677"
+       y1="2.9719031"
+       x2="48.913002"
+       y2="2.5548496" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3207"
+       id="radialGradient1503"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.29707,0,0,0.1578,6.81152,-7.300115)"
+       cx="49.011971"
+       cy="2.6743078"
+       fx="49.011971"
+       fy="2.6743078"
+       r="1.7246193" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3235"
+       id="linearGradient1505"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.297068,-1.880044e-3,1.880044e-3,1.297068,6.796523,-10.3225)"
+       x1="48.498562"
+       y1="0.81150496"
+       x2="48.732723"
+       y2="2.3657269" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3251"
+       id="linearGradient1507"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.28993,0,0,1.29707,7.14915,-10.31269)"
+       x1="46.051746"
+       y1="3.0999987"
+       x2="46.051746"
+       y2="2.395859" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3273"
+       id="radialGradient1509"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.860164,0,0,0.1578,24.50481,-7.300115)"
+       cx="49.011971"
+       cy="2.6743078"
+       fx="49.011971"
+       fy="2.6743078"
+       r="1.7246193" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3251"
+       id="linearGradient1511"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.279856,0,0,1.29707,-133.1341,-10.31269)"
+       x1="46.051746"
+       y1="3.0999987"
+       x2="46.051746"
+       y2="2.395859" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3259"
+       id="radialGradient1513"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.853446,0,0,0.1578,-115.9141,-7.300115)"
+       cx="49.011971"
+       cy="2.6743078"
+       fx="49.011971"
+       fy="2.6743078"
+       r="1.7246193" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3303"
+       id="radialGradient1515"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,2.608014e-2,0,7.26766)"
+       cx="34.677639"
+       cy="7.4622769"
+       fx="34.677639"
+       fy="7.4622769"
+       r="47.595196" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3325"
+       id="radialGradient1517"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-1.511766,-6.865741e-3,4.187271e-5,-9.110636e-3,87.10184,7.76835)"
+       cx="34.677639"
+       cy="7.4622769"
+       fx="34.677639"
+       fy="7.4622769"
+       r="47.595196" />
+    <linearGradient
+       id="linearGradient3325">
+      <stop
+         id="stop3327"
+         offset="0"
+         style="stop-color:#ffffff;stop-opacity:1;" />
+      <stop
+         id="stop3329"
+         offset="1"
+         style="stop-color:#ffffff;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3311">
+      <stop
+         style="stop-color:#2d2d2d;stop-opacity:1;"
+         offset="0"
+         id="stop3313" />
+      <stop
+         id="stop3319"
+         offset="0.5"
+         style="stop-color:#000000;stop-opacity:1;" />
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="1"
+         id="stop3315" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3303">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0.68345326;"
+         offset="0"
+         id="stop3305" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop3307" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3273">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0.55035973;"
+         offset="0"
+         id="stop3275" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop3277" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3259">
+      <stop
+         id="stop3261"
+         offset="0"
+         style="stop-color:#ffffff;stop-opacity:0.55035973;" />
+      <stop
+         id="stop3263"
+         offset="1"
+         style="stop-color:#000000;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2280">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop2282" />
+      <stop
+         style="stop-color:#aeaeae;stop-opacity:1;"
+         offset="1"
+         id="stop2284" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3217">
+      <stop
+         style="stop-color:#252525;stop-opacity:1;"
+         offset="0"
+         id="stop3219" />
+      <stop
+         style="stop-color:#252525;stop-opacity:0;"
+         offset="1"
+         id="stop3221" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3207">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop3209" />
+      <stop
+         style="stop-color:#252525;stop-opacity:0;"
+         offset="1"
+         id="stop3211" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2257">
+      <stop
+         style="stop-color:#b4942a;stop-opacity:1;"
+         offset="0"
+         id="stop2259" />
+      <stop
+         style="stop-color:#e4dcc9;stop-opacity:1"
+         offset="1"
+         id="stop2261" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3235"
+       inkscape:collect="always">
+      <stop
+         id="stop3237"
+         offset="0"
+         style="stop-color:#ffffff;stop-opacity:1;" />
+      <stop
+         id="stop3239"
+         offset="1"
+         style="stop-color:#ffffff;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3251">
+      <stop
+         id="stop3253"
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;" />
+      <stop
+         style="stop-color:#090909;stop-opacity:0.67870039;"
+         offset="0.7087912"
+         id="stop14161" />
+      <stop
+         id="stop3255"
+         offset="1"
+         style="stop-color:#131313;stop-opacity:0;" />
+    </linearGradient>
+    <foreignObject
+       requiredExtensions="http://ns.adobe.com/AdobeIllustrator/10.0/";
+       x="0"
+       y="0"
+       width="1"
+       height="1"
+       id="foreignObject1939">
+      <i:pgfRef
+         xlink:href="#adobe_illustrator_pgf" />
+    </foreignObject>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3291"
+       id="radialGradient1566"
+       cx="63.912209"
+       cy="115.70919"
+       fx="98.520187"
+       fy="119.62119"
+       r="63.912209"
+       gradientTransform="matrix(1,0,0,0.197802,0,92.82166)"
+       gradientUnits="userSpaceOnUse" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3291"
+       id="radialGradient1739"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,0.197802,0,92.82166)"
+       cx="63.912209"
+       cy="115.7093"
+       fx="63.912209"
+       fy="115.7093"
+       r="63.912209" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3291"
+       id="radialGradient2096"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,0.197802,0,92.82166)"
+       cx="63.912209"
+       cy="115.70919"
+       fx="98.520187"
+       fy="119.62119"
+       r="63.912209" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3291"
+       id="radialGradient2098"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,0.197802,0,92.82166)"
+       cx="63.912209"
+       cy="115.7093"
+       fx="63.912209"
+       fy="115.7093"
+       r="63.912209" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4171">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop4173" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop4175" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient5176">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop5178" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop5180" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient5142">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop5144" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop5146" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient5111">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop5113" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop5115" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4206">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop4208" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop4210" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3319">
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="0"
+         id="stop3321" />
+      <stop
+         id="stop2287"
+         offset="0.86263734"
+         style="stop-color:#000000;stop-opacity:0;" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0.27797833;"
+         offset="1"
+         id="stop3323" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3201">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop3203" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop3205" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient2288">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop2290" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop2292" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3291">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop3293" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop3295" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3225">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop3227" />
+      <stop
+         style="stop-color:#aeaeae;stop-opacity:1;"
+         offset="1"
+         id="stop3229" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3291"
+       id="radialGradient3297"
+       cx="63.912209"
+       cy="115.70919"
+       fx="98.520187"
+       fy="119.62119"
+       r="63.912209"
+       gradientTransform="matrix(1,0,0,0.197802,0,92.82166)"
+       gradientUnits="userSpaceOnUse" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3201"
+       id="radialGradient4208"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,0.350763,0,33.62806)"
+       cx="53.82272"
+       cy="51.796238"
+       fx="53.363365"
+       fy="53.704475"
+       r="5.2844129" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3201"
+       id="radialGradient4210"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,0.350763,0,33.62806)"
+       cx="53.82272"
+       cy="51.796238"
+       fx="53.363365"
+       fy="53.704475"
+       r="5.2844129" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4206"
+       id="radialGradient4214"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,1.008658,0,-0.322745)"
+       cx="36.920532"
+       cy="37.277058"
+       fx="28.846222"
+       fy="20.466549"
+       r="31.704992" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5142"
+       id="linearGradient4216"
+       gradientUnits="userSpaceOnUse"
+       x1="67.70639"
+       y1="137.9411"
+       x2="60.432804"
+       y2="42.427299" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5111"
+       id="linearGradient4218"
+       gradientUnits="userSpaceOnUse"
+       x1="34.841892"
+       y1="-7.142458"
+       x2="35.561386"
+       y2="11.415728" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5111"
+       id="linearGradient4220"
+       gradientUnits="userSpaceOnUse"
+       x1="34.841892"
+       y1="-7.142458"
+       x2="35.561386"
+       y2="11.415728" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5111"
+       id="linearGradient4222"
+       gradientUnits="userSpaceOnUse"
+       x1="34.841892"
+       y1="-7.142458"
+       x2="35.561386"
+       y2="11.415728" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5176"
+       id="radialGradient4224"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.743822,4.610586e-7,-4.175093e-7,0.670083,-30.109,9.1749)"
+       cx="40.478767"
+       cy="27.809845"
+       fx="40.478779"
+       fy="42.86652"
+       r="30.543081" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5111"
+       id="linearGradient4226"
+       gradientUnits="userSpaceOnUse"
+       x1="34.841892"
+       y1="-7.142458"
+       x2="35.561386"
+       y2="11.415728" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5111"
+       id="linearGradient4229"
+       gradientUnits="userSpaceOnUse"
+       x1="34.841892"
+       y1="-7.142458"
+       x2="35.561386"
+       y2="11.415728" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5111"
+       id="linearGradient4231"
+       gradientUnits="userSpaceOnUse"
+       x1="34.841892"
+       y1="-7.142458"
+       x2="35.561386"
+       y2="11.415728" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4171"
+       id="linearGradient1380"
+       gradientUnits="userSpaceOnUse"
+       x1="67.515175"
+       y1="86.706062"
+       x2="63.401665"
+       y2="61.357201"
+       gradientTransform="matrix(1.004983,0,0,1.005797,-3.514071e-3,-9.765696e-2)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3225"
+       id="linearGradient1395"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-0.342816,0.414732,0.409438,0.344388,68.74377,42.51885)"
+       spreadMethod="reflect"
+       x1="28.071711"
+       y1="20.204628"
+       x2="40.198689"
+       y2="20.20463" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2288"
+       id="radialGradient1401"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.599945,-0.725801,7.847448,6.600672,-444.8151,-313.0422)"
+       cx="32.489605"
+       cy="69.225014"
+       fx="29.738691"
+       fy="69.225014"
+       r="8.3332367" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2288"
+       id="radialGradient1404"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.599945,-0.725801,7.847448,6.600672,-444.8151,-313.0422)"
+       cx="49.157429"
+       cy="62.380642"
+       fx="45.900455"
+       fy="62.298256"
+       r="8.3332367" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3319"
+       id="radialGradient1412"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.993088,2.250037e-2,-2.180165e-2,0.952313,1.715624,0.815028)"
+       cx="39.126225"
+       cy="44.269405"
+       fx="39.12624"
+       fy="44.269413"
+       r="34.469494" />
+    <linearGradient
+       gradientUnits="userSpaceOnUse"
+       y2="97.761848"
+       x2="-44.159863"
+       y1="103.68449"
+       x1="-39.098888"
+       id="linearGradient3155"
+       xlink:href="#linearGradient3149"
+       inkscape:collect="always" />
+    <radialGradient
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.778981,-0.705005,0.742951,0.820909,-84.56197,13.42081)"
+       r="46"
+       fy="99.774971"
+       fx="-24.85253"
+       cy="99.774971"
+       cx="-24.85253"
+       id="radialGradient3145"
+       xlink:href="#linearGradient3139"
+       inkscape:collect="always" />
+    <radialGradient
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.888472,-5.288986e-2,5.892076e-2,3.079452,42.93675,-212.2837)"
+       r="45"
+       fy="106.49996"
+       fx="-25"
+       cy="106.49996"
+       cx="-25"
+       id="radialGradient3083"
+       xlink:href="#linearGradient3075"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3075">
+      <stop
+         id="stop3077"
+         offset="0"
+         style="stop-color:#ffffff;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.42597079"
+         id="stop3093" />
+      <stop
+         style="stop-color:#f1f1f1;stop-opacity:1;"
+         offset="0.5892781"
+         id="stop3085" />
+      <stop
+         id="stop3087"
+         offset="0.80219781"
+         style="stop-color:#eaeaea;stop-opacity:1;" />
+      <stop
+         id="stop3079"
+         offset="1"
+         style="stop-color:#dfdfdf;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3139">
+      <stop
+         id="stop3141"
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0.68235294;"
+         offset="0.40659341"
+         id="stop3162" />
+      <stop
+         id="stop3164"
+         offset="0.45329672"
+         style="stop-color:#000000;stop-opacity:0.48736462;" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0.36101082;"
+         offset="0.5"
+         id="stop3158" />
+      <stop
+         id="stop3160"
+         offset="0.70604396"
+         style="stop-color:#000000;stop-opacity:0.15162455;" />
+      <stop
+         id="stop3143"
+         offset="1"
+         style="stop-color:#000000;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3149">
+      <stop
+         id="stop3151"
+         offset="0"
+         style="stop-color:#8e8e8e;stop-opacity:1;" />
+      <stop
+         id="stop3153"
+         offset="1"
+         style="stop-color:#f8f8f8;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3461">
+      <stop
+         id="stop3463"
+         offset="0"
+         style="stop-color:#bbbbbb;stop-opacity:1;" />
+      <stop
+         id="stop3465"
+         offset="1"
+         style="stop-color:#bbbbbb;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3471">
+      <stop
+         id="stop3473"
+         offset="0"
+         style="stop-color:#000000;stop-opacity:0.48736462;" />
+      <stop
+         id="stop3475"
+         offset="1"
+         style="stop-color:#000000;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2345"
+       inkscape:collect="always">
+      <stop
+         id="stop2347"
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;" />
+      <stop
+         id="stop2349"
+         offset="1"
+         style="stop-color:#000000;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4440">
+      <stop
+         id="stop4442"
+         offset="0"
+         style="stop-color:#7d7d7d;stop-opacity:1;" />
+      <stop
+         style="stop-color:#b1b1b1;stop-opacity:1.0000000;"
+         offset="0.50000000"
+         id="stop4448" />
+      <stop
+         id="stop4444"
+         offset="1.0000000"
+         style="stop-color:#686868;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4454">
+      <stop
+         id="stop4456"
+         offset="0.0000000"
+         style="stop-color:#ce5c00;stop-opacity:0.20784314" />
+      <stop
+         id="stop4458"
+         offset="1.0000000"
+         style="stop-color:#f57900;stop-opacity:0.67619050" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4467">
+      <stop
+         id="stop4469"
+         offset="0"
+         style="stop-color:#ffffff;stop-opacity:1;" />
+      <stop
+         id="stop4471"
+         offset="1.0000000"
+         style="stop-color:#ffffff;stop-opacity:0.24761905;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4477"
+       inkscape:collect="always">
+      <stop
+         id="stop4479"
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;" />
+      <stop
+         id="stop4481"
+         offset="1"
+         style="stop-color:#000000;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4487"
+       inkscape:collect="always">
+      <stop
+         id="stop4489"
+         offset="0"
+         style="stop-color:#ffffff;stop-opacity:1;" />
+      <stop
+         id="stop4491"
+         offset="1"
+         style="stop-color:#ffffff;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2366">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop2368" />
+      <stop
+         id="stop2374"
+         offset="0.50000000"
+         style="stop-color:#ffffff;stop-opacity:0.21904762;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2370" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2846">
+      <stop
+         style="stop-color:#8a8a8a;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2848" />
+      <stop
+         style="stop-color:#484848;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2850" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2997">
+      <stop
+         id="stop2999"
+         offset="0"
+         style="stop-color:#7d7d7d;stop-opacity:1;" />
+      <stop
+         style="stop-color:#b1b1b1;stop-opacity:1.0000000;"
+         offset="0.50000000"
+         id="stop3001" />
+      <stop
+         id="stop3003"
+         offset="1.0000000"
+         style="stop-color:#686868;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2991">
+      <stop
+         id="stop2993"
+         offset="0.0000000"
+         style="stop-color:#729fcf;stop-opacity:0.20784314;" />
+      <stop
+         id="stop2995"
+         offset="1.0000000"
+         style="stop-color:#729fcf;stop-opacity:0.67619050;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2985">
+      <stop
+         id="stop2987"
+         offset="0"
+         style="stop-color:#ffffff;stop-opacity:1;" />
+      <stop
+         id="stop2989"
+         offset="1.0000000"
+         style="stop-color:#ffffff;stop-opacity:0.24761905;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2965">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop2967" />
+      <stop
+         id="stop2969"
+         offset="0.50000000"
+         style="stop-color:#ffffff;stop-opacity:0.21904762;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2971" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2959">
+      <stop
+         style="stop-color:#8a8a8a;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2961" />
+      <stop
+         style="stop-color:#484848;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2963" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4477"
+       id="radialGradient3034"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,0.237968,0,28.93278)"
+       cx="24.130018"
+       cy="37.967922"
+       fx="24.130018"
+       fy="37.967922"
+       r="16.528622" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2366"
+       id="linearGradient3040"
+       gradientUnits="userSpaceOnUse"
+       x1="18.292673"
+       y1="13.602121"
+       x2="17.500893"
+       y2="25.743469" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4487"
+       id="radialGradient3042"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,0.237968,0,28.93278)"
+       cx="24.130018"
+       cy="37.967922"
+       fx="24.130018"
+       fy="37.967922"
+       r="16.528622" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4454"
+       id="radialGradient3044"
+       gradientUnits="userSpaceOnUse"
+       cx="18.240929"
+       cy="21.817987"
+       fx="18.240929"
+       fy="21.817987"
+       r="8.3085051" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4467"
+       id="radialGradient3049"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.592963,0,0,2.252104,-24.89514,-18.08455)"
+       cx="15.414371"
+       cy="13.078408"
+       fx="15.414371"
+       fy="13.078408"
+       r="6.6562500" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4440"
+       id="linearGradient3056"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.334593,0,0,1.291292,-6.809229,-6.604212)"
+       x1="30.656250"
+       y1="34.000000"
+       x2="33.218750"
+       y2="31.062500" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2846"
+       id="linearGradient3060"
+       gradientUnits="userSpaceOnUse"
+       x1="27.366341"
+       y1="26.580296"
+       x2="31.335964"
+       y2="30.557772"
+       gradientTransform="translate(0.164613,0.856446)" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     gridtolerance="50"
+     guidetolerance="50"
+     objecttolerance="10"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="31.998886"
+     inkscape:cx="22.841726"
+     inkscape:cy="24.537509"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     width="48px"
+     height="48px"
+     inkscape:window-width="987"
+     inkscape:window-height="859"
+     inkscape:window-x="97"
+     inkscape:window-y="114"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     inkscape:guide-points="false"
+     inkscape:grid-points="true"
+     showgrid="true"
+     gridspacingx="0.5px"
+     gridspacingy="0.5px">
+    <sodipodi:guide
+       orientation="horizontal"
+       position="69.953064"
+       id="guide2827" />
+    <sodipodi:guide
+       orientation="vertical"
+       position="-33.082496"
+       id="guide2829" />
+    <sodipodi:guide
+       orientation="vertical"
+       position="84.852814"
+       id="guide2831" />
+    <sodipodi:guide
+       orientation="horizontal"
+       position="-46.214479"
+       id="guide2835" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <rect
+       style="opacity:0.57786889;fill:white;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.63199997;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1"
+       id="rect1327"
+       width="1"
+       height="0"
+       x="101.96484"
+       y="50.58419" />
+    <rect
+       style="opacity:0.57786889;fill:white;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.63199997;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1"
+       id="rect2306"
+       width="1"
+       height="0"
+       x="-155.40515"
+       y="7.1223102" />
+    <path
+       sodipodi:nodetypes="ccc"
+       id="path2318"
+       d="M -314.34258,-66.093757 L -314.34258,-68.888466 L -314.34258,-66.093757 z "
+       style="fill:white;fill-opacity:0.75688076;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1" />
+    <g
+       style="opacity:0.40163933"
+       transform="matrix(-0.782995,0,0,0.650879,-2637.201,620.4239)"
+       id="g4640" />
+    <g
+       id="g4646"
+       transform="matrix(-0.782995,0,0,0.650879,-2637.201,620.4239)"
+       style="opacity:0.40163933" />
+    <g
+       id="g4730"
+       transform="matrix(1,0,0,0.650879,563.7618,627.7517)"
+       style="opacity:0.40163933" />
+    <g
+       style="opacity:0.40163933"
+       transform="matrix(1,0,0,0.650879,563.7618,627.7517)"
+       id="g4748" />
+    <path
+       transform="matrix(1.446431,0,0,1.51999,-10.80992,-16.89523)"
+       d="M 40.65864 37.967922 A 16.528622 3.9332814 0 1 1  7.6013966,37.967922 A 16.528622 3.9332814 0 1 1  40.65864 37.967922 z"
+       sodipodi:ry="3.9332814"
+       sodipodi:rx="16.528622"
+       sodipodi:cy="37.967922"
+       sodipodi:cx="24.130018"
+       id="path4475"
+       style="opacity:0.17112301;color:black;fill:url(#radialGradient3034);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       sodipodi:type="arc" />
+    <path
+       style="color:black;fill:#dcdcdc;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3060);stroke-width:2.00000095;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 18.792182,4.0000008 C 10.653052,4.0000008 4.0473812,10.605672 4.0473812,18.744802 C 4.0473812,26.883932 10.653052,33.489604 18.792182,33.489604 C 22.271737,33.489604 25.343183,32.105211 27.865905,30.086957 C 27.660528,31.093838 27.78787,32.122325 28.622049,32.846882 L 39.586133,42.374292 C 40.819549,43.445621 42.673595,43.305252 43.744923,42.071835 C 44.816251,40.838417 44.675882,38.984373 43.442466,37.913045 L 32.478382,28.385634 C 31.806855,27.802355 30.985504,27.629665 30.172144,27.742912 C 32.158844,25.23049 33.536983,22.194109 33.536983,18.744802 C 33.536983,10.605672 26.931312,4.0000008 18.792182,4.0000008 z M 18.716567,5.2261841 C 26.356026,5.2261841 32.008342,10.015135 32.008342,18.517959 C 32.008342,27.193072 26.191652,31.809734 18.716567,31.809734 C 11.413618,31.809734 5.4247936,26.331642 5.4247936,18.517959 C 5.4247936,10.533852 11.249432,5.226184 18.716567,5.2261841 z "
+       id="path2844"
+       sodipodi:nodetypes="csscccscccscczzzz" />
+    <path
+       id="path4430"
+       d="M 18.767518,3.9368011 C 10.602078,3.9368011 3.9750538,10.563825 3.9750538,18.729265 C 3.9750538,26.894705 10.602078,33.521729 18.767518,33.521729 C 22.258321,33.521729 25.339695,32.132862 27.870573,30.108084 C 27.664532,31.11822 27.792285,32.150031 28.62916,32.87693 L 39.628686,42.435137 C 40.866089,43.509929 42.726128,43.369107 43.800919,42.131702 C 44.87571,40.894298 44.734887,39.03426 43.497484,37.959469 L 32.497959,28.401261 C 31.824261,27.816097 31.000255,27.642848 30.184266,27.756462 C 32.177388,25.235918 33.559982,22.189722 33.559982,18.729265 C 33.559982,10.563825 26.932958,3.9368011 18.767518,3.9368011 z M 18.691659,7.1228703 C 24.972767,7.1228705 30.070477,12.220581 30.070477,18.501689 C 30.070477,24.782797 24.972767,29.880507 18.691659,29.880507 C 12.410551,29.880507 7.3128406,24.782797 7.3128406,18.501689 C 7.3128408,12.220581 12.410551,7.1228703 18.691659,7.1228703 z "
+       style="color:black;fill:#dcdcdc;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.00000036;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+    <path
+       sodipodi:nodetypes="ccccc"
+       id="path4438"
+       d="M 39.671617,42.434136 C 39.192945,40.160949 41.068947,37.622714 43.25567,37.645761 C 43.25567,37.645761 32.495303,28.38765 32.495303,28.38765 C 29.550512,28.330944 28.225801,30.660266 28.718489,32.987572 L 39.671617,42.434136 z "
+       style="color:black;fill:url(#linearGradient3056);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+    <path
+       transform="matrix(1.245743,0,0,1.245743,-3.260733,-5.320587)"
+       d="M 28.549437 18.920233 A 11.048544 11.048544 0 1 1  6.4523487,18.920233 A 11.048544 11.048544 0 1 1  28.549437 18.920233 z"
+       sodipodi:ry="11.048544"
+       sodipodi:rx="11.048544"
+       sodipodi:cy="18.920233"
+       sodipodi:cx="17.500893"
+       id="path4450"
+       style="color:black;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3040);stroke-width:0.8027336;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       sodipodi:type="arc" />
+    <path
+       sodipodi:type="arc"
+       style="color:black;fill:url(#radialGradient3042);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       id="path4485"
+       sodipodi:cx="24.130018"
+       sodipodi:cy="37.967922"
+       sodipodi:rx="16.528622"
+       sodipodi:ry="3.9332814"
+       d="M 40.65864 37.967922 A 16.528622 3.9332814 0 1 1  7.6013966,37.967922 A 16.528622 3.9332814 0 1 1  40.65864 37.967922 z"
+       transform="matrix(0.497764,0,0,0.609621,9.138139,16.47574)" />
+    <rect
+       transform="matrix(0.752986,0.658037,-0.648902,0.760872,0,0)"
+       ry="1.8879365"
+       rx="2.1366615"
+       y="0.67746985"
+       x="41.054398"
+       height="4.4404783"
+       width="19.048445"
+       id="rect4495"
+       style="opacity:0.43315507;color:black;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:white;stroke-width:1.00003123;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+    <path
+       transform="matrix(1.398614,0,0,1.398614,-6.059725,-7.442512)"
+       d="M 25.897786 18.478292 A 8.3085051 8.3085051 0 1 1  9.280776,18.478292 A 8.3085051 8.3085051 0 1 1  25.897786 18.478292 z"
+       sodipodi:ry="8.3085051"
+       sodipodi:rx="8.3085051"
+       sodipodi:cy="18.478292"
+       sodipodi:cx="17.589281"
+       id="path4452"
+       style="color:black;fill:url(#radialGradient3044);fill-opacity:1;fill-rule:evenodd;stroke:#ce5c00;stroke-width:0.71499395;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dashoffset:0;stroke-opacity:1;visibility:visible"
+       sodipodi:type="arc" />
+    <path
+       style="fill:#272c2e;fill-opacity:1;fill-rule:evenodd;stroke:black;stroke-width:0.98407042px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 27.166932,14.788887 C 25.733227,13.44292 20.883226,16.995107 23.033784,19.014059 C 25.184342,21.033011 28.600636,16.134854 27.166932,14.788887 z "
+       id="path2116"
+       sodipodi:nodetypes="css" />
+    <path
+       style="fill:#272c2e;fill-opacity:1;fill-rule:evenodd;stroke:black;stroke-width:0.92405277px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 23.925869,25.433801 C 24.573186,26.022493 28.163554,24.011307 27.442852,22.036782 C 26.722156,20.062255 21.98392,23.667724 23.925869,25.433801 z "
+       id="path2118"
+       sodipodi:nodetypes="css" />
+    <path
+       style="fill:#272c2e;fill-opacity:1;fill-rule:evenodd;stroke:black;stroke-width:0.98604864px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 21.003845,9.7167316 C 22.487885,11.022282 20.43569,17.078901 18.209632,15.120575 C 15.983573,13.162251 19.519806,8.4111821 21.003845,9.7167316 z "
+       id="path3032"
+       sodipodi:nodetypes="css" />
+    <path
+       style="fill:#272c2e;fill-opacity:1;fill-rule:evenodd;stroke:black;stroke-width:0.92799336px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 11.656901,15.199887 C 11.023082,14.593518 12.003308,10.096465 13.832909,10.488128 C 15.66251,10.879786 13.558353,17.018991 11.656901,15.199887 z "
+       id="path3034"
+       sodipodi:nodetypes="css" />
+    <path
+       style="fill:#272c2e;fill-opacity:1;fill-rule:evenodd;stroke:black;stroke-width:1.03811264px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 10.974025,18.60242 C 8.430812,22.619158 17.228659,29.979463 20.571676,26.631844 C 21.35606,25.846381 20.807635,25.293693 20.607132,23.247952 C 20.388463,21.016882 21.240785,20.040808 18.58725,19.186273 C 17.241764,16.695215 16.389441,17.671288 14.208696,17.895002 C 12.13379,18.10786 11.570746,17.659961 10.974025,18.60242 z "
+       id="path2097"
+       sodipodi:nodetypes="csscss" />
+    <path
+       id="path4462"
+       d="M 18.321528,8.2531398 C 13.113938,8.2531398 8.8969811,12.470097 8.8969811,17.677687 C 8.8969811,19.181662 9.3172883,20.56546 9.944153,21.82759 C 11.196533,22.289203 12.52691,22.603273 13.93892,22.603273 C 20.109875,22.603273 25.038202,17.741636 25.419026,11.666144 C 23.688062,9.6206128 21.208987,8.2531398 18.321528,8.2531398 z "
+       style="opacity:0.6011236;color:black;fill:url(#radialGradient3049);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+  </g>
+</svg>

Added: trunk/data/languages/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/data/languages/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,20 @@
+include $(top_srcdir)/Makefile.decl
+
+configdir = $(datadir)/tracker/languages
+
+config_DATA =  		\
+	stopwords.da 	\
+	stopwords.de 	\
+	stopwords.en 	\
+	stopwords.es 	\
+	stopwords.fi 	\
+	stopwords.fr 	\
+	stopwords.hu	\
+	stopwords.it 	\
+	stopwords.nb 	\
+	stopwords.nl 	\
+	stopwords.pt 	\
+	stopwords.ru 	\
+	stopwords.sv 	
+
+EXTRA_DIST = $(config_DATA)

Added: trunk/data/languages/stopwords.da
==============================================================================
--- (empty file)
+++ trunk/data/languages/stopwords.da	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,101 @@
+af
+alle
+andet
+andre
+at
+begge
+da
+de
+den
+denne
+der
+deres
+det
+dette
+dig
+din
+dog
+du
+ej
+eller
+en
+end
+ene
+eneste
+enhver
+et
+fem
+fire
+flere
+fleste
+for
+fordi
+forrige
+fra
+fÃ
+fÃr
+god
+han
+hans
+har
+hendes
+her
+hun
+hvad
+hvem
+hver
+hvilken
+hvis
+hvor
+hvordan
+hvorfor
+hvornÃr
+i
+ikke
+ind
+ingen
+intet
+jeg
+jeres
+kan
+kom
+kommer
+lav
+lidt
+lille
+man
+mand
+mange
+med
+meget
+men
+mens
+mere
+mig
+ned
+ni
+nogen
+noget
+ny
+nyt
+nÃr
+nÃste
+nÃsten
+og
+op
+otte
+over
+pÃ
+se
+seks
+ses
+som
+stor
+store
+syv
+ti
+til
+to
+tre
+ud
+var

Added: trunk/data/languages/stopwords.de
==============================================================================
--- (empty file)
+++ trunk/data/languages/stopwords.de	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,129 @@
+aber
+als
+am
+an
+auch
+auf
+aus
+bei
+bin
+bis
+bist
+da
+dadurch
+daher
+darum
+das
+daÃ
+dass
+dein
+deine
+dem
+den
+der
+des
+dessen
+deshalb
+die
+dies
+dieser
+dieses
+doch
+dort
+du
+durch
+ein
+eine
+einem
+einen
+einer
+eines
+er
+es
+euer
+eure
+fÃr
+hatte
+hatten
+hattest
+hattet
+hier 	
+hinter
+ich
+ihr
+ihre
+im
+in
+ist
+ja
+jede
+jedem
+jeden
+jeder
+jedes
+jener
+jenes
+jetzt
+kann
+kannst
+kÃnnen
+kÃnnt
+machen
+mein
+meine
+mit
+muÃ
+muÃt
+musst
+mÃssen
+mÃÃt
+nach
+nachdem
+nein
+nicht
+nun
+oder
+seid
+sein
+seine
+sich
+sie
+sind
+soll
+sollen
+sollst
+sollt
+sonst
+soweit
+sowie
+und
+unser
+unsere
+unter
+vom
+von
+vor
+wann
+warum
+was
+weiter
+weitere
+wenn
+wer
+werde
+werden
+werdet
+weshalb
+wie
+wieder
+wieso
+wir
+wird
+wirst
+wo
+woher
+wohin
+zu
+zum
+zur
+Ãber

Added: trunk/data/languages/stopwords.en
==============================================================================
--- (empty file)
+++ trunk/data/languages/stopwords.en	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,743 @@
+a
+a's
+able
+about
+above
+according
+accordingly
+across
+actually
+after
+afterwards
+again
+against
+ain't
+all
+allow
+allows
+almost
+alone
+along
+already
+also
+although
+always
+am
+among
+amongst
+an
+and
+another
+any
+anybody
+anyhow
+anyone
+anything
+anyway
+anyways
+anywhere
+apart
+appear
+appreciate
+appropriate
+are
+aren't
+around
+as
+aside
+ask
+asking
+associated
+at
+auto
+available
+away
+awfully
+b
+be
+became
+because
+become
+becomes
+becoming
+been
+before
+beforehand
+begin
+behind
+being
+believe
+below
+beside
+besides
+best
+better
+between
+beyond
+both
+break
+brief
+but
+by
+c
+c'mon
+c's
+came
+can
+can't
+cannot
+cant
+case
+cause
+causes
+certain
+certainly
+changes
+char
+class
+clearly
+co
+com
+come
+comes
+concerning
+consequently
+consider
+considering
+const
+contain
+containing
+contains
+continue
+corresponding
+could
+couldn't
+course
+currently
+d
+definitely
+described
+despite
+did
+didn't
+different
+do
+does
+doesn't
+doing
+don't
+done
+double
+down
+downwards
+during
+e
+each
+edu
+eg
+eight
+either
+else
+elsewhere
+end
+enough
+entirely
+enum
+especially
+et
+etc
+even
+ever
+every
+everybody
+everyone
+everything
+everywhere
+ex
+exactly
+example
+except
+f
+false
+far
+few
+fifth
+first
+five
+float
+followed
+following
+follows
+for
+former
+formerly
+forth
+four
+from
+further
+furthermore
+g
+get
+gets
+getting
+given
+gives
+go
+goes
+going
+gone
+got
+gotten
+greetings
+h
+had
+hadn't
+happens
+hardly
+has
+hasn't
+have
+haven't
+having
+he
+he's
+hello
+help
+hence
+her
+here
+here's
+hereafter
+hereby
+herein
+hereupon
+hers
+herself
+hi
+him
+himself
+his
+hither
+home
+hopefully
+how
+howbeit
+however
+i
+i'd
+i'll
+i'm
+i've
+ie
+if
+ignored
+immediate
+in
+inasmuch
+inc
+include
+indeed
+indicate
+indicated
+indicates
+inner
+insofar
+instead
+int
+into
+inward
+is
+isn't
+it
+it'd
+it'll
+it's
+its
+itself
+j
+just
+k
+keep
+keeps
+kept
+know
+knows
+known
+l
+last
+lately
+later
+latter
+latterly
+least
+less
+lest
+let
+let's
+like
+liked
+likely
+little
+long
+look
+looking
+looks
+ltd
+m
+mainly
+many
+may
+maybe
+me
+mean
+meanwhile
+merely
+might
+more
+moreover
+most
+mostly
+much
+must
+my
+myself
+n
+name
+namely
+nd
+near
+nearly
+necessary
+need
+needs
+neither
+never
+nevertheless
+new
+next
+nine
+no
+nobody
+non
+none
+noone
+nor
+normally
+not
+nothing
+novel
+now
+nowhere
+o
+obviously
+of
+off
+often
+oh
+ok
+okay
+old
+on
+once
+one
+ones
+only
+onto
+or
+other
+others
+otherwise
+ought
+our
+ours
+ourselves
+out
+outside
+over
+overall
+own
+p
+particular
+particularly
+per
+perhaps
+placed
+please
+plus
+possible
+presumably
+probably
+provides
+q
+que
+quite
+qv
+r
+rather
+rd
+re
+really
+reasonably
+regarding
+regardless
+regards
+relatively
+respectively
+return
+right
+s
+said
+same
+saw
+say
+saying
+says
+second
+secondly
+see
+seeing
+seem
+seemed
+seeming
+seems
+seen
+self
+selves
+sensible
+sent
+serious
+seriously
+seven
+several
+shall
+she
+should
+shouldn't
+since
+six
+so
+some
+somebody
+somehow
+someone
+something
+sometime
+sometimes
+somewhat
+somewhere
+soon
+sorry
+specified
+specify
+specifying
+static
+still
+struct
+sub
+such
+sup
+sure
+switch
+t
+t's
+take
+taken
+tell
+tends
+th
+than
+thank
+thanks
+thanx
+that
+that's
+thats
+the
+their
+theirs
+them
+themselves
+then
+thence
+there
+there's
+thereafter
+thereby
+therefore
+therein
+theres
+thereupon
+these
+they
+they'd
+they'll
+they're
+they've
+think
+third
+this
+thorough
+thoroughly
+those
+though
+three
+through
+throughout
+thru
+thus
+to
+together
+too
+took
+toward
+towards
+tried
+tries
+true
+truly
+try
+trying
+twice
+two
+type
+typedef
+u
+un
+under
+unfortunately
+unless
+unlikely
+unsigned
+until
+unto
+up
+upon
+us
+use
+used
+useful
+uses
+using
+usually
+uucp
+v
+value
+various
+very
+via
+viz
+vs
+w
+want
+wants
+was
+wasn't
+way
+we
+we'd
+we'll
+we're
+we've
+welcome
+well
+went
+were
+weren't
+what
+what's
+whatever
+when
+whence
+whenever
+where
+where's
+whereafter
+whereas
+whereby
+wherein
+whereupon
+wherever
+whether
+which
+while
+whither
+who
+who's
+whoever
+whole
+whom
+whose
+why
+will
+willing
+wish
+with
+within
+without
+won't
+wonder
+would
+would
+wouldn't
+x
+y
+yes
+yet
+you
+you'd
+you'll
+you're
+you've
+your
+yours
+yourself
+yourselves
+z
+zero
+home
+if
+else
+elseif
+then
+for
+next
+case
+and
+assert
+def
+del
+elif
+exec
+from
+global
+import
+uses
+lambda
+not
+or
+pass
+print
+printf
+numeric
+array
+input
+output
+range
+type
+zero
+try
+catch
+except
+finally
+continue
+const
+abstract
+struct
+true
+false
+new
+throw
+raise
+this
+delete
+inline
+operator
+template
+virtual
+dynamic
+break
+asm
+auto
+enum
+goto
+long
+register
+return
+short
+static
+sizeof
+typedef
+union
+unsigned
+select
+int
+integer
+char
+float
+double
+single
+begin
+end
+while
+class
+void
+data
+gboolean
+gpointer
+gchar
+gint
+guint
+glong
+guint32
+gint32
+gsize
+lib
+define
+function
+procedure
+set
+get
+result
+using
+internal
+interface
+delegate
+base
+byte
+decimal
+event
+explicit
+foreach
+lock
+unlock
+namespace
+ref
+readonly
+object
+out
+params
+typeof
+uint
+ulong
+public
+private
+protected
+list
+index
+count
+add
+ifdef
+ifndef
+else
+http
+include
+www
+property
+read
+write
+declare
+exit
+make
+error
+bool
+key
+extern
+string
+length
+default
+close
+open
+publish
+fit
+rght
+local

Added: trunk/data/languages/stopwords.es
==============================================================================
--- (empty file)
+++ trunk/data/languages/stopwords.es	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,178 @@
+un
+una
+unas
+unos
+uno
+sobre
+todo
+tambiÃn
+tras
+otro
+algÃn
+alguno
+alguna
+algunos
+algunas
+ser
+es
+soy
+eres
+somos
+sois
+estoy
+esta
+estamos
+estais
+estan
+como
+en
+para
+atras
+porque
+por quÃ
+estado
+estaba
+ante
+antes
+siendo
+ambos
+pero
+por
+poder
+puede
+puedo
+podemos
+podeis
+pueden
+fui
+fue
+fuimos
+fueron
+hacer
+hago
+hace
+hacemos
+haceis
+hacen
+cada
+fin
+incluso
+primero
+desde
+conseguir
+consigo
+consigue
+consigues
+conseguimos
+consiguen
+ir
+voy
+va
+vamos
+vais
+van
+vaya
+gueno
+ha
+tener
+tengo
+tiene
+tenemos
+teneis
+tienen
+el
+la
+lo
+las
+los
+su
+aqui
+mio
+tuyo
+ellos
+ellas
+nos
+nosotros
+vosotros
+vosotras
+si
+dentro
+solo
+solamente
+saber
+sabes
+sabe
+sabemos
+sabeis
+saben
+ultimo
+largo
+bastante
+haces
+muchos
+aquellos
+aquellas
+sus
+entonces
+tiempo
+verdad
+verdadero
+verdadera
+cierto
+ciertos
+cierta
+ciertas
+intentar
+intento
+intenta
+intentas
+intentamos
+intentais
+intentan
+dos
+bajo
+arriba
+encima
+usar
+uso
+usas
+usa
+usamos
+usais
+usan
+emplear
+empleo
+empleas
+emplean
+ampleamos
+empleais
+valor
+muy
+era
+eras
+eramos
+eran
+modo
+bien
+cual
+cuando
+donde
+mientras
+quien
+con
+entre
+sin
+trabajo
+trabajar
+trabajas
+trabaja
+trabajamos
+trabajais
+trabajan
+podria
+podrias
+podriamos
+podrian
+podriais
+yo
+aquel

Added: trunk/data/languages/stopwords.fi
==============================================================================
--- (empty file)
+++ trunk/data/languages/stopwords.fi	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,68 @@
+alla
+ansiosta
+ehkÃ
+ei
+enemmÃn
+ennen
+etessa
+f
+haikki
+he
+hitaasti
+hoikein
+hyvin
+hÃn
+ilman
+ja
+jos
+jÃlkeen
+kanssa
+kaukana
+kenties
+keskellÃ
+kesken
+koskaan
+kuinkan
+kukka
+kylliksi
+kyllÃ
+liian
+lla
+lla
+luona
+lÃhellÃ
+lÃpi
+me
+miksi
+mikÃ
+milloin
+milloinkan
+minÃ
+missÃ
+miten
+nopeasti
+nyt
+oikea
+oikealla
+paljon
+siellÃ
+sinÃ
+ssa
+sta
+suoraan
+tai
+takana
+takia
+tarpeeksi
+te
+tÃssÃ
+ulkopuolella
+vahemmÃn
+vasen
+vasenmalla
+vastan
+vielÃ
+vieressÃ
+vÃhÃn
+yhdessÃ
+ylÃs

Added: trunk/data/languages/stopwords.fr
==============================================================================
--- (empty file)
+++ trunk/data/languages/stopwords.fr	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,145 @@
+au
+aux
+avec
+ce
+ces
+dans
+de
+des
+du
+elle
+en
+et
+eux
+il
+je
+la
+le
+leur
+lui
+ma
+mais
+me
+mÃme
+mes
+moi
+mon
+ne
+nos
+notre
+nous
+on
+ou
+par
+pas
+pour
+qu
+que
+qui
+sa
+se
+ses
+son
+sur
+ta
+te
+tes
+toi
+ton
+tu
+un
+une
+vos
+votre
+vous
+ÃtÃ
+ÃtÃe
+ÃtÃes
+ÃtÃs
+Ãtant
+Ãtante
+Ãtants
+Ãtantes
+suis
+es
+est
+sommes
+Ãtes
+sont
+serai
+seras
+sera
+serons
+serez
+seront
+serais
+serait
+serions
+seriez
+seraient
+Ãtais
+Ãtait
+Ãtions
+Ãtiez
+Ãtaient
+fus
+fut
+fÃmes
+fÃtes
+furent
+sois
+soit
+soyons
+soyez
+soient
+fusse
+fusses
+fÃt
+fussions
+fussiez
+fussent
+ayant
+ayante
+ayantes
+ayants
+eu
+eue
+eues
+eus
+ai
+as
+avons
+avez
+ont
+aurai
+auras
+aura
+aurons
+aurez
+auront
+aurais
+aurait
+aurions
+auriez
+auraient
+avais
+avait
+avions
+aviez
+avaient
+eut
+eÃmes
+eÃtes
+eurent
+aie
+aies
+ait
+ayons
+ayez
+aient
+eusse
+eusses
+eÃt
+eussions
+eussiez
+eussent

Added: trunk/data/languages/stopwords.hu
==============================================================================
--- (empty file)
+++ trunk/data/languages/stopwords.hu	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,403 @@
+a
+ÃÂ
+ahogy
+ahol
+aki
+akik
+akkor
+alatt
+ÃÂltal
+ÃÂltalÃÂban
+amely
+amelyek
+amelyekben
+amelyeket
+amelyet
+amelynek
+ami
+amit
+amolyan
+amp
+amÃÂg
+amikor
+ÃÂt
+abban
+ahhoz
+annak
+arra
+arrÃÂl
+az
+azok
+azon
+azt
+azzal
+azÃÂrt
+aztÃÂn
+azutÃÂn
+azonban
+b
+bÃÂr
+be
+belÃÂl
+benne
+c
+cikk
+cikkek
+cikkeket
+csak
+d
+de
+e
+ÃÂ
+eddig
+egÃÂsz
+egy
+egyes
+egyetlen
+egyÃÂb
+egyik
+egyre
+ekkor
+el
+elÃÂg
+ellen
+elÃÂ
+elÃÂszÃÂr
+elÃÂtt
+elsÃÂ
+ÃÂn
+ÃÂppen
+ebben
+ehhez
+emilyen
+ennek
+erre
+ez
+ezt
+ezek
+ezen
+ezzel
+ezÃÂrt
+ÃÂs
+f
+fel
+felÃÂ
+g
+h
+hanem
+hiszen
+hogy
+hogyan
+i
+ÃÂ
+igen
+ÃÂgy
+illetve
+ill.
+ill
+ilyen
+ilyenkor
+is
+ison
+ismÃÂt
+itt
+j
+jÃÂ
+jÃÂl
+jobban
+k
+kell
+kellett
+keresztÃÂl
+keressÃÂnk
+ki
+kÃÂvÃÂl
+kÃÂzÃÂtt
+kÃÂzÃÂl
+l
+legalÃÂbb
+lehet
+lehetett
+legyen
+lenne
+lenni
+lesz
+lett
+m
+maga
+magÃÂt
+majd
+majd
+mÃÂr
+mÃÂs
+mÃÂsik
+meg
+mÃÂg
+mellett
+mert
+mely
+melyek
+mi
+mit
+mÃÂg
+miÃÂrt
+milyen
+mikor
+minden
+mindent
+mindenki
+mindig
+mint
+mintha
+mivel
+most
+n
+nagy
+nagyobb
+nagyon
+ne
+nÃÂha
+nekem
+neki
+nem
+nÃÂhÃÂny
+nÃÂlkÃÂl
+nincs
+o
+ÃÂ
+olyan
+ott
+ÃÂssze
+ÃÂ
+ÃÂ
+ÃÂk
+ÃÂket
+p
+pedig
+persze
+q
+r
+rÃÂ
+s
+sajÃÂt
+sem
+semmi
+sok
+sokat
+sokkal
+sz
+szÃÂmÃÂra
+szemben
+szerint
+szinte
+t
+talÃÂn
+tehÃÂt
+teljes
+tovÃÂbb
+tovÃÂbbÃÂ
+tÃÂbb
+u
+ÃÂ
+ÃÂgy
+ugyanis
+ÃÂj
+ÃÂjabb
+ÃÂjra
+utÃÂn
+utÃÂna
+utolsÃÂ
+ÃÂ
+ÃÂ
+v
+vagy
+vagyis
+valaki
+valamely
+valami
+valamint
+valÃÂ
+vagyok
+van
+vannak
+volt
+voltam
+voltak
+voltunk
+vissza
+vele
+viszont
+volna
+szÃÂmolnak
+szÃÂlnak
+szÃÂl
+w
+x
+y
+z
+zs
+a
+ahogy
+ahol
+aki
+akkor
+alatt
+ÃÂltalÃÂban
+ÃÂltal
+amely
+amÃÂg
+amikor
+ami
+amolyan
+arra
+ÃÂt
+az
+azÃÂrt
+azonban
+azon
+aztÃÂn
+azt
+azutÃÂn
+azzal
+bÃÂr
+be
+belÃÂl
+benne
+cikk
+csak
+de
+eddig
+egÃÂsz
+egy
+egyÃÂb
+egyes
+egyetlen
+egyik
+egyre
+ekkor
+el
+elÃÂg
+ellen
+elÃÂ
+elÃÂszÃÂr
+elÃÂtt
+elsÃÂ
+emilyen
+ÃÂn
+ÃÂppen
+erre
+ÃÂs
+e
+ez
+ezen
+ezÃÂrt
+ezzel
+fel
+felÃÂ
+hanem
+hiszen
+hogy
+hogyan
+igen
+ÃÂgy
+ill.
+illetve
+ill
+ilyen
+ilyenkor
+ismÃÂt
+ison
+itt
+jÃÂ
+jobban
+jÃÂl
+kell
+keres
+keresztÃÂl
+ki
+kÃÂvÃÂl
+kÃÂzÃÂtt
+kÃÂzÃÂl
+legalÃÂbb
+legyen
+lehet
+lenni
+lett
+maga
+maga
+majd
+mÃÂr
+mÃÂs
+mÃÂsik
+mÃÂg
+meg
+mellett
+mely
+mert
+miÃÂrt
+mÃÂg
+mikor
+milyen
+minden
+mindenki
+mindig
+mi
+mint
+mintha
+mivel
+most
+nagy
+nagyobb
+nagyon
+ne
+nÃÂha
+nÃÂhÃÂny
+neki
+nÃÂlkÃÂl
+nem
+nincs
+ÃÂk
+olyan
+ÃÂ
+ÃÂssze
+ott
+pedig
+persze
+rÃÂ
+sajÃÂt
+s
+sem
+semmi
+sokkal
+sok
+szÃÂmÃÂra
+szÃÂmol
+szemben
+szerint
+szinte
+szÃÂl
+talÃÂn
+tehÃÂt
+teljes
+tovÃÂbbÃÂ
+tovÃÂbb
+ÃÂgy
+ugyanis
+ÃÂj
+ÃÂjabb
+ÃÂjra
+utÃÂna
+utÃÂn
+utolsÃÂ
+vagy
+vagyis
+valaki
+valamely
+valami
+valamint
+valÃÂ
+van
+vissza
+viszont
+volt
+

Added: trunk/data/languages/stopwords.it
==============================================================================
--- (empty file)
+++ trunk/data/languages/stopwords.it	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,134 @@
+a
+adesso
+ai
+al
+alla
+allo
+allora
+altre
+altri
+altro
+anche
+ancora
+avere
+aveva
+avevano
+ben
+buono
+che
+chi
+cinque
+comprare
+con
+consecutivi
+consecutivo
+cosa
+cui
+da
+del
+della
+dello
+dentro
+deve
+devo
+di
+doppio
+due
+e
+ecco
+fare
+fine
+fino
+fra
+gente
+giu
+ha
+hai
+hanno
+ho
+il
+indietro
+invece
+io
+la
+lavoro
+le
+lei
+lo
+loro
+lui
+lungo
+ma
+me
+meglio
+molta
+molti
+molto
+nei
+nella
+no
+noi
+nome
+nostro
+nove
+nuovi
+nuovo
+o
+oltre
+ora
+otto
+peggio
+pero
+persone
+piu
+poco
+primo
+promesso
+qua
+quarto
+quasi
+quattro
+quello
+questo
+qui
+quindi
+quinto
+rispetto
+sara
+secondo
+sei
+sembra
+sembrava
+senza
+sette
+sia
+siamo
+siete
+solo
+sono
+sopra
+soprattutto
+sotto
+stati
+stato
+stesso
+su
+subito
+sul
+sulla
+tanto
+te
+tempo
+terzo
+tra
+tre
+triplo
+ultimo
+un
+una
+uno
+va
+vai
+voi
+volte
+vostro

Added: trunk/data/languages/stopwords.nb
==============================================================================
--- (empty file)
+++ trunk/data/languages/stopwords.nb	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,117 @@
+alle
+andre
+arbeid
+av
+begge
+bort
+bra
+bruke
+da
+denne
+der
+deres
+det
+din
+disse
+du
+eller
+en
+ene
+eneste
+enhver
+enn
+er
+et
+folk
+for
+fordi
+forsÃke
+fra
+fÃ
+fÃr
+fÃrst
+gjorde
+gjÃre
+god
+gÃ
+ha
+hadde
+han
+hans
+hennes
+her
+hva
+hvem
+hver
+hvilken
+hvis
+hvor
+hvordan
+hvorfor
+ikke
+inn
+innen
+kan
+kunne
+lage
+lang
+lik
+like
+makt
+mange
+med
+meg
+meget
+men
+mens
+mer
+mest
+min
+mye
+mÃ
+mÃte
+navn
+nei
+ny
+nÃ
+nÃr
+og
+ogsÃ
+om
+opp
+oss
+over
+part
+punkt
+pÃ
+rett
+riktig
+samme
+sant
+si
+siden
+sist
+skulle
+slik
+slutt
+som
+start
+stille
+tid
+til
+tilbake
+tilstand
+under
+ut
+uten
+var
+ved
+verdi
+vi
+vil
+ville
+vite
+vÃr
+vÃre
+vÃrt
+Ã

Added: trunk/data/languages/stopwords.nl
==============================================================================
--- (empty file)
+++ trunk/data/languages/stopwords.nl	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,48 @@
+aan
+af
+al
+als
+bij
+dan
+dat
+die
+dit
+een
+en
+er
+had
+heb
+hem
+het
+hij
+hoe
+hun
+ik
+in
+is
+je
+kan
+me
+men
+met
+mij
+nog
+nu
+of
+ons
+ook
+te
+tot
+uit
+van
+was
+wat
+we
+wel
+wij
+zal
+ze
+zei
+zij
+zo
+zou

Added: trunk/data/languages/stopwords.pt
==============================================================================
--- (empty file)
+++ trunk/data/languages/stopwords.pt	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,147 @@
+Ãltimo
+Ã
+acerca
+agora
+algmas
+alguns
+ali
+ambos
+antes
+apontar
+aquela
+aquelas
+aquele
+aqueles
+aqui
+atrÃs
+bem
+bom
+cada
+caminho
+cima
+com
+como
+comprido
+conhecido
+corrente
+das
+debaixo
+dentro
+desde
+desligado
+deve
+devem
+deverÃ
+direita
+diz
+dizer
+dois
+dos
+e
+ela
+ele
+eles
+em
+enquanto
+entÃo
+estÃ
+estÃo
+estado
+estar
+estarÃ
+este
+estes
+esteve
+estive
+estivemos
+estiveram
+eu
+farÃ
+faz
+fazer
+fazia
+fez
+fim
+foi
+fora
+horas
+iniciar
+inicio
+ir
+irÃ
+ista
+iste
+isto
+ligado
+maioria
+maiorias
+mais
+mas
+mesmo
+meu
+muito
+muitos
+nÃs
+nÃo
+nome
+nosso
+novo
+o
+onde
+os
+ou
+outro
+para
+parte
+pegar
+pelo
+pessoas
+pode
+poderÃ
+podia
+por
+porque
+povo
+promeiro
+quÃ
+qual
+qualquer
+quando
+quem
+quieto
+sÃo
+saber
+sem
+ser
+seu
+somente
+tÃm
+tal
+tambÃm
+tem
+tempo
+tenho
+tentar
+tentaram
+tente
+tentei
+teu
+teve
+tipo
+tive
+todos
+trabalhar
+trabalho
+tu
+um
+uma
+umas
+uns
+usa
+usar
+valor
+veja
+ver
+verdade
+verdadeiro
+vocÃ

Added: trunk/data/languages/stopwords.ru
==============================================================================
Binary file. No diff available.

Added: trunk/data/languages/stopwords.sv
==============================================================================
--- (empty file)
+++ trunk/data/languages/stopwords.sv	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,104 @@
+ab
+aldrig
+all
+alla
+alltid
+att
+av
+avser
+avses
+bakom
+bra
+bredvid
+de
+dem
+den
+denna
+deras
+dess
+det
+detta
+du
+dÃ
+dÃr
+efter
+eftersom
+efterÃt
+ej
+eller
+emot
+en
+ett
+fastÃn
+fort
+framfÃr
+frÃn
+fÃr
+genom
+gott
+hamske
+han
+hellre
+hon
+hos
+hur
+hÃr
+i
+in
+ingen
+innan
+inte
+ja
+jag
+lite
+lÃngsamt
+lÃngt
+man
+med
+medan
+mellan
+mer
+mera
+mindre
+mot
+myckett
+nej
+nere
+ni
+nu
+nÃr
+nÃra
+och
+oksa
+om
+pÃ
+sin
+skall
+som
+sÃ
+sÃdan
+till
+tillrÃckligt
+tillsammans
+trots
+under
+uppe
+ut
+utan
+utom
+vad
+var
+varfÃr
+vart
+varthÃn
+vem
+vems
+vi
+vid
+vilken
+vÃl
+Ãn
+Ãnnu
+Ãr
+Ãnyo
+Ãver

Added: trunk/data/libtracker-gtk.pc.in
==============================================================================
--- (empty file)
+++ trunk/data/libtracker-gtk.pc.in	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,12 @@
+prefix= prefix@
+exec_prefix=${prefix}
+libdir= libdir@
+includedir= includedir@
+
+Name: libtracker-gtk
+Description: Handy GTK+ widgets for apps that use tracker
+Version: @VERSION@
+Requires.private: cairo
+Requires: tracker gtk+-2.0 glib-2.0
+Libs: -L${libdir} -ltracker-gtk
+Cflags: -I${includedir}/libtracker-gtk

Added: trunk/data/modules/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/data/modules/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,11 @@
+include $(top_srcdir)/Makefile.decl
+
+configdir = $(datadir)/tracker/modules
+
+config_DATA =						\
+	applications.module				\
+	evolution.module				\
+	files.module					\
+	gaim-conversations.module
+
+EXTRA_DIST = $(config_DATA)

Added: trunk/data/modules/applications.module
==============================================================================
--- (empty file)
+++ trunk/data/modules/applications.module	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,19 @@
+[General]
+Description=Applications
+Enabled=true
+
+[Monitors]
+Directories=
+RecurseDirectories=/usr/share/applications;
+
+[Ignored]
+Directories=
+Files=
+
+[Index]
+Service=Applications
+MimeTypes=
+Files=*.desktop
+
+[Specific]
+# Options specific to this module

Added: trunk/data/modules/evolution.module
==============================================================================
--- (empty file)
+++ trunk/data/modules/evolution.module	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,19 @@
+[General]
+Description=Evolution
+Enabled=true
+
+[Monitors]
+Directories=
+RecurseDirectories=$HOME/.evolution/mail/local/;$HOME/.evolution/mail/imap/
+
+[Ignored]
+Directories=
+Files=
+
+[Index]
+Service=EvolutionEmails
+MimeTypes=
+Files=
+
+[Specific]
+# Options specific to this module

Added: trunk/data/modules/files.module
==============================================================================
--- (empty file)
+++ trunk/data/modules/files.module	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,19 @@
+[General]
+Description=Files
+Enabled=true
+
+[Monitors]
+Directories=
+RecurseDirectories=
+
+[Ignored]
+Directories=po;CVS;.svn;.git
+Files=*~*;*.o;*.la;*.lo;*.loT;*.in;*.csproj;*.m4;*.rej;*.gmo;*.orig;*.pc;*.omf;*.aux;*.tmp;*.po;*.vmdk;*.vm*;*.nvram;*.part;autom4te;conftest;confstat;Makefile;SCCS;litmain.sh;libtool;config.status;confdefs.h;
+
+[Index]
+Service=
+MimeTypes=
+Files=
+
+[Specific]
+# Options specific to this module

Added: trunk/data/modules/gaim-conversations.module
==============================================================================
--- (empty file)
+++ trunk/data/modules/gaim-conversations.module	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,19 @@
+[General]
+Description=GAIM Conversations
+Enabled=true
+
+[Monitors]
+Directories=
+RecurseDirectories=$HOME/.gaim/logs;$HOME/.purple/logs;
+
+[Ignored]
+Directories=
+Files=
+
+[Index]
+Service=Files
+MimeTypes=
+Files=
+
+[Specific]
+# Options specific to this module

Added: trunk/data/services/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/data/services/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,23 @@
+include $(top_srcdir)/Makefile.decl
+
+configdir = $(datadir)/tracker/services
+
+config_DATA = 				\
+	application.metadata 		\
+	audio.metadata 			\
+	default.metadata 		\
+	default.service 		\
+	document.metadata 		\
+	email.metadata 			\
+	file.metadata 			\
+	image.metadata 			\
+	video.metadata 			\
+	xesam-convenience.metadata 	\
+	xesam-convenience.service 	\
+	xesam.metadata 			\
+	xesam.service 			\
+	xesam-virtual.metadata 		\
+	xesam-service.smapping 		\
+	xesam-metadata.mmapping
+
+EXTRA_DIST = $(config_DATA)

Added: trunk/data/services/application.metadata
==============================================================================
--- (empty file)
+++ trunk/data/services/application.metadata	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,56 @@
+[App:Name]
+DisplayName=Name
+Description=Application name
+DataType=index
+Weight=25
+Filtered=false
+
+
+[App:DisplayName]
+DisplayName=Display name
+Description=Application display name
+DataType=index
+Weight=10
+Filtered=false
+
+[App:GenericName]
+DisplayName=Generic name
+Description=Application generic name
+DataType=index
+Weight=10
+Filtered=false
+
+[App:Comment]
+DisplayName=Comments
+Description=Application comments
+DataType=index
+Weight=5
+
+
+[App:Exec]
+DisplayName=Name
+Description=Application name
+DataType=string
+Weight=1
+
+[App:Icon]
+DisplayName=Icon
+Description=Application icon name
+DataType=string
+Weight=1
+
+[App:MimeType]
+DisplayName=Mime type
+Description=Application supported mime types
+DataType=keyword
+Weight=1
+MultipleValues=true
+
+[App:Categories]
+DisplayName=Categories
+Description=Application categories
+DataType=keyword
+Weight=5
+MultipleValues=true
+
+

Added: trunk/data/services/audio.metadata
==============================================================================
--- (empty file)
+++ trunk/data/services/audio.metadata	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,173 @@
+[Audio:Title]
+DisplayName=Title
+Description=Track title
+DataType=index
+Parent=DC:Title
+Weight=20
+Filtered=false
+
+[Audio:Artist]
+DisplayName=Artist
+Description=Track artist
+DataType=index
+Parent=DC:Creator
+Weight=15
+Filtered=false
+
+[Audio:Album]
+DisplayName=Title
+Description=Track title
+DataType=index
+Weight=10
+Filtered=false
+
+[Audio:Genre]
+DisplayName=Genre
+Description=The type or genre of the music track
+DataType=keyword
+Parent=DC:Type
+Weight=5
+
+[Audio:Duration]
+DisplayName=Duration
+Description=The length in seconds of the music track
+DataType=integer
+
+[Audio:ReleaseDate]
+DisplayName=Release date
+Description=The date the track was released
+DataType=date
+Parent=DC:Date
+
+[Audio:AlbumArtist]
+DisplayName=Album artist
+Description=The artist of the track's album
+DataType=index
+Parent=DC:Creator
+Weight=10
+Filtered=false
+
+[Audio:AlbumTrackCount]
+DisplayName=Album track count
+Description=The number of tracks in the album
+DataType=integer
+
+[Audio:TrackNo]
+DisplayName=Track number
+Description=The position of the track relative to the others
+DataType=integer
+
+[Audio:DiscNo]
+DisplayName=Disc number
+Description=On which disc the track is located
+DataType=integer
+
+[Audio:Performer]
+DisplayName=Performer
+Description=The individual or group performing the track
+DataType=index
+Weight=5
+Parent=DC:Contributor
+
+[Audio:TrackGain]
+DisplayName=Track gain
+Description=The amount of gain needed for the track
+DataType=double
+
+[Audio:PeakTrackGain]
+DisplayName=Peak track gain
+Description=The peak amount of gain needed for the track
+DataType=double
+
+[Audio:AlbumGain]
+DisplayName=Album gain
+Description=The amount of gain needed for the entire album
+DataType=double
+
+[Audio:AlbumPeakGain]
+DisplayName=Peak album gain
+Description=The peak amount of gain needed for the entire album
+DataType=double
+
+[Audio:Comment]
+DisplayName=Comments
+Description=General purpose comments
+DataType=index
+Weight=1
+Parent=DC:Description
+
+[Audio:Codec]
+DisplayName=Codec
+Description=Codec name
+DataType=index
+Weight=1
+
+[Audio:CodecVersion]
+DisplayName=Codec version
+Description=Codec version string
+DataType=string
+
+[Audio:Samplerate]
+DisplayName=Sample rate
+Description=Sample rate of track in Hz
+DataType=double
+
+[Audio:Bitrate]
+DisplayName=Bitrate
+Description=Bitrate in bits/sec
+DataType=double
+
+[Audio:Channels]
+DisplayName=Channels
+Description=The number of channels in the track
+DataType=integer
+
+[Audio:LastPlay]
+DisplayName=Last Played
+Description=The date and time the track was last played
+DataType=date
+Parent=DC:Date
+Embedded=false
+
+[Audio:PlayCount]
+DisplayName=Play Count
+Description=Number of times the track has been played
+DataType=integer
+Embedded=false
+
+[Audio:DateAdded]
+DisplayName=Date Added
+Description=Date track was first added
+DataType=date
+
+[Audio:Lyrics]
+DisplayName=Lyrics
+Description=Lyrics of the track
+DataType=index
+weight=1
+
+[Audio:MBAlbumID]
+DisplayName=MusicBrainz album ID
+Description=The MusicBrainz album ID for the track
+DataType=string
+Parent=DC:Identifier
+
+[Audio:MBArtistID]
+DisplayName=MusicBrainz artist ID
+Description=The MusicBrainz artist ID for the track
+DataType=string
+Parent=DC:Identifier
+
+[Audio:MBAlbumArtistID]
+DisplayName=MusicBrainz album artist ID
+Description=The MusicBrainz album artist ID for the track
+DataType=string
+Parent=DC:Identifier
+
+[Audio:MBTrackID]
+DisplayName=MusicBrainz track ID
+Description=The MusicBrainz track ID
+DataType=string
+Parent=DC:Identifier
+
+

Added: trunk/data/services/default.metadata
==============================================================================
--- (empty file)
+++ trunk/data/services/default.metadata	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,114 @@
+[DC:Contributor]
+Abstract=true
+DisplayName=Contributor
+Description=Contributors to the resource (other than the authors)
+DataType=index
+
+[DC:Coverage]
+Abstract=true
+DisplayName=Coverage
+Description=The extent or scope of the resource
+DataType=index
+
+[DC:Creator]
+Abstract=true
+DisplayName=Author
+Description=The authors of the resource
+DataType=index
+
+[DC:Date]
+Abstract=true
+DisplayName=Date
+Description=Date that something interesting happened to the resource
+DataType=date
+
+[DC:Description]
+Abstract=true
+DisplayName=Description
+Description=A textual description of the content of the resource
+DataType=index
+
+[DC:Format]
+Abstract=true
+DisplayName=Format
+Description=The file format(mime) or type used when saving the resource
+DataType=keyword
+
+[DC:Identifier]
+Abstract=true
+DisplayName=Identifier
+Description=Unique identifier of the resource
+DataType=index
+
+[DC:Language]
+Abstract=true
+DisplayName=Langauge
+Description=Language used in the resource
+DataType=index
+
+[DC:Publishers]
+Abstract=true
+DisplayName=Publishers
+Description=Publishers of the resource
+DataType=index
+
+[DC:Relation]
+Abstract=true
+DisplayName=Relationship
+Description=Relationships to other resources
+DataType=index
+
+[DC:Rights]
+Abstract=true
+DisplayName=Rights
+Description=Informal rights statement of the resource
+DataType=index
+
+[DC:Source]
+Abstract=true
+DisplayName=Source
+Description=Unique identifier of the work from which this resource was derived
+DataType=index
+
+[DC:Subject]
+Abstract=true
+DisplayName=Subject
+Description=specifies the topic of the content of the resource
+DataType=index
+
+[DC:Keywords]
+Abstract=true
+DisplayName=Keywords
+Description=Keywords that are used to tag a resource
+DataType=keyword
+
+[DC:Title]
+Abstract=true
+DisplayName=Title
+Description=specifies the topic of the content of the resource
+DataType=index
+
+[DC:Type]
+Abstract=true
+DisplayName=Type
+Description=specifies the type of the content of the resource
+DataType=index
+
+[User:Rank]
+DisplayName=Rank
+Description=User settable rank or score of the resource
+FieldName=Rank
+DataType=integer
+Embedded=false
+
+[User:Keywords]
+DisplayName=Keywords
+Description=User settable keywords which are used to tag a resource
+Parent=DC:Keywords
+DataType=keyword
+Weight=50
+Embedded=false
+MultipleValues=true
+Filtered=false
+
+

Added: trunk/data/services/default.service
==============================================================================
--- (empty file)
+++ trunk/data/services/default.service	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,299 @@
+[Files]
+DisplayName=All Files
+Description=All files in the filesystem
+PropertyPrefix=File
+UIVisible=true
+Icon=system-file-manager
+UIView=icon
+ShowServiceFiles=true
+ShowServiceDirectories=true
+
+[Folders]
+DisplayName=Folders
+Description=Folders in the filesystem
+Parent=Files
+UIVisible=true
+UIView=icon
+MimePrefixes=x-directory/;inode/;
+Icon=folder
+ShowServiceFiles=true
+ShowServiceDirectories=true
+HasFullText=false
+HasMetadata=false
+HasThumbs=false
+
+[Documents]
+DisplayName=Documents
+Description=Office and PDF based files
+PropertyPrefix=Doc
+Parent=Files
+UIVisible=true
+Icon=x-office-document
+KeyMetadata1=Doc:Title
+KeyMetadata2=Doc:Author
+KeyMetadata3=Doc:Created
+TabularMetadata=File:Name;File:Mime;Doc:Title;Doc:Author;File:Size;File:Modified;Doc:Created;
+TileMetadata=Doc:Title;Doc:Subject;Doc:Author;Doc:Created;Doc:PageCount;File:Size;
+ContentMetadata=File:Contents
+Mimes=application/rtf;text/richtext;application/msword;application/pdf;application/postscript;application/x-dvi;application/vnd.ms-excel;vnd.ms-powerpoint;application/x-abiword;text/html;text/sgml;text/x-tex;application/x-mswrite;application/x-applix-word;application/docbook+xml;application/x-kword;application/x-kword-crypt;application/x-lyx;application/vnd.lotus-1-2-3;application/x-applix-spreadsheet;application/x-gnumeric;application/x-kspread;application/x-kspread-crypt;application/x-quattropro;application/x-sc;application/x-siag;application/x-magicpoint;application/x-kpresenter;application/illustrator;application/vnd.corel-draw;application/vnd.stardivision.draw;application/vnd.oasis.opendocument.graphics;application/x-dia-diagram;application/x-karbon;application/x-killustrator;application/x-kivio;application/x-kontour;application/x-wpg;
+MimePrefixes=application/vnd.oasis.opendocument;application/vnd.sun.xml;application/vnd.stardivision;
+ShowServiceFiles=true
+ShowServiceDirectories=true
+HasMetadata=true
+HasFullText=true
+HasThumbs=true
+
+[WebHistory]
+DisplayName=Web History
+Description=Web History
+UIVisible=true
+Icon=x-office-document
+KeyMetadata1=Doc:Title
+KeyMetadata2=Doc:URL
+KeyMetadata3=Doc:Keywords
+KeyMetadata4=User:Keywords
+TabularMetadata=File:Name;File:Mime;Doc:Title;Doc:URL;Doc:Author;File:Size;File:Modified;Doc:Created;
+TileMetadata=Doc:Title;Doc:URL;Doc:Subject;Doc:Author;Doc:Created;Doc:PageCount;File:Size;
+ShowServiceFiles=false
+ShowServiceDirectories=false
+HasMetadata=true
+HasFullText=true
+HasThumbs=true
+
+
+[Images]
+DisplayName=Images
+Description=Image based files
+PropertyPrefix=Image
+Parent=Files
+UIVisible=true
+UIView=icon
+Icon=image-x-generic
+KeyMetadata1=Image:Title
+KeyMetadata2=Image:Height
+KeyMetadata3=Image:Width
+KeyMetadata4=Image:Date
+KeyMetadata5=Image:Software
+KeyMetadata6=Image:Creator
+TabularMetadata=File:Name;Image:Height;Image:Width;Image:Date;File:Modified;Image:Creator;Image:Software;
+TileMetadata=Image:Title;Image:Height;Image:Width;Image:Date;Image:Creator;Image:Software;Image:Comments;
+Mimes=application/vnd.oasis.opendocument.image;application/x-krita;
+MimePrefixes=image/;
+ShowServiceFiles=true
+ShowServiceDirectories=true
+HasMetadata=true
+HasFullText=false
+HasThumbs=true
+
+
+[Music]
+DisplayName=Music
+Description=Music based files
+PropertyPrefix=Audio
+Parent=Files
+UIVisible=true
+UIView=tabular
+Icon=audio-x-generic
+KeyMetadata1=Audio:Title
+KeyMetadata2=Audio:Artist
+KeyMetadata3=Audio:Album
+KeyMetadata4=Audio:Genre
+KeyMetadata5=Audio:Duration
+KeyMetadata6=Audio:ReleaseDate
+KeyMetadata7=Audio:TrackNo
+KeyMetadata8=Audio:Bitrate
+KeyMetadata9=Audio:PlayCount
+KeyMetadata10=Audio:DateAdded
+KeyMetadata11=Audio:LastPlay
+TabularMetadata=Audio:Title;Audio:Artist;Audio:Album;Audio:Genre;Audio:Duration;Audio:ReleaseDate;
+TileMetadata=Audio:Title;Audio:Artist;Audio:Album;Audio:Genre;Audio:Duration;Audio:ReleaseDate;
+Mimes=application/ogg;
+MimePrefixes=audio/;
+ShowServiceFiles=true
+ShowServiceDirectories=true
+HasMetadata=true
+HasFullText=false
+HasThumbs=false
+
+
+[Videos]
+DisplayName=Videos
+Description=Video based files
+PropertyPrefix=Video
+Parent=Files
+UIVisible=true
+UIView=icon
+Icon=video-x-generic
+KeyMetadata1=Video:Title
+KeyMetadata2=Video:Author
+KeyMetadata3=Video:Height
+KeyMetadata4=Video:Width
+KeyMetadata5=Video:Duration
+KeyMetadata6=Audio:Bitrate
+TabularMetadata=File:Name;Video:Title;Video:Author;Video:Height;Video:Width;Video:Duration;Video:Bitrate;
+TileMetadata=Video:Title;Video:Author;Video:Height;Video:Width;Video:Duration;Video:Bitrate;
+MimePrefixes=video/;
+ShowServiceFiles=true
+ShowServiceDirectories=true
+HasMetadata=true
+HasFullText=false
+HasThumbs=true
+
+
+[Text]
+DisplayName=Text
+Description=Text based files
+Parent=Files
+UIVisible=true
+Icon=text-x-generic
+ContentMetadata=File:Contents
+Mimes=text/plain;text/x-authors;text/x-changelog;text/x-copying;text/x-credits;text/x-install;text/x-readme;
+ShowServiceFiles=true
+ShowServiceDirectories=true
+HasMetadata=false
+HasFullText=true
+HasThumbs=false
+
+
+[Development]
+DisplayName=Development
+Description=Development and source code files
+Parent=Files
+UIVisible=true
+Icon=applications-development
+ContentMetadata=File:Contents
+Mimes=application/x-perl;application/x-shellscript;application/x-php;application/x-java;application/x-javascript;application/x-glade;application/x-csh;application/x-class-file;application/x-awk;application/x-asp;application/x-ruby;application/x-m4;text/x-m4;text/x-c++;text/x-adasrc;text/x-c;text/x-c++hdr;text/x-chdr;text/x-csharp;text/x-c++src;text/x-csrc;text/x-dcl;text/x-dsrc;text/x-emacs-lisp;text/x-fortran;text/x-haskell;text/x-literate-haskell;text/x-java;text/x-java-source" ,text/x-makefile;text/x-objcsrc;text/x-pascal;text/x-patch;text/x-python;text/x-scheme;text/x-sql;text/x-tcl;
+ShowServiceFiles=true
+ShowServiceDirectories=true
+HasMetadata=false
+HasFullText=true
+HasThumbs=false
+
+[Other]
+DisplayName=Other Files
+Description=All other files that do not belong in any other category
+Parent=Files
+ShowServiceFiles=true
+ShowServiceDirectories=true
+HasMetadata=true
+HasFullText=true
+HasThumbs=true
+
+[Emails]
+DisplayName=Emails
+Description=All Emails
+PropertyPrefix=Email
+UIVisible=true
+UIMetadata1=Email:Subject
+UIMetadata2=Email:Sender
+Icon=stock_mail-open
+TabularMetadata=Email:Sender;Email:Subject;Email:Date;
+TileMetadata=Email:Sender;Email:Subject;Email:Date;Email:SentTo;Email:CC;Email:Attachments
+ContentMetadata=Email:Body
+
+[EvolutionEmails]
+DisplayName=Evolution Emails
+Description=Evolution based emails
+Parent=Emails
+ViewerExec=evolution "%1"
+KeyMetadata1=Email:Subject
+KeyMetadata2=Email:Sender
+KeyMetadata3=Email:Date
+TabularMetadata=Email:Sender;Email:Subject;Email:Date;
+TileMetadata=Email:Sender;Email:Subject;Email:Date;Email:SentTo;Email:CC;Email:Attachments
+ContentMetadata=Email:Body
+
+[ModestEmails]
+DisplayName=Modest Emails
+Description=Modest based emails
+Parent=Emails
+ViewerExec=modest-open "%1"
+KeyMetadata1=Email:Subject
+KeyMetadata2=Email:Sender
+KeyMetadata3=Email:Date
+TabularMetadata=Email:Sender;Email:Subject;Email:Date;
+TileMetadata=Email:Sender;Email:Subject;Email:Date;Email:SentTo;Email:CC;Email:Attachments
+ContentMetadata=Email:Body
+
+[ThunderbirdEmails]
+DisplayName=Thunderbird Emails
+Description=Thunderbird based emails
+Parent=Emails
+ViewerExec=thunderbird -viewtracker "%1"
+KeyMetadata1=Email:Subject
+KeyMetadata2=Email:Sender
+KeyMetadata3=Email:Date
+TabularMetadata=Email:Sender;Email:Subject;Email:Date;
+TileMetadata=Email:Sender;Email:Subject;Email:Date;Email:SentTo;Email:CC;Email:Attachments
+ContentMetadata=Email:Body
+
+[KMailEmails]
+DisplayName=KMail Emails
+Description=KMail based emails
+Parent=Emails
+ViewerExec=kmail "%1"
+KeyMetadata1=Email:Subject
+KeyMetadata2=Email:Sender
+KeyMetadata3=Email:Date
+TabularMetadata=Email:Sender;Email:Subject;Email:Date;
+TileMetadata=Email:Sender;Email:Subject;Email:Date;Email:SentTo;Email:CC;Email:Attachments
+ContentMetadata=Email:Body
+
+[EmailAttachments]
+DisplayName=Email Attachments
+Description=All files that are attached to an Email
+UIVisible=true
+Icon=stock_attach
+
+[EvolutionAttachments]
+DisplayName=Evolution Email Attachments
+Description=All files that are attached to an Evolution Email
+Parent=EmailAttachments
+Icon=stock_attach
+
+[ModestAttachments]
+DisplayName=Modest Email Attachments
+Description=All files that are attached to an Modest Email
+Parent=EmailAttachments
+Icon=stock_attach
+
+[KMailAttachments]
+DisplayName=KMail Email Attachments
+Description=All files that are attached to an KMail Email
+Parent=EmailAttachments
+Icon=stock_attach
+
+[Conversations]
+DisplayName=Conversations
+Description=Conversation log files
+UIVisible=true
+Icon=stock_help-chat
+ShowServiceFiles=true
+HasMetadata=false
+HasFullText=true
+HasThumbs=false
+
+[GaimConversations]
+DisplayName=Gaim Conversations
+Description=All Gaim Conversation logs
+Parent=Conversations
+Icon=stock_help-chat
+ShowServiceFiles=true
+HasMetadata=false
+HasFullText=true
+HasThumbs=false
+
+[Applications]
+DisplayName=Applications
+Description=Application files
+PropertyPrefix=App
+UIVisible=true
+Icon=stock_active
+ShowServiceFiles=true
+KeyMetadata1=App:DisplayName
+KeyMetadata2=App:Exec
+KeyMetadata3=App:Icon
+TileMetadata=App:GenericName;AppComment;App:Categories
+HasMetadata=false
+HasFullText=false
+HasThumbs=false

Added: trunk/data/services/document.metadata
==============================================================================
--- (empty file)
+++ trunk/data/services/document.metadata	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,63 @@
+[Doc:Title]
+DisplayName=Title
+Description=The title of the document
+DataType=index
+Parent=DC:Title
+Weight=25
+Filtered=false
+
+[Doc:Subject]
+DisplayName=Subject
+Description=The subject or topic of the document
+DataType=index
+Parent=DC:Subject
+Weight=20
+Filtered=false
+
+[Doc:Author]
+DisplayName=Author
+Description=The author of the document
+DataType=index
+Parent=DC:Creator
+Weight=20
+Filtered=false
+
+[Doc:Keywords]
+DisplayName=Keywords
+Description=keywords embedded in the document
+DataType=keyword
+Parent=DC:Keywords
+Weight=25
+Filtered=false
+Delimited=true
+
+[Doc:Comments]
+DisplayName=Comments
+Description=The comments embedded in the document
+DataType=index
+Parent=DC:Description
+Weight=10
+
+[Doc:PageCount]
+DisplayName=Page count
+Description=Number of pages in the document
+DataType=integer
+
+[Doc:WordCount]
+DisplayName=Word count
+Description=Number of words in the document
+DataType=integer
+
+[Doc:Created]
+DisplayName=Created
+Description=Date document was created
+DataType=date
+Parent=DC:Date
+
+[Doc:URL]
+DisplayName=URL
+Description=URL to this Doc
+DataType=index
+Weight=20
+Filtered=false
+

Added: trunk/data/services/email.metadata
==============================================================================
--- (empty file)
+++ trunk/data/services/email.metadata	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,64 @@
+[Email:Recipient]
+Abstract=true
+DisplayName=Recipient
+Description=The recepient of an email
+DataType=Indexable
+
+[Email:Body]
+DisplayName=Body
+Description=The body contents of the email
+DataType=CLOB
+Weight=1
+
+[Email:Date]
+DisplayName=Date
+Description=Date email was sent
+DataType=DateTime
+Parent=DC:Date
+
+[Email:Sender]
+DisplayName=Sender
+Description=The sender of the email
+DataType=Indexable
+Parent=DC:Creator
+Weight=10
+
+[Email:Subject]
+DisplayName=Subject
+Description=The subject of the email
+DataType=Indexable
+Parent=DC:Subject
+Weight=20
+
+[Email:SentTo]
+DisplayName=Sent to
+Description=The group of people the email was sent to
+DataType=Indexable
+MultipleValues=true
+Parent=Email:Recipient
+Weight=10
+
+[Email:CC]
+DisplayName=CC
+Description=The CC recipients of the email
+DataType=Indexable
+MultipleValues=true
+Parent=Email:Recipient
+Weight=5
+
+[Email:Attachments]
+DisplayName=Attachments
+Description=The names of the attachments
+DataType=Indexable
+Weight=5
+
+[Email:AttachmentsDelimited]
+DisplayName=AttachmentsDelimited
+Description=The names of the attachments with extra delimiting
+DataType=Indexable
+Weight=5
+
+
+
+
+

Added: trunk/data/services/file.metadata
==============================================================================
--- (empty file)
+++ trunk/data/services/file.metadata	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,99 @@
+[File:Name]
+DisplayName=Filename
+Description=Name of File
+DataType=index
+FieldName=Name
+Parent=DC:Identifier
+Weight=10
+Filtered=false
+
+[File:Ext]
+DisplayName=Extension
+Description=File extension
+DataType=index
+Weight=15
+Filtered=false
+
+[File:Path]
+DisplayName=Path
+Description=File Path
+DataType=index
+FieldName=Path
+Parent=DC:Identifier
+Weight=1
+Filtered=false
+
+[File:NameDelimited]
+DisplayName=Keywords
+Description=Name of File
+DataType=index
+Weight=5
+Filtered=false
+Delimited=true
+
+[File:Contents]
+DisplayName=Contents
+Description=File Contents
+DataType=fulltext
+Weight=1
+
+[File:Link]
+DisplayName=Link
+Description=File Link
+DataType=index
+Parent=DC:Relation
+Weight=1
+
+[File:Mime]
+DisplayName=Mime Type
+Description=File Mime Type
+DataType=keyword
+FieldName=Mime
+Parent=DC:Format
+Weight=10
+
+[File:Size]
+DisplayName=Size
+Description=File size in bytes
+DataType=integer
+FieldName=Size
+
+[File:License]
+DisplayName=License
+Description=File License Type
+DataType=index
+Parent=DC:Rights
+Weight=1
+
+[File:Copyright]
+DisplayName=Copyright
+Description=Copyright owners of the file
+DataType=index
+Parent=DC:Rights
+Weight=1
+
+[File:Modified]
+DisplayName=Modified
+Description=Last modified date
+DataType=date
+Parent=DC:Date
+FieldName=IndexTime
+
+[File:Accessed]
+DisplayName=Accessed
+Description=Last acessed date
+DataType=date
+Parent=DC:Date
+FieldName=Accessed
+
+[File:Other]
+DisplayName=Other
+Description=Other details about a file
+DataType=index
+Weight=1
+
+[File:Language]
+DisplayName=Language
+Description=File language
+DataType=index
+Weight=1

Added: trunk/data/services/image.metadata
==============================================================================
--- (empty file)
+++ trunk/data/services/image.metadata	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,138 @@
+[Image:Title]
+DisplayName=Title
+Description=The title of the image
+DataType=index
+Parent=DC:Title
+Weight=10
+Filtered=false
+
+[Image:Keywords]
+DisplayName=Keywords
+Description=The keywords embedded in the image
+DataType=keyword
+Parent=DC:Keywords
+Weight=20
+Filtered=false
+Delimited=true
+
+[Image:Height]
+DisplayName=Height
+Description=Height in pixels of the image
+DataType=integer
+
+[Image:Width]
+DisplayName=Width
+Description=Width in pixels of the image
+DataType=integer
+
+[Image:Album]
+DisplayName=Album
+Description=The name of the album in which the image resides
+DataType=index
+Weight=5
+
+[Image:Date]
+DisplayName=Created
+Description=Date image was created or shot
+DataType=DateTime
+Parent=DC:Date
+
+[Image:Creator]
+DisplayName=Creator
+Description=The person who created the image
+DataType=index
+Parent=DC:Creator
+Weight=10
+Filtered=false
+
+[Image:Comments]
+DisplayName=Comments
+Description=The comments embedded in the image
+DataType=index
+Parent=DC:Description
+Weight=5
+
+
+[Image:Description]
+DisplayName=Description
+Description=The description embedded in the image
+DataType=index
+Parent=DC:Description
+Weight=5
+
+[Image:Software]
+DisplayName=Software
+Description=The software used to create the image
+DataType=index
+Weight=1
+
+[Image:CameraMake]
+DisplayName=Camera make
+Description=The camera used to create the image
+DataType=index
+Weight=1
+
+[Image:CameraModel]
+DisplayName=Camera model
+Description=The model number of the camera used to create the image
+DataType=index
+Weight=1
+
+[Image:Orientation]
+DisplayName=Orientation
+Description=The Orientation mode of the image (portrait/landscape)
+DataType=string
+
+[Image:ExposureProgram]
+DisplayName=Exposure program
+Description=The class of the program used by the camera to set exposure when the picture is taken
+DataType=string
+
+[Image:ExposureTime]
+DisplayName=Exposure time
+Description=Exposure time in seconds
+DataType=integer
+
+[Image:FNumber]
+DisplayName=F number
+Description=The F number
+DataType=integer
+
+[Image:Flash]
+DisplayName=Flash
+Description=Indicates the status of flash when the image was shot (0=off, 1=on)
+DataType=integer
+
+[Image:FocalLength]
+DisplayName=Focal length
+Description= The actual focal length of the lens in mm
+DataType=double
+
+[Image:ISOSpeed]
+DisplayName=ISO speed
+Description=Indicates the ISO Speed and ISO Latitude of the camera or input device as specified in ISO 12232.
+DataType=integer
+
+[Image:MeteringMode]
+DisplayName=Metering mode
+Description=The metering mode
+DataType=string
+
+[Image:WhiteBalance]
+DisplayName=White balance
+Description=Indicates the white balance mode set when the image was shot (auto/manual)
+DataType=string
+
+
+
+
+
+
+
+
+
+
+
+
+
+

Added: trunk/data/services/video.metadata
==============================================================================
--- (empty file)
+++ trunk/data/services/video.metadata	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,51 @@
+[Video:Title]
+DisplayName=Title
+Description=Video title
+DataType=index
+Parent=DC:Title
+Weight=20
+Filtered=false
+
+[Video:Author]
+DisplayName=Author
+Description=Video author
+DataType=index
+Parent=DC:Creator
+Weight=15
+
+[Video:Height]
+DisplayName=Height
+Description=The height in pixels
+DataType=integer
+
+[Video:Width]
+DisplayName=Width
+Description=The width in pixels
+DataType=integer
+
+[Video:Duration]
+DisplayName=Duration
+Description=Duration in number of seconds
+DataType=integer
+
+[Video:Comments]
+DisplayName=Comments
+Description=General purpose comments
+DataType=index
+Weight=1
+Parent=DC:Description
+
+[Video:FrameRate]
+DisplayName=Frame rate
+Description=Number of frames per seconds
+DataType=double
+
+[Video:Codec]
+DisplayName=Codec
+Description=Codec used for the video
+DataType=string
+
+[Video:Bitrate]
+DisplayName=Bitrate
+Description=Bitrate in bits/sec
+DataType=double

Added: trunk/data/services/xesam-convenience.metadata
==============================================================================
--- (empty file)
+++ trunk/data/services/xesam-convenience.metadata	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,301 @@
+
+[xesam:actionAccessClassification]
+Parents=
+Categories=xesam:Event;xesam:Journal;xesam:Task
+ValueType=List of strings
+Description=PIM entry access classification
+
+
+[xesam:actionContact]
+Parents=
+Categories=xesam:Event;xesam:FreeBusy;xesam:Journal;xesam:Task
+ValueType=List of strings
+Description=PIM entry contact
+
+
+[xesam:actionDuration]
+Parents=
+Categories=xesam:Alarm;xesam:Event;xesam:FreeBusy;xesam:Task
+ValueType=List of strings
+Description=PIM entry action duration
+
+
+[xesam:actionEnd]
+Parents=
+Categories=xesam:Event;xesam:FreeBusy
+ValueType=List of strings
+Description=PIM entry action end
+
+
+[xesam:actionExceptionDate]
+Parents=
+Categories=xesam:Event;xesam:Journal;xesam:Task
+ValueType=List of strings
+Description=PIM entry exception date
+
+
+[xesam:actionExceptionRule]
+Parents=
+Categories=xesam:Event;xesam:Journal;xesam:Task
+ValueType=List of strings
+Description=PIM entry exception rule
+
+
+[xesam:actionLocation]
+Parents=
+Categories=xesam:Event;xesam:Task
+ValueType=List of strings
+Description=PIM entry location
+
+
+[xesam:actionOrganizer]
+Parents=
+Categories=xesam:Event;xesam:FreeBusy;xesam:Journal;xesam:Task
+ValueType=List of strings
+Description=PIM entry organizer
+
+
+[xesam:actionPriority]
+Parents=
+Categories=xesam:Event;xesam:Task
+ValueType=List of strings
+Description=PIM entry priority
+
+
+[xesam:actionRecurrenceDate]
+Parents=
+Categories=xesam:Event;xesam:Journal;xesam:Task
+ValueType=List of strings
+Description=PIM entry recurrence date
+
+
+[xesam:actionRecurrenceID]
+Parents=
+Categories=xesam:Event;xesam:Journal;xesam:Task
+ValueType=List of strings
+Description=PIM entry recurrence ID
+
+
+[xesam:actionRecurrenceRule]
+Parents=
+Categories=xesam:Event;xesam:Journal;xesam:Task
+ValueType=List of strings
+Description=PIM entry recurrence rule
+
+
+[xesam:actionResources]
+Parents=
+Categories=xesam:Event;xesam:Task
+ValueType=List of strings
+Description=PIM activity has alarm
+
+
+[xesam:actionStart]
+Parents=
+Categories=xesam:Event;xesam:FreeBusy;xesam:Task
+ValueType=List of strings
+Description=PIM entry action start
+
+
+[xesam:actionStatus]
+Parents=
+Categories=xesam:Event;xesam:Journal;xesam:Task
+ValueType=List of strings
+Description=PIM entry status
+
+
+[xesam:actionTrigger]
+Parents=
+Categories=xesam:Alarm;xesam:Event;xesam:Task
+ValueType=List of strings
+Description=PIM entry action trigger
+
+
+[xesam:actionURL]
+Parents=
+Categories=xesam:Event;xesam:FreeBusy;xesam:Journal;xesam:Task
+ValueType=List of Urls
+Description=PIM entry URL
+
+
+[xesam:aimContactMedium]
+Parents=xesam:imContactMedium
+Categories=xesam:Contact
+ValueType=List of strings
+Description=Contact AIM ID
+
+
+[xesam:alarmAction]
+Parents=
+Categories=xesam:Alarm
+ValueType=List of strings
+Description=Alarm action
+
+
+[xesam:alarmRepeat]
+Parents=
+Categories=xesam:Alarm
+ValueType=List of strings
+Description=Alarm repeat
+
+
+[xesam:applicationDesktopEntryExec]
+Parents=
+Categories=xesam:ApplicationDesktopEntry
+ValueType=string
+Description=Command to execute, possibly with arguments
+
+
+[xesam:attendee]
+Parents=
+Categories=xesam:Alarm;xesam:Event;xesam:FreeBusy;xesam:Journal;xesam:Task
+ValueType=List of strings
+Description=PIM entry attendee
+
+
+[xesam:desktopEntryIcon]
+Parents=
+Categories=xesam:DesktopEntry
+ValueType=string
+Description=Desktop entry Icon field value conforming to http://freedesktop.org/wiki/Standards/icon-theme-spec
+
+
+[xesam:desktopMenuCategory]
+Parents=
+Categories=xesam:DesktopEntry
+ValueType=List of strings
+Description=Category in which the entry should be shown in the desktop menu. http://www.freedesktop.org/Standards/menu-spec
+
+
+[xesam:eventEnd]
+Parents=
+Categories=xesam:Event
+ValueType=List of strings
+Description=Event end time
+
+
+[xesam:eventLocation]
+Parents=
+Categories=xesam:Event
+ValueType=List of strings
+Description=Event location
+
+
+[xesam:eventStart]
+Parents=
+Categories=xesam:Event
+ValueType=List of strings
+Description=Event start time
+
+
+[xesam:eventTransparrent]
+Parents=
+Categories=xesam:Event
+ValueType=List of strings
+Description=Is event transparrent(makes person busy)
+
+
+[xesam:icqContactMedium]
+Parents=xesam:imContactMedium
+Categories=xesam:Contact
+ValueType=List of strings
+Description=Contact ICQ ID
+
+
+[xesam:imdbId]
+Parents=xesam:id
+Categories=xesam:Video
+ValueType=Abstract field. Contains no data.
+Description=IMDB.com video ID
+
+
+[xesam:isrc]
+Parents=xesam:id
+Categories=xesam:Media
+ValueType=Abstract field. Contains no data.
+Description=International Standard Recording Code
+
+
+[xesam:msnContactMedium]
+Parents=xesam:imContactMedium
+Categories=xesam:Contact
+ValueType=List of strings
+Description=Contact MSN ID
+
+
+[xesam:musicBrainzAlbumArtistID]
+Parents=xesam:id
+Categories=xesam:Audio
+ValueType=List of strings
+Description=MusicBrainz album artist ID in UUID format
+
+
+[xesam:musicBrainzAlbumID]
+Parents=xesam:id
+Categories=xesam:Audio
+ValueType=List of strings
+Description=MusicBrainz album ID in UUID format
+
+
+[xesam:musicBrainzArtistID]
+Parents=xesam:id
+Categories=xesam:Audio
+ValueType=List of strings
+Description=MusicBrainz artist ID in UUID format
+
+
+[xesam:musicBrainzFingerprint]
+Parents=xesam:fingerprint
+Categories=xesam:Audio
+ValueType=Abstract field. Contains no data.
+Description=MusicBrainz track fingerprint
+
+
+[xesam:musicBrainzTrackID]
+Parents=xesam:id
+Categories=xesam:Audio
+ValueType=List of strings
+Description=MusicBrainz track ID in UUID format
+
+
+[xesam:skypeContactMedium]
+Parents=xesam:imContactMedium
+Categories=xesam:Contact
+ValueType=List of strings
+Description=Contact Skype ID
+
+
+[xesam:supportedMimeType]
+Parents=
+Categories=xesam:ApplicationDesktopEntry
+ValueType=List of strings
+Description=The MIME type supported by this application
+
+
+[xesam:taskCompleted]
+Parents=
+Categories=xesam:Task
+ValueType=List of booleans
+Description=Is task completed?
+
+
+[xesam:taskDue]
+Parents=
+Categories=xesam:Task
+ValueType=List of dateTimes
+Description=Task due date/time
+
+
+[xesam:taskPercentComplete]
+Parents=
+Categories=xesam:Task
+ValueType=List of integers
+Description=Task completeness
+
+
+[xesam:yahooContactMedium]
+Parents=xesam:imContactMedium
+Categories=xesam:Contact
+ValueType=List of strings
+Description=Contact Yahoo ID
+

Added: trunk/data/services/xesam-convenience.service
==============================================================================
--- (empty file)
+++ trunk/data/services/xesam-convenience.service	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,32 @@
+
+[xesam:Alarm]
+Parents=xesam:PIM
+Description=Alarm
+
+[xesam:ApplicationDesktopEntry]
+Parents=xesam:DesktopEntry
+Description=Application Desktop Entry
+
+[xesam:DesktopEntry]
+Parents=xesam:Content
+Description=Desktop Entry(typically a .desktop file)
+
+[xesam:Event]
+Parents=xesam:PIM
+Description=Event
+
+[xesam:FreeBusy]
+Parents=xesam:PIM
+Description=FreeBusy
+
+[xesam:Journal]
+Parents=xesam:PIM
+Description=Journal
+
+[xesam:PIM]
+Parents=xesam:Content
+Description=Generic PIM
+
+[xesam:Task]
+Parents=xesam:PIM
+Description=Task

Added: trunk/data/services/xesam-metadata.mmapping
==============================================================================
--- (empty file)
+++ trunk/data/services/xesam-metadata.mmapping	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,1021 @@
+
+[xesam:35mmEquivalent]
+MetaName=
+
+[xesam:acl]
+MetaName=
+
+
+[xesam:album]
+MetaName=Audio:Album
+
+
+[xesam:albumArtist]
+MetaName=Audio:Artist
+
+[xesam:albumGain]
+MetaName=
+
+[xesam:albumPeakGain]
+MetaName=
+
+
+[xesam:albumTrackCount]
+MetaName=
+
+[xesam:aperture]
+MetaName=
+
+[xesam:artist]
+MetaName=Audio:Artist
+
+
+[xesam:asText]
+MetaName=
+
+
+[xesam:aspectRatio]
+MetaName=
+
+[xesam:attachmentEncoding]
+MetaName=
+
+[xesam:audioBPM]
+MetaName=
+
+
+[xesam:audioBitrate]
+MetaName=
+
+
+[xesam:audioChannels]
+MetaName=
+
+
+[xesam:audioCodec]
+MetaName=
+
+
+[xesam:audioCodecType]
+MetaName=
+
+
+[xesam:audioSampleBitDepth]
+MetaName=
+
+
+[xesam:audioSampleCount]
+MetaName=
+
+
+[xesam:audioSampleDataType]
+MetaName=
+
+
+[xesam:audioSampleRate]
+MetaName=
+
+
+[xesam:author]
+MetaName=Email:Sender
+
+
+[xesam:autoRating]
+MetaName=
+
+
+[xesam:baseRevisionID]
+MetaName=
+
+
+[xesam:bcc]
+MetaName=
+
+
+[xesam:birthDate]
+MetaName=
+
+
+[xesam:blogContactURL]
+MetaName=
+
+
+[xesam:cameraManufacturer]
+MetaName=
+
+
+[xesam:cameraModel]
+MetaName=
+
+
+[xesam:cc]
+MetaName=Email:CC
+
+
+[xesam:ccdWidth]
+MetaName=
+
+
+[xesam:cellPhoneNumber]
+MetaName=
+
+
+[xesam:changeCommitTime]
+MetaName=
+
+
+[xesam:changeCommitter]
+MetaName=
+
+
+[xesam:characterCount]
+MetaName=
+
+
+[xesam:charset]
+MetaName=
+
+
+[xesam:chatRoom]
+MetaName=
+
+
+[xesam:colorCount]
+MetaName=
+
+
+[xesam:colorSpace]
+MetaName=
+
+
+[xesam:columnCount]
+MetaName=
+
+
+[xesam:comment]
+MetaName=
+
+
+[xesam:commentCharacterCount]
+MetaName=
+
+
+[xesam:commitDiff]
+MetaName=
+
+
+[xesam:communicationChannel]
+MetaName=
+
+
+[xesam:composer]
+MetaName=
+
+
+[xesam:compressionAlgorithm]
+MetaName=
+
+
+[xesam:compressionLevel]
+MetaName=
+
+
+[xesam:conflicts]
+MetaName=
+
+
+[xesam:contactMedium]
+MetaName=
+
+
+[xesam:contactNick]
+MetaName=
+
+
+[xesam:contactURL]
+MetaName=
+
+
+[xesam:contains]
+MetaName=
+
+
+[xesam:contentComment]
+MetaName=
+
+
+[xesam:contentCreated]
+MetaName=
+
+
+[xesam:contentKeyword]
+MetaName=
+
+
+[xesam:contentModified]
+MetaName=
+
+
+[xesam:contentType]
+MetaName=
+
+
+[xesam:contributor]
+MetaName=
+
+
+[xesam:copyright]
+MetaName=
+
+
+[xesam:creator]
+MetaName=
+
+
+[xesam:definesClass]
+MetaName=
+
+
+[xesam:definesFunction]
+MetaName=
+
+
+[xesam:definesGlobalVariable]
+MetaName=
+
+
+[xesam:deletionTime]
+MetaName=
+
+
+[xesam:depends]
+MetaName=
+
+
+[xesam:derivedFrom]
+MetaName=
+
+
+[xesam:description]
+MetaName=
+
+
+[xesam:discNumber]
+MetaName=
+
+
+[xesam:disclaimer]
+MetaName=
+
+
+[xesam:documentCategory]
+MetaName=
+
+
+[xesam:emailAddress]
+MetaName=
+
+
+[xesam:exposureBias]
+MetaName=
+
+
+[xesam:exposureProgram]
+MetaName=
+
+
+[xesam:exposureTime]
+MetaName=
+
+
+[xesam:familyName]
+MetaName=
+
+
+[xesam:faxPhoneNumber]
+MetaName=
+
+
+[xesam:fileExtension]
+MetaName=
+
+
+[xesam:fileSystemType]
+MetaName=
+
+
+[xesam:fingerprint]
+MetaName=
+
+
+[xesam:firstUsed]
+MetaName=
+
+
+[xesam:flashUsed]
+MetaName=
+
+
+[xesam:focalLength]
+MetaName=
+
+
+[xesam:focusDistance]
+MetaName=
+
+
+[xesam:formatSubtype]
+MetaName=
+
+
+[xesam:frameCount]
+MetaName=
+
+
+[xesam:frameRate]
+MetaName=
+
+
+[xesam:freeSpace]
+MetaName=
+
+
+[xesam:gender]
+MetaName=
+
+
+[xesam:generator]
+MetaName=
+
+
+[xesam:generatorOptions]
+MetaName=
+
+
+[xesam:genre]
+MetaName=Audio:Genre
+
+
+[xesam:givenName]
+MetaName=
+
+
+[xesam:group]
+MetaName=
+
+
+[xesam:height]
+MetaName=
+
+
+[xesam:homeEmailAddress]
+MetaName=
+
+
+[xesam:homePhoneNumber]
+MetaName=
+
+
+[xesam:homePostalAddress]
+MetaName=
+
+
+[xesam:homepageContactURL]
+MetaName=
+
+
+[xesam:honorificPrefix]
+MetaName=
+
+
+[xesam:honorificSuffix]
+MetaName=
+
+
+[xesam:horizontalResolution]
+MetaName=
+
+
+[xesam:id]
+MetaName=
+
+
+[xesam:imContactMedium]
+MetaName=
+
+
+[xesam:inReplyTo]
+MetaName=
+
+
+[xesam:interest]
+MetaName=
+
+
+[xesam:interlaceMode]
+MetaName=
+
+
+[xesam:ircContactMedium]
+MetaName=
+
+
+[xesam:isContentEncrypted]
+MetaName=
+
+
+[xesam:isEncrypted]
+MetaName=
+
+
+[xesam:isImportant]
+MetaName=
+
+
+[xesam:isInProgress]
+MetaName=
+
+
+[xesam:isPublicChannel]
+MetaName=
+
+
+[xesam:isRead]
+MetaName=
+
+
+[xesam:isSourceEncrypted]
+MetaName=
+
+
+[xesam:isoEquivalent]
+MetaName=
+
+
+[xesam:jabberContactMedium]
+MetaName=
+
+
+[xesam:keyword]
+MetaName=
+
+
+[xesam:knows]
+MetaName=
+
+
+[xesam:language]
+MetaName=
+
+
+[xesam:lastRefreshed]
+MetaName=
+
+
+[xesam:lastUsed]
+MetaName=
+
+
+[xesam:legal]
+MetaName=
+
+
+[xesam:license]
+MetaName=
+
+
+[xesam:licenseType]
+MetaName=
+
+
+[xesam:lineCount]
+MetaName=
+
+
+[xesam:links]
+MetaName=
+
+
+[xesam:localRevision]
+MetaName=
+
+
+[xesam:lyricist]
+MetaName=
+
+
+[xesam:mailingList]
+MetaName=
+
+
+[xesam:mailingPostalAddress]
+MetaName=
+
+
+[xesam:maintainer]
+MetaName=
+
+
+[xesam:markupCharacterCount]
+MetaName=
+
+
+[xesam:md5Hash]
+MetaName=
+
+
+[xesam:mediaBitrate]
+MetaName=
+
+
+[xesam:mediaCodec]
+MetaName=
+
+
+[xesam:mediaCodecType]
+MetaName=
+
+
+[xesam:mediaDuration]
+MetaName=
+
+
+[xesam:mergeConflict]
+MetaName=
+
+
+[xesam:meteringMode]
+MetaName=
+
+
+[xesam:mimeType]
+MetaName=
+
+
+[xesam:mountPoint]
+MetaName=
+
+
+[xesam:name]
+MetaName=
+
+
+[xesam:newsGroup]
+MetaName=
+
+
+[xesam:occupiedSpace]
+MetaName=
+
+
+[xesam:orientation]
+MetaName=
+
+
+[xesam:originURL]
+MetaName=
+
+
+[xesam:originalLocation]
+MetaName=
+
+
+[xesam:otherName]
+MetaName=
+
+
+[xesam:owner]
+MetaName=
+
+
+[xesam:pageCount]
+MetaName=
+
+
+[xesam:paragrapCount]
+MetaName=
+
+
+[xesam:performer]
+MetaName=
+
+
+[xesam:permissions]
+MetaName=
+
+
+[xesam:personPhoto]
+MetaName=
+
+
+[xesam:phoneNumber]
+MetaName=
+
+
+[xesam:physicalAddress]
+MetaName=
+
+
+[xesam:pixelDataBitDepth]
+MetaName=
+
+
+[xesam:pixelDataType]
+MetaName=
+
+
+[xesam:primaryRecipient]
+MetaName=Email:Recipient
+
+
+[xesam:programmingLanguage]
+MetaName=
+
+
+[xesam:receptionTime]
+MetaName=
+
+
+[xesam:recipient]
+MetaName=Email:Recipient
+
+
+[xesam:related]
+MetaName=
+
+
+[xesam:remotePassword]
+MetaName=
+
+
+[xesam:remotePort]
+MetaName=
+
+
+[xesam:remoteServer]
+MetaName=
+
+
+[xesam:remoteUser]
+MetaName=
+
+
+[xesam:replyTo]
+MetaName=
+
+
+[xesam:rowCount]
+MetaName=
+
+
+[xesam:rssFeed]
+MetaName=
+
+
+[xesam:sampleBitDepth]
+MetaName=
+
+
+[xesam:sampleConfiguration]
+MetaName=
+
+
+[xesam:sampleDataType]
+MetaName=
+
+
+[xesam:secondaryRecipient]
+MetaName=Email:Recipient
+
+
+[xesam:seenAttachedAs]
+MetaName=
+
+
+[xesam:setCount]
+MetaName=
+
+
+[xesam:setRate]
+MetaName=
+
+
+[xesam:sha1Hash]
+MetaName=
+
+
+[xesam:size]
+MetaName=
+
+
+[xesam:sourceCreated]
+MetaName=
+
+
+[xesam:sourceModified]
+MetaName=
+
+
+[xesam:storedSize]
+MetaName=
+
+
+[xesam:subject]
+MetaName=
+
+
+[xesam:summary]
+MetaName=
+
+
+[xesam:supercedes]
+MetaName=
+
+
+[xesam:targetQuality]
+MetaName=
+
+
+[xesam:title]
+MetaName=Audio:Title;Doc:Title;Video:Title
+
+
+[xesam:to]
+MetaName=Email:Recipient
+
+
+[xesam:totalSpace]
+MetaName=
+
+
+[xesam:totalUncompressedSize]
+MetaName=
+
+
+[xesam:trackGain]
+MetaName=
+
+
+[xesam:trackNumber]
+MetaName=Audio:TrackNo
+
+
+[xesam:trackPeakGain]
+MetaName=
+
+
+[xesam:url]
+MetaName=File:Name
+
+
+[xesam:useCount]
+MetaName=
+
+
+[xesam:userComment]
+MetaName=
+
+
+[xesam:userKeyword]
+MetaName=
+
+
+[xesam:userRating]
+MetaName=
+
+
+[xesam:usesNamespace]
+MetaName=
+
+
+[xesam:version]
+MetaName=
+
+
+
+[xesam:verticalResolution]
+MetaName=
+
+
+[xesam:videoBitrate]
+MetaName=
+
+
+[xesam:videoCodec]
+MetaName=
+
+
+[xesam:videoCodecType]
+MetaName=
+
+
+[xesam:whiteBalance]
+MetaName=
+
+
+[xesam:width]
+MetaName=
+
+
+[xesam:wordCount]
+MetaName=
+
+
+[xesam:workEmailAddress]
+MetaName=
+
+
+[xesam:workPhoneNumber]
+MetaName=
+
+
+[xesam:workPostalAddress]
+MetaName=
+
+
+[xesam:actionAccessClassification]
+MetaName=
+
+
+[xesam:actionContact]
+MetaName=
+
+
+[xesam:actionDuration]
+MetaName=
+
+
+[xesam:actionEnd]
+MetaName=
+
+
+[xesam:actionExceptionDate]
+MetaName=
+
+
+[xesam:actionExceptionRule]
+MetaName=
+
+
+[xesam:actionLocation]
+MetaName=
+
+
+[xesam:actionOrganizer]
+MetaName=
+
+
+[xesam:actionPriority]
+MetaName=
+
+
+[xesam:actionRecurrenceDate]
+MetaName=
+
+
+[xesam:actionRecurrenceID]
+MetaName=
+
+
+[xesam:actionRecurrenceRule]
+MetaName=
+
+
+[xesam:actionResources]
+MetaName=
+
+
+[xesam:actionStart]
+MetaName=
+
+
+[xesam:actionStatus]
+MetaName=
+
+
+[xesam:actionTrigger]
+MetaName=
+
+
+[xesam:actionURL]
+MetaName=
+
+
+[xesam:aimContactMedium]
+MetaName=
+
+
+[xesam:alarmAction]
+MetaName=
+
+
+[xesam:alarmRepeat]
+MetaName=
+
+
+[xesam:applicationDesktopEntryExec]
+MetaName=
+
+
+[xesam:attendee]
+MetaName=
+
+
+[xesam:desktopEntryIcon]
+MetaName=
+
+
+[xesam:desktopMenuCategory]
+MetaName=
+
+
+[xesam:eventEnd]
+MetaName=
+
+
+[xesam:eventLocation]
+MetaName=
+
+
+[xesam:eventStart]
+MetaName=
+
+
+[xesam:eventTransparrent]
+MetaName=
+
+
+[xesam:icqContactMedium]
+MetaName=
+
+
+[xesam:imdbId]
+MetaName=
+
+
+[xesam:isrc]
+MetaName=
+
+
+[xesam:msnContactMedium]
+MetaName=
+
+
+[xesam:musicBrainzAlbumArtistID]
+MetaName=
+
+
+[xesam:musicBrainzAlbumID]
+MetaName=
+
+
+[xesam:musicBrainzArtistID]
+MetaName=
+
+
+[xesam:musicBrainzFingerprint]
+MetaName=
+
+
+[xesam:musicBrainzTrackID]
+MetaName=
+
+
+[xesam:skypeContactMedium]
+MetaName=
+
+
+[xesam:supportedMimeType]
+MetaName=
+
+
+[xesam:taskCompleted]
+MetaName=
+
+
+[xesam:taskDue]
+MetaName=
+
+
+[xesam:taskPercentComplete]
+MetaName=
+
+
+[xesam:yahooContactMedium]
+MetaName=
+
+
+[xesam:contentCategory]
+MetaName=
+
+
+[xesam:sourceCategory]
+MetaName=
+
+
+[xesam:relevancyRating]
+MetaName=
+
+
+[xesam:snippet]
+MetaName=

Added: trunk/data/services/xesam-service.smapping
==============================================================================
--- (empty file)
+++ trunk/data/services/xesam-service.smapping	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,207 @@
+
+[xesam:Annotation]
+TypeName=
+
+[xesam:Archive]
+TypeName=
+
+[xesam:ArchivedFile]
+TypeName=
+
+[xesam:Audio]
+TypeName=Audio
+
+[xesam:AudioList]
+TypeName=
+
+[xesam:BlockDevice]
+TypeName=
+
+[xesam:Bookmark]
+TypeName=
+
+[xesam:CommunicationChannel]
+TypeName=
+
+[xesam:Contact]
+TypeName=
+
+[xesam:ContactGroup]
+TypeName=
+
+[xesam:Content]
+TypeName=
+
+[xesam:DataObject]
+TypeName=
+
+[xesam:DeletedFile]
+TypeName=
+
+[xesam:Document]
+TypeName=Documents
+
+[xesam:Documentation]
+TypeName=Documents
+
+[xesam:Email]
+TypeName=Email
+
+[xesam:EmailAttachment]
+TypeName=EmailAttachment
+
+[xesam:EmbeddedObject]
+TypeName=
+
+[xesam:File]
+TypeName=File
+
+[xesam:FileSystem]
+TypeName=
+
+[xesam:Filelike]
+TypeName=
+
+[xesam:Folder]
+TypeName=Folder
+
+[xesam:IMAP4Message]
+TypeName=
+
+[xesam:IMMessage]
+TypeName=
+
+[xesam:Image]
+TypeName=Image
+
+[xesam:MailingList]
+TypeName=
+
+[xesam:MailingListEmail]
+TypeName=
+
+[xesam:Media]
+TypeName=
+
+[xesam:MediaList]
+TypeName=
+
+[xesam:Message]
+TypeName=
+
+[xesam:MessageboxMessage]
+TypeName=
+
+[xesam:Music]
+TypeName=Audio
+
+[xesam:NewsGroup]
+TypeName=
+
+[xesam:NewsGroupEmail]
+TypeName=
+
+[xesam:OfflineMedia]
+TypeName=
+
+[xesam:Organization]
+TypeName=
+
+[xesam:POP3Message]
+TypeName=
+
+[xesam:Person]
+TypeName=
+
+[xesam:PersonalEmail]
+TypeName=Email
+
+[xesam:Photo]
+TypeName=
+
+[xesam:Presentation]
+TypeName=
+
+[xesam:Project]
+TypeName=
+
+[xesam:RSSFeed]
+TypeName=
+
+[xesam:RSSMessage]
+TypeName=
+
+[xesam:RemoteFile]
+TypeName=
+
+[xesam:RemoteMessageboxMessage]
+TypeName=
+
+[xesam:RemoteResource]
+TypeName=
+
+[xesam:RevisionControlledFile]
+TypeName=
+
+[xesam:RevisionControlledRepository]
+TypeName=
+
+[xesam:SoftwarePackage]
+TypeName=
+
+[xesam:Source]
+TypeName=
+
+[xesam:SourceCode]
+TypeName=
+
+[xesam:Spreadsheet]
+TypeName=
+
+[xesam:SystemResource]
+TypeName=
+
+[xesam:Tag]
+TypeName=
+
+[xesam:Text]
+TypeName=
+
+[xesam:TextDocument]
+TypeName=
+
+[xesam:UncategorizedText]
+TypeName=Text
+
+[xesam:Video]
+TypeName=Video
+
+[xesam:Visual]
+TypeName=
+
+[xesam:XML]
+TypeName=
+
+[xesam:Alarm]
+TypeName=
+
+[xesam:ApplicationDesktopEntry]
+TypeName=
+
+[xesam:DesktopEntry]
+TypeName=
+
+[xesam:Event]
+TypeName=
+
+[xesam:FreeBusy]
+TypeName=
+
+[xesam:Journal]
+TypeName=
+
+[xesam:PIM]
+TypeName=
+
+[xesam:Task]
+TypeName=
\ No newline at end of file

Added: trunk/data/services/xesam-virtual.metadata
==============================================================================
--- (empty file)
+++ trunk/data/services/xesam-virtual.metadata	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,23 @@
+[xesam:contentCategory]
+Parents=
+Categories=xesam:DataObject
+ValueType=string
+Description=Identifier of content category
+
+[xesam:sourceCategory]
+Parents=
+Categories=xesam:DataObject
+ValueType=List of strings
+Description=Identifier of source category
+
+[xesam:relevancyRating]
+Parents=
+Categories=xesam:DataObject
+ValueType=float
+Description=Query relevancy rating of the object 
+
+[xesam:snippet]
+Parents=
+Categories=xesam:DataObject
+ValueType=string
+Description=Data snippet relevant to the search query 

Added: trunk/data/services/xesam.metadata
==============================================================================
--- (empty file)
+++ trunk/data/services/xesam.metadata	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,1470 @@
+
+[xesam:35mmEquivalent]
+Parents=
+Categories=xesam:Photo
+ValueType=float
+Description=Photo metering mode
+
+
+[xesam:acl]
+Parents=
+Categories=xesam:Filelike
+ValueType=List of strings
+Description=File access control list
+
+
+[xesam:album]
+Parents=
+Categories=xesam:Media
+ValueType=string
+Description=Media album
+
+
+[xesam:albumArtist]
+Parents=xesam:author
+Categories=xesam:Audio
+ValueType=List of strings
+Description=Music album artist
+
+
+[xesam:albumGain]
+Parents=
+Categories=xesam:Audio
+ValueType=float
+Description=Gain adjustment of album
+
+
+[xesam:albumPeakGain]
+Parents=
+Categories=xesam:Audio
+ValueType=float
+Description=Peak gain adjustment of album
+
+
+[xesam:albumTrackCount]
+Parents=
+Categories=xesam:Audio
+ValueType=integer
+Description=Album track count
+
+
+[xesam:aperture]
+Parents=
+Categories=xesam:Photo
+ValueType=float
+Description=Photo aperture
+
+
+[xesam:artist]
+Parents=xesam:author
+Categories=xesam:Audio
+ValueType=List of strings
+Description=Music artist
+
+
+[xesam:asText]
+Parents=
+Categories=xesam:Content
+ValueType=string
+Description=Content plain-text representation for indexing purposes
+
+
+[xesam:aspectRatio]
+Parents=
+Categories=xesam:Visual
+ValueType=string
+Description=Visual content aspect ratio
+
+
+[xesam:attachmentEncoding]
+Parents=
+Categories=xesam:EmailAttachment
+ValueType=string
+Description=Email attachment encoding(base64,utf-7, etc)
+
+
+[xesam:audioBPM]
+Parents=
+Categories=xesam:Audio
+ValueType=integer
+Description=Beats per minute
+
+
+[xesam:audioBitrate]
+Parents=xesam:mediaBitrate
+Categories=xesam:Audio
+ValueType=integer
+Description=Audio Bitrate
+
+
+[xesam:audioChannels]
+Parents=xesam:sampleConfiguration
+Categories=xesam:Audio
+ValueType=string
+Description=Audio channels
+
+
+[xesam:audioCodec]
+Parents=xesam:mediaCodec
+Categories=xesam:Audio
+ValueType=string
+Description=Audio codec
+
+
+[xesam:audioCodecType]
+Parents=xesam:mediaCodecType
+Categories=xesam:Audio
+ValueType=string
+Description=Audio codec type
+
+
+[xesam:audioSampleBitDepth]
+Parents=xesam:sampleBitDepth
+Categories=xesam:Audio
+ValueType=integer
+Description=Audio sample data bit depth
+
+
+[xesam:audioSampleCount]
+Parents=xesam:setCount
+Categories=xesam:Audio
+ValueType=integer
+Description=Audio sample count
+
+
+[xesam:audioSampleDataType]
+Parents=xesam:sampleDataType
+Categories=xesam:Audio
+ValueType=string
+Description=Audio sample data type
+
+
+[xesam:audioSampleRate]
+Parents=xesam:setRate
+Categories=xesam:Audio
+ValueType=float
+Description=Audio sample rate
+
+
+[xesam:author]
+Parents=xesam:creator
+Categories=xesam:Content
+ValueType=List of strings
+Description=Content author. Primary contributor.
+
+
+[xesam:autoRating]
+Parents=
+Categories=xesam:Source
+ValueType=Abstract field. Contains no data.
+Description=Rating of the object provided automatically by software, inferred from user behavior or other indirect indicators.
+
+
+[xesam:baseRevisionID]
+Parents=
+Categories=xesam:RevisionControlledFile
+ValueType=string
+Description=RevisionID on which a revision-controlled file is based
+
+
+[xesam:bcc]
+Parents=xesam:secondaryRecipient
+Categories=xesam:Email
+ValueType=List of strings
+Description=BCC:
+
+
+[xesam:birthDate]
+Parents=
+Categories=xesam:Person
+ValueType=dateTime
+Description=Contact birthDate
+
+
+[xesam:blogContactURL]
+Parents=xesam:contactURL
+Categories=xesam:Contact
+ValueType=List of Urls
+Description=Contact blog URL
+
+
+[xesam:cameraManufacturer]
+Parents=
+Categories=xesam:Photo
+ValueType=string
+Description=Photo camera manufacturer
+
+
+[xesam:cameraModel]
+Parents=
+Categories=xesam:Photo
+ValueType=string
+Description=Photo camera model
+
+
+[xesam:cc]
+Parents=xesam:secondaryRecipient
+Categories=xesam:Email
+ValueType=List of strings
+Description=CC:
+
+
+[xesam:ccdWidth]
+Parents=
+Categories=xesam:Photo
+ValueType=float
+Description=Photo CCD Width
+
+
+[xesam:cellPhoneNumber]
+Parents=xesam:phoneNumber
+Categories=xesam:Contact
+ValueType=List of strings
+Description=Contact cell phone number
+
+
+[xesam:changeCommitTime]
+Parents=
+Categories=xesam:RevisionControlledFile
+ValueType=dateTime
+Description=Time of the last change to the base file in the repository(preceding the baseRevisionID?)
+
+
+[xesam:changeCommitter]
+Parents=
+Categories=xesam:RevisionControlledFile
+ValueType=string
+Description=Who made the last change to the base file in the repository(preceding the baseRevisionID?)
+
+
+[xesam:characterCount]
+Parents=
+Categories=xesam:Text
+ValueType=integer
+Description=Text character count
+
+
+[xesam:charset]
+Parents=
+Categories=xesam:Content
+ValueType=string
+Description=Content charset encoding
+
+
+[xesam:chatRoom]
+Parents=xesam:communicationChannel
+Categories=xesam:IMMessage
+ValueType=List of strings
+Description=Chatroom this message belongs to
+
+
+[xesam:colorCount]
+Parents=
+Categories=xesam:Visual
+ValueType=integer
+Description=Visual content color count for palettes
+
+
+[xesam:colorSpace]
+Parents=xesam:sampleConfiguration
+Categories=xesam:Visual
+ValueType=string
+Description=Visual content color space(RGB, CMYK etc)
+
+
+[xesam:columnCount]
+Parents=
+Categories=xesam:Spreadsheet
+ValueType=integer
+Description=Spreadsheet column count
+
+
+[xesam:comment]
+Parents=
+Categories=xesam:DataObject
+ValueType=List of strings
+Description=Object comment
+
+
+[xesam:commentCharacterCount]
+Parents=
+Categories=xesam:SourceCode
+ValueType=integer
+Description=Source code comment character count
+
+
+[xesam:commitDiff]
+Parents=
+Categories=xesam:RevisionControlledFile
+ValueType=Abstract field. Contains no data.
+Description=The diff of the content and the base file
+
+
+[xesam:communicationChannel]
+Parents=
+Categories=xesam:Message
+ValueType=List of strings
+Description=Message communication channel like chatroom name or mailing list
+
+
+[xesam:composer]
+Parents=xesam:author
+Categories=xesam:Audio
+ValueType=List of strings
+Description=Music composer
+
+
+[xesam:compressionAlgorithm]
+Parents=
+Categories=xesam:ArchivedFile
+ValueType=string
+Description=Compression algorithm for archivers which support several
+
+
+[xesam:compressionLevel]
+Parents=
+Categories=xesam:ArchivedFile;xesam:Media
+ValueType=string
+Description=Level of compression. How much effort was spent towards achieving maximal compression ratio.
+
+
+[xesam:conflicts]
+Parents=xesam:related
+Categories=xesam:SoftwarePackage
+ValueType=List of Uris
+Description=Software conflicts with
+
+
+[xesam:contactMedium]
+Parents=
+Categories=xesam:Contact
+ValueType=List of strings
+Description=Generic contact medium
+
+
+[xesam:contactNick]
+Parents=
+Categories=xesam:Contact
+ValueType=List of strings
+Description=Contact nick
+
+
+[xesam:contactURL]
+Parents=xesam:contactMedium
+Categories=xesam:Contact
+ValueType=List of Urls
+Description=Contact URL
+
+
+[xesam:contains]
+Parents=xesam:depends
+Categories=xesam:Content
+ValueType=List of Uris
+Description=Containment relation
+
+
+[xesam:contentComment]
+Parents=xesam:comment
+Categories=xesam:Content
+ValueType=string
+Description=Content comment
+
+
+[xesam:contentCreated]
+Parents=
+Categories=xesam:Content
+ValueType=dateTime
+Description=Content creation time
+
+
+[xesam:contentKeyword]
+Parents=xesam:keyword
+Categories=xesam:Content
+ValueType=List of strings
+Description=Content keyword/tag
+
+
+[xesam:contentModified]
+Parents=
+Categories=xesam:Content
+ValueType=dateTime
+Description=Content last modification time
+
+
+[xesam:contentType]
+Parents=
+Categories=xesam:Email
+ValueType=string
+Description=Email content mime type/charset
+
+
+[xesam:contributor]
+Parents=xesam:creator
+Categories=xesam:Content
+ValueType=List of strings
+Description=Content contributor. Secondary contributor.
+
+
+[xesam:copyright]
+Parents=xesam:legal
+Categories=xesam:Content
+ValueType=List of strings
+Description=Content copyright
+
+
+[xesam:creator]
+Parents=
+Categories=xesam:Content
+ValueType=Abstract field. Contains no data.
+Description=Abstract content creator. Use children
+
+
+[xesam:definesClass]
+Parents=
+Categories=xesam:SourceCode
+ValueType=List of strings
+Description=Source code defines class
+
+
+[xesam:definesFunction]
+Parents=
+Categories=xesam:SourceCode
+ValueType=List of strings
+Description=Source code defines function
+
+
+[xesam:definesGlobalVariable]
+Parents=
+Categories=xesam:SourceCode
+ValueType=List of strings
+Description=Source code defines global variable
+
+
+[xesam:deletionTime]
+Parents=
+Categories=xesam:DeletedFile
+ValueType=dateTime
+Description=File deletion time
+
+
+[xesam:depends]
+Parents=xesam:related
+Categories=xesam:Content
+ValueType=List of Uris
+Description=Dependency relation
+
+
+[xesam:derivedFrom]
+Parents=xesam:links
+Categories=xesam:Content
+ValueType=List of Uris
+Description=Links to the original content from which this content is derived
+
+
+[xesam:description]
+Parents=
+Categories=xesam:Content
+ValueType=string
+Description=Content description. Description of content an order of magnitude more elaborate than Title
+
+
+[xesam:discNumber]
+Parents=
+Categories=xesam:Audio
+ValueType=integer
+Description=Audio cd number
+
+
+[xesam:disclaimer]
+Parents=xesam:legal
+Categories=xesam:Content
+ValueType=List of strings
+Description=Content disclaimer
+
+
+[xesam:documentCategory]
+Parents=
+Categories=xesam:Document
+ValueType=List of strings
+Description=Document category: book, article, flyer, pamphlet whatever
+
+
+[xesam:emailAddress]
+Parents=xesam:contactMedium
+Categories=xesam:Contact
+ValueType=List of strings
+Description=Contact email address
+
+
+[xesam:exposureBias]
+Parents=
+Categories=xesam:Photo
+ValueType=string
+Description=Photo exposure bias
+
+
+[xesam:exposureProgram]
+Parents=
+Categories=xesam:Photo
+ValueType=string
+Description=Photo exposure program
+
+
+[xesam:exposureTime]
+Parents=
+Categories=xesam:Photo
+ValueType=dateTime
+Description=Photo exposure time
+
+
+[xesam:familyName]
+Parents=
+Categories=xesam:Person
+ValueType=List of strings
+Description=Person family name
+
+
+[xesam:faxPhoneNumber]
+Parents=xesam:phoneNumber
+Categories=xesam:Contact
+ValueType=List of strings
+Description=Contact fax phone number
+
+
+[xesam:fileExtension]
+Parents=
+Categories=xesam:Filelike
+ValueType=string
+Description=File extension
+
+
+[xesam:fileSystemType]
+Parents=
+Categories=xesam:FileSystem
+ValueType=string
+Description=File system type e.g. ext3
+
+
+[xesam:fingerprint]
+Parents=
+Categories=xesam:Content
+ValueType=Abstract field. Contains no data.
+Description=Content fingerprint: a small ID calculated from content byte stream, aimed at uniquely identifying the content. Abstract.
+
+
+[xesam:firstUsed]
+Parents=
+Categories=xesam:Source
+ValueType=dateTime
+Description=When the content was used for the first time
+
+
+[xesam:flashUsed]
+Parents=
+Categories=xesam:Photo
+ValueType=string
+Description=Photo flash used
+
+
+[xesam:focalLength]
+Parents=
+Categories=xesam:Photo
+ValueType=float
+Description=Photo focal length
+
+
+[xesam:focusDistance]
+Parents=
+Categories=xesam:Photo
+ValueType=float
+Description=Photo focus distance
+
+
+[xesam:formatSubtype]
+Parents=
+Categories=xesam:Content
+ValueType=List of strings
+Description=Format subtype. Use to indicate format extensions/specifics
+
+
+[xesam:frameCount]
+Parents=xesam:setCount
+Categories=xesam:Visual
+ValueType=integer
+Description=Visual content frame count
+
+
+[xesam:frameRate]
+Parents=xesam:setRate
+Categories=xesam:Visual
+ValueType=float
+Description=Visual content frame rate
+
+
+[xesam:freeSpace]
+Parents=
+Categories=xesam:FileSystem
+ValueType=integer
+Description=File system free space
+
+
+[xesam:gender]
+Parents=
+Categories=xesam:Person
+ValueType=string
+Description=Contact gender
+
+
+[xesam:generator]
+Parents=
+Categories=xesam:Content
+ValueType=List of strings
+Description=Software used to generate the content byte stream
+
+
+[xesam:generatorOptions]
+Parents=
+Categories=xesam:Content
+ValueType=List of strings
+Description=Generator software options
+
+
+[xesam:genre]
+Parents=
+Categories=xesam:Media
+ValueType=List of strings
+Description=Media genre
+
+
+[xesam:givenName]
+Parents=
+Categories=xesam:Person
+ValueType=List of strings
+Description=Person given name
+
+
+[xesam:group]
+Parents=
+Categories=xesam:Filelike
+ValueType=List of strings
+Description=File group
+
+
+[xesam:height]
+Parents=
+Categories=xesam:Visual
+ValueType=integer
+Description=Visual content height
+
+
+[xesam:homeEmailAddress]
+Parents=xesam:emailAddress
+Categories=xesam:Person
+ValueType=List of strings
+Description=Contact home email address
+
+
+[xesam:homePhoneNumber]
+Parents=xesam:phoneNumber
+Categories=xesam:Person
+ValueType=List of strings
+Description=Contact home phone number
+
+
+[xesam:homePostalAddress]
+Parents=xesam:physicalAddress
+Categories=xesam:Person
+ValueType=List of strings
+Description=Contact home address
+
+
+[xesam:homepageContactURL]
+Parents=xesam:contactURL
+Categories=xesam:Contact
+ValueType=List of Urls
+Description=Contact homepage URL
+
+
+[xesam:honorificPrefix]
+Parents=
+Categories=xesam:Person
+ValueType=List of strings
+Description=Person honorific name prefix
+
+
+[xesam:honorificSuffix]
+Parents=
+Categories=xesam:Person
+ValueType=List of strings
+Description=Person honorific name suffix
+
+
+[xesam:horizontalResolution]
+Parents=
+Categories=xesam:Visual
+ValueType=integer
+Description=Visual content horizontal resolution
+
+
+[xesam:id]
+Parents=
+Categories=xesam:Content
+ValueType=List of strings
+Description=Content ID
+
+
+[xesam:imContactMedium]
+Parents=xesam:contactMedium
+Categories=xesam:Contact
+ValueType=List of strings
+Description=Generic IM contact medium
+
+
+[xesam:inReplyTo]
+Parents=xesam:derivedFrom
+Categories=xesam:Email
+ValueType=List of strings
+Description=In-Reply-To:
+
+
+[xesam:interest]
+Parents=
+Categories=xesam:Contact
+ValueType=List of strings
+Description=Contact interests/hobbies
+
+
+[xesam:interlaceMode]
+Parents=
+Categories=xesam:Visual
+ValueType=string
+Description=Visual content interlace mode
+
+
+[xesam:ircContactMedium]
+Parents=xesam:imContactMedium
+Categories=xesam:Contact
+ValueType=List of strings
+Description=Contact IRC ID server
+
+
+[xesam:isContentEncrypted]
+Parents=
+Categories=xesam:Content
+ValueType=boolean
+Description=Is some portion of content encrypted? Setting this field doesn't imply that all data is encrypted.
+
+
+[xesam:isEncrypted]
+Parents=
+Categories=xesam:DataObject
+ValueType=List of booleans
+Description=Is Object or part of it encrypted?
+
+
+[xesam:isImportant]
+Parents=
+Categories=xesam:MessageboxMessage
+ValueType=boolean
+Description=Is the message important
+
+
+[xesam:isInProgress]
+Parents=
+Categories=xesam:MessageboxMessage
+ValueType=boolean
+Description=Is the message in progress
+
+
+[xesam:isPublicChannel]
+Parents=
+Categories=xesam:CommunicationChannel
+ValueType=boolean
+Description=Is channel public?
+
+
+[xesam:isRead]
+Parents=
+Categories=xesam:MessageboxMessage
+ValueType=boolean
+Description=Is the message read
+
+
+[xesam:isSourceEncrypted]
+Parents=xesam:isEncrypted
+Categories=xesam:ArchivedFile
+ValueType=boolean
+Description=Is archived file password-protected?
+
+
+[xesam:isoEquivalent]
+Parents=
+Categories=xesam:Photo
+ValueType=string
+Description=Photo ISO equivalent
+
+
+[xesam:jabberContactMedium]
+Parents=xesam:imContactMedium
+Categories=xesam:Contact
+ValueType=List of strings
+Description=Contact Jabber ID
+
+
+[xesam:keyword]
+Parents=
+Categories=xesam:DataObject
+ValueType=List of strings
+Description=Object keyword/tag
+
+
+[xesam:knows]
+Parents=xesam:related
+Categories=xesam:Contact
+ValueType=List of Uris
+Description=FOAF:knows relation. Points to a contact known by this contact.
+
+
+[xesam:language]
+Parents=
+Categories=xesam:Content
+ValueType=List of strings
+Description=Content language
+
+
+[xesam:lastRefreshed]
+Parents=
+Categories=xesam:RemoteResource
+ValueType=dateTime
+Description=Last time the resource info was refreshed
+
+
+[xesam:lastUsed]
+Parents=
+Categories=xesam:Source
+ValueType=dateTime
+Description=When the content was last used. Different from last access as this only accounts usage by the user e.g. playing a song as opposed to apps scanning the HD
+
+
+[xesam:legal]
+Parents=
+Categories=xesam:Content
+ValueType=List of strings
+Description=Abstract content legal notice.
+
+
+[xesam:license]
+Parents=xesam:legal
+Categories=xesam:Content
+ValueType=List of strings
+Description=Content license
+
+
+[xesam:licenseType]
+Parents=xesam:legal
+Categories=xesam:Content
+ValueType=List of strings
+Description=Content license type
+
+
+[xesam:lineCount]
+Parents=
+Categories=xesam:Text
+ValueType=integer
+Description=Text line count
+
+
+[xesam:links]
+Parents=xesam:related
+Categories=xesam:Content
+ValueType=List of Uris
+Description=Linking/mention relation
+
+
+[xesam:localRevision]
+Parents=
+Categories=xesam:Source
+ValueType=string
+Description=Local revision number. An automatically generated ID that is changed everytime the generator software/revisioning system deems the content has changed.
+
+
+[xesam:lyricist]
+Parents=xesam:author
+Categories=xesam:Audio
+ValueType=List of strings
+Description=Music lyricist
+
+
+[xesam:mailingList]
+Parents=xesam:communicationChannel
+Categories=xesam:MailingListEmail
+ValueType=List of strings
+Description=Mailing list this message belongs to
+
+
+[xesam:mailingPostalAddress]
+Parents=xesam:physicalAddress
+Categories=xesam:Contact
+ValueType=List of strings
+Description=Contact mailing address
+
+
+[xesam:maintainer]
+Parents=xesam:creator
+Categories=xesam:Content
+ValueType=List of strings
+Description=Content maintainer.
+
+
+[xesam:markupCharacterCount]
+Parents=
+Categories=xesam:XML
+ValueType=integer
+Description=XML markup character count
+
+
+[xesam:md5Hash]
+Parents=xesam:fingerprint
+Categories=xesam:Content
+ValueType=Abstract field. Contains no data.
+Description=MD5 hash
+
+
+[xesam:mediaBitrate]
+Parents=
+Categories=xesam:Media
+ValueType=List of integers
+Description=Media bitrate
+
+
+[xesam:mediaCodec]
+Parents=
+Categories=xesam:Media
+ValueType=List of strings
+Description=Media codec
+
+
+[xesam:mediaCodecType]
+Parents=
+Categories=xesam:Media
+ValueType=List of strings
+Description=Media codec type: lossless, CBR, ABR, VBR
+
+
+[xesam:mediaDuration]
+Parents=
+Categories=xesam:Media
+ValueType=List of dateTimes
+Description=Media duration
+
+
+[xesam:mergeConflict]
+Parents=
+Categories=xesam:RevisionControlledFile
+ValueType=boolean
+Description=If true, the file has a merge conflict(can't be cleanly merged into the repository)
+
+
+[xesam:meteringMode]
+Parents=
+Categories=xesam:Photo
+ValueType=string
+Description=Photo metering mode
+
+
+[xesam:mimeType]
+Parents=
+Categories=xesam:Content
+ValueType=string
+Description=Content mime-type
+
+
+[xesam:mountPoint]
+Parents=
+Categories=xesam:FileSystem
+ValueType=List of strings
+Description=File system mount point
+
+
+[xesam:name]
+Parents=
+Categories=xesam:Source
+ValueType=string
+Description=Name provided by container
+
+
+[xesam:newsGroup]
+Parents=xesam:communicationChannel
+Categories=xesam:NewsGroupEmail
+ValueType=List of strings
+Description=News group this message belongs to
+
+
+[xesam:occupiedSpace]
+Parents=
+Categories=xesam:FileSystem
+ValueType=integer
+Description=File system occupied space
+
+
+[xesam:orientation]
+Parents=
+Categories=xesam:Photo
+ValueType=string
+Description=Photo orientation
+
+
+[xesam:originURL]
+Parents=
+Categories=xesam:Source
+ValueType=Url
+Description=Origin URL, e.g. where the file had been downloaded from
+
+
+[xesam:originalLocation]
+Parents=
+Categories=xesam:DeletedFile
+ValueType=string
+Description=Deleted file original location
+
+
+[xesam:otherName]
+Parents=
+Categories=xesam:Person
+ValueType=List of strings
+Description=Person other name
+
+
+[xesam:owner]
+Parents=
+Categories=xesam:Filelike
+ValueType=string
+Description=File owner
+
+
+[xesam:pageCount]
+Parents=
+Categories=xesam:Document
+ValueType=integer
+Description=Document page count. Slide count for presentations
+
+
+[xesam:paragrapCount]
+Parents=
+Categories=xesam:Document
+ValueType=integer
+Description=Document paragraph count
+
+
+[xesam:performer]
+Parents=xesam:author
+Categories=xesam:Audio
+ValueType=List of strings
+Description=Music performer
+
+
+[xesam:permissions]
+Parents=
+Categories=xesam:Filelike
+ValueType=string
+Description=File permissions
+
+
+[xesam:personPhoto]
+Parents=
+Categories=xesam:Person
+ValueType=List of Uris
+Description=Contact photo/avatar
+
+
+[xesam:phoneNumber]
+Parents=xesam:contactMedium
+Categories=xesam:Contact
+ValueType=List of strings
+Description=Contact phone number
+
+
+[xesam:physicalAddress]
+Parents=xesam:contactMedium
+Categories=xesam:Contact
+ValueType=List of strings
+Description=Contact postal address
+
+
+[xesam:pixelDataBitDepth]
+Parents=xesam:sampleBitDepth
+Categories=xesam:Visual
+ValueType=integer
+Description=Visual content pixel data bit depth
+
+
+[xesam:pixelDataType]
+Parents=xesam:sampleDataType
+Categories=xesam:Visual
+ValueType=string
+Description=Visual content pixel data type
+
+
+[xesam:primaryRecipient]
+Parents=xesam:recipient
+Categories=xesam:Message
+ValueType=List of strings
+Description=Message primary recipient
+
+
+[xesam:programmingLanguage]
+Parents=
+Categories=xesam:SourceCode
+ValueType=string
+Description=Source code programming language
+
+
+[xesam:receptionTime]
+Parents=
+Categories=xesam:Message
+ValueType=dateTime
+Description=Message reception time
+
+
+[xesam:recipient]
+Parents=
+Categories=xesam:Message
+ValueType=List of strings
+Description=Message recipient
+
+
+[xesam:related]
+Parents=
+Categories=xesam:Content
+ValueType=List of Uris
+Description=Abstract content relation. Use children
+
+
+[xesam:remotePassword]
+Parents=
+Categories=xesam:RemoteResource
+ValueType=string
+Description=Remote resource user password
+
+
+[xesam:remotePort]
+Parents=
+Categories=xesam:RemoteResource
+ValueType=integer
+Description=Server port
+
+
+[xesam:remoteServer]
+Parents=
+Categories=xesam:RemoteResource
+ValueType=string
+Description=The server hosting the remote resource
+
+
+[xesam:remoteUser]
+Parents=
+Categories=xesam:RemoteResource
+ValueType=string
+Description=Remote resource user name
+
+
+[xesam:replyTo]
+Parents=
+Categories=xesam:Email
+ValueType=string
+Description=ReplyTo:
+
+
+[xesam:rowCount]
+Parents=xesam:lineCount
+Categories=xesam:Spreadsheet
+ValueType=integer
+Description=Spreadsheet row count
+
+
+[xesam:rssFeed]
+Parents=xesam:communicationChannel
+Categories=xesam:RSSMessage
+ValueType=List of strings
+Description=RSS feed that provided the message
+
+
+[xesam:sampleBitDepth]
+Parents=
+Categories=xesam:Media
+ValueType=List of integers
+Description=Media sample data bit depth: 8, 16, 24, 32 etc
+
+
+[xesam:sampleConfiguration]
+Parents=
+Categories=xesam:Media
+ValueType=Abstract field. Contains no data.
+Description=Media sample configuration/arrangement of components
+
+
+[xesam:sampleDataType]
+Parents=
+Categories=xesam:Media
+ValueType=List of strings
+Description=Media sample data type: int, float
+
+
+[xesam:secondaryRecipient]
+Parents=xesam:recipient
+Categories=xesam:Message
+ValueType=List of strings
+Description=Message secondary recipient
+
+
+[xesam:seenAttachedAs]
+Parents=xesam:name
+Categories=xesam:OfflineMedia
+ValueType=List of strings
+Description=Name of block device seen to contain the Content when it was online.
+
+
+[xesam:setCount]
+Parents=
+Categories=xesam:Media
+ValueType=List of integers
+Description=Media set count. Sample count for audio(set=one sample per channel), frame count for video
+
+
+[xesam:setRate]
+Parents=
+Categories=xesam:Media
+ValueType=List of floats
+Description=Media set rate. Sample rate for audio(set=one sample per channel), FPS for video
+
+
+[xesam:sha1Hash]
+Parents=xesam:fingerprint
+Categories=xesam:Content
+ValueType=Abstract field. Contains no data.
+Description=SHA1 hash
+
+
+[xesam:size]
+Parents=
+Categories=xesam:Content
+ValueType=integer
+Description=Content/data size in bytes. See also storageSize
+
+
+[xesam:sourceCreated]
+Parents=
+Categories=xesam:Source
+ValueType=dateTime
+Description=Local copy creation time
+
+
+[xesam:sourceModified]
+Parents=
+Categories=xesam:Source
+ValueType=dateTime
+Description=Local copy modification time
+
+
+[xesam:storedSize]
+Parents=
+Categories=xesam:Source
+ValueType=integer
+Description=Actual space occupied by the object in the source storage. e.g. compressed file size in archive
+
+
+[xesam:subject]
+Parents=
+Categories=xesam:Content
+ValueType=string
+Description=Content subject. The shortest possible description of content
+
+
+[xesam:summary]
+Parents=
+Categories=xesam:Content
+ValueType=string
+Description=Content summary. Description of content an order of magnitude more elaborate than Description
+
+
+[xesam:supercedes]
+Parents=xesam:related
+Categories=xesam:SoftwarePackage
+ValueType=List of Uris
+Description=Software supercedes
+
+
+[xesam:targetQuality]
+Parents=
+Categories=xesam:Media
+ValueType=List of strings
+Description=The desired level of quality loss of lossy compression
+
+
+[xesam:title]
+Parents=
+Categories=xesam:Content
+ValueType=string
+Description=Content title. Description of content an order of magnitude more elaborate than Subject
+
+
+[xesam:to]
+Parents=xesam:primaryRecipient
+Categories=xesam:Email
+ValueType=List of strings
+Description=To:
+
+
+[xesam:totalSpace]
+Parents=
+Categories=xesam:FileSystem
+ValueType=integer
+Description=File system total usable space, unlike size, which is the byte size of the entire filesystem including overhead.
+
+
+[xesam:totalUncompressedSize]
+Parents=
+Categories=xesam:Archive
+ValueType=integer
+Description=Archive total uncompressed size
+
+
+[xesam:trackGain]
+Parents=
+Categories=xesam:Audio
+ValueType=float
+Description=Gain adjustment of track
+
+
+[xesam:trackNumber]
+Parents=
+Categories=xesam:Audio
+ValueType=integer
+Description=Audio track number
+
+
+[xesam:trackPeakGain]
+Parents=
+Categories=xesam:Audio
+ValueType=float
+Description=Peak gain adjustment of track
+
+
+[xesam:url]
+Parents=
+Categories=xesam:Source
+ValueType=List of Urls
+Description=URL to access the content
+
+
+[xesam:useCount]
+Parents=
+Categories=xesam:Source
+ValueType=integer
+Description=How many times the content was used. Only usage by the user(not general software access) counts.
+
+
+[xesam:userComment]
+Parents=xesam:comment
+Categories=xesam:Source
+ValueType=List of strings
+Description=User-provided comment
+
+
+[xesam:userKeyword]
+Parents=xesam:keyword
+Categories=xesam:Source
+ValueType=List of strings
+Description=User-provided keywords
+
+
+[xesam:userRating]
+Parents=
+Categories=xesam:Source
+ValueType=Abstract field. Contains no data.
+Description=User-provided rating of the object
+
+
+[xesam:usesNamespace]
+Parents=
+Categories=xesam:XML
+ValueType=List of strings
+Description=Namespace referenced by the XML
+
+
+[xesam:version]
+Parents=
+Categories=xesam:Content
+ValueType=string
+Description=Content version
+
+
+[xesam:verticalResolution]
+Parents=
+Categories=xesam:Visual
+ValueType=integer
+Description=Visual content vertical resolution
+
+
+[xesam:videoBitrate]
+Parents=xesam:mediaBitrate
+Categories=xesam:Video
+ValueType=integer
+Description=Video Bitrate
+
+
+[xesam:videoCodec]
+Parents=xesam:mediaCodec
+Categories=xesam:Video
+ValueType=string
+Description=Video codec
+
+
+[xesam:videoCodecType]
+Parents=xesam:mediaCodecType
+Categories=xesam:Video
+ValueType=string
+Description=Video codec type
+
+
+[xesam:whiteBalance]
+Parents=
+Categories=xesam:Photo
+ValueType=string
+Description=Photo white balance
+
+
+[xesam:width]
+Parents=
+Categories=xesam:Visual
+ValueType=integer
+Description=Visual content width
+
+
+[xesam:wordCount]
+Parents=
+Categories=xesam:Text
+ValueType=integer
+Description=Text word count
+
+
+[xesam:workEmailAddress]
+Parents=xesam:emailAddress
+Categories=xesam:Person
+ValueType=List of strings
+Description=Contact work email address
+
+
+[xesam:workPhoneNumber]
+Parents=xesam:phoneNumber
+Categories=xesam:Person
+ValueType=List of strings
+Description=Contact work phone number
+
+
+[xesam:workPostalAddress]
+Parents=xesam:physicalAddress
+Categories=xesam:Person
+ValueType=List of strings
+Description=Contact work address
+

Added: trunk/data/services/xesam.service
==============================================================================
--- (empty file)
+++ trunk/data/services/xesam.service	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,244 @@
+
+[xesam:Annotation]
+Parents=xesam:Content
+Description=Generic annotation. Annotation provides a set of document description properties(like subject, title, description) for a list of objects it links to. It can link to other annotations, however interpretation of this may differ between specific annotation classes..
+
+[xesam:Archive]
+Parents=xesam:Content
+Description=Generic archive
+
+[xesam:ArchivedFile]
+Parents=xesam:Filelike
+Description=File stored in an archive
+
+[xesam:Audio]
+Parents=xesam:Media
+Description=Defines audio aspect of content. The content itself may have other aspects.
+
+[xesam:AudioList]
+Parents=xesam:MediaList
+Description=Generic audio list(playlist). Linking to other content types is forbidden
+
+[xesam:BlockDevice]
+Parents=xesam:Source
+Description=Generic block device. Typically contains partitions/filesystems
+
+[xesam:Bookmark]
+Parents=xesam:Annotation
+Description=Bookmark. Currently there's nothing that would distinguish bookmarks and tags
+
+[xesam:CommunicationChannel]
+Parents=xesam:Content
+Description=Communication channel
+
+[xesam:Contact]
+Parents=xesam:Content
+Description=Contact
+
+[xesam:ContactGroup]
+Parents=xesam:Content
+Description=ContactGroup
+
+[xesam:Content]
+Parents=xesam:DataObject
+Description=Generic content
+
+[xesam:DataObject]
+Parents=
+Description=Generic data object. Unites form and essense aspects represented by Content and Source. Used to aggreate properties that may be extracted from both content and source.
+
+[xesam:DeletedFile]
+Parents=xesam:Filelike
+Description=File deleted to trash
+
+[xesam:Document]
+Parents=xesam:Text
+Description=Document is an arrangement of various atomic data types with text being the primary data type.
+
+[xesam:Documentation]
+Parents=xesam:Document
+Description=Documentation is a document containing help, manuals, guides.
+
+[xesam:Email]
+Parents=xesam:Message
+Description=Email message
+
+[xesam:EmailAttachment]
+Parents=xesam:EmbeddedObject
+Description=Generic storage
+
+[xesam:EmbeddedObject]
+Parents=xesam:Source
+Description=Generic embedded/inlined object: attachment, inlined SVG, script etc.
+
+[xesam:File]
+Parents=xesam:Filelike
+Description=Regular file stored in a filesystem
+
+[xesam:FileSystem]
+Parents=xesam:Content
+Description=Filesystem differs from other containers in that it has total/free/occupied space(though DBs too have similar properties), has volume(content.title), UUID for *ix(content.ID), mount point(if mounted)
+
+[xesam:Filelike]
+Parents=xesam:Source
+Description=File-like object
+
+[xesam:Folder]
+Parents=xesam:Annotation
+Description=Generic folder. In general, folders represent a tree-like structure(taxonomy), however on occasion this rule may violated in cases like symlinks.
+
+[xesam:IMAP4Message]
+Parents=xesam:RemoteMessageboxMessage
+Description=IMAP4 mailbox message
+
+[xesam:IMMessage]
+Parents=xesam:Message
+Description=Generic Instant Messaging message
+
+[xesam:Image]
+Parents=xesam:Visual
+Description=Visual content
+
+[xesam:MailingList]
+Parents=xesam:ContactGroup;xesam:CommunicationChannel
+Description=Mailing list
+
+[xesam:MailingListEmail]
+Parents=xesam:Email
+Description=Email message addressed at/received from a mailing list
+
+[xesam:Media]
+Parents=xesam:Content
+Description=Generic raster/sampled media is considered consisting of Sets of Samples being reproduced(played/shown) at once. We describe: sample data type(int/float), data bit depth,configuration(color space for images, channel count for audio); set configuration(pixel dimensions for image); set count and rate.
+
+[xesam:MediaList]
+Parents=xesam:Annotation
+Description=Generic media content list(playlist). Linking to other content types is forbidden
+
+[xesam:Message]
+Parents=xesam:Content
+Description=Generic message
+
+[xesam:MessageboxMessage]
+Parents=xesam:Source
+Description=Message stored in a message box
+
+[xesam:Music]
+Parents=xesam:Audio
+Description=Music content
+
+[xesam:NewsGroup]
+Parents=xesam:CommunicationChannel
+Description=News group
+
+[xesam:NewsGroupEmail]
+Parents=xesam:Email
+Description=Email message addressed at/received from a news group
+
+[xesam:OfflineMedia]
+Parents=xesam:Source
+Description=Generic offline media. e.g. USB drive not attached at this moment.
+
+[xesam:Organization]
+Parents=xesam:Contact
+Description=Organization
+
+[xesam:POP3Message]
+Parents=xesam:RemoteMessageboxMessage
+Description=POP3 mailbox message
+
+[xesam:Person]
+Parents=xesam:Contact
+Description=Person
+
+[xesam:PersonalEmail]
+Parents=xesam:Email
+Description=Personal email message(not related to a mailing list or discussion group)
+
+[xesam:Photo]
+Parents=xesam:Image
+Description=An image with EXIF tags(photo)
+
+[xesam:Presentation]
+Parents=xesam:Document
+Description=Presentation document
+
+[xesam:Project]
+Parents=xesam:Annotation
+Description=Generic project
+
+[xesam:RSSFeed]
+Parents=xesam:CommunicationChannel
+Description=RSS feed
+
+[xesam:RSSMessage]
+Parents=xesam:Message
+Description=RSS message(RSS feed item)
+
+[xesam:RemoteFile]
+Parents=xesam:RemoteResource;xesam:Filelike
+Description=Remote file
+
+[xesam:RemoteMessageboxMessage]
+Parents=xesam:RemoteResource;xesam:MessageboxMessage
+Description=Remote messagebox message
+
+[xesam:RemoteResource]
+Parents=xesam:Source
+Description=Generic remote resource
+
+[xesam:RevisionControlledFile]
+Parents=xesam:File
+Description=File managed by a revision control system
+
+[xesam:RevisionControlledRepository]
+Parents=xesam:Content
+Description=Revision-controlled repository. In case of distributed repositories, those must be linked with derivation relations.
+
+[xesam:SoftwarePackage]
+Parents=xesam:Content
+Description=Software distribution package
+
+[xesam:Source]
+Parents=xesam:DataObject
+Description=Generic source
+
+[xesam:SourceCode]
+Parents=xesam:Text
+Description=Source Code
+
+[xesam:Spreadsheet]
+Parents=xesam:Document
+Description=Spreadsheet document
+
+[xesam:SystemResource]
+Parents=xesam:Source
+Description=Generic system resource like man documentation
+
+[xesam:Tag]
+Parents=xesam:Annotation
+Description=Tag/keyword. As opposed to folders, there are no limitations on the structure of tags and arbitrary overlaps are possible.
+
+[xesam:Text]
+Parents=xesam:Content
+Description=Defines a textual aspect of content. Properties represent only actual content intended for the user, not markup. Other parts of content like markup should be described using other clsses. Abstract class.
+
+[xesam:TextDocument]
+Parents=xesam:Document
+Description=Text document. For word processing apps.
+
+[xesam:UncategorizedText]
+Parents=xesam:Text
+Description=Text content which doesn't fit any other text-based classes. Corresponds to Tracker's Text category.
+
+[xesam:Video]
+Parents=xesam:Audio;xesam:Visual
+Description=Video content
+
+[xesam:Visual]
+Parents=xesam:Media
+Description=Visual content
+
+[xesam:XML]
+Parents=xesam:Text
+Description=XML content

Added: trunk/data/sqlite-cache.sql
==============================================================================
--- (empty file)
+++ trunk/data/sqlite-cache.sql	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,86 @@
+CREATE TABLE SearchResults1
+(
+	SID		Integer primary key not null,
+	Score		Integer
+);
+
+
+CREATE TABLE SearchResults2
+(
+	SID		Integer primary key not null,
+	Score		Integer
+);
+
+
+/* table for files waiting to be processed */
+CREATE TABLE  FilePending
+(
+	ID			Integer primary key AUTOINCREMENT not null,
+	FileID 			Integer default 0,
+	Action			Integer default 0,
+	PendingDate		Integer,
+	FileUri			Text  not null,
+	MimeType		Text ,
+	IsDir			Integer default 0,
+	IsNew			Integer default 0,
+	RefreshEmbedded		Integer default 0,
+	RefreshContents		Integer default 0,	
+	ServiceTypeID		Integer default 0
+);
+
+
+/* temp tables */
+CREATE TABLE  FileTemp
+(
+	ID			Integer primary key not null,
+	FileID 			Integer default 0,
+	Action			Integer default 0,
+	FileUri			Text  not null,
+	MimeType		Text ,
+	IsDir			Integer default 0,
+	IsNew			Integer default 0,
+	RefreshEmbedded		Integer default 0,
+	RefreshContents		Integer default 0,	
+	ServiceTypeID		Integer default 0
+);
+
+CREATE TABLE  MetadataTemp
+(
+	ID			Integer primary key not null,
+	FileID 			Integer default 0,
+	Action			Integer default 0,
+	FileUri			Text  not null,
+	MimeType		Text ,
+	IsDir			Integer default 0,
+	IsNew			Integer default 0,
+	RefreshEmbedded		Integer default 0,
+	RefreshContents		Integer default 0,	
+	ServiceTypeID		Integer default 0
+);
+
+
+CREATE TABLE  FileWatches
+(
+	WatchID 	Integer not null, 
+	URI 		Text not null,  
+
+	primary key (WatchID), 
+	unique (URI)
+);
+
+
+CREATE TABLE Events
+(
+	ID		Integer primary key autoincrement,
+	ServiceID	Integer not null,
+	BeingHandled	Integer default 0,
+	EventType	Text
+);
+
+CREATE TABLE LiveSearches
+(
+	ServiceID	Integer not null,
+	SearchID	Text,
+
+	Unique (ServiceID, SearchID)
+);

Added: trunk/data/sqlite-contents.sql
==============================================================================
--- (empty file)
+++ trunk/data/sqlite-contents.sql	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,8 @@
+
+CREATE TABLE ServiceContents 
+(
+	ServiceID Int not null, 
+	MetadataID Int not null, 
+	Content Text, 
+	primary key (ServiceID, MetadataID)
+);
\ No newline at end of file

Added: trunk/data/sqlite-email.sql
==============================================================================
--- (empty file)
+++ trunk/data/sqlite-email.sql	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,28 @@
+
+CREATE TABLE MailSummary
+(
+	ID		Integer primary key AUTOINCREMENT not null,
+	MailApp		Integer not null,
+	MailType	Integer not null,
+	FileName	Text not null,
+	Path		Text not null,
+	UriPrefix	Text,
+	NeedsChecking	Integer default 0,
+	MailCount	Integer,
+	JunkCount	Integer,
+	DeleteCount	Integer,
+	Offset		Integer,
+	LastOffset	Integer,
+	MTime		integer,
+
+	unique (Path)
+);
+
+
+CREATE TABLE JunkMail
+(
+	UID			integer not null,
+	SummaryID		Integer not null,
+
+	primary key (UID, SummaryID)
+);

Added: trunk/data/sqlite-metadata.sql
==============================================================================
--- (empty file)
+++ trunk/data/sqlite-metadata.sql	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,64 @@
+
+/* describes the types of metadata */
+CREATE TABLE  MetaDataTypes 
+(
+	ID	 		Integer primary key AUTOINCREMENT not null,
+	MetaName		Text not null  COLLATE NOCASE, 
+	DataTypeID		Integer default 1,    /* 0=Keyword, 1=indexable, 2=Clob (compressed indexable text),  3=String, 4=Integer, 5=Double,  6=DateTime, 7=Blob, 8=Struct, 9=ServiceLink */
+	DisplayName		text,
+	Description		text default ' ',
+	Enabled			integer default 1, /* used to prevent use of this metadata type */
+	UIVisible		integer default 0, /* should this metadata type be visible in a search criteria UI  */
+	WriteExec		text default ' ', /* used to specify an external program that can write an *embedded* metadata to a file */
+	Alias			text default ' ', /* alternate name for this type (XESAM specs?) */
+	FieldName		text default ' ', /* filedname if present in the services table */
+	Weight			Integer default 1, /* weight of metdata type in ranking */
+	Embedded		Integer default 1, /* 1 if metadata extracted from the file by the indexer and is not updateable by the user. 0 - this metadata can be updated by the user and is external to the file */
+	MultipleValues		Integer default 0, /* 0= type cannot have multiple values per entity, 1= type can have more than 1 value per entity */
+	Delimited		Integer default 0, /* if 1, extra delimiters (hyphen and underscore) are used to break word */
+	Filtered		Integer default 1, /* if 1, words are filtered for numerics (if numeric indexing is disabled), stopwords and min length */
+	Abstract		Integer default 0, /* if 0, can be used for storing metadata - Abstract type classes cannot store metadata and can only be used for searching its decendants */
+	StemMetadata		Integer default 1, /* 1 if metadata should be stemmed */
+	SideCar			Integer default 0, /* should this metadata be backed up in an xmp sidecar file */
+	FileName		Text default ' ',
+
+	Unique (MetaName)
+);
+
+insert into MetaDataTypes (MetaName) values ('default');
+
+CREATE INDEX  MetaDataTypesIndex1 ON MetaDataTypes (Alias);
+
+
+/* flattened table to store metadata inter-relationships */
+CREATE TABLE  MetaDataChildren
+(
+	MetaDataID		integer not null,
+	ChildID			integer not null,
+
+	primary key (MetaDataID, ChildID)
+
+);
+
+
+/* for specifying fixed non-extensible metadata group/structs */
+CREATE TABLE  MetaDataGroup
+(
+	MetaDataGroupID		integer not null,
+	ChildID			integer not null,
+
+	primary key (MetaDataGroupID, ChildID)
+
+);
+
+
+/* future-proof table for future addional options specific to a certain metadata type  */
+CREATE TABLE MetadataOptions
+(
+	MetaDataID		Integer not null,
+	OptionName		Text not null,
+	OptionValue		Text default ' ',
+
+	primary key (MetaDataID, OptionName)
+);
+

Added: trunk/data/sqlite-service-triggers.sql
==============================================================================
--- (empty file)
+++ trunk/data/sqlite-service-triggers.sql	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,9 @@
+CREATE TRIGGER delete_service BEFORE DELETE ON Services 
+BEGIN  
+	DELETE FROM ServiceMetaData WHERE ServiceID = old.ID;
+	DELETE FROM ServiceKeywordMetaData WHERE ServiceID = old.ID;
+	DELETE FROM ServiceNumericMetaData WHERE ServiceID = old.ID;
+	DELETE FROM ChildServices WHERE (ParentID = old.ID);
+	DELETE FROM ChildServices WHERE (ChildID = old.ID);
+	
+END;!

Added: trunk/data/sqlite-service-types.sql
==============================================================================
--- (empty file)
+++ trunk/data/sqlite-service-types.sql	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,123 @@
+
+CREATE TABLE  ServiceTypes
+(
+	TypeID 			Integer primary key AUTOINCREMENT not null,
+	TypeName		Text COLLATE NOCASE not null,
+
+	TypeCount		Integer default 0,
+
+	DisplayName		Text default ' ',
+	Parent			Text default ' ',
+	PropertyPrefix		Text default ' ',
+	Enabled			Integer default 1, 
+	Embedded		Integer default 1, /* service is created by the indexer if embedded. User or app defined services are not embedded */
+	ChildResource		Integer default 0, /* service is a child service */
+	
+	CreateDesktopFile	Integer default 0, /* used by a UI to indicate whether it should create a desktop file for the service if its copied (using the ViewerExec field + uri) */
+
+	/* useful for a UI when determining what actions a hit can have */
+	CanCopy			Integer default 1, 
+	CanDelete		Integer default 1,
+
+	ShowServiceFiles	Integer default 0,
+	ShowServiceDirectories  Integer default 0,
+
+	HasMetadata		Integer default 1,
+	HasFullText		Integer default 1,
+	HasThumbs		Integer default 1,
+	
+	ContentMetadata		Text default ' ', /* the content field is the one most likely to be used for showing a search snippet */ 
+
+	KeyMetadata1		Text default ' ', /* the most commonly requested metadata (especially for tables/grid views) is cached int he services table for extra fast retrieval */
+	KeyMetadata2		Text default ' ',
+	KeyMetadata3		Text default ' ',
+	KeyMetadata4		Text default ' ',
+	KeyMetadata5		Text default ' ',
+	KeyMetadata6		Text default ' ',
+	KeyMetadata7		Text default ' ',
+	KeyMetadata8		Text default ' ',
+	KeyMetadata9		Text default ' ',
+	KeyMetadata10		Text default ' ',
+	KeyMetadata11		Text default ' ',
+
+	UIVisible		Integer default 0,	/* should service appear in a search GUI? */
+	UITitle			Text default ' ',	/* title format as displayed in the metadata tile */
+	UIMetadata1		Text default ' ',	/*UI fields to show in GUI for a hit - if not set then Name,Path,Mime are used */
+	UIMetadata2		Text default ' ',
+	UIMetadata3		Text default ' ',
+	UIView			Text default 'default',
+
+	Description		Text default ' ',
+	Database		integer default 0, /* 0 = DB_FILES, 1 = DB_EMAILS, 2 = DB_MISC, 3 = DB_USER */
+	Icon			Text default ' ',
+
+	IndexerExec		Text default ' ',
+	IndexerOutput		Text default 'stdout',
+	ThumbExec		Text default ' ',
+	ViewerExec		Text default ' ',
+
+	WatchFolders		Text default ' ',
+	IncludeGlob		Text default ' ',
+	ExcludeGlob		Text default ' ',
+
+	FileName		Text default ' ',
+
+	unique (TypeName)
+);
+
+insert into ServiceTypes (TypeName) values ('default');
+
+/* metadata that should appear in a tabular view and/or metadata tile for the service */
+CREATE TABLE ServiceTileMetadata
+(
+	ServiceTypeID		Integer not null,
+	MetaName		Text not null,
+
+	primary key (ServiceTypeID, MetaName)
+);
+
+
+CREATE TABLE ServiceTabularMetadata
+(
+	ServiceTypeID		Integer not null,
+	MetaName		Text not null,
+
+	primary key (ServiceTypeID, MetaName)
+);
+
+
+/* option sspecific to a certain service type go here */
+CREATE TABLE ServiceTypeOptions
+(
+	ServiceTypeID		Integer not null,
+	OptionName		Text not null,
+	OptionValue		Text default ' ',
+
+	primary key (ServiceTypeID, OptionName)
+);
+
+
+
+
+/* these two only apply to file based services */
+CREATE TABLE  FileMimes
+(
+	Mime			Text primary key not null,
+	ServiceTypeID		Integer default 0,
+	ThumbExec		Text default ' ',
+	MetadataExec		Text default ' ',
+	FullTextExec		Text default ' '
+
+);
+
+CREATE TABLE  FileMimePrefixes
+(
+	MimePrefix		Text primary key not null,
+	ServiceTypeID		Integer default 0,
+	ThumbExec		Text default ' ',
+	MetadataExec		Text default ' ',
+	FullTextExec		Text default ' '
+
+);
+
+

Added: trunk/data/sqlite-service.sql
==============================================================================
--- (empty file)
+++ trunk/data/sqlite-service.sql	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,99 @@
+/* basic info for a file or service object */
+CREATE TABLE  Services
+(
+	ID            		Integer primary key not null,
+	ServiceTypeID		Integer  default 0, /* see ServiceTypes table above for ID values. A value of 0 indicates a group resource rather than a service */
+	Path 			Text  not null  COLLATE UTF8, /* non-file objects should use service name here */
+	Name	 		Text default ' ' COLLATE UTF8, /* name of file or object - the combination path and name must be unique for all objects */
+	Enabled			Integer default 1,
+	Mime			Text default ' ',
+	Size			Integer default 0,
+	Rank			Integer default 5,
+	ParentID		Integer,
+
+	KeyMetadata1		Text,
+	KeyMetadata2		Text,
+	KeyMetadata3		Text,
+	KeyMetadata4		Text,
+	KeyMetadata5		Text,
+	KeyMetadata6		Text,
+	KeyMetadata7		Text,
+	KeyMetadata8		Text,
+	KeyMetadata9		Text,
+	KeyMetadata10		Text,
+	KeyMetadata11		Text,
+
+	Icon			Text,
+	CanWrite		Integer default 1,
+	CanExecute		Integer default 1,
+
+	LanguageId		Integer default 0,
+	IsDirectory   		Integer default 0,
+    	IsLink        		Integer default 0,
+	AuxilaryID		Integer default 0, /* link to Volumes table for files, link to MailSummary table for emails*/
+	IndexTime  		Integer default 0, /* should equal st_mtime for file if up-to-date */
+	Accessed  		Integer default 0, /* last accessed */
+	Offset			Integer default 0, /* last used disk offset for indexable files that always grow (like chat logs) or email offset */
+	MD5			Text,
+
+    	unique (Path, Name)
+
+);
+
+CREATE INDEX  ServiceIndex1 ON Services (ServiceTypeID);
+
+
+/* child service relationships for a specific group/struct metadata */
+CREATE TABLE ChildServices
+(
+	ParentID            		Integer not null,
+	ChildID				Integer not null,
+	MetaDataID			Integer not null,
+
+	primary key (ParentID, ChildID, MetaDataID)
+);
+
+CREATE INDEX  ChildServicesIndex1 ON ChildServices (ChildID);
+
+
+/* utf-8 based literal metadata. */
+CREATE TABLE  ServiceMetaData 
+(
+	ID			Integer primary key AUTOINCREMENT not null,
+	ServiceID		Integer not null,
+	MetaDataID 		Integer  not null,
+	MetaDataValue     	Text,
+	MetaDataDisplay		Text
+
+);
+
+CREATE INDEX  ServiceMetaDataIndex1 ON ServiceMetaData (ServiceID);
+CREATE INDEX  ServiceMetaDataIndex2 ON ServiceMetaData (MetaDataID);
+
+/* metadata for all keyword types - keywords are db indexed for fast searching - they are also not processed like other metadata. */
+CREATE TABLE  ServiceKeywordMetaData 
+(
+	ID			Integer primary key AUTOINCREMENT not null,
+	ServiceID		Integer not null,
+	MetaDataID 		Integer not null,
+	MetaDataValue		Text COLLATE NOCASE
+);
+
+CREATE INDEX  ServiceKeywordMetaDataIndex1 ON ServiceKeywordMetaData (MetaDataID, MetaDataValue);
+CREATE INDEX  ServiceKeywordMetaDataIndex2 ON ServiceKeywordMetaData (ServiceID);
+
+
+/* metadata for all integer/date types */
+CREATE TABLE  ServiceNumericMetaData 
+(
+	ID			Integer primary key AUTOINCREMENT not null,
+	ServiceID		Integer not null,
+	MetaDataID 		Integer not null,
+	MetaDataValue		Integer not null
+);
+
+CREATE INDEX  ServiceNumericMetaDataIndex1 ON ServiceNumericMetaData (MetaDataID, MetaDataValue);
+CREATE INDEX  ServiceNumericMetaDataIndex2 ON ServiceNumericMetaData (ServiceID);
+
+
+

Added: trunk/data/sqlite-stored-procs.sql
==============================================================================
--- (empty file)
+++ trunk/data/sqlite-stored-procs.sql	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,233 @@
+
+GetMetaDataTypeID select ID From MetaDataTypes where MetaName = ?;
+
+GetMetaDataType select DataTypeID From MetaDataTypes where MetaName = ?;
+
+GetRelatedServiceIDs select TypeId from ServiceTypes where TypeName = ? or Parent = ?;
+
+GetFileByID  SELECT  DISTINCT Path , Name, Mime, ServiceTypeID  FROM Services WHERE ID = ? and Enabled = 1;
+
+GetFileByID2  SELECT DISTINCT (Path || '/' || Name) as uri, GetServiceName (ServiceTypeID), Mime FROM Services WHERE ID = ? and Enabled = 1;
+
+GetFileByID3  SELECT  DISTINCT Path , Name, Mime, ServiceTypeID  FROM Services WHERE ID = ?;
+
+GetEmailByID2  SELECT DISTINCT (S.Path || '/' || S.Name) as uri, 'Emails', S.Mime, M1.MetaDataDisplay, M2.MetaDataDisplay FROM Services S Left Outer Join ServiceMetaData M1 on S.ID = M1.ServiceID and M1.MetaDataID = (select ID From MetaDataTypes where MetaName ='Email:Subject') Left Outer Join ServiceMetaData M2 on S.ID = M2.ServiceID and M2.MetaDataID = (select ID From MetaDataTypes where MetaName ='Email:Sender') WHERE S.ID = ?;
+
+GetEmailByID  SELECT DISTINCT (S.Path || '/' || S.Name) as uri, 'Emails', S.Mime, S.KeyMetadata1, S.KeyMetadata2, S.KeyMetadata3 FROM Services S  WHERE S.ID = ?;
+
+GetApplicationByID  SELECT DISTINCT (S.Path || '/' || S.Name) as uri, 'Applications', 'Application', S.KeyMetadata1, S.KeyMetadata2, S.KeyMetadata3 FROM Services S  WHERE S.ID = ?;
+
+GetFileMTime SELECT M.MetaDataValue  FROM Services F inner join ServiceNumericMetaData M on F.ID = M.ServiceID WHERE F.Path = ? and F.Name = ? and M.MetaDataID = (select ID From MetaDataTypes where MetaName ='File:Modified');
+
+GetServices SELECT TypeName, Description, Parent  FROM ServiceTypes ORDER BY TypeID;
+GetAllServices SELECT TypeID, TypeName, Parent, PropertyPrefix, Enabled, Embedded, HasMetadata, HasFullText, HasThumbs, ContentMetadata, Database, ShowServiceFiles, ShowServiceDirectories, KeyMetadata1, KeyMetadata2, KeyMetadata3, KeyMetadata4, KeyMetadata5, KeyMetadata6, KeyMetadata7, KeyMetadata8, KeyMetadata9, KeyMetadata10, KeyMetadata11  FROM ServiceTypes;
+
+# GetNewID and UpdateNewID are deprecated !!
+GetNewID SELECT OptionValue FROM Options WHERE OptionKey = 'Sequence';
+UpdateNewID UPDATE Options set OptionValue = ? WHERE OptionKey = 'Sequence';
+CreateEvent INSERT INTO Events (ServiceID, EventType) VALUES (?,?); 
+GetEvents SELECT ID, ServiceID, EventType FROM Events WHERE BeingHandled = 1;
+SetEventsBeingHandled UPDATE Events SET BeingHandled = 1;
+
+GetLiveSearchAllIDs SELECT X.ServiceID FROM cache.LiveSearches AS X WHERE X.SearchID = ?
+GetLiveSearchDeletedIDs SELECT E.ServiceID FROM Events as E, cache.LiveSearches as X WHERE E.ServiceID = X.ServiceID AND X.SearchID = ? AND E.EventType = 'Delete';
+DeleteLiveSearchDeletedIDs DELETE FROM cache.LiveSearches AS Y WHERE Y.ServiceID IN SELECT ServiceID FROM Events as E, cache.LiveSearches as X WHERE E.ServiceID = X.ServiceID AND X.SearchID = ? AND E.EventType = 'Delete'
+
+GetLiveSearchHitCount SELECT count(*) FROM cache.LiveSearches WHERE SearchID = ?;
+LiveSearchStopSearch DELETE FROM cache.LiveSearches WHERE SearchID = ?
+
+# GetNewEventID and UpdateNewEventID are deprecated !!
+GetNewEventID SELECT OptionValue FROM Options WHERE OptionKey = 'EventSequence';
+UpdateNewEventID UPDATE Options set OptionValue = ? WHERE OptionKey = 'EventSequence';
+
+GetUpdateCount SELECT OptionValue FROM Options WHERE OptionKey = 'UpdateCount';
+SetUpdateCount UPDATE Options set OptionValue = ?  WHERE OptionKey = 'UpdateCount';
+
+GetOption SELECT OptionValue FROM Options WHERE OptionKey = ?;
+SetOption REPLACE into Options (OptionKey, OptionValue) values (?,?);
+
+CreateService INSERT INTO Services (ID, Path, Name, ServiceTypeID, Mime, Size, IsDirectory, IsLink, Offset, IndexTime, AuxilaryID) VALUES (?,?,?,?,?,?,?,?,?,?,?); 
+
+MoveService UPDATE Services SET Path = ?, Name = ? WHERE Path = ? AND Name = ?;
+MoveServiceChildren UPDATE Services SET Path = replace (Path, ?, ?);
+
+GetServiceID SELECT ID, IndexTime, IsDirectory, ServiceTypeID FROM Services WHERE Path = ? AND Name = ?;
+
+SelectFileChild SELECT ID, Path, Name, IsDirectory  FROM Services WHERE Path = ?;
+
+SelectFileChildWithoutDirs SELECT Path, Name  FROM Services WHERE Path = ? and IsDirectory = 0;
+
+SelectFileSubFolders SELECT ID, Path, Name, IsDirectory FROM Services WHERE (Path = ?  or Path glob ?) And IsDirectory = 1;
+
+SelectSubFileIDs SELECT ID FROM Services WHERE (Path = ?  or Path glob ?);
+
+UpdateFile UPDATE Services SET ServiceTypeID=?, Path=?, Name=?, Mime=?, Size=?, IndexTime =?, Offset=? WHERE ID = ?; 
+UpdateFileMTime UPDATE Services SET IndexTime=? where Path = ? and Name = ?;
+UpdateFileMove 	UPDATE Services SET Path = ?, Name = ? WHERE ID = ?;
+UpdateFileMoveChild UPDATE Services SET Path = ? WHERE Path = ?; 
+
+DeleteService1 Delete FROM Services Where (ID = ?);
+DeleteService2 Delete FROM Services Where (Path = ?);
+DeleteService3 Delete FROM Services Where (Path glob ?);
+DeleteService4 Delete FROM BackupServices Where (Path = ?);
+DeleteService5 Delete FROM BackupServices Where (Path glob ?);
+DeleteService6 Delete FROM BackupServices Where (Path = ? and Name = ?);
+DeleteService7 Delete FROM ServiceLinks Where (SourcePath = ? and SourceName = ?);
+DeleteService8 Delete FROM ServiceLinks Where (SourcePath = ?) or (SourcePath glob ?);
+DeleteService9 Delete FROM ServiceLinks Where (DestPath = ? and DestName = ?);
+DeleteService10 Delete FROM ServiceLinks Where (DestPath = ?) or (DestPath glob ?); 
+DeleteService11 DELETE FROM ServiceContents where ServiceID = ?;
+
+DeleteServiceMetadata DELETE FROM ServiceMetaData WHERE ServiceID = ?;
+DeleteServiceKeywordMetadata DELETE FROM ServiceMetaData WHERE ServiceID = ?;
+DeleteServiceNumericMetadata DELETE FROM ServiceMetaData WHERE ServiceID = ?;
+
+DeleteEmbeddedServiceMetadata1 DELETE FROM ServiceMetaData WHERE ServiceID = ? and MetaDataID in (select ID from MetaDataTypes where Embedded = 1);
+DeleteEmbeddedServiceMetadata2 DELETE FROM ServiceKeywordMetaData WHERE ServiceID = ? and MetaDataID in (select ID from MetaDataTypes where Embedded = 1);
+DeleteEmbeddedServiceMetadata3 DELETE FROM ServiceNumericMetaData WHERE ServiceID = ? and MetaDataID in (select ID from MetaDataTypes where Embedded = 1);
+
+GetByServiceType SELECT  DISTINCT F.Path || '/' || F.Name as uri  FROM Services F WHERE F.ServiceTypeID in (select TypeId from ServiceTypes where TypeName = ? or Parent = ?) LIMIT ?,?;
+
+SaveServiceContents REPLACE into ServiceContents (ServiceID, MetadataID, Content) values (?,?,compress (?));
+DeleteContent DELETE FROM ServiceContents where ServiceID = ? and MetadataId = ?;
+DeleteAllContents DELETE FROM ServiceContents where ServiceID = ?;
+GetContents Select uncompress (Content) from ServiceContents where ServiceID = ? and MetadataID = ? and Content is not null;
+GetFileContents Select substr(uncompress (Content), ?, ?) from ServiceContents where ServiceID = ?;
+GetAllContents Select uncompress (Content) from ServiceContents where ServiceID = ? and Content is not null;
+
+GetKeywordList Select distinct K.MetaDataValue, count(*) as totalcount from Services S, ServiceKeywordMetaData K where K.ServiceID = S.ID AND (S.ServiceTypeID in (select TypeId from ServiceTypes where TypeName = ? or Parent = ?)) AND  K.MetaDataId = 19 group by K.MetaDataValue order by totalcount desc, K.MetaDataValue asc;
+GetKeywords Select MetaDataValue from ServiceKeywordMetaData where ServiceID = (select ID From Services where Path = ? and Name  = ?) and MetaDataId in (select ChildID from MetaDataChildren where MetadataId = (select ID from MetadataTypes T where MetaName = 'DC:Keywords'));
+
+GetAllIndexable SELECT S.MetaDataValue as MetaValue, M.Weight, M.Filtered, M.Delimited  FROM ServiceMetaData S, MetaDataTypes M WHERE  S.MetaDataID = M.ID AND S.ServiceID = ? And S.MetaDataValue is not null and M.DatatypeID = 1 and M.Embedded >= ?;
+GetAllIndexableKeywords SELECT K.MetaDataValue as MetaValue, M.Weight, M.Filtered, M.Delimited as MetaWeight FROM ServiceKeywordMetaData K, MetaDataTypes M WHERE  K.MetaDataID = M.ID AND K.ServiceID = ? And K.MetaDataValue is not null and M.DatatypeID = 0 and M.Embedded >= ?;
+
+GetMetadataKeyword SELECT MetaDataValue FROM ServiceKeywordMetaData WHERE ServiceID = ? AND MetaDataID = ?;
+GetMetadata SELECT MetaDataDisplay FROM ServiceMetaData WHERE ServiceID = ? AND MetaDataID = ?;
+GetMetadataNumeric SELECT MetaDataValue FROM ServiceNumericMetaData WHERE ServiceID = ? AND MetaDataID = ?;
+
+GetMetadataIDValueKeyword SELECT MetadataID, MetadataValue FROM ServiceKeywordMetadata WHERE ServiceID = ? ORDER BY MetadataID
+GetMetadataIDValue SELECT MetadataID, MetadataValue FROM ServiceMetadata WHERE ServiceID = ? ORDER BY MetadataID
+GetMetadataIDValueNumeric SELECT MetadataID, MetadataValue FROM ServiceNumericMetadata WHERE ServiceID = ? ORDER BY MetadataID
+
+SetMetadataKeyword INSERT INTO ServiceKeywordMetaData (ServiceID, MetaDataID, MetaDataValue) VALUES (?,?,?);
+SetMetadata INSERT INTO ServiceMetaData (ServiceID, MetaDataID, MetaDataValue, MetaDataDisplay) VALUES (?,?,?,?);
+SetMetadataNumeric INSERT INTO ServiceNumericMetaData (ServiceID, MetaDataID, MetaDataValue) VALUES (?,?,?);
+
+DeleteMetadataKeyword DELETE FROM ServiceKeywordMetaData where ServiceID = ? and  MetaDataID=?;
+DeleteMetadata DELETE FROM ServiceMetaData where ServiceID = ? and  MetaDataID=?;
+DeleteMetadataNumeric DELETE FROM ServiceNumericMetaData where ServiceID = ? and  MetaDataID=?;
+
+DeleteMetadataKeywordValue DELETE FROM ServiceKeywordMetaData where ServiceID = ? and  MetaDataID=? and MetaDataValue = ?;
+DeleteMetadataValue DELETE FROM ServiceMetaData where ServiceID = ? and  MetaDataID=? and MetaDataDisplay=?;
+DeleteMetadataNumericValue DELETE FROM ServiceNumericMetaData where ServiceID = ? and  MetaDataID=? and MetaDataValue=?;
+
+GetMetadataTypeInfo Select ID, MetaName, DataTypeID, DisplayName, Description, Enabled, UIVisible, FieldName, Weight, Embedded, MultipleValues, Delimited, Filtered, Abstract FROM MetaDataTypes where MetaName = ?;
+GetMetadataTypes SELECT ID, MetaName, DataTypeID, FieldName, Weight, Embedded, MultipleValues, Delimited, Filtered, Abstract FROM MetaDataTypes;
+GetMetadataTypeDetails SELECT ID, MetaName, DataTypeID, DisplayName, Description, Enabled, UIVisible, FieldName, Weight, Embedded, MultipleValues, Delimited, Filtered, Abstract FROM MetaDataTypes;
+GetMetadataTypesLike SELECT ID, MetaName, DataTypeID, DisplayName, Description, Enabled, UIVisible, FieldName, Weight, Embedded, MultipleValues, Delimited, Filtered, Abstract FROM MetaDataTypes WHERE MetaName glob ?;
+GetWriteableMetadataTypes SELECT ID, MetaName, DataTypeID, DisplayName, Description, Enabled, UIVisible, FieldName, Weight, Embedded, MultipleValues, Delimited, Filtered, Abstract FROM MetaDataTypes where Embedded = 0;
+GetWriteableMetadataTypesLike SELECT ID, MetaName, DataTypeID, DisplayName, Description, Enabled, UIVisible, FieldName, Weight, Embedded, MultipleValues, Delimited, Filtered, Abstract FROM MetaDataTypes WHERE MetaName glob ? and  Embedded = 0;
+
+InsertMetaDataChildren INSERT INTO  MetaDataChildren (ChildID,MetadataID) VALUES (?,(select ID from MetaDataTypes where MetaName = ?));
+GetMetadataAliases SELECT distinct M.MetaName, M.ID from MetaDataTypes M, MetaDataChildren C where M.ID = C.ChildID and C.MetaDataID = ?; 
+GetMetadataAliasesForName SELECT distinct M.MetaName, M.ID from MetaDataTypes M, MetaDataChildren C where M.ID = C.ChildID and C.MetaDataID = (select ID from MetaDataTypes where MetaName = ?) union select M.MetaName, M.ID from MetaDataTypes M where M.MetaName = ?; 
+
+SelectRegisteredClasses SELECT DISTINCT TypeName FROM ServiceTypes;
+
+InsertMetadataType INSERT INTO MetaDataTypes (MetaName) Values (?);
+
+InsertServiceType REPLACE INTO  ServiceTypes (TypeName) Values (?);
+InsertServiceTileMetadata REPLACE Into ServiceTileMetadata (ServiceTypeID, MetaName) VALUES (?, ?);
+InsertServiceTabularMetadata REPLACE Into ServiceTabularMetadata (ServiceTypeID, MetaName) VALUES (?, ?);
+
+GetServiceTypes select TypeID, TypeName, Parent, Enabled, Embedded, HasMetadata, HasFullText, HasThumbs, ContentMetadata, KeyMetadata1,  KeyMetadata2, KeyMetadata3, KeyMetadata4, KeyMetadata5, KeyMetadata6, KeyMetadata7, KeyMetadata8, KeyMetadata9, KeyMetadata10, KeyMetadata11 From ServiceTypes;
+GetServiceTypeDetails select TypeID, TypeName, DisplayName, Parent, Enabled, Embedded, ChildResource, CreateDesktopFile, CanCopy, CanDelete, HasMetadata, HasFullText, HasThumbs, ContentMetadata, UIView, Description, Database, Icon, IndexerExec, ThumbExec, ViewerExec, UIVisible, UIMetadata1, UIMetadata2, UIMetadata3, KeyMetadata1, KeyMetadata2, KeyMetadata3, KeyMetadata4, KeyMetadata5, KeyMetadata6 FROM ServiceTypes;
+GetServiceTypeDetailsByName select TypeID, TypeName, DisplayName, Parent, Enabled, Embedded, ChildResource, CreateDesktopFile, CanCopy, CanDelete, HasMetadata, HasFullText, HasThumbs, ContentMetadata, UIView, Description, Database, Icon, IndexerExec, ThumbExec, ViewerExec, UIVisible, UIMetadata1, UIMetadata2, UIMetadata3, KeyMetadata1, KeyMetadata2, KeyMetadata3, KeyMetadata4, KeyMetadata5, KeyMetadata6 FROM ServiceTypes where TypeName = ?;
+GetServiceTypeDetailsByID select TypeId, TypeName, DisplayName, Parent, Enabled, Embedded, ChildResource, CreateDesktopFile, CanCopy, CanDelete, HasMetadata, HasFullText, HasThumbs, ContentMetadata, UIView, Description, Database, Icon, IndexerExec, ThumbExec, ViewerExec, UIVisible, UIMetadata1, UIMetadata2, UIMetadata3, KeyMetadata1, KeyMetadata2, KeyMetadata3, KeyMetadata4, KeyMetadata5, KeyMetadata6 FROM ServiceTypes where TypeID = ?;
+GetServiceTile	select M.MetaName, M.ID from MetaDataTypes M where M.ID in (select MetadataTypeID  from ServiceTileMetadata where ServiceTypeID = ?);
+GetServiceTable select M.MetaName, M.ID from MetaDataTypes M where M.ID in (select MetadataTypeID  from ServiceTabularMetadata where ServiceTypeID = ?);
+
+InsertMimes replace into FileMimes (Mime) Values (?);
+InsertMimePrefixes replace into FileMimePrefixes (MimePrefix) Values (?);
+
+GetMimeForServiceId select Mime from FileMimes where ServiceTypeId = ?;
+GetMimePrefixForServiceId select MimePrefix from FileMimePrefixes where ServiceTypeId = ?;
+
+ExistsPendingFiles select count (*) from FilePending where Action <> 20;
+InsertPendingFile INSERT INTO FilePending (FileID, Action, PendingDate, FileUri, MimeType, IsDir, IsNew, RefreshEmbedded, RefreshContents, ServiceTypeID) VALUES (?,?,?,?,?,?,?,?,?,?);
+CountPendingMetadataFiles select count (*) from FilePending where Action = 20;
+SelectPendingByUri SELECT  FileID, FileUri, Action, MimeType, IsDir, IsNew, RefreshEmbedded, RefreshContents, ServiceTypeID FROM FilePending WHERE FileUri = ?;
+UpdatePendingFile UPDATE FilePending SET PendingDate = ?, Action = ? WHERE FileUri = ?;
+DeletePendingFile DELETE FROM FilePending WHERE FileUri = ?;
+
+GetWatchUri select URI from FileWatches where WatchID = ?;
+GetWatchID select WatchID from FileWatches where URI = ?;
+GetSubWatches select WatchID from FileWatches where URI glob ?;
+DeleteWatch delete from FileWatches where URI = ?;
+DeleteSubWatches delete from FileWatches where URI glob ?;
+InsertWatch insert into FileWatches (URI, WatchID) values (?,?);
+
+InsertSearchResult1 insert into SearchResults1 (SID, Score) values (?,?);
+DeleteSearchResults1 delete from SearchResults1;
+
+GetMboxes  select MailApp, MailType, Filename, Path, UriPrefix, Offset, LastOffset, MailCount, JunkCount, DeleteCount, Mtime  from MailSummary;
+GetMBoxDetails select MailApp, MailType, Filename, UriPrefix, Offset, LastOffset, MailCount, JunkCount, DeleteCount, Mtime  from MailSummary where path = ?;
+GetMboxID select ID from MailSummary where path = ?;
+GetMBoxPath select path from MailSummary where FileName = ?;
+InsertMboxDetails insert into MailSummary (MailApp, MailType, FileName, Path, UriPrefix, Offset, LastOffset, MailCount, JunkCount, DeleteCount, Mtime) values (?,?,?,?,?,0,0,0,0,0,0);
+UpdateMboxOffset update MailSummary set Offset = ? where Path = ?;
+UpdateMboxCounts update MailSummary set MailCount = ?, JunkCount = ?, DeleteCount = ? where Path = ?;
+
+SetJunkMbox update MailSummary set NeedsChecking = ? where path = ?;
+
+LookupJunk select SummaryID from JunkMail where UID = ? and SummaryID = ?;
+InsertJunk insert into JunkMail values (?,?);
+
+selectStats Select Sum(TypeCount) from ServiceTypes where TypeName = ? or TypeName in (select TypeName from ServiceTypes where Parent = ?);
+IncStat UPDATE ServiceTypes set TypeCount = (TypeCount + 1) where TypeName = ?;
+DecStat UPDATE ServiceTypes set TypeCount = (TypeCount - 1) where TypeName = ?;
+GetStats Select TypeName, TypeCount from ServiceTypes Group By TypeName order by TypeID asc;
+
+InsertBackupService INSERT INTO BackupServices (Path, Name) select S.Path, S.Name from Services S where S.ID = ?;
+UpdateBackupService Update BackupServices set Path = ? and Name = ? where Path = ? and Name = ?;
+GetBackupService Select ID from BackupServices where Path = ? and  Name = ?;
+GetBackupServiceByID Select B.ID from BackupServices B, Services S where B.Path = S.Path and  B.Name = S.Name and S.ID = ?;
+GetBackupMetadata select M.MetaName, B.UserValue from BackupMetadata B, MetadataTypes M, BackupServices BS where B.MetadataID = M.ID and B.ServiceID = BS.ID and BS.Path = ? and BS.Name = ?;
+SetBackupMetadata INSERT INTO BackupMetadata (ServiceID, MetadataID, UserValue) VALUES (?,?,?);
+DeleteBackupMetadataValue Delete From BackupMetadata where  ServiceID = ? and MetadataID = ? and UserValue = ?;
+DeleteBackupMetadata Delete From BackupMetadata where  ServiceID = ? and MetadataID = ?;
+
+GetWords Select word from HitIndex where word glob ?;
+GetWordBatch Select ROWID, word, HitCount, HitArraySize from HitIndex limit 100;
+GetHitDetails Select ROWID, HitCount, HitArraySize From HitIndex where word = ?;
+DeleteWord delete from HitIndex where ROWID = ?;
+InsertHitDetails Insert into HitIndex (Word, HitCount, HitArraySize, HitArray) Values (?,?,?, ZeroBlob(?));
+UpdateHitDetails Update HitIndex set HitCount = ?, HitArraySize = ? where ROWID = ?;
+ResizeHitDetails Update HitIndex set HitCount = ?, HitArraySize = ?, HitArray = Zeroblob(?) where ROWID = ?;
+
+InsertXesamMetadataType INSERT INTO XesamMetaDataTypes (MetaName) Values (?);
+InsertXesamServiceType INSERT INTO XesamServiceTypes (TypeName) Values (?);
+InsertXesamMetaDataMapping INSERT INTO XesamMetaDataMapping (XesamMetaName, MetaName) Values (?, ?);
+InsertXesamServiceMapping INSERT INTO XesamServiceMapping (XesamTypeName, TypeName) Values (?, ?);
+InsertXesamServiceLookup REPLACE INTO XesamServiceLookup (XesamTypeName, TypeName) Values (?, ?);
+InsertXesamMetaDataLookup REPLACE INTO XesamMetaDataLookup (XesamMetaName, MetaName) Values (?, ?);
+
+GetXesamServiceParents SELECT TypeName, Parents FROM XesamServiceTypes;
+GetXesamServiceChildren SELECT Child FROM XesamServiceChildren WHERE Parent = ?;
+GetXesamServiceMappings SELECT TypeName FROM XesamServiceMapping WHERE XesamTypeName = ?;
+GetXesamServiceLookups SELECT DISTINCT TypeName FROM XesamServiceLookup WHERE XesamTypeName = ?;
+
+GetXesamMetaDataParents SELECT MetaName, Parents FROM XesamMetaDataTypes;
+GetXesamMetaDataChildren SELECT Child FROM XesamMetaDataChildren WHERE Parent = ?;
+GetXesamMetaDataMappings SELECT MetaName FROM XesamMetaDataMapping WHERE XesamMetaName = ?;
+GetXesamMetaDataLookups SELECT DISTINCT MetaName FROM XesamMetaDataLookup WHERE XesamMetaName = ?;
+GetXesamMetaDataTextLookups SELECT DISTINCT L.MetaName FROM XesamMetaDataLookup L INNER JOIN XesamMetaDataTypes T ON (T.MetaName = L.XesamMetaName) WHERE T.DataTypeID = 3;
+
+GetXesamMetaDataTypes SELECT ID, MetaName, DataTypeID, FieldName, Weight, Embedded, MultipleValues, Delimited, Filtered, Abstract FROM XesamMetaDataTypes;
+GetXesamServiceTypes SELECT TypeID, TypeName, Parents, PropertyPrefix, Enabled, Embedded, HasMetadata, HasFullText, HasThumbs, ContentMetadata, Database, ShowServiceFiles, ShowServiceDirectories, KeyMetadata1, KeyMetadata2, KeyMetadata3, KeyMetadata4, KeyMetadata5, KeyMetadata6, KeyMetadata7, KeyMetadata8, KeyMetadata9, KeyMetadata10, KeyMetadata11  FROM XesamServiceTypes;
+
+InsertXesamMimes replace into XesamFileMimes (Mime) Values (?);
+InsertXesamMimePrefixes replace into XesamFileMimePrefixes (MimePrefix) Values (?);
+
+GetXesamMimeForServiceId select Mime from XesamFileMimes where ServiceTypeId = ?;
+GetXesamMimePrefixForServiceId select MimePrefix from XesamFileMimePrefixes where ServiceTypeId = ?;

Added: trunk/data/sqlite-tracker-triggers.sql
==============================================================================
--- (empty file)
+++ trunk/data/sqlite-tracker-triggers.sql	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,4 @@
+CREATE TRIGGER delete_backup_service BEFORE DELETE ON BackupServices 
+BEGIN  
+	DELETE FROM BackupMetaData WHERE ServiceID = old.ID;
+END;!

Added: trunk/data/sqlite-tracker.sql
==============================================================================
--- (empty file)
+++ trunk/data/sqlite-tracker.sql	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,83 @@
+
+CREATE TABLE Options (	
+	OptionKey 	Text COLLATE NOCASE not null,	
+	OptionValue	Text COLLATE NOCASE
+);
+
+insert Into Options (OptionKey, OptionValue) values ('DBVersion', '20');
+insert Into Options (OptionKey, OptionValue) values ('Sequence', '1');
+insert Into Options (OptionKey, OptionValue) values ('EventSequence', '1');
+insert Into Options (OptionKey, OptionValue) values ('UpdateCount', '0');
+
+
+/* store volume and HAL info here for files */
+CREATE TABLE  Volumes
+(
+	VolumeID 	Integer primary key AUTOINCREMENT not null,
+	UDI		Text,
+	VolumeName	Text,
+	MountPath	Text,
+	Enabled		Integer default 0
+
+);
+
+
+/* provides links from one service entity to another (entities can be in different databases) */
+CREATE TABLE  ServiceLinks
+(
+	ID			Integer primary key AUTOINCREMENT not null,
+	MetadataID		Integer not null,
+	SourcePath		Text,
+	SourceName		Text,
+	DestPath		Text,
+	DestName		Text
+);
+
+
+
+/* following two tables are used to backup any user/app defined metadata for indexed/embedded services only */ 
+CREATE TABLE  BackupServices
+(
+	ID            		Integer primary key AUTOINCREMENT not null,
+	Path 			Text  not null, 
+	Name	 		Text,
+
+	unique (Path, Name)
+
+);
+
+
+CREATE TABLE  BackupMetaData
+(
+	ID			Integer primary key  AUTOINCREMENT not null,
+	ServiceID		Integer not null,
+	MetaDataID 		Integer  not null,
+	UserValue		Text
+	
+	 
+);
+
+CREATE INDEX  BackupMetaDataIndex1 ON BackupMetaData (ServiceID, MetaDataID);
+
+
+
+CREATE TABLE KeywordImages
+(
+	Keyword 	Text primary key,
+	Image		Text
+);
+
+
+/* allow aliasing of VFolders with nice names */
+CREATE TABLE  VFolders
+(
+	Path			Text  not null,
+	Name			Text  not null,
+	Query			text not null,
+	RDF			text,
+	Type			Integer default 0,
+	active			Integer,
+
+	primary key (Path, Name)
+
+);

Added: trunk/data/sqlite-triggers.sql
==============================================================================

Added: trunk/data/sqlite-user-data.sql
==============================================================================
--- (empty file)
+++ trunk/data/sqlite-user-data.sql	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,24 @@
+/* basic info for a user defined file or service object */
+CREATE TABLE  UserServices
+(
+	ID            		Integer primary key not null,
+	Path 			Text  not null, 
+	Name	 		Text, 
+
+    	unique (Path, Name)
+
+);
+
+
+
+/* de-normalised metadata which is unprocessed (not normalized or casefolded) for display only - never used for searching */
+CREATE TABLE  UserMetaDataDisplay 
+(
+	ServiceID		Integer not null,
+	MetaDataID 		Integer  not null,
+	UserValue		Text,
+	Length			Integer.	
+	
+	primary key (ServiceID, MetaDataID)
+);
+

Added: trunk/data/sqlite-xesam.sql
==============================================================================
--- (empty file)
+++ trunk/data/sqlite-xesam.sql	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,167 @@
+
+CREATE TABLE  XesamMetaDataTypes 
+(
+	ID	 		Integer primary key AUTOINCREMENT not null,
+	MetaName		Text not null  COLLATE NOCASE, 
+	DataTypeID		Integer default 0,
+	DisplayName		text,
+	Description		text default ' ',
+	Enabled			integer default 1, /* used to prevent use of this metadata type */
+	UIVisible		integer default 0, /* should this metadata type be visible in a search criteria UI  */
+	WriteExec		text default ' ', /* used to specify an external program that can write an *embedded* metadata to a file */
+	Alias			text default ' ', /* alternate name for this type (XESAM specs?) */
+	FieldName		text default ' ', /* filedname if present in the services table */
+	Weight			Integer default 1, /* weight of metdata type in ranking */
+	Embedded		Integer default 1, /* 1 if metadata extracted from the file by the indexer and is not updateable by the user. 0 - this metadata can be updated by the user and is external to the file */
+	MultipleValues		Integer default 0, /* 0= type cannot have multiple values per entity, 1= type can have more than 1 value per entity */
+	Delimited		Integer default 0, /* if 1, extra delimiters (hyphen and underscore) are used to break word */
+	Filtered		Integer default 1, /* if 1, words are filtered for numerics (if numeric indexing is disabled), stopwords and min length */
+	Abstract		Integer default 0, /* if 0, can be used for storing metadata - Abstract type classes cannot store metadata and can only be used for searching its decendants */
+	StemMetadata		Integer default 1, /* 1 if metadata should be stemmed */
+	SideCar			Integer default 0, /* should this metadata be backed up in an xmp sidecar file */
+	FileName		Text default ' ',
+
+	Categories		text default ' ',
+	Parents			text default ' ',
+
+	Unique (MetaName)
+);
+
+CREATE TABLE  XesamServiceTypes
+(
+	TypeID 			Integer primary key AUTOINCREMENT not null,
+	TypeName		Text COLLATE NOCASE not null,
+
+	TypeCount		Integer default 0,
+
+	DisplayName		Text default ' ',
+	PropertyPrefix          Text default ' ',
+	Enabled			Integer default 1, 
+	Embedded		Integer default 1, /* service is created by the indexer if embedded. User or app defined services are not embedded */
+	ChildResource		Integer default 0, /* service is a child service */
+	
+	CreateDesktopFile	Integer default 0, /* used by a UI to indicate whether it should create a desktop file for the service if its copied (using the ViewerExec field + uri) */
+
+	/* useful for a UI when determining what actions a hit can have */
+	CanCopy			Integer default 1, 
+	CanDelete		Integer default 1,
+
+	ShowServiceFiles	Integer default 0,
+	ShowServiceDirectories  Integer default 0,
+
+	HasMetadata		Integer default 1,
+	HasFullText		Integer default 1,
+	HasThumbs		Integer default 1,
+	
+	ContentMetadata		Text default ' ', /* the content field is the one most likely to be used for showing a search snippet */ 
+
+	KeyMetadata1		Text default ' ', /* the most commonly requested metadata (especially for tables/grid views) is cached int he services table for extra fast retrieval */
+	KeyMetadata2		Text default ' ',
+	KeyMetadata3		Text default ' ',
+	KeyMetadata4		Text default ' ',
+	KeyMetadata5		Text default ' ',
+	KeyMetadata6		Text default ' ',
+	KeyMetadata7		Text default ' ',
+	KeyMetadata8		Text default ' ',
+	KeyMetadata9		Text default ' ',
+	KeyMetadata10		Text default ' ',
+	KeyMetadata11		Text default ' ',
+
+	UIVisible		Integer default 0,	/* should service appear in a search GUI? */
+	UITitle			Text default ' ',	/* title format as displayed in the metadata tile */
+	UIMetadata1		Text default ' ',	/*UI fields to show in GUI for a hit - if not set then Name,Path,Mime are used */
+	UIMetadata2		Text default ' ',
+	UIMetadata3		Text default ' ',
+	UIView			Text default 'default',
+
+	Description		Text default ' ',
+	Database		integer default 0, /* 0 = DB_FILES, 1 = DB_EMAILS, 2 = DB_MISC, 3 = DB_USER */
+	Icon			Text default ' ',
+
+	IndexerExec		Text default ' ',
+	IndexerOutput		Text default 'stdout',
+	ThumbExec		Text default ' ',
+	ViewerExec		Text default ' ',
+
+	WatchFolders		Text default ' ',
+	IncludeGlob		Text default ' ',
+	ExcludeGlob		Text default ' ',
+
+	FileName		Text default ' ',
+
+	Parents			text default ' ',
+
+	unique (TypeName)
+);
+
+CREATE TABLE  XesamServiceMapping
+(
+	ID			Integer primary key AUTOINCREMENT not null,
+	XesamTypeName		Text,
+	TypeName		Text,
+
+	unique (XesamTypeName, TypeName)
+);
+
+CREATE TABLE XesamMetaDataMapping
+(
+	ID			Integer primary key AUTOINCREMENT not null,
+	XesamMetaName		Text,
+	MetaName		Text,
+
+	unique (XesamMetaName, MetaName)
+);
+
+CREATE TABLE XesamServiceChildren
+(
+	Parent			Text,
+	Child			Text,
+
+	unique (Parent, Child)
+);
+
+CREATE TABLE XesamMetaDataChildren
+(
+	Parent			Text,
+	Child			Text,
+
+	unique (Parent, Child)
+);
+
+CREATE TABLE XesamServiceLookup
+(	
+	ID			Integer primary key AUTOINCREMENT not null,
+	XesamTypeName		Text,
+	TypeName		Text,
+
+	unique (XesamTypeName, TypeName)
+);
+
+CREATE TABLE XesamMetaDataLookup
+(	
+	ID			Integer primary key AUTOINCREMENT not null,
+	XesamMetaName		Text,
+	MetaName		Text,
+
+	unique (XesamMetaName, MetaName)
+);
+
+CREATE TABLE  XesamFileMimes
+(
+	Mime			Text primary key not null,
+	ServiceTypeID		Integer default 0,
+	ThumbExec		Text default ' ',
+	MetadataExec		Text default ' ',
+	FullTextExec		Text default ' '
+
+);
+
+CREATE TABLE  XesamFileMimePrefixes
+(
+	MimePrefix		Text primary key not null,
+	ServiceTypeID		Integer default 0,
+	ThumbExec		Text default ' ',
+	MetadataExec		Text default ' ',
+	FullTextExec		Text default ' '
+
+);
\ No newline at end of file

Added: trunk/data/tracker-stop-words.txt
==============================================================================
--- (empty file)
+++ trunk/data/tracker-stop-words.txt	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,109 @@
+a's able about above according
+accordingly across actually after afterwards
+again against ain't all allow
+allows almost alone along already
+also although always am among
+amongst an and 
+anybody anyhow anyone anything anyway
+anyways anywhere apart appear appreciate
+appropriate are aren't around as
+aside ask asking associated at
+available away awfully be became
+because become becomes becoming been
+before beforehand behind being believe
+below beside besides best better
+between beyond both brief but
+by c'mon c's came can
+can't cannot cant cause causes
+certain certainly changes clearly co
+com come comes concerning consequently
+consider considering contain containing contains
+corresponding could couldn't course currently
+definitely described despite did didn't
+different do does doesn't doing
+don't done down downwards during
+each edu eg eight either
+else elsewhere enough entirely especially
+et etc even ever every
+everybody everyone everything everywhere ex
+exactly example except far few finally
+fifth first five followed following
+follows for former formerly forth
+four from further furthermore get
+gets getting given gives go
+goes going gone got gotten
+greetings had hadn't happens hardly
+has hasn't have haven't having
+he he's hello help hence
+her here here's hereafter hereby
+herein hereupon hers herself hi
+him himself his hither hopefully
+how howbeit however i'd i'll
+i'm i've ie if ignored
+immediate in inasmuch inc indeed
+indicate indicated indicates inner insofar
+instead int into inward is isn't
+it it'd it'll it's its 
+itself just keep keeps kept
+know knows known last lately
+later latter latterly least less
+lest let let's like liked
+likely little look looking looks
+ltd mainly many may maybe
+me mean meanwhile merely might
+more moreover most mostly much
+must my myself name namely
+nd near nearly necessary need
+needs neither never nevertheless new
+next nine no nobody non
+none noone nor normally not
+nothing novel now nowhere obviously
+of off often oh ok
+okay old on once one
+ones only onto or
+others otherwise ought our ours
+ourselves out outside over overall
+own particular particularly per perhaps
+placed please plus possible presumably
+probably provides que quite qv
+rather rd re really reasonably
+regarding regardless regards relatively respectively
+right said same saw say
+saying says second secondly see
+seeing seem seemed seeming seems
+seen self selves sensible sent
+serious seriously seven several shall
+she should shouldn't since six
+so some somebody somehow someone
+something sometime sometimes somewhat somewhere
+soon sorry specified specify specifying
+still sub such sup sure
+t's take taken tell tends
+th than thank thanks thanx
+that that's thats the their
+theirs them themselves then thence
+there there's thereafter thereby therefore
+therein theres thereupon these they
+they'd they'll they're they've think
+third this thorough thoroughly those
+though three through throughout thru
+thus to together too took
+toward towards tried tries truly
+try trying twice two un
+under unfortunately unless unlikely until
+unto up upon us use
+used useful uses using usually
+value various very via viz
+vs want wants was wasn't
+way we we'd we'll we're
+we've welcome well went were
+weren't what what's whatever when
+whence whenever where where's whereafter
+whereas whereby wherein whereupon wherever
+whether which while whither who
+who's whoever whole whom whose
+why will willing wish with
+within without won't wonder would
+would wouldn't yes yet you
+you'd you'll you're you've your
+yours yourself yourselves zero  

Added: trunk/data/tracker.pc.in
==============================================================================
--- (empty file)
+++ trunk/data/tracker.pc.in	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,12 @@
+prefix= prefix@
+exec_prefix= exec_prefix@
+libdir= libdir@
+includedir= includedir@
+
+Name: tracker
+Description: Tracker : Indexer, metadata harvester and metadata database
+Version: @VERSION@
+Requires: glib-2.0 dbus-1 dbus-glib-1
+Libs: -L${libdir} -ltrackerclient
+Cflags: -DDBUS_API_SUBJECT_TO_CHANGE -I${includedir}
+

Added: trunk/data/trackerd.desktop.in.in
==============================================================================
--- (empty file)
+++ trunk/data/trackerd.desktop.in.in	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,20 @@
+[Desktop Entry]
+Encoding=UTF-8
+_Name=Tracker
+_Comment=Tracker search and indexing service
+Icon=
+Exec= libexec@/trackerd
+Terminal=false
+Type=Application
+Categories=
+X-GNOME-Autostart-enabled=true
+X-KDE-autostart-after=panel
+X-KDE-StartupNotify=false
+X-KDE-UniqueApplet=true
+NoDisplay=true
+OnlyShowIn=GNOME;KDE;XFCE;
+X-GNOME-Bugzilla-Bugzilla=GNOME
+X-GNOME-Bugzilla-Product=tracker
+X-GNOME-Bugzilla-Component=Indexer
+X-GNOME-Bugzilla-Version= VERSION@
+

Added: trunk/docs/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/docs/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,32 @@
+include $(top_srcdir)/Makefile.decl
+
+if HAVE_GNOME
+tst = tracker-search-tool.1
+endif
+
+if ENABLE_TRACKERAPPLET
+ta = tracker-applet.1
+endif
+
+if ENABLE_PREFERENCES
+tp = tracker-preferences.1
+endif
+
+man_MANS = 			\
+	tracker-extract.1	\
+	tracker-files.1		\
+	tracker-meta-folder.1	\
+	tracker-query.1		\
+	tracker-search.1	\
+	tracker-stats.1		\
+	trackerd.1		\
+	tracker-thumbnailer.1	\
+	tracker-tag.1		\
+	tracker-status.1	\
+	tracker.cfg.5		\
+	tracker-services.7	\
+	$(tst)			\
+	$(ta)			\
+	$(tp)
+
+EXTRA_DIST = $(man_MANS)

Added: trunk/docs/tracker-applet.1
==============================================================================
--- (empty file)
+++ trunk/docs/tracker-applet.1	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,21 @@
+.TH tracker-applet 1 "November, 2007" GNU "User Commands"
+
+.SH NAME
+tracker-applet \- The tracker tray-icon and on-click-search-entry
+
+.SH SYNOPSIS
+.B tracker-applet
+
+.SH DESCRIPTION
+.B tracker-applet
+is a
+.BR trackerd (1)
+system tray icon, meant to provide a fast way of accessing tracker's
+database, configuring tracker through
+.BR tracker-preferences
+and displaying information, by means of popups, icon changes and the
+statistics page to the user.
+
+.SH SEE ALSO
+.BR tracker-search-tool (1),
+.BR trackerd (1).

Added: trunk/docs/tracker-extract.1
==============================================================================
--- (empty file)
+++ trunk/docs/tracker-extract.1	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,16 @@
+.TH tracker-extract 1 "July 2007" GNU "User Commands"
+
+.SH NAME
+tracker-extract \- extract metadata from file and display them.
+
+.SH SYNOPSYS
+.B tracker-extract
+.I file
+
+.SH DESCRIPTION
+.B tracker-extract
+read the file provided in paramater and extract the metadata from this
+file; then it displays the metadata on the standard output.
+
+.SH SEE ALSO
+.BR trackerd (1)

Added: trunk/docs/tracker-files.1
==============================================================================
--- (empty file)
+++ trunk/docs/tracker-files.1	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,31 @@
+.TH tracker-files 1 "July 2007" GNU "User Commands"
+
+.SH NAME
+tracker-files \- Return files filtered by the mime type or their category
+(called ServiceType)
+
+.SH SYNOPSIS
+.B tracker-files
+[-s ServiceType|-m MimeType]
+
+.SH DESCRIPTION
+.B tracker-files
+returns files that match the ServiceType or the mime-type provided;
+you can
+
+.SH OPTIONS
+.TP
+\-s ServiceType
+Specify the kind of the files you want to find. For more information on
+valid ServiceType see
+.BR tracker-services (7).
+
+.TP
+\-m MimeType
+Specify the mime-type of the files you want to find; see
+http://www.iana.org/assignments/media-types/ for the complete list of
+existing mime-type.
+
+.SH SEE ALSO
+.BR trackerd (1),
+.BR tracker-services (7).

Added: trunk/docs/tracker-meta-folder.1
==============================================================================
--- (empty file)
+++ trunk/docs/tracker-meta-folder.1	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,15 @@
+.TH tracker-meta-folder 1 "July, 2007" GNU "User Commands"
+
+.SH NAME
+tracker-meta-folder \- return list of files indexed by tracker for a folder
+
+.SH SYNOPSIS
+.B tracker-meta-folder
+folder
+
+.SH DESCRIPTION
+.B tracker-meta-folder
+returns the list of files indexed by tracker and stored under the folder provided as parameter.
+
+.SH SEE ALSO
+.BR trackerd(1).

Added: trunk/docs/tracker-preferences.1
==============================================================================
--- (empty file)
+++ trunk/docs/tracker-preferences.1	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,17 @@
+.TH tracker-preferences 1 "November, 2007" GNU "User Commands"
+
+.SH NAME
+tracker-preferences \- Preferences editor for trackerd
+
+.SH SYNOPSIS
+.B tracker-preferences
+
+.SH DESCRIPTION
+.B tracker-preferences
+is a graphical configuration front end for the
+.BR trackerd (1)
+indexer and search daemon.
+
+.SH SEE ALSO
+.BR tracker-search-tool (1),
+.BR trackerd (1).

Added: trunk/docs/tracker-query.1
==============================================================================
--- (empty file)
+++ trunk/docs/tracker-query.1	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,36 @@
+.TH tracker-query 1 "July 2007" GNU "User Commands"
+
+.SH NAME
+tracker-query \- command line tool to query tracker database 
+
+.SH SYNOPSIS
+.B tracker-query
+.I [OPTIONS] query-file.rdf [MetaDataFields...]
+
+.SH DESCRIPTION
+.B tracker-query
+Exectutes an RDF query and prints the result. Additionally the behavior
+can be customized using below described options.
+
+.SH OPTIONS
+.TP
+\-?, --help
+Show a brief help message.
+
+.TP
+\-s, --service=ServiceName
+Search for files from a specific service. ServiceName has to be one of
+trackers services.  For mor information on services, see
+.BR tracker-services (7).
+
+.TP
+\-t, --search-term=SerchTerm
+Adds a full text search filter to the query.
+
+.TP
+\-k, --keyword=Keyword
+Adds a keyword filter to the query.
+
+.SH "SEE ALSO"
+.BR trackerd (1),
+.BR tracker-services (7).

Added: trunk/docs/tracker-search-tool.1
==============================================================================
--- (empty file)
+++ trunk/docs/tracker-search-tool.1	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,58 @@
+.TH tracker-search-tool 1 "July, 2007" GNU "User Commands"
+
+.SH NAME
+tracker-search-tool \- Gnome Tracker Search Tool
+
+.SH SYNOPSIS
+\fBtracker-search-tool\fR [\fIoptions\fR] [\fIEXPRESSION\fR]
+
+.SH DESCRIPTION
+.B tracker-search-tool 
+is a graphical search front end for Gnome.
+It uses 
+.B trackerd
+to get instant search results.
+
+.SH OPTIONS
+.SS "Help Options:"
+.TP
+\-?, \fB\-\-help\fR
+Show help options
+.TP
+\fB\-\-help\-all\fR
+Show all help options
+.TP
+\fB\-\-help\-gtk\fR
+Show GTK+ Options
+.TP
+\fB\-\-help\-bonobo\-activation\fR
+Show Bonobo Activation options
+.TP
+\fB\-\-help\-gnome\fR
+Show GNOME options
+.TP
+\fB\-\-help\-gnome\-session\fR
+Show session management options
+.TP
+\fB\-\-help\-gnome\-ui\fR
+Show GNOME GUI options
+.SS "Application Options:"
+.TP
+\fB\-s\fR, \fB\-\-service\fR=\fISERVICE\fR
+Search from a specific service. See
+.BR tracker-services (7)
+for more information.
+.TP
+\fB\-\-display\fR=\fIDISPLAY\fR
+X display to use
+
+.SH SEE ALSO
+.BR tracker-search (1),
+.BR trackerd (1),
+.BR tracker-services (7).
+
+.SH AUTHOR
+tracker-search-tool was written by Jamie McCracken <jamiemcc gnome org>.
+.PP
+This manual page was written by Michael Biebl <biebl debian org>,
+for the Debian project (but may be used by others).

Added: trunk/docs/tracker-search.1
==============================================================================
--- (empty file)
+++ trunk/docs/tracker-search.1	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,39 @@
+.TH tracker-search 1 "July 2007" GNU "User Commands"
+
+.SH NAME
+tracker-search \- command line tool to search documents indexed by
+trackerd
+
+.SH SYNOPSIS
+.B tracker-search
+[OPTIONS]
+.I expression
+
+.SH DESCRIPTION
+.B tracker-search
+searches all by
+.BR trackerd(1)
+indexed content for
+.I expression.
+.I Expression
+can be any number of words to be searched for.
+
+.SH OPTIONS
+.TP
+\-?, --help
+Give a short help message.
+
+.TP
+\-l, --limit=N
+Limit search to N results.
+
+.TP
+\-s, --service=SERVICE
+Search by a specific service. For more information on tracker services,
+see
+.BR tracker-services (7).
+
+.SH "SEE ALSO"
+.BR trackerd (1),
+.BR tracker-search-tool (1),
+.BR tracker-services (7).

Added: trunk/docs/tracker-services.7
==============================================================================
--- (empty file)
+++ trunk/docs/tracker-services.7	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,60 @@
+.TH tracker-services 7 "July 2007" GNU "Conventions"
+
+.SH NAME
+tracker-services \- service types of trackerd
+
+.SH INTRODUCTION
+Trackerd understands a number of different services which are used to
+group search results and documents together. All searches and queries can
+be refined by specifying a service to search from.
+.PP
+For example one could search for "Roses" in the service Conversations
+which would only return hits from chat conversations.
+
+.SH SERVICE TYPES
+.TP
+Files
+Search for files only. This does not include Emails or Conversations.
+.TP
+Folders
+Search for folders i.e. directories.
+.TP
+Documents
+Searches for general documents like word processing,
+spreadsheets or text files.
+.TP
+Emails
+Searches for Emails.
+.TP
+EmailAttachments
+Search es for Email attachments.
+.TP
+Music
+Search for music files.
+.TP
+Images
+Search for image files.
+.TP
+Videos
+Search for video files.
+.TP
+Text
+Search for text files, like word processing files, but e.g. not
+spreadsheets.
+.TP
+Development
+Search for development files. This includes make files,
+source code files and the like.
+.TP
+Other
+Search for documents which could not be put in one of the other services.
+.TP
+Applications
+Search for applications.
+
+.SH SEE ALSO
+.BR trackerd (1),
+.BR tracker-search (1),
+.BR tracker-search-tool (1),
+.BR tracker-query (1),
+.BR tracker-files (1).

Added: trunk/docs/tracker-stats.1
==============================================================================
--- (empty file)
+++ trunk/docs/tracker-stats.1	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,24 @@
+.TH tracker-stats 1 "July 2007" GNU "User Commands"
+
+.SH NAME
+tracker-stats\- command line tool that provides statistics on files
+indexed by
+.B trackerd 
+
+.SH SYNOPSIS
+.B tracker-stats [OPTIONS] 
+
+.SH DESCRIPTION
+.B tracker-stats
+returns statistics on files indexed by tracker indexing daemon, ordered
+by service type.
+
+.SH OPTIONS
+.TP
+\-?, --help
+Show a brief help message.
+
+.SH "SEE ALSO"
+.BR trackerd (1),
+.BR tracker-status (1),
+.BR tracker-services (7).

Added: trunk/docs/tracker-status.1
==============================================================================
--- (empty file)
+++ trunk/docs/tracker-status.1	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,17 @@
+.TH tracker-status 1 "July 2007" GNU "User Commands"
+
+.SH NAME
+tracker-status\- command line tool that reports the trackerd status
+
+.SH SYNOPSIS
+.B tracker-status
+
+.SH DESCRIPTION
+.B tracker-status
+Prints trackerd's status. Which can be one of the following:
+.PP
+Initializing, Watching, Indexing, Pending, Optimizing, Idle, Shutdown.
+
+.SH SEE ALSO
+.BR trackerd (1),
+.BR tracker-stats (1)

Added: trunk/docs/tracker-tag.1
==============================================================================
--- (empty file)
+++ trunk/docs/tracker-tag.1	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,45 @@
+.TH tracker-tag 1 "July, 2007" GNU "User Commands"
+
+.SH NAME
+tracker-tag \- command line tool for setting and searching tags/keywords
+
+.SH SYNOPSIS
+\fBtracker-tag\fR [\fIoptions\fR] \fIFILE\fR...
+
+.SH DESCRIPTION
+.B tracker-tag
+allows you to modify the tags that are associated with a file.
+The actual information is stored in a 
+.B tracker
+database.
+
+.SH OPTIONS
+.TP
+.B \-?, \-\-help
+Show summary of options.
+.TP
+.B \-a, \-\-add=TAG
+Add specified tag to a file.
+.TP
+.B \-r, \-\-remove=TAG
+Remove specified tag from a file.
+.TP
+.B \-R, \-\-remove-all
+Remove all tags associated with a file.
+.TP
+.B \-l, \-\-list
+List all tags associated with a file.
+.br
+If no file name is given, all known tags and their count is listed.
+.TP
+.B \-s, \-\-search=TAG
+Search for files matching the given tag.
+
+.SH SEE ALSO
+.BR trackerd (1).
+
+.SH AUTHOR
+tracker-tag was written by Jamie McCracken <jamiemcc gnome org>.
+.PP
+This manual page was written by Michael Biebl <biebl debian org>,
+for the Debian project (but may be used by others).

Added: trunk/docs/tracker-thumbnailer.1
==============================================================================
--- (empty file)
+++ trunk/docs/tracker-thumbnailer.1	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,48 @@
+.TH tracker-thumbnailer 1 "July, 2007" GNU "User Commands"
+
+.SH NAME
+tracker-thumbnailer \- create freedesktop.org compliant thumbnails
+
+.SH SYNOPSIS
+\fBtracker-thumbnailer\fR \fIFILE\fR \fIMIMETYPE\fR \fIICONSIZE\fR 
+
+.SH DESCRIPTION
+.B tracker-thumbnailer
+takes an image file as input and produces a thumbnail which is compliant
+to the freedesktop.org thumbnail specification.
+.BR
+It relies on external tools like \fBconvert\fR to do the 
+actual transformation.
+.PP
+.B tracker-thumbnailer
+is called by 
+.B trackerd
+to create thumbnails which are then used by frontends like
+.B tracker-search-tool
+to illustrate the search results.
+
+.SH OPTIONS
+.TP
+\fIFILE\fR
+Image file for which a thumbnail should be created.
+.TP
+\fIMIMETYPE\fR
+Mime type of the image/document, e.g. image/png or application/pdf.
+If no thumbnailer is installed for this mime type, \fBtracker-thumbnailer\fR
+will output a error message.
+.TP
+\fIICONSIZE\fR
+Size of the generated thumbnail.
+Currently supported is only \fBnormal\fR, which is 128x128 pixel.
+
+.SH SEE ALSO
+.BR trackerd (1),
+.BR tracker-search (1),
+.BR tracker-search-tool (1),
+.BR convert (1).
+
+.SH AUTHOR
+tracker-thumbnailer was written by Edward Duffy <eduffy gmail com>.
+.PP
+This manual page was written by Michael Biebl <biebl debian org>,
+for the Debian project (but may be used by others).

Added: trunk/docs/tracker.cfg.5
==============================================================================
--- (empty file)
+++ trunk/docs/tracker.cfg.5	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,206 @@
+.TH tracker.cfg 5 "July 2007" GNU "Configuration Files"
+
+.SH NAME
+tracker.cfg \- configuration file for the trackerd search daemon
+
+.SH DESCRIPTION
+The tracker.cfg file resides in the $HOME/.config/tracker directory.
+.PP
+The file is organized in sections, each section having a number of
+related options.
+.SS Comments
+Empty lines and those starting with ; or # are ignored.
+.SS Value Types
+.TP
+VALUE
+Denotes a positive integer.
+.TP
+BOOLEAN
+Denotes either true or false.
+.TP
+Any other value
+Any special keyword is to be replaced by one of the documented valid
+values, e.g. pathnames or language codes.
+
+.SH EXAMPLE
+.PP
+[General]
+.PP
+Verbosity=0
+.PP
+InitialSleep=50
+
+.SH Sections
+.SS General
+This section may contain general settings for trackerd.
+.TP
+Verbosity=VALUE
+Specifies how verbose trackerd should be logging. VALUE can be one of
+0 through 3.
+.RS
+.TP
+0
+Show no but error and critical output, this is the default behavior.
+.TP
+1
+Show minimal output, i.e. not just errors.
+.TP
+2
+Show verbose output.
+.TP
+3
+Show debugging output, enable this only if you know what you are doing.
+.RE
+.TP
+LowMemoryMode=BOOLEAN
+Enables low memory mode. Tracker may be slightly less fast at
+indexing/search but also needs less system memory.
+.TP
+InitialSleep=VALUE
+Sets the initial sleep time for trackerd. This is a number of seconds
+that trackerd wait before indexing.
+
+.SS Watches
+.TP
+WatchDirectoryRoots=DIRECTORY[;MORE;DIRECTORIES]
+Sets the directories trackerd should watch and index. Defaults to the
+users home directory.
+.TP
+NoWatchDirectory=DIRECTORY[;MORE;DIRECTORIES]
+Sets directories trackerd should ignore, i.e. not index or watch.
+.TP
+CrawlDirectory=DIRECTORY[;MORE;DIRECTORIES]
+Sets directories trackerd should index once but not watch for changes.
+.TP
+EnableWatching=BOOLEAN
+Enables or disables watching of directories, i.e. re-index files when
+they change.
+
+.SS Indexing
+This section may contain indexing related options.
+.TP
+Throttle=VALUE
+Sets the throttling value for the Indexer. VALUE can be any number from
+0 to 20 whereas higher values decrease indexing speed.
+.TP
+EnableIndexing=BOOLEAN
+Enable or disable the indexer.
+.TP
+EnableFileContentIndexing=BOOLEAN
+Enable or disable indexing of files text contents.
+.TP
+EnableThumbnails=BOOLEAN
+Enable or disable the generation of thumbnails.
+.TP
+NoIndexFileTypes=FILEGLOB[;MORE;FILEGLOBS...]
+List of partial file patterns (glob) separated by semicolons that specify
+files not to index. Only basic metadata (i.e. information retrieved by
+stat(2)) is indexed.
+.TP
+MinWordLength=VALUE
+Sets the minimum word length to index. Words having less then VALUE
+characters will not be indexed.
+.TP
+MaxWordLength=VALUE
+Sets the maximum word length to index. Words having more then VALUE
+characters will not be indexed.
+.TP
+Language=LANGCODE
+Sets the language specific stemmer and stopword list to use. Valid
+values are:
+.RS
+.TP
+en
+English
+.TP
+da
+Danish
+.TP
+nl
+dutch
+.TP
+fi
+Finnish
+.TP
+fr
+French
+.TP
+de
+German
+.TP
+it
+Italian
+.TP
+nb
+Norwegian
+.TP
+pt
+Portuguese
+.TP
+ru
+Russian
+.TP
+es
+Spanish
+.TP
+sv
+Swedish
+.RE
+.TP
+EnableStemmer=BOOLEAN
+Enable or disable the stemmer.
+.TP
+BatteryIndex=BOOLEAN
+Enable or disable indexing when on battery power.
+.TP
+BatteryIndexInitial=BOOLEAN
+Enable or disable initial index sweep (i.e. the very first indexing)
+when on battery power.
+
+.SS Emails
+This section may contain Email specific options.
+.TP
+IndexEvolutionEmails=BOOLEAN
+Enable or disable indexing for Evolution emails.
+.TP
+IndexModestEmails=BOOLEAN
+Enable or disable indexing for Modest emails.
+.TP
+IndexThunderbirdEmails=BOOLEAN
+Enable or disable indexing for Thunderbird emails.
+
+.SS Performance
+Trackerd performance related options.
+.TP
+MaxTextToIndex=VALUE
+Maximum size of text in bytes to index from a file's text contents.
+Defaults to 1048576.
+.TP
+MaxWordsToIndex=VALUE
+Maximum number of unique words to index from a file's text contents.
+Defaults to 10000.
+.TP
+OptimizationSweepCount=VALUE
+Specifies the no of entities to index before determining whether to
+perform index optimization. Default to 10000.
+.TP
+MaxBucketCount=VALUE
+Sets the maximum bucket count for the indexer. Defaults to 524288.
+.TP
+MinBucketCount=VALUE
+Sets the minimum bucket count. Defaults to 65536
+.TP
+Divisions=VALUE
+Sets no. of divisions of the index file. Default to 4.
+.TP
+BucketRatio=VALUE
+Selects the desired ratio of used records to buckets to be used when
+optimizing index (should be a value between 0 and 4). Defaults to 1.
+.TP
+Padding=VALUE
+Alters how much padding is used to prevent index relocations. Higher
+values improve indexing speed but waste more disk space. Value should
+be in range (1..8). Defaults to 2.
+
+.SH SEE ALSO
+.BR tracker(1)

Added: trunk/docs/trackerd.1
==============================================================================
--- (empty file)
+++ trunk/docs/trackerd.1	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,112 @@
+.TH trackerd 1 "July 2007" GNU "User Commands"
+
+.SH NAME
+trackerd \- indexer daemon for tracker search tool
+
+.SH SYNOPSIS
+.B trackerd [OPTIONS]
+
+.SH DESCRIPTION
+.B Trackerd
+provides both a powerful indexer and an extensible metadata database. It
+indexes files including the text contents and any available metadata to
+provide fast search services.
+.PP
+By default, trackerd will index all files in a user's home directory (with
+the exception of hidden files). This can be overridden by specifying
+one or more directories to be indexed instead in the configuration file
+($HOME/.config/tracker/tracker.cfg) and/or by using the --include-dir
+option to specify other directories to be indexed.
+
+.SH OPTIONS
+.TP
+\-?, --help
+A brief help message including some examples.
+
+.TP
+\-e, --exclude-dir=/PATH/DIR
+Specifies a directory to be excluded from indexing (including all
+subdirectories). Can be repeated to exclude more than one directory.
+
+.TP
+\-i, --include-dir=/PATH/DIR
+Specifies a directory to be included for indexing. All non-hidden
+subdirectories of the specified directory will also be indexed. If this
+param is set then the user's home directory is no longer indexed by
+default (you can still add the home directory with this option).
+
+.TP
+\-c, --crawl-dir=/PATH/DIR
+Specify directories to crawl. Crawling of directories means index every
+indexable content but do not watch for directory or file changes.
+
+.TP
+\-n, --no-indexing
+Prevent
+.B trackerd
+from indexing and watching. Searching is still
+possible.
+
+.TP
+\-v, --verbosity=value
+Makes trackerd log/print more information. Value can be one of 0 - show
+only errors, 1 - show only minimal output, 2 - show detailed output and
+3 - show debug output (don't do this, unless you know what you are doing).
+
+.TP
+\-t, --throttle=value
+Value to throttle indexing. Value must be in range 0-20, with lower
+values increasing indexing speed.
+
+.TP
+\-m, --low-memory
+Minimise memory usage. Note that this may slow down indexing.
+
+.TP
+\-s, --initial-sleep=value
+Set the initial sleep time in seconds. This is the time
+.B trackerd
+waits at startup before indexing.
+
+.TP
+\-l, --language=value
+Language to use for stemmer and stop words list (ISO 639-1 2 characters
+code).
+
+.TP
+\-R, --reindex
+This forces trackerd to re-index all watched files and directories.
+Since every watched directory and any previously indexed file has to be
+read and processed again, this may degrade the whole system performance,
+see also --throttle.
+
+.TP
+\-f, --fatal-errors
+Makes any errors fatal, i.e. forces abortion of
+.B trackerd.
+This switch is only useful for debugging.
+
+.SH FILES
+.I $HOME/.config/tracker/tracker.cfg
+.RS
+Trackerd user configuration file. See
+.BR tracker.cfg (5)
+and
+.BR tracker-preferences (1)
+for further details.
+
+.SH NOTES
+.B Trackerd
+needs a
+.BR dbus-daemon(1)
+to be running within your current session.
+
+.SH SEE ALSO
+.BR tracker-search-tool (1),
+.BR tracker-search (1),
+.BR tracker-tag (1),
+.BR tracker-stats (1),
+.BR tracer-query (1),
+.BR tracker-meta-folder (1),
+.BR tracker-files (1),
+.BR tracker-services (7).

Added: trunk/extensions/firefox-extension/Makefile
==============================================================================
--- (empty file)
+++ trunk/extensions/firefox-extension/Makefile	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,53 @@
+
+PROJECT = firefox-extension-xesam
+VERSION = 1.0.1
+
+all: $(PROJECT).xpi
+
+XPI_FILES = 			\
+	chrome.manifest		\
+	install.rdf		\
+	chrome/content/contents.rdf		\
+	chrome/content/beagleOverlay.js	\
+	chrome/content/beagleOverlay.xul	\
+	chrome/content/beaglePrefs.js		\
+	chrome/content/beaglePrefs.xul		\
+	chrome/content/beagleAddFilter.xul	\
+	chrome/content/beagleAddFilter.js	\
+	chrome/content/indexLink.xul	\
+	chrome/content/indexLink.js	\
+	chrome/content/indexBookmark.js	\
+	chrome/content/md5.js			\
+	chrome/content/utils.js	\
+	chrome/content/i18n.js	\
+	chrome/content/json.js	\
+	\
+	chrome/content/jslib/jslib.js		\
+	chrome/content/jslib/modules.js	\
+	chrome/content/jslib/debug/debug.js	\
+	chrome/content/jslib/io/dir.js		\
+	chrome/content/jslib/io/dirUtils.js	\
+	chrome/content/jslib/io/file.js	\
+	chrome/content/jslib/io/fileUtils.js	\
+	chrome/content/jslib/io/filesystem.js	\
+	\
+	chrome/locale/en-US/contents.rdf	\
+	chrome/locale/en-US/beagle.dtd		\
+	chrome/locale/en-US/beagle.properties	\
+	\
+	chrome/skin/classic/contents.rdf	\
+	chrome/skin/classic/beagle-big.png	\
+	chrome/skin/classic/beagle-disabled.png\
+	chrome/skin/classic/beagle-error.png	\
+	chrome/skin/classic/beagle.png\
+	chrome/skin/classic/overlay.css	
+
+$(PROJECT).xpi: $(XPI_FILES)
+	zip -q9 $@ $^
+
+
+EXTRA_DIST =			\
+	$(XPI_FILES)
+
+CLEANFILES =			\
+	$(PROJECT).xpi

Added: trunk/extensions/firefox-extension/README
==============================================================================
--- (empty file)
+++ trunk/extensions/firefox-extension/README	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,34 @@
+
+Beagle Extension: Index webpages you visit using the Beagle Indexing Engine.
+An Extension for the Firefox Browser.  
+
+Installing:
+	Open the generated beagle.xpi in Firefox or Mozilla.  
+	This will fire the Extension Manager.
+
+Features:
+	Uses beagle's IndexingService backend to index data.
+	Using domain/wildcard/regular expression to exclude/include the urls 
+	Import/export preferences 
+	Auto index when browsing (just click the status icon for enable/disable auto-indexing) 
+	    - Index referrer URL while indexing web-pages 
+	On-demand indexing 
+	    - Index current page 
+	    - Index link (for all the different filetypes beagle filters) 
+	    - Index image , the alt text is indexed. 
+	    - Store the URL of the current page while indexing, which may be useful while searching 
+	Index firefox bookmarks
+	    - Name, url, shortcut url(keywords),description and the path (name of ancestor folders is saved. 
+	    - Menu item bookmarks->Index The Modified Bookmarks 
+	    - Index the bookmarks when the window close ( you can turn it on/off in preference window) 
+	    - Last-index-date is saved . So only new bookmarks will be indexed. 
+	
+Debug:
+	You can set browser.dom.window.dump.enabled to true, in about:config. Open firefox in terminal, then you can see the debug information. You should also check javascript error console.
+
+Localization:
+	Add your localization files to chrome/locale/your-language/ 
+
+Visit http://beagle-project.org/Browser_Extension for details.
+This extension was written as a part of Google Summer of Code project, 2007 by Tao Fei (filia tao gmail com).
+

Added: trunk/extensions/firefox-extension/chrome.manifest
==============================================================================
--- (empty file)
+++ trunk/extensions/firefox-extension/chrome.manifest	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,6 @@
+content beagle  chrome/content/
+locale beagle en-US chrome/locale/en-US/
+skin   beagle classic/1.0 chrome/skin/classic/
+overlay chrome://browser/content/browser.xul chrome://beagle/content/beagleOverlay.xul
+overlay chrome://navigator/content/navigator.xul chrome://beagle/content/beagleOverlay.xul
+

Added: trunk/extensions/firefox-extension/chrome/content/beagleAddFilter.js
==============================================================================
--- (empty file)
+++ trunk/extensions/firefox-extension/chrome/content/beagleAddFilter.js	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,11 @@
+function beaglePrefsAddFilterClicked() {
+	var pattern = document.getElementById('beagle.add.filter.pattern').value;
+    var name = document.getElementById('beagle.add.filter.name').value;
+    var patternType = document.getElementById('beagle.add.filter.patterntype').selectedItem.value;
+	var type = window.arguments[0];
+	var elementId = 'beagle.'+type+'.list';
+	var listbox = window.opener.document.getElementById(elementId);
+    appendRow(listbox,name,pattern,patternType);
+}
+
+

Added: trunk/extensions/firefox-extension/chrome/content/beagleAddFilter.xul
==============================================================================
--- (empty file)
+++ trunk/extensions/firefox-extension/chrome/content/beagleAddFilter.xul	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,45 @@
+<?xml version="1.0"?>
+
+<!DOCTYPE overlay SYSTEM "chrome://beagle/locale/beagle.dtd">
+
+<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
+
+
+<dialog id="beagle.addPattern"
+  buttons="accept,cancel"
+  title="&beagle.pref.filter.add.dlg.title;"
+  ondialogaccept="beaglePrefsAddFilterClicked();"
+  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";>
+  
+<script type="application/x-javascript" 
+		src="chrome://beagle/content/utils.js" />
+<script type="application/x-javascript" 
+		src="chrome://beagle/content/beagleAddFilter.js" />
+
+<grid flex="1">
+	<columns>
+		<column/>
+		<column flex="1"/>
+	</columns>
+    <rows>
+		<row>
+            <label>&beagle.pref.filter.name.label;</label><textbox id="beagle.add.filter.name" />
+        </row>
+        <row>
+			<label>&beagle.pref.filter.patterntype.label;</label>
+            <menulist id="beagle.add.filter.patterntype">
+                <menupopup>
+                    <menuitem value="domain" label="domain" />
+                    <menuitem value="wildcard"  label="wildcard"/>
+                    <menuitem value="regular expression" label="regular expression"/>
+                </menupopup>
+            </menulist>
+        </row>
+		<row>
+			<label>&beagle.pref.filter.pattern.label;</label><textbox id="beagle.add.filter.pattern" />
+		</row>
+	</rows>
+</grid>
+
+</dialog> 
+

Added: trunk/extensions/firefox-extension/chrome/content/beagleOverlay.js
==============================================================================
--- (empty file)
+++ trunk/extensions/firefox-extension/chrome/content/beagleOverlay.js	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,709 @@
+/*
+ * Beagle Extension: Index webpages you visit using the Beagle Indexing Engine.
+ * An Extension for the Firefox  Browser.
+ */
+
+var beagle = {
+    //some constant 
+    RUN_BEAGLE_NOT_FOUND:-1,
+    RUN_INITED: 0,
+    RUN_ENABLED : 1,
+    RUN_DISABLED : 2,
+    RUN_ERROR : 3,
+    
+    pref : beaglePref,
+
+    ENV:Components.classes["@mozilla.org/process/environment;1"].getService(Components.interfaces.nsIEnvironment),
+
+    FILE_UTILS:new FileUtils(),// js lib  file utils
+    
+    /**
+     * see http://www.xulplanet.com/references/xpcomref/comps/c_embeddingbrowsernsWebBrowserPersist1.html
+     */
+    get PersistMask(){
+        var comp = Components.classes["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"].createInstance(Components.interfaces.nsIWebBrowserPersist);
+        return (comp.PERSIST_FLAGS_FROM_CACHE | 
+                comp.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
+                comp.PERSIST_FLAGS_NO_BASE_TAG_MODIFICATIONS |
+                comp.PERSIST_FLAGS_DONT_FIXUP_LINKS |
+                comp.PERSIST_FLAGS_DONT_CHANGE_FILENAMES |
+                comp.PERSIST_FLAGS_CLEANUP_ON_FAILURE);
+    },
+
+    get EncodeMask(){
+        var comp = Components.classes["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"].createInstance(Components.interfaces.nsIWebBrowserPersist);
+        return (comp.ENCODE_FLAGS_RAW | comp.ENCODE_FLAGS_ABSOLUTE_LINKS);
+    },
+
+    get STATUS_ICON(){ 
+        return document.getElementById('beagle-notifier-status');
+    },
+    /**
+     * beagle data path (should be ~/.beagle/ToIndex)
+     */
+    dataPath : null,
+    
+    
+    /**
+     * the path to beagle search ,it is used for search (for link/page/text)
+     */
+    get beagleSearchPath() { 
+        var path = this.ENV.get("PATH");
+        if (path) {
+            var split = path.split(':');
+            var idx = 0;
+            while (idx < split.length) 
+            {
+                var trypath = split[idx++] + '/' + "beagle-search";
+                if (this.FILE_UTILS.exists(trypath))
+                    return trypath;
+            }
+        }
+        return undefined;
+    },
+    
+    /**
+     * save the tasks for the purpose of extra meta data
+     */ 
+    tasks: [],
+   
+    /**
+     * always call this before any index work begins 
+     */
+    startTask : function(url,extrameta)
+    {
+        this.tasks[url] = {meta:extrameta};
+    },
+
+    /**
+     * get the content file path for a give url and type 
+     * type can be "web" or "bookmark"
+     */
+    getContentPath: function(url,type)
+    {
+        if(typeof type == "undefined")
+            type = "web";
+        var hash = hex_md5(url);
+        return this.dataPath + "/firefox-xesam-"+ type + "-" + hash;
+        
+    },
+
+    /**
+     * get the meta file path for a give url and type 
+     * type can be "web" or "bookmark"
+     */
+    getMetaPath: function(url,type)
+    {
+        if(typeof type == "undefined")
+            type = "web";
+        var hash = hex_md5(url);
+        return this.dataPath + "/.firefox-xesam-"+ type + "-" + hash;
+    },
+
+    /**
+     * init beagle
+     * check environment
+     * init state
+     * init some varible
+     * add event listeners 
+     * import pref from oldExtension (if beagle.first.run)
+     */
+    init : function()
+    {
+        log("init");
+        if(!this.checkEnv())
+        {
+            this.runStatus = this.RUN_BEAGLE_NOT_FOUND;
+            this.error(_("beagle_not_found"));
+        }
+        else
+        {
+            if(this.pref.get('beagle.autoindex.active'))
+            {
+                this.enable();
+            }
+            else
+            {
+                this.disable();
+            }
+            this.dataPath = this.ENV.get("HOME") + "/.xesam/Firefox/ToIndex";  
+            if(!this.FILE_UTILS.exists(this.dataPath))
+                ;// do something here ? is it safe to create the dir ?
+        }
+        this.addEventListener();
+        if (this.pref.get("beagle.first.run"))
+        {
+            this.pref.firstRunImport();
+            this.pref.set("beagle.first.run",false);
+        }
+    },
+    
+    /**
+     * add the event listeners
+     * 
+     */
+    addEventListener : function ()
+    {
+        // Add listener for page loads
+        if (this.runStatus != this.RUN_BEAGLE_NOT_FOUND && document.getElementById("appcontent"))
+        {
+            document.getElementById("appcontent").addEventListener(
+                "load",
+                Function.bind(this.onPageLoad,this),
+                true
+            );
+            document.getElementById("contentAreaContextMenu").addEventListener(
+                "popupshowing", 
+                Function.bind(this.initContextMenu,this), 
+                false
+            );
+        }
+        if(this.runStatus != this.RUN_BEAGLE_NOT_FOUND)
+        {
+            window.addEventListener(
+                "unload",
+                function(){
+                    if(beaglePref.get("beagle.bookmark.active"))
+                        bookmarkIndexer.indexModified(false);
+                },
+                false
+            );
+/*            var observerService =
+                 Components.classes["@mozilla.org/observer-service;1"]
+                    .getService(Components.interfaces.nsIObserverService)
+            var observer = {
+                observe: function(subject,topic,data){
+                    log("index bookmarks when exit");
+                    dump(bookmarkIndexer.indexModified());
+                    log(" done r");
+                }
+            };
+            observerService.addObserver(observer,"quit-application",false);
+*/
+        }
+        this.STATUS_ICON.addEventListener(
+            'click',
+            Function.bind(this.onIconClick,this),
+            false
+        );
+    },
+
+    /**
+     * show the beagle context menu
+     */
+    initContextMenu : function (e)
+    {
+        if(e.originalTarget.id != "contentAreaContextMenu")
+            return;
+        //log(" gContextMenu " + gContextMenu );
+        gContextMenu.showItem("beagle-context-index-this-link", gContextMenu.onLink && !gContextMenu.onMailtoLink); 
+        gContextMenu.showItem("beagle-context-index-this-image", gContextMenu.onImage && gContextMenu.onLoadedImage); 
+        gContextMenu.showItem("beagle-context-search-link", gContextMenu.onLink); 
+        gContextMenu.showItem("beagle-context-search-text", gContextMenu.isTextSelected);
+        document.getElementById("beagle-context-search-text").setAttribute("label",
+            _f("beagle_context_search_text",[getBrowserSelection(16)]));
+    },
+
+    /**
+     *  check enviroment 
+     */
+    checkEnv : function()
+    {
+        var index_dir = this.ENV.get("HOME") + "/.xesam/Firefox/ToIndex";
+        try {
+            // Check if destination directory exists
+            var dir = Components.classes ["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
+            dir.initWithPath (index_dir);
+            if (dir.exists ()) {
+                if (!dir.isDirectory ()) {
+                    alert("Destination directory exists but is not a directory!");
+                    return false;
+                }
+            }
+            else {
+                 //alert("we need to create it");
+                 dir.create (Components.interfaces.nsIFile.DIRECTORY_TYPE, 0755);
+            } 
+        }
+        catch (ex) {
+                 return false;
+        } 
+        return true;
+
+    },
+
+    /**
+     * Check the page, 
+     * 1. the protocal (We will NOT index about:* file:///* )
+     * 2. check is the page  itself 
+     * TODO: for file:/// chrome:// and so on Index OR NOT
+     */
+    checkPage : function(page)
+    {
+        if (!page)
+        {
+            log("[checkPage the page doesn't seems to be a page");
+            return false;
+        } 
+        if (!page.location ||
+            !page.location.href ||
+            page.location.href.indexOf("about:") == 0 ||
+            page.location.href.indexOf("file:") == 0 )
+        {
+            log("checkPage  strage page " + page );
+            return false;
+        }
+        return true;
+    },
+
+    /*
+     * check weather the url should index
+     * return true if it need to index
+     */
+    shouldIndex : function(page)
+    {
+
+        var prefObject = this.pref.load();
+        
+        //check https
+        if (page.location.protocol == "https:" && !prefObject['beagle.security.active'])
+        {
+            return false;
+        }
+        var lists = ['beagle.exclude.list','beagle.include.list'];
+        var flags = [false,false];
+        for(var j = 0; j < 2; j++)
+        {
+            var list = parseJSON(prefObject[lists[j]]);
+            var len = list.length;
+            var flag = false;
+            for(var i = 0; i < len && !flag; i++)
+            {
+                switch(list[i]['patternType'])
+                {
+                case 'domain':
+                    //what means a domain matches
+                    //www.google.com matches google.com and matches .com
+                    //www.agoogle.com NOT matches google.com but matches com
+                    //www.com.google. NOT matches .com 
+                    var hostname = page.location.hostname;
+                    var pattern = list[i]['pattern'];
+                    if (pattern[0] != '.')
+                        pattern = "." + pattern;
+                    flag = hostname.isEndWith(pattern) ||(hostname == list[i]['pattern']);
+                    break;
+                case 'wildcard':
+                    var re =  RegExp(list[i]['pattern'].wilcard2RE());
+                    flag = (page.location.href.match(re) != null);
+                    break;
+                case 'regular expression':
+                    var re = RegExp(list[i]['pattern']);
+                    flag = (page.location.href.match(re) != null)
+                    break;
+                default:
+                    log("invaild rule" + list[i]); 
+                    //something wrong;
+                    break;
+                }
+            }
+            flags[j] = flag;
+        }
+        log("[Should Index ?][exclude=" + flags[0] + "][include=" + flags[1] + "]");
+        if(!flags[0] && !flags[1])
+            return prefObject['beagle.default.action'] == 1;
+        if(flags[0] && flags[1])
+            return prefObject['beagle.conflict.action'] == 1;
+        return flags[1];
+
+    },
+    
+    /**
+     * just set the status label 
+     */
+    setStatusLabel : function (msg)
+    {
+        setTimeout(
+            function(){document.getElementById('statusbar-display').label = msg},
+            100
+        );
+    },
+    
+    /**
+     * prompt extra keywords on demand-index
+     */
+    promptExtraKeywords : function(url)
+    {
+        if(this.pref.get("beagle.prompt.keywords.active"))
+        {
+            var prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
+                                    .getService(Components.interfaces.nsIPromptService);
+            var input = { value: "" };
+            var chk = { value:false };
+            result = prompts.prompt(window, 
+                _("beagle_prompt_keywords_title"),
+                _("beagle_prompt_keywords_text"), input, null, chk);
+            if (result && input.value != "")
+            {
+                this.tasks[url]["meta"].push("t:dc:keyword="+ input.value);
+            }
+        }
+    },
+
+    /***************************************************
+     *        IO related code 
+     **************************************************/
+
+    /**
+     * write page content (NOT the HTML source, 
+     * the DOM instead, it may include dym contnent created by js)
+     */
+    writeContent : function(page, tmpfilepath)
+    {
+        var tmpfile = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
+        tmpfile.initWithPath(tmpfilepath);
+
+        var persist = Components.classes["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"].createInstance(Components.interfaces.nsIWebBrowserPersist);
+        persist.persistFlags = this.PersistMask;
+        persist.saveDocument(page, tmpfile, null, null, this.EncodeMask, 0);
+    },
+
+    /**
+     * for non-html and non-text file . save it 
+     * progressListener is used by index-link. to show the progree.
+     */
+    saveFile : function(url,path,progressListener)
+    {
+        var tmpfile = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
+        tmpfile.initWithPath(path);
+        var cacheKey  = Components.classes['@mozilla.org/supports-string;1'].createInstance(Components.interfaces.nsISupportsString);
+        cacheKey.data = url;
+        var urifix  = Components.classes['@mozilla.org/docshell/urifixup;1'].getService(Components.interfaces.nsIURIFixup);
+        var uri     = urifix.createFixupURI(url, 0);
+        var hosturi = null;
+        if (uri.host.length > 0)
+        {
+            hosturi = urifix.createFixupURI(uri.host, 0);
+        }
+        this.persist = Components.classes['@mozilla.org/embedding/browser/nsWebBrowserPersist;1'].createInstance(Components.interfaces.nsIWebBrowserPersist);
+        this.persist.persistFlags = this.PersistMask;
+        if(progressListener)
+            this.persist.progressListener = progressListener; 
+        this.persist.saveURI(uri, cacheKey, hosturi, null, null, tmpfile);
+    },
+
+    /**
+     * write raw-meatadata 
+     */
+    writeRawMetadata : function(meta, tmpfilepath)
+    {
+        var tmpfile = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
+        tmpfile.initWithPath(tmpfilepath);
+
+        var stream = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
+        stream.QueryInterface(Components.interfaces.nsIOutputStream);
+        stream.init(tmpfile, 0x04 | 0x08 | 0x20, 0600, 0);
+
+        var line;
+        log("writing metas ");
+        for(var i = 0; i < meta.length; i++)
+        {
+            line = meta[i] + "\n";
+            log(meta[i]);
+            stream.write(line, line.length);
+        }
+        stream.flush();
+        stream.close();
+    },
+
+
+    /**
+     * write meatadata of page  
+     * include URI hittype mimetype characterset referrer 
+     * if any extra meta is set in task, write it too.
+     */
+    writeMetadata : function(page, tmpfilepath)
+    {
+        var url = page.location.href;
+        var meta = [
+            url,
+            'WebHistory',
+            page.contentType,
+            "k:_unindexed:encoding="+page.characterSet,
+            ];
+        if(typeof page.referrer != "undefined" && page.referrer != "")
+        {
+            meta.push("t:fixme:referrer=" + page.referrer);
+        }
+        meta = meta.concat(this.tasks[url]['meta'])
+        beagle.writeRawMetadata(meta,tmpfilepath);
+    },
+
+    /**
+     * index a page 
+     * write content and meta
+     */
+    indexPage : function(page)
+    {
+        var url = page.location.href;
+        log(" We will index " + url ); 
+        
+        try {
+            this.writeContent(page, this.getContentPath(url));
+            this.writeMetadata(page, this.getMetaPath(url));
+        } catch (ex) {
+            log ("beaglePageLoad: beagleWriteContent/Metadata failed: " + ex );
+            if(confirm(_('beagle_write_error_confirm')))
+                this.disable();
+            return;
+        }
+        this.setStatusLabel(_f("beagle_statuslabel_indexing",[url]));
+    },
+    
+    /**
+     * index a file (non-html and non-text) 
+     * we assume the content is saved already.
+     * just write meta here
+     */
+    indexFile : function(url,contentType)
+    {
+        log(" We will index " + url ); 
+        
+        var meta = [url,'WebHistory',contentType];
+        meta = meta.concat(this.tasks[url]['meta'])
+        try {
+            this.writeRawMetadata(meta, this.getMetaPath(url));
+        } catch (ex) {
+            log ("[indexFile] beage write Metadata failed: " + ex + "\n");
+            if(confirm(_('beagle_write_error_confirm')))
+                this.disable();
+            return;
+        }
+        this.setStatusLabel(_f("beagle_statuslabel_indexing",[url]));
+    },
+    
+    /****************************************************************************
+     *                      Event Handlers 
+     ***************************************************************************/
+
+    /**
+     * index this page (event handler)
+     */
+    indexThisPage : function()
+    {
+        var doc = document.getElementById('content').selectedBrowser.contentDocument;
+        if(!this.checkPage(doc))
+            return;
+        var url = doc.location.href;
+        this.startTask(url,[]);
+        this.promptExtraKeywords(url);
+        if(doc.contentType.match(/(text|html|xml)/i))// a document
+        {
+            this.indexPage(doc);
+        }
+        else
+        {
+            this.saveFile(url,this.getContentPath(url),null);
+            this.indexFile(url,doc.contentType);
+        }
+    },
+
+    /**
+     * index link (event handler)
+     */
+    indexLink : function()
+    {
+        var url = gContextMenu.linkURL; 
+        if (!url)
+            return;
+        var referrer = gBrowser.currentURI.spec;
+        this.startTask(url,
+            ["t:fixme:referrer=" + referrer]
+            );
+        window.openDialog("chrome://beagle/content/indexLink.xul",
+            "","chrome,centerscreen,all,resizable,dialog=no",url,referrer);
+    },
+    /**
+     * index image (event handler)
+     */
+    indexImage : function()
+    {
+        var image = gContextMenu.target; 
+        if(image.tagName.toLowerCase() != 'img' || !image.src)
+            return;
+        var url = image.src;
+        var referrer = gBrowser.currentURI.spec;
+        this.startTask(url,[
+                "t:alttext="+(image.getAttribute('alt')?image.getAttribute('alt'):""),
+                "t:fixme:referrer="+referrer]
+                );
+        window.openDialog("chrome://beagle/content/indexLink.xul",
+            "","chrome,centerscreen,all,resizable,dialog=no",url,referrer);
+    },
+
+    /**
+     * callback for link loaded  (called from indexLink.js)
+     * TODO: what if the url is no longer the url we passed to indexLink
+     */
+    onLinkLoad : function(url,contentType,doc,orginalURL)
+    {   
+        if(url != orginalURL)
+        {
+            log(url)
+            log(orginalURL);
+            this.tasks[url] = this.tasks[orginalURL];
+        }
+        this.promptExtraKeywords(url);
+        if(contentType.match(/(text|html|xml)/i) && doc)// a document
+        {
+            if(!this.checkPage(doc))
+                return;
+            this.indexPage(doc);
+        }
+        else
+        {
+            this.indexFile(url,contentType);
+        }
+    },
+
+    /**
+     * called when page is loaded (event handler)
+     */
+    onPageLoad : function(event)
+    { 
+        log("Page Loaded ");
+        //if disabled or error
+        if(this.runStatus != this.RUN_ENABLED)
+        {
+            log(" NOT RUN_ENABLED status .  NO INDEX");
+            return;
+        }
+        var page = event.originalTarget;
+        if (!this.checkPage(page))
+            return;
+        if (!this.shouldIndex(page))
+            return;
+        var url = page.location.href;
+        this.startTask(url,[]);
+        if(page.contentType.match(/(text|html|xml)/i))// a document
+        {
+            this.indexPage(page);
+        }
+        else
+        {
+            this.saveFile(url,this.getContentPath(url),null);
+            this.indexFile(url,page.contentType);
+        }
+    },   
+    
+        
+    /**
+     * add exclude /include rule
+     * the "domain" rule
+     */
+    quickAddRule : function (page,flag)
+    {
+        try{
+            var domain =  page.location.hostname;
+            this.pref.addRule("qa_" + domain,domain,"domain",flag);
+        }
+        catch(e){
+            alert(_("beagle_quick_add_rule_error"));
+        }
+    },
+
+    /**
+     * show preference winodw (event handler)
+     */
+    showPrefs : function()
+    {
+        window.openDialog('chrome://beagle/content/beaglePrefs.xul',
+                'PrefWindow',
+                'chrome,resizable=no',
+                'browser');
+    },
+
+    /**
+     * status icon clicked. (event handler)
+     * toggle auto-index
+     */
+    onIconClick : function(event)
+    {
+       // Left-click event (also single click, like Mac).
+        if (event.button == 0 && event.ctrlKey == 0) {
+            switch(this.runStatus)
+            {
+            case this.RUN_ENABLED:
+                // currently enabled. disable 
+                this.disable();
+                break;
+            case this.RUN_DISABLED:
+                // currently disabled  enable.
+                this.enable();
+                break;
+            default:
+                // last run was an error, show the error
+                alert("Error running Beagle Indexer: " + this.RunStatus);
+                break;
+            }
+        }
+    },
+
+    /**
+     * call beagle search by query 
+     */
+    search : function(query)
+    {
+        if(!this.beagleSearchPath)
+            return;
+        try {
+            log("Running beagle search with query: "+ query );
+            var retval = this.FILE_UTILS.spawn(this.beagleSearchPath, ["", query]);
+            if (retval) 
+                alert("Error running beagle search: " + retval);
+        } 
+        catch(e){
+                alert("Caught error from beagle-search: " + e);
+        }
+    },
+    
+    /************************************************************
+     *  status switch functions
+     ************************************************************/
+    
+    /**
+     * disable beagle auto index
+     */
+    disable : function()
+    {
+        this.runStatus = this.RUN_DISABLED;
+        this.STATUS_ICON.setAttribute("status","00f");
+        this.STATUS_ICON.setAttribute("tooltiptext",_("beagle_tooltip_disabled"));
+        this.pref.set("beagle.autoindex.active",false);
+    },
+
+    /**
+     * enable beagle auto index
+     */
+    enable : function()
+    {
+        this.runStatus = this.RUN_ENABLED;
+        this.STATUS_ICON.setAttribute("status","000");
+        this.STATUS_ICON.setAttribute("tooltiptext",_("beagle_tooltip_actived"));
+        this.pref.set("beagle.autoindex.active",true);
+    },
+    
+    /**
+     * error occours
+     */
+    error : function(msg)
+    {
+        this.runStatus = this.RUN_ERROR;
+        this.STATUS_ICON.setAttribute("status","f00");
+        this.STATUS_ICON.setAttribute("tooltiptext",_f("beagle_tooltip_error",[msg]));
+        this.pref.set("beagle.autoindex.active",false);
+    },
+};
+
+// Create event listener.
+window.addEventListener('load', Function.bind(beagle.init,beagle),false); 
+

Added: trunk/extensions/firefox-extension/chrome/content/beagleOverlay.xul
==============================================================================
--- (empty file)
+++ trunk/extensions/firefox-extension/chrome/content/beagleOverlay.xul	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,125 @@
+<?xml version="1.0"?>
+
+<?xml-stylesheet href="chrome://beagle/skin/overlay.css" type="text/css"?>
+<!DOCTYPE overlay SYSTEM "chrome://beagle/locale/beagle.dtd">
+
+<overlay id="BeagleOverlay"
+         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";>
+         
+<stringbundleset id="stringbundleset">
+    <stringbundle id="beagleStrings"
+            src="chrome://beagle/locale/beagle.properties"/>
+</stringbundleset>
+
+
+
+<!-- jslib sources used for executing beagle -->
+<script type="text/javascript" src="jslib/jslib.js"/>
+<script type="text/javascript" src="jslib/io/file.js"/>
+<script type="text/javascript" src="jslib/io/fileUtils.js"/>
+<script type="text/javascript" src="jslib/io/dir.js"/>
+<script type="text/javascript" src="jslib/io/dirUtils.js"/>
+
+<script type="text/javascript" src="utils.js" />
+<script type="text/javascript" src="i18n.js"/>
+<script type="text/javascript" src="md5.js" />
+<script type="text/javascript" src="json.js" />
+<script type="text/javascript" src="beaglePrefs.js" />
+<script type="text/javascript" src="beagleOverlay.js" />
+<script type="text/javascript" src="indexBookmark.js" />
+
+<commandset id="mainCommandSet">
+    <command id="IndexThisCmd" label="&beagle.run.index.this.label;" 
+        oncommand="beagle.indexThisPage();"/>
+    <command id="AlwaysIndexThisSiteCmd" label="&beagle.run.always.index.label;"  
+        oncommand="beagle.quickAddRule(document.getElementById('content').selectedBrowser.contentDocument,beaglePref.RULE_INCLUDE)"/>
+    <command id="NeverIndexThisSiteCmd" label="&beagle.run.never.index.label;" 
+        oncommand="beagle.quickAddRule(document.getElementById('content').selectedBrowser.contentDocument,beaglePref.RULE_EXCLUDE)"/>
+    <command id="ShowPrefsCmd" label="&beagle.run.preferences.label;" 
+            oncommand="beagle.showPrefs()"/>
+    <command id="IndexThisLinkCmd" label="&beagle.run.index.link.label;" 
+        oncommand="beagle.indexLink()"/>
+    <command id="IndexThisImageCmd" label="&beagle.run.index.image.label;" 
+        oncommand="beagle.indexImage()"/>
+    <command id="SearchLinkCmd" label="&beagle.context.search.link.label;"
+        oncommand="beagle.search(gContextMenu.linkURL);"/>
+    <command id="SearchPageCmd" label="&beagle.context.search.page.label;"
+        oncommand="beagle.search(getWebNavigation().currentURI.spec);"/>
+    <command id="SearchTextCmd" 
+        oncommand="beagle.search(getBrowserSelection());"/>
+    <!--
+    <command id="IndexAllBookmarksCmd" label="&beagle.bookmark.index.all.label;"
+        oncommand="BookmarkIndexer.indexAll();"/>
+    -->
+    <command id="IndexModifiedBookmarkCmd" label="&beagle.bookmark.index.modified.label;"
+        oncommand="bookmarkIndexer.indexModified(true);"/>
+</commandset>
+
+<!-- toolbar is not necessary -->
+<!--
+<toolbarpalette id="BrowserToolbarPalette">
+    <toolbarbutton id="beagle-button" type="menu-button"
+                   class="toolbarbutton-1 chromeclass-toolbar-additional"
+                   context="beagle-toolbar-popup"
+                   tooltiptext="&beagle.run.index.this.label;"
+                   label="beagle">
+        <menupopup id="beagle-toolbar-popup" >
+            <menuitem  command="IndexThisCmd"/>
+            <menuseparator />
+            <menuitem  command="AlwaysIndexThisSiteCmd"/> 
+            <menuitem  command="NeverIndexThisSiteCmd"/> 
+            <menuseparator />
+            <menuitem  command="ShowPrefsCmd"/> 
+        </menupopup>
+    </toolbarbutton>
+</toolbarpalette>
+-->
+
+<!--  Add  Index Bookmark menuitem in Bookmarks Menu -->
+<!--  position=4 to place item after Bookmark Manager which is normally 3 with Firefox 1.5-->
+<menupopup id="menu_BookmarksPopup" contextmenu='bookmarks-context-menu'>
+    <menuitem id="beagle-index-modified-bookmarks"
+	command="IndexModifiedBookmarkCmd"/>
+</menupopup>
+
+
+<popup id="contentAreaContextMenu">
+    <menuseparator />
+    <menu label="&beagle.run.context.menu.label;">
+        <menupopup id="beagle-content-popup">
+            <menuitem  id="beagle-context-index-this-link" command="IndexThisLinkCmd"/>
+            <menuitem  id="beagle-context-index-this-image" command="IndexThisImageCmd"/>
+            <menuitem  id="beagle-context-index-this" command="IndexThisCmd"/>
+            <menuseparator />
+            <menuitem id="beagle-context-search-link" command="SearchLinkCmd"/>
+            <menuitem id="beagle-context-search-text" command="SearchTextCmd"/>
+            <menuitem id="beagle-context-search-page" command="SearchPageCmd"/>
+            <menuseparator />
+            <menuitem command="AlwaysIndexThisSiteCmd"/> 
+            <menuitem command="NeverIndexThisSiteCmd"/> 
+            <menuseparator />
+            <menuitem command="ShowPrefsCmd"/> 
+        </menupopup>
+    </menu>
+    <menuseparator />
+
+</popup>
+
+<statusbar id="status-bar">
+    <statusbarpanel
+        class="statusbarpanel-menu-iconic"
+        id="beagle-notifier-status"
+        context='beagle-status-popup'
+        status="000"
+        >
+    </statusbarpanel> 
+    <menupopup id="beagle-status-popup" position="before_end">
+        <menuitem command="IndexThisCmd"/>
+        <menuseparator />
+        <menuitem command="AlwaysIndexThisSiteCmd"/> 
+        <menuitem command="NeverIndexThisSiteCmd"/> 
+        <menuseparator />
+        <menuitem command="ShowPrefsCmd"/> 
+   </menupopup>
+</statusbar> 
+</overlay>

Added: trunk/extensions/firefox-extension/chrome/content/beaglePrefs.js
==============================================================================
--- (empty file)
+++ trunk/extensions/firefox-extension/chrome/content/beaglePrefs.js	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,324 @@
+/*
+ * Beagle Extension: Index webpages you visit using the Beagle Indexing Engine.
+ * An Extension for the Firefox Browser.
+ */
+
+// Initiate a new preference instance.
+var gPrefService = Components.classes['@mozilla.org/preferences-service;1'].getService(Components.interfaces.nsIPrefBranch);
+
+var beaglePref = {
+   
+    //some constant
+    RULE_INCLUDE : 1,
+    RULE_EXCLUDE : 2,
+
+    // Declare Pref Keys and Type.
+    prefKeys : { 
+      'beagle.security.active':{'type':'bool','default':false},
+      'beagle.bookmark.active':{'type':'bool','default':false},
+      'beagle.prompt.keywords.active':{'type':'bool','default':false},
+      'beagle.default.action':{'type':'int','default':1},
+      'beagle.conflict.action':{'type':'int','default':1},
+      'beagle.include.list':{'type':'string','default':"[]"},
+      'beagle.exclude.list':{'type':'string','default':"[]"},
+      'beagle.autoindex.active':{'type':'bool','default':true},
+      'beagle.bookmark.last.indexed.date':{'type':'string','default':'0'},
+      'beagle.first.run':{'type':'bool','default':true},
+    },
+
+   
+    //functions used to get/set pref
+    func_factory:{
+        'get':{
+            'bool': Function.bind(gPrefService.getBoolPref,gPrefService),
+            'int': Function.bind(gPrefService.getIntPref,gPrefService),
+            'string' : Function.bind(gPrefService.getCharPref,gPrefService)
+        },
+        'set':{
+            'bool': Function.bind(gPrefService.setBoolPref,gPrefService),
+            'int' : Function.bind(gPrefService.setIntPref,gPrefService),
+            'string' : Function.bind(gPrefService.setCharPref,gPrefService)
+        }
+    },
+
+    prefObject : {},
+    
+    /**
+     * get the pref value by key
+     * we will use right type according to prefKeys
+     */
+    get : function(key)
+    {
+        if(!this.prefKeys.hasOwnProperty(key))
+            return null;
+        try{
+            return this.func_factory['get'][this.prefKeys[key]['type']].call(null,key);
+        }
+        catch(ex){
+            log("[beaglPref.get " + key + "] " + ex );
+            return this.prefKeys[key]['default']; 
+        }
+    },
+
+    /**
+     * set pref value 
+     * we will use right type according to prefKeys
+     */
+    set : function(key,value)
+    {
+        if(!this.prefKeys.hasOwnProperty(key))
+            return false;
+        try{
+            this.func_factory['set'][this.prefKeys[key]['type']].call(null,key,value);
+            return true;
+        }
+        catch(ex){
+            return false; 
+        }
+        
+    },
+
+    /*
+     * Load Prefs into a javascript object (this.prefObject)
+     *
+     */
+    load : function()
+    {
+        //log(toJSONString(this.prefKeys));
+        
+        for(key in this.prefKeys)
+        {
+            if(!this.prefKeys.hasOwnProperty(key))
+                continue;
+            var value = this.get(key);
+            if(value != null)
+                this.prefObject[key] = value;
+            else
+                log(key + " is null" );
+        }
+        return this.prefObject;
+    },
+
+    /*
+     * Save Prefs (in this.prefObject) into firefox
+     */
+    save : function()
+    {
+        for(key in this.prefKeys)
+        {
+            this.set(key, this.prefObject[key]);
+        }
+        log("Save Beagle Prefs:" + toJSONString(this.prefObject) );
+    },
+
+    /**
+     * init beagle pref , load pref, init UI
+     */
+    init : function ()
+    {
+        log("beaglePref init");
+        this.load(); 
+        this.UIInit();
+    },
+
+    /**
+     *  init the UI
+     */
+    UIInit : function ()
+    {
+        log("beaglePref uiinit");
+        var checkboxElements = ["beagle.security.active","beagle.bookmark.active","beagle.prompt.keywords.active"]
+        for(var i = 0; i < checkboxElements.length; i++)
+        {
+            var elementID = checkboxElements[i];
+            try{
+                document.getElementById(elementID).checked = this.prefObject[elementID]
+             }
+            catch(ex){
+                log(ex);
+                document.getElementById(elementID).checked = true;
+             }
+        }
+
+        var radioElements = ["beagle.default.action","beagle.conflict.action"]
+        for(var i = 0; i < radioElements.length; i++)
+        {
+            var elementID = radioElements[i];
+            var radios = document.getElementById(elementID).getElementsByTagName('radio');
+            try{
+                for (var j = 0; j < radios.length; j++)
+                {
+                    if(radios[j].value == this.prefObject[elementID])
+                    {
+                        document.getElementById(elementID).selectedItem = radios[j]
+                        break;
+                    }
+                }
+            }
+            catch(ex){
+                log(ex);
+            }
+        }
+     
+        //beagle.include.list and beagle.exclude.list
+        var listElements = ["beagle.include.list","beagle.exclude.list"];
+        for (var i = 0; i < listElements.length; i++)
+        {
+            var elementID = listElements[i];
+            try{
+                var items = parseJSON(this.prefObject[elementID]); 
+                var listbox = document.getElementById(elementID) ;
+                //log("listbox.getRowCount:" + listbox.getRowCount() + '\n');
+                var num = listbox.getRowCount();
+                for (var j = 0; j < num; j++)
+                    listbox.removeItemAt(0);
+                
+                for (var j = 0; j < items.length; j++){
+                    appendRow(listbox,items[j]['name'],items[j]['pattern'],items[j]['patternType']);
+                 }
+            } catch(ex) {
+                log(ex);
+                log(this.prefObject[elementID]);
+            }
+        }
+     
+    },
+
+    /****************************************************************************
+     *                      Event Handlers 
+     ***************************************************************************/
+    
+    /*
+     *  This function is called when the save button is clicked
+     */
+    onSave : function ()
+    {
+        var prefs = {};
+        
+        var checkboxElements = ["beagle.security.active","beagle.bookmark.active","beagle.prompt.keywords.active"]
+        for(var i = 0; i < checkboxElements.length; i++)
+        {
+            var elementID = checkboxElements[i];
+            try{
+                prefs[elementID] = document.getElementById(elementID).checked;
+             }
+            catch(e){
+                prefs[elementID] = false;
+             }
+        }
+
+        var radioElements = ["beagle.default.action","beagle.conflict.action"]
+        for(var i = 0; i < radioElements.length; i++)
+        {
+            var elementID = radioElements[i];
+            try{
+                prefs[elementID] = document.getElementById(elementID).value;
+             }
+            catch(e){
+             }
+        }
+             
+        //beagle.include.list and beagle.exclude.list
+        var listElementIDs = ["beagle.include.list","beagle.exclude.list"];
+        for (var i = 0; i < listElementIDs.length; i++)
+        {
+            var elementID = listElementIDs[i];
+            try {
+                var items = new Array() ;
+                var listbox = document.getElementById(elementID) ;
+
+                for (var j = 0; j < listbox.getRowCount(); j++){
+                    var listitem =  listbox.getItemAtIndex(j);
+                    var name = listitem.getElementsByTagName('listcell')[0].getAttribute('value');
+                    var pattern = listitem.getElementsByTagName('listcell')[1].getAttribute('value');
+                    var patternType = listitem.getElementsByTagName('listcell')[2].getAttribute('value');
+                    items.push({'name':name,'pattern':pattern,'patternType':patternType});
+                }
+                var value = toJSONString(items);
+                prefs[elementID] = value;
+            } catch(e) {
+                // We don't seem to care about this.
+            }
+        }
+
+        this.prefObject = prefs;
+        this.save();   
+    },
+
+    /**
+     * open a dialog to add a filter 
+     */
+    onAddFilter : function (type)
+    {
+        window.openDialog(
+            'chrome://beagle/content/beagleAddFilter.xul',
+            "add_filter_dialog", 
+            'chrome,modal',
+            type
+        );
+    },
+
+    /**
+     *remove a filter
+     */
+    onRemoveFilter : function(type) 
+    {
+        try{
+            var listbox = document.getElementById('beagle.'+type+'.list');
+            listbox.removeItemAt(listbox.selectedIndex);
+        } catch(e){
+            //ignore
+        }
+    },
+
+    
+    /**
+     * Add Exclude / Include rule
+     * name the rule name
+     * pattern the pattern
+     * type the pattern type 
+     * flag  RULE_INCLUDE or RULE_EXCLUDE
+     */
+    addRule : function (name,pattern,type,flag)
+    {
+        switch(flag)
+        {
+        case this.RULE_INCLUDE:
+            key = "beagle.include.list";
+            break;
+        case this.RULE_EXCLUDE:
+            key = "beagle.exclude.list";
+            break;
+        default:
+            //error
+            return;
+        }
+        var rules = parseJSON(this.get(key));
+        rules.push({"name":name,"pattern":pattern,"patternType":type});
+        this.set(key,toJSONString(rules));
+    },
+
+    /**
+     * First Run import (from old extension)
+     */
+    firstRunImport : function()
+    {
+        try{
+            this.set("beagle.autoindex.active",gPrefService.getBoolPref("beagle.enabled"));
+            this.set("beagle.security.active",gPrefService.getBoolPref("beagle.security.active"));
+            var filters = gPrefService.getCharPref("beagle.security.filters").split(";");
+            var excludeList = parseJSON(this.get("beagle.exclude.list"));
+            for(var i = 0; i < filters.length; i++)
+            {
+                if(filters[i] != "")         
+                    excludeList.push({"name":"Import_"+i,"pattern":filters[i],"patternType":"domain"});
+            }
+            this.set("beagle.exclude.list",toJSONString(excludeList));
+        }
+        catch(ex){
+            log("first run import error");
+            log(ex);
+        }
+    },
+}
+

Added: trunk/extensions/firefox-extension/chrome/content/beaglePrefs.xul
==============================================================================
--- (empty file)
+++ trunk/extensions/firefox-extension/chrome/content/beaglePrefs.xul	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,141 @@
+<?xml version="1.0"?>
+
+<!DOCTYPE window SYSTEM "chrome://beagle/locale/beagle.dtd">
+
+<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
+
+<dialog id="beagle-pref"
+    buttons="accept,cancel,help" 
+    onload="beaglePref.init();" 
+    ondialogaccept="beaglePref.onSave();window.close();"
+    ondialoghelp="window.open('http://beagle-project.org/Browser_Extension#Firefox_Extension');"
+    persist="screenX screenY"
+    title="&beagle.pref.label;"
+    flex="1"
+    style="min-width:500px"
+    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";>
+    <stringbundleset id="stringbundleset">
+        <stringbundle id="beagleStrings"
+                src="chrome://beagle/locale/beagle.properties"/>
+    </stringbundleset>
+
+
+    <!-- jslib sources used for executing beagle -->
+    <script type="text/javascript" src="jslib/jslib.js"/>
+    <script type="text/javascript" src="jslib/io/file.js"/>
+    <script type="text/javascript" src="jslib/io/fileUtils.js"/>
+
+    <script type="text/javascript" src="utils.js" />
+
+    <script type="text/javascript" src="json.js" />
+      
+    <script type="text/javascript" src="i18n.js" />
+    
+    <script type="text/javascript" src="beaglePrefs.js" />
+    
+    
+    <groupbox>
+        <caption label="&beagle.pref.groupbox.general.label;" />
+        <!--checkbox 
+          id="beagle.context.active" 
+          label="&beagle.pref.context.active.label;"
+          type="checkbox" 
+        /-->
+
+        <checkbox 
+          id="beagle.security.active" 
+          label="&beagle.pref.security.active.label;"
+          type="checkbox" 
+          />
+        <checkbox 
+          id="beagle.bookmark.active" 
+          label="&beagle.pref.bookmark.active.label;"
+          type="checkbox" 
+          />
+         <checkbox 
+          id="beagle.prompt.keywords.active" 
+          label="&beagle.pref.prompt.keywords.active.label;"
+          type="checkbox" 
+          />
+        <hbox>
+            <label tooltiptext="&beagle.pref.default.action.tooltip;">&beagle.pref.default.action.label;:</label>
+            <radiogroup id="beagle.default.action" orient="horizontal">
+                <radio id="beagle.default.action.noindex" value="0" label="&beagle.pref.action.noindex.label;" />
+                <radio id="beagle.default.action.index" value="1" label="&beagle.pref.action.index.label;"/>
+            </radiogroup>
+        </hbox>
+        <hbox>
+            <label tooltiptext="&beagle.pref.conflict.action.tooltip;">&beagle.pref.conflict.action.label;:</label>
+            <radiogroup id="beagle.conflict.action" orient="horizontal">
+                <radio id="beagle.conflict.action.noindex" value="0" label="&beagle.pref.action.noindex.label;" />
+                <radio id="beagle.conflict.action.index" value="1" label="&beagle.pref.action.index.label;"/>
+            </radiogroup>
+        </hbox>
+
+    </groupbox>
+
+    <!--The Patterns to Include-->		  
+    <groupbox flex="1">
+        <caption label="&beagle.pref.groupbox.include.label;"/>
+        <hbox flex="1">
+        <vbox flex="1">
+        <listbox flex="1" rows="4" id="beagle.include.list">
+        <listhead>
+            <listheader id="includeName" label="&beagle.pref.filter.name.label;" />
+            <listheader id="includePattern" label="&beagle.pref.filter.pattern.label;" />
+            <listheader id="includePatternType" label="&beagle.pref.filter.patterntype.label;" />
+        </listhead>
+        <listcols>
+            <listcol />
+            <listcol flex="2"/>
+            <listcol flex="1"/>
+        </listcols>
+        </listbox>
+        </vbox>
+        <vbox>
+        <button command="AddIncludeFilterCmd" />
+        <button command="RemoveIncludeFilterCmd"  />
+        </vbox>
+        </hbox>
+    </groupbox>
+
+
+    <!--The Patterns to Exclude-->		  
+    <groupbox flex="1">
+        <caption label="&beagle.pref.groupbox.exclude.label;"/>	
+        <hbox flex="1">
+        <vbox flex="1">
+        <listbox flex="1" rows="4" id="beagle.exclude.list">
+        <listhead>
+            <listheader id="excludeName" label="&beagle.pref.filter.name.label;" />
+            <listheader id="excludePattern" label="&beagle.pref.filter.pattern.label;" />
+            <listheader id="excludePatternType" label="&beagle.pref.filter.patterntype.label;" />
+        </listhead>
+        <listcols>
+            <listcol />
+            <listcol flex="2"/>
+            <listcol flex="1"/>
+        </listcols>
+        </listbox>
+        </vbox>
+        <vbox>
+        <button command="AddExcludeFilterCmd" />
+        <button command="RemoveExcludeFilterCmd"  />
+        </vbox>
+        </hbox>
+    </groupbox>
+
+    <commandset>
+        <command id="AddIncludeFilterCmd" label="&beagle.pref.filter.add.label;" 
+            oncommand="beaglePref.onAddFilter('include')"/>
+        <command id="AddExcludeFilterCmd" label="&beagle.pref.filter.add.label;" 
+            oncommand="beaglePref.onAddFilter('exclude')"/>
+        <command id="RemoveIncludeFilterCmd" label="&beagle.pref.filter.remove.label;" 
+            oncommand="beaglePref.onRemoveFilter('include')"/>
+        <command id="RemoveExcludeFilterCmd" label="&beagle.pref.filter.remove.label;" 
+            oncommand="beaglePref.onRemoveFilter('exclude')"/>
+    </commandset>
+</dialog> 
+
+
+

Added: trunk/extensions/firefox-extension/chrome/content/contents.rdf
==============================================================================
--- (empty file)
+++ trunk/extensions/firefox-extension/chrome/content/contents.rdf	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"; 
+	 xmlns:chrome="http://www.mozilla.org/rdf/chrome#";>
+
+  <RDF:Seq RDF:about="urn:mozilla:package:root">
+    <RDF:li RDF:resource="urn:mozilla:package:beagle"/>
+  </RDF:Seq>
+    
+  <RDF:Seq RDF:about="urn:mozilla:overlays">
+    <RDF:li RDF:resource="chrome://browser/content/browser.xul"/>
+    <RDF:li RDF:resource="chrome://navigator/content/navigator.xul"/>
+  </RDF:Seq>
+     
+  <RDF:Seq RDF:about="chrome://browser/content/browser.xul">
+    <RDF:li>chrome://beagle/content/beagleOverlay.xul</RDF:li>
+  </RDF:Seq>
+  
+  <RDF:Seq about="chrome://navigator/content/navigator.xul">
+    <RDF:li>chrome://beagle/content/beagleOverlay.xul</RDF:li>
+  </RDF:Seq>  
+  
+  <RDF:Description 
+    RDF:about="urn:mozilla:package:beagle"
+    chrome:displayName="Beagle Indexer 0.2"
+    chrome:author="Filia Tao"
+    chrome:authorURL="mailto:filia tao gmail com"
+    chrome:name="beagle"
+    chrome:extension="true"
+    chrome:description="Index webpages you visit using the Beagle Indexing Engine."
+    chrome:settingsURL="chrome://beagle/content/beaglePrefs.xul">
+  </RDF:Description>
+
+</RDF:RDF>

Added: trunk/extensions/firefox-extension/chrome/content/i18n.js
==============================================================================
--- (empty file)
+++ trunk/extensions/firefox-extension/chrome/content/i18n.js	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,54 @@
+//check the string budles
+var bundles  = new Array();
+
+function gettext(key)
+{
+    //dump("\ncallled gettext  bundles.length = " + bundles.length + "\n");
+    var ret = null;
+    for(var i = 0 ; i < bundles.length; i++)
+    {
+        try{
+            ret = bundles[i].getString(key);
+        }
+        catch(e){dump(e); }
+        if (ret != null)
+            break;  
+    }
+    //not found , just return the  orginal string 
+    if (ret == null) 
+        ret = key;
+    //dump(ret + '\n');
+    return ret;
+}
+
+var _ = gettext;
+
+function getformatedtext(key,subs)
+{
+    var ret = null;
+    for(var i = 0 ; i < bundles.length; i++)
+    {
+        try{
+            ret = bundles[i].getString(key);
+            if (ret != null)
+            {
+                return bundles[i].getFormattedString(key,subs);
+            }
+        }
+        catch(e){}
+    }
+    //not found , just return the   orginal string 
+    if (ret == null) 
+        ret = key;
+    return ret;
+}
+
+var _f = getformatedtext;
+
+function initBundles(){
+    bundles.push(document.getElementById('beagleStrings'));
+}
+
+window.addEventListener('load', initBundles, false); 
+
+

Added: trunk/extensions/firefox-extension/chrome/content/indexBookmark.js
==============================================================================
--- (empty file)
+++ trunk/extensions/firefox-extension/chrome/content/indexBookmark.js	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,207 @@
+/**
+* index bookmarks.
+* Include URL,name,shorcurURL (the keywords), description
+* After index, a last-indexed-date is saved.
+* Later only index the modified bookmark or new bookmarks.
+*/
+
+
+//Used to include only one time bookmark.js and avoid error message about already specified constant
+try{
+    if(ADD_BM_DIALOG_FEATURES) {}
+} catch(e){
+    var loader =  Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+		    .getService(Components.interfaces.mozIJSSubScriptLoader);
+    loader.loadSubScript("chrome://browser/content/bookmarks/bookmarks.js");
+}
+if(!BMDS)
+{
+    //init bookmark js service
+    initServices();
+    initBMService();
+}
+
+/**
+ * a class for Bookmark
+ */
+function Bookmark(bmRes,path)
+{
+    this.bmRes = bmRes;
+    this.URL = this.getLiteral(this.URLArc);
+    this.Name = this.getLiteral(this.NameArc);
+    this.ShortcutURL = this.getLiteral(this.ShorcurURLArc);
+    this.Description = this.getLiteral(this.DescriptionArc);
+    this.LastModifiedDate = this.getDate(this.LastModifiedDateArc);
+    this.LastVisitDate = this.getDate(this.LastVisitDateArc);
+    this.BookmarkAddDate = this.getDate(this.BookmarkAddDateArc);
+    this.type = BookmarksUtils.resolveType(this.bmRes);
+    this.path = path; 
+}
+
+Bookmark.prototype = {
+    URLArc:             RDF.GetResource(gNC_NS + "URL"),
+    //FeedURLArc:         RDF.GetResource(gNCNS + "FeedURL",
+    NameArc:            RDF.GetResource(gNC_NS + "Name"),
+    ShortcutURLArc:     RDF.GetResource(gNC_NS + "ShortcutURL"),
+    DescriptionArc:     RDF.GetResource(gNC_NS + "Description"),
+    LastModifiedDateArc:RDF.GetResource(gWEB_NS + "LastModifiedDate"),
+    LastVisitDateArc:   RDF.GetResource(gWEB_NS + "LastVisitDate"),
+    BookmarkAddDateArc:   RDF.GetResource(gNC_NS + "BookmarkAddDate"),
+    
+    /**
+     * is bookmark newer than lastIndexDate
+     */
+    isModified: function(lastIndexDate)
+    {   
+        var last_modified = this.LastModifiedDate;
+        if (!last_modified)
+            last_modified = this.BookmarkAddDate;
+        return last_modified && last_modified > lastIndexDate;
+    },
+
+    /**
+     * folder / seperator / livebookmark  are not "bookmark"
+     */
+    isBookmark: function()
+    {
+        var parent = BMSVC.getParent(this.bmRes);
+        if (parent) 
+            var type = BookmarksUtils.resolveType(parent);
+        if (type == "Livemark") 
+            return false;
+        return !!this.URL;
+    },
+
+    getLiteral:function(arc) 
+    {
+        try{
+            var target = BMDS.GetTarget(this.bmRes, arc, true);          
+            if (target) {
+                   return target.QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
+            }
+        } catch (e) { /* probably a bad interface */ }
+        return null;
+    },
+
+    getDate:function(arc) 
+    {
+        try{
+            var target = BMDS.GetTarget(this.bmRes, arc, true);          
+            if (target) {
+                   return target.QueryInterface(Components.interfaces.nsIRDFDate).Value/1000;
+            }
+        } catch (e) { /* probably a bad interface */ }
+        return null;
+    },
+
+    /**
+     * get children bookmarks 
+     */
+    getChildren:function()
+    {
+        var container = Components.classes["@mozilla.org/rdf/container;1"]
+                    .createInstance(Components.interfaces.nsIRDFContainer);
+        container.Init(BMDS, this.bmRes);
+        var bookmarks = new Array();
+        var elements = container.GetElements();
+        while (elements.hasMoreElements()) {
+            var element = elements.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
+            bookmarks.push(new Bookmark(element,this.path + " " + this.Name))
+        }
+        return bookmarks;
+    }
+
+}
+
+var bookmarkIndexer = {
+    
+    /**
+     * get the bookmark  one by one 
+     * if filter(bookmark) == true do action(bookmark)
+     * return the num of indexed bookmarks
+     */
+    walk: function(bm,filter,action)
+    {
+        var num = 0;
+        switch(bm.type)
+        {
+        //folder. walk it's chidren
+        case "Folder":
+        case "PersonalToolbarFolder":
+        case "IEFavoriteFolder":
+            var children = bm.getChildren();
+            for(var i = 0; i < children.length; i++)
+                num += this.walk(children[i],filter,action);
+            break;
+        default:
+            if(filter.call(null,bm))
+            {
+                action.call(null,bm);
+                num ++;
+            }
+            break;
+        }
+        return num;
+    },
+    
+    /**
+     * Index a bookmark.
+     * write meta to metafile and write a empty content file
+     */
+    indexBookmark: function(bookmark)
+    {
+        log("index bookmark " + bookmark.URL );
+        var meta = [
+            bookmark.URL,
+            "FirefoxBookmark",
+            "text/plain", //TODO what the content type should be 
+            "t:name=" + bookmark.Name,
+            "t:path=" + bookmark.path,
+        ];
+        if(bookmark.Description)
+            meta.push("t:description=" + bookmark.Description);
+        if(bookmark.ShortcutURL)
+            meta.push("t:shortcuturl=" + bookmark.ShortcutURL);
+        //if(bookmark.LastModifiedDate)
+        //    meta.push("k:lastmodifieddate=" + bookmark.LastModifiedDate);
+        //if(bookmark.LastVisitDate)
+        //    meta.push("k:lastvisitdate=" + bookmark.LastVisitDate);
+        beagle.writeRawMetadata(meta,beagle.getMetaPath(bookmark.URL,"bookmark"));
+        // a little hack , write empty content to content file
+        beagle.writeRawMetadata([],beagle.getContentPath(bookmark.URL,"bookmark"));
+    },
+    
+    /**
+     * Index all the bookmarks. 
+     * It is not used.
+     */
+    indexAll:function()
+    {
+        this.walk(
+            function(bookmark){return bookmark.isBookmark();}, 
+            this.indexBookmark
+        );
+        beaglePref.set("beagle.bookmark.last.indexed.date","" + (new Date()).getTime());
+    },
+    
+    /**
+     * Index the modifled (or new ) bookmarks.
+     * if report is true , alert the num of indexed bookmarks
+     */
+    indexModified:function(report)
+    {
+        var root = new Bookmark(RDF.GetResource("NC:BookmarksRoot"),"");
+        var lastIndexDate = beaglePref.get("beagle.bookmark.last.indexed.date");
+        var num = this.walk(
+            root,
+            function(bookmark){return bookmark.isBookmark() && bookmark.isModified(lastIndexDate);},
+            this.indexBookmark
+        );
+        beaglePref.set("beagle.bookmark.last.indexed.date","" + (new Date()).getTime());
+        if(report)
+           alert(_f("beagle_index_bookmark_finish",[num]));
+        log(_f("beagle_index_bookmark_finish",[num]));
+    }
+}
+
+

Added: trunk/extensions/firefox-extension/chrome/content/indexLink.js
==============================================================================
--- (empty file)
+++ trunk/extensions/firefox-extension/chrome/content/indexLink.js	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,338 @@
+/**
+ * a browser used to download the link/image and index it
+ */
+
+beagleInvisibleBrowser = {
+
+    get ELEMENT() { return document.getElementById("beagle-invisible-browser"); },
+    
+    get STATUS_ELEMENT() { return document.getElementById("beagle-index-link-status");},
+    
+    get START_BUTTON() { return document.getElementById("beagle-index-link-start");},
+    
+    get STOP_BUTTON() { return document.getElementById("beagle-index-link-stop");},
+    
+    currentURL: null,
+
+    referrer : null, 
+
+    currentContentType:null,
+    
+    isDocument:null,
+    
+    sniffer: null,
+    
+    persist : null, 
+   
+    /**
+     * get current URL . It might be differen't with this.currentURL
+     * because of meta redirection / javascript redirection
+     */
+    getCurrentURL : function()
+    {
+        if(this.isDocument)
+        {
+            return this.ELEMENT.currentURI.spec;
+        }
+        else
+            return this.currentURL;
+    },
+    /**
+     * connect to sever
+     * sniff the contentType 
+     */
+	connect : function(url,referrer)
+	{
+        this.currentURL = url;
+        this.referrer = referrer;
+        this.sniffer = new headerSniffer(
+            url,
+            referrer,
+            Function.bind(this.onGetContentType,this),
+            Function.bind(this.onGetResponseError,this)
+        );
+        this.sniffer.httpHead();
+        this.STATUS_ELEMENT.value = _f("beagle_index_link_connect",[url]);
+    },
+
+    /**
+     * for non-html file . save it (to ~/.beagle/ToIndex)
+     */
+    save : function(url,path)
+    {
+        window.opener.beagle.saveFile(url,path,this);
+    },
+
+    /**
+     * for html/xml file . load it  (and then index the DOM)
+     * TODO: more thing can be just do once ? not every time
+     * TODO: what allow ? 
+     */
+	load : function(url,referrer)
+	{
+        try{
+            this.ELEMENT.webProgress.addProgressListener(this, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
+        }
+        catch(ex){log(ex)};
+        this.ELEMENT._load_cb = Function.bind(this.doIndex,this);
+        this.ELEMENT.addEventListener("load", this.ELEMENT._load_cb, true);
+		this.ELEMENT.docShell.allowJavascript = true;
+		this.ELEMENT.docShell.allowImages     = false;
+		this.ELEMENT.docShell.allowMetaRedirects = true;
+		this.ELEMENT.docShell.QueryInterface(Components.interfaces.nsIDocShellHistory).useGlobalHistory = false;
+		this.ELEMENT.loadURI(url,null, null);
+	},
+
+    /**
+     * called when the start button is clicked
+     * TODO:shall we re-sniff when reload?
+     */
+    reload : function()
+    {
+        this.START_BUTTON.disabled=true;
+        this.STOP_BUTTON.disabled=false;
+        this.connect(this.currentURL,this.referrer);
+    },
+
+    /**
+     * called when the stop button is clicked
+     */
+    stop : function()
+    {
+        this.START_BUTTON.disabled=false;
+        this.STOP_BUTTON.disabled=true;
+		this.STATUS_ELEMENT.value = _("beagle_index_link_stop");
+        
+        if(this.currentContentType == null)  //not get contenttype yet
+        {
+            this.sniffer.cancel();
+            return;
+        }
+        if(this.isDocument)
+        {
+            this.ELEMENT.stop();
+            this.ELEMENT.removeEventListener('load',this.ELEMENT._load_cb,true);
+        }
+        else
+        {
+            
+            this.persist.progressListener =  null;
+            this.persist.cancelSave();
+            //if we cancel save . It's our responsibility to clean the tmp file
+            try{
+                var tmpfile = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
+                tmpfile.initWithPath(window.opener.beagle.getContentPath(this.currentURL));
+                tmpfile.remove(false);
+            }
+            catch(ex){ log(ex);}
+        }
+        this.currentContentType = null;
+    },
+
+    /**
+     * call window.opener.beagle to index the file/document
+     */
+	doIndex : function()
+	{
+        this.STOP_BUTTON.disabled=true;
+		this.STATUS_ELEMENT.value = _f("beagle_index_link_saving",[this.getCurrentURL()]);
+        window.opener.beagle.onLinkLoad(
+            this.getCurrentURL(),
+            this.currentContentType,
+            this.ELEMENT.contentDocument,
+            window.arguments[0]);
+        window.close();
+	},
+
+	QueryInterface : function(aIID)
+	{
+		if (aIID.equals(Components.interfaces.nsIWebProgressListener) ||
+			aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
+			aIID.equals(Components.interfaces.nsIXULBrowserWindow) ||
+			aIID.equals(Components.interfaces.nsISupports))
+			return this;
+		throw Components.results.NS_NOINTERFACE;
+	},
+
+    /***************************************************************
+     * the five functions below is progresslistener interface 
+     ***************************************************************/
+
+	onStateChange : function(aWebProgress, aRequest, aStateFlags, aStatus)
+	{
+        //alert(aStateFlags);
+		if ( aStateFlags & Components.interfaces.nsIWebProgressListener.STATE_START )
+		{
+			this.STATUS_ELEMENT.value = _f("beagle_index_link_start",[this.currentURL]);
+		}
+		if ( !this.isDocument && aStateFlags & Components.interfaces.nsIWebProgressListener.STATE_STOP )
+        {
+            this.STATUS_ELEMENT.value = _f("beagle_index_link_saving",[this.currentURL]);
+            window.opener.beagle.onLinkLoad(this.currentURL,this.currentContentType,null,window.arguments[0]);
+            window.close();
+        }
+	},
+
+	onProgressChange : function(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress)
+	{
+        if ( aCurTotalProgress != aMaxTotalProgress )
+        {
+            var progress = (aMaxSelfProgress > 0) ? Math.round(aCurSelfProgress / aMaxSelfProgress * 100) + "%" : aCurSelfProgress + "Bytes";
+            this.STATUS_ELEMENT.value = _f("beagle_index_link_progress",[progress,this.currentURL]); 
+        } 
+	},
+
+	onStatusChange   : function() {},
+	onLocationChange : function() {},
+	onSecurityChange : function() {},
+    /**
+     * pass it as a callback to sniffer
+     * when we get the contentType , it is called.
+     */
+    onGetContentType : function(contentType,url)
+    {
+        if(!contentType)
+            contentType ="text/html";
+        this.currentContentType = contentType;
+        this.currentURL = url;
+        if(contentType.match(/(text|html|xml)/i))
+        {   
+            this.isDocument = true;
+            this.load(url,this.referrer);
+        }
+        else
+        {
+            this.isDocument = false;
+            this.save(url,window.opener.beagle.getContentPath(url));
+        }
+    },
+    /**
+     * pass it as a callback to sniffer
+     * when some error occurs, it is called
+     */
+    onGetResponseError : function(msg)
+    {
+        this.STATUS_ELEMENT.value = msg;
+    },
+
+};
+
+
+/**
+ * sniff the head 
+ * here is used to get the mimetype of the given url
+ */
+function headerSniffer(URLSpec, RefURLSpec,onSuccess,onError)
+{
+	this.URLSpec    = URLSpec;
+	this.refURLSpec = RefURLSpec;
+    this.onSuccess = onSuccess;
+    this.onError = onError;
+}
+
+
+headerSniffer.prototype = {
+
+	_URL     : Components.classes['@mozilla.org/network/standard-url;1'].createInstance(Components.interfaces.nsIURL),
+    _IO      : Components.classes['@mozilla.org/network/io-service;1'].getService(Components.interfaces.nsIIOService),
+	_channel : null,
+	_headers : null,
+    
+    /**
+     * send http head 
+     */
+	httpHead : function()
+	{
+		this._channel = null;
+		this._headers = {};
+		try {
+			this._URL.spec = this.URLSpec;
+			this._channel = this._IO.newChannelFromURI(this._URL).QueryInterface(Components.interfaces.nsIHttpChannel);
+			this._channel.loadFlags = this._channel.LOAD_BYPASS_CACHE;
+			this._channel.setRequestHeader("User-Agent", navigator.userAgent, false);
+			if ( this.refURLSpec ) this._channel.setRequestHeader("Referer", this.refURLSpec, false);
+		} catch(ex) {
+			this.onError(_("beagle_index_link_invalid_url"));
+		}
+		try {
+			this._channel.requestMethod = "HEAD";
+			this._channel.asyncOpen(this, this);
+		} catch(ex) {
+			this.onError(ex);
+		}
+	},
+
+    /**
+     * cancel sniff 
+     * I didn't find any way to stop the request ?
+     * So here  we will just remove the callback function
+     */
+    cancel : function()
+    {
+        log("sniff canceled");
+        this.onSuccess =  function(){};
+        this.onError = function(){};
+    },
+
+    /**
+     * get response header
+     */
+	getHeader : function(header_name)
+	{
+	 	try { return this._channel.getResponseHeader(header_name); } catch(ex) { return ""; }
+	},
+
+    /**
+     * get http status 
+     */
+	getStatus : function()
+	{
+		try { return this._channel.responseStatus; } catch(ex) { return ""; }
+	},
+	
+    onDataAvailable : function() {},
+	onStartRequest  : function() {},
+	onStopRequest   : function() { this.onHttpSuccess(); },
+    
+    /**
+     * get http reponse successfully 
+     */
+	onHttpSuccess : function()
+	{
+		var contentType = this.getHeader("Content-Type");
+		var httpStatus = this.getStatus();
+		
+        switch ( httpStatus )
+		{
+			case 404 : this.onError(_("beagle_index_link_http_403")); return;
+			case 403 : this.onError(_("beagle_index_link_http_404")); return;
+			case 500 : this.onError(_("beagle_index_link_http_500")); return;
+		}
+
+        //if redirect 
+		var redirectURL = this.getHeader("Location");
+		if ( redirectURL )
+		{
+			if ( redirectURL.indexOf("http") != 0 ) redirectURL = this._URL.resolve(redirectURL);
+			//re-sniffer 
+            this.URLSpec = redirectURL;
+            this.httpHead();
+			return;
+		}
+        //contenType may looks like text/html; charset=UTF-8
+        //we only need text/html
+        contentType = contentType.split(';',1)[0];
+        log("get contenttype = " + contentType);
+        this.onSuccess(contentType,this.URLSpec);
+        
+	},
+
+};
+
+
+window.onload = function()
+{
+    //window.arguments[0] is the url to load
+    //window.arguments[1] is referrer
+    beagleInvisibleBrowser.connect(window.arguments[0],window.arguments[1]);
+}

Added: trunk/extensions/firefox-extension/chrome/content/indexLink.xul
==============================================================================
--- (empty file)
+++ trunk/extensions/firefox-extension/chrome/content/indexLink.xul	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,35 @@
+<?xml version="1.0"?>
+
+
+<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
+
+<!DOCTYPE window SYSTEM "chrome://beagle/locale/beagle.dtd">
+
+<window id="beagle-index-link"
+    title="Beagle Index "
+    style="width:400px" 
+    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";>
+    
+<stringbundleset id="stringbundleset">
+     <stringbundle id="beagleStrings"
+         src="chrome://beagle/locale/beagle.properties"/>
+</stringbundleset>
+
+<script type="application/x-javascript" 
+		src="chrome://beagle/content/i18n.js" />
+<script type="application/x-javascript" 
+		src="chrome://beagle/content/utils.js" />
+<script type="application/x-javascript" 
+		src="chrome://beagle/content/indexLink.js" />
+
+<textbox id="beagle-index-link-status" readonly="true" value="" />
+<hbox>
+    <button id="beagle-index-link-start" label="&beagle.index.link.start.label;" 
+        oncommand="beagleInvisibleBrowser.reload();" disabled="true"/>
+    <button id="beagle-index-link-stop" label="&beagle.index.link.stop.label;"  
+        oncommand="beagleInvisibleBrowser.stop();" />
+</hbox>
+
+<browser id="beagle-invisible-browser" type="content" collapsed="true"/>
+
+</window> 

Added: trunk/extensions/firefox-extension/chrome/content/jslib/debug/debug.js
==============================================================================
--- (empty file)
+++ trunk/extensions/firefox-extension/chrome/content/jslib/debug/debug.js	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,156 @@
+/*
+
+The contents of this file are subject to the Mozilla Public
+License Version 1.1 (the "License"); you may not use this file
+except in compliance with the License. You may obtain a copy of
+the License at http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS
+IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+implied. See the License for the specific language governing
+rights and limitations under the License.
+
+The Original Code is jslib team code.
+The Initial Developer of the Original Code is jslib team.
+
+Portions created by jslib team are
+Copyright (C) 2000 jslib team.  All
+Rights Reserved.
+
+Original Author: Pete Collins <pete mozdev org>
+Contributor(s):
+  Henrik Gemal <http://gemal.dk>
+*/
+
+/************** DUBUG ******************/
+if (typeof(JS_LIB_LOADED) == "boolean") 
+{
+
+  const JS_DEBUG_LOADED = true;
+  const JS_DEBUG_FILE = "debug.js";
+
+  /****************************************************************
+  * void jslibDebug(aOutString)                                   *
+  * aOutString is an argument of string debug message             *
+  * returns void                                                  *
+  *   eg:                                                         * 
+  *       var msg="Testing function";                             *
+  *       jslibDebug(msg);                                        *
+  *                                                               *
+  *   outputs: Testing function                                   *
+  ****************************************************************/
+
+  // DEPRECATED 
+  function jslib_debug(aOutString) { return jslibDebug(aOutString); }
+
+  function jslibDebug(aOutString) 
+  {
+    if (!JS_LIB_DEBUG)
+      return; 
+
+    if (JS_LIB_DEBUG_ALERT)
+      alert(aOutString);
+
+    dump(aOutString+'\n');
+  }
+
+  // print to stdout
+  function jslibPrint(aOutString) 
+  {
+    dump(aOutString+'\n');
+  }
+
+  // print to stdout
+  function jslibPrintDebug(aMsg, aOutString) 
+  {
+    if (!aMsg) aMsg = "JSLIB_DEBUG: ";
+    dump(aMsg+aOutString+'\n');
+  }
+
+  // print to stdout
+  function jslibPrintBracket(aOutString) 
+  {
+    dump("["+aOutString+']\n');
+  }
+
+  // print message
+  function jslibPrintMsg(aOutStr1, aOutStr2) 
+  {
+    dump(aOutStr1+": "+aOutStr2+"\n");
+  }
+
+
+  /****************************************************************
+  * void jslibError(e, aType, aResults, aCaller)                  *
+  * e        - argument of results exception                      *
+  * aType    - argument of string error type message              *
+  * aResults - argument of string Components.results name         *
+  * aCaller  - argument of string caller filename and func name   *
+  * returns void                                                  *
+  *   Ex:                                                         * 
+  *       jslibError(null, "Missing file path argument\n",        *
+  *                 "NS_ERROR_XPC_NOT_ENOUGH_ARGS",               *
+  *                 JS_LIB_FILE+": include");                     *
+  *                                                               *
+  *   outputs:                                                    *
+  *       -----======[ ERROR ]=====-----                          *
+  *       Error in jslib.js: include:  Missing file path argument *
+  *                                                               *
+  *       NS_ERROR_NUMBER:   NS_ERROR_XPC_NOT_ENOUGH_ARGS         *
+  *       ------------------------------                          *
+  *                                                               *
+  ****************************************************************/
+
+  function jslibError(e) 
+  {
+		var rv = null;
+    var errMsg="";
+    if (typeof(e) == 'object') {
+      var m, n, r, l, ln, fn = "";
+      try {
+        rv = e.result;
+        m  = e.message;
+        fn = e.filename;
+        l  = e.location; 
+        ln = l.lineNumber; 
+      } catch (e) {}
+      errMsg+="FileName:          "+fn+"\n"           +
+              "Result:            "+rv+"\n"           +
+              "Message:           "+m+"\n"            +
+              "LineNumber:        "+ln+"\n";
+    }
+
+    errMsg = "\n-----======[ jsLib ERROR ]=====-----\n" + errMsg;
+    errMsg += "-------------------------------------\n";
+
+    jslibDebug(errMsg);
+
+		return rv;
+  }
+
+	function jslibErrorMsg (e)
+	{
+		jslibDebug(e);
+		return null;
+	}
+
+	function jslibErrorWarn (e)
+	{
+		jslibDebug("jsLib warn: "+e);
+		return null;
+	}
+
+	function jslibErrorMsg (e)
+	{
+		jslibDebug(e);
+		return jslibRes[e];
+	}
+
+  // Welcome message
+  jslibDebug('*** load: '+JS_DEBUG_FILE+' OK');
+  jslibDebug(JS_LIB_HELP);
+  jslibDebug("\n\n*********************\nJS_LIB DEBUG IS ON\n*********************\n\n");
+
+} 
+                                                                                                    
+

Added: trunk/extensions/firefox-extension/chrome/content/jslib/io/dir.js
==============================================================================
--- (empty file)
+++ trunk/extensions/firefox-extension/chrome/content/jslib/io/dir.js	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,231 @@
+/*** -*- Mode: Javascript; tab-width: 2;
+
+The contents of this file are subject to the Mozilla Public
+License Version 1.1 (the "License"); you may not use this file
+except in compliance with the License. You may obtain a copy of
+the License at http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS
+IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+implied. See the License for the specific language governing
+rights and limitations under the License.
+
+The Original Code is Collabnet code.
+The Initial Developer of the Original Code is Collabnet.
+
+Portions created by Collabnet are Copyright (C) 2000 Collabnet.
+All Rights Reserved.
+
+Contributor(s): Pete Collins, Doug Turner, Brendan Eich, Warren Harris,
+                Eric Plaster, Martin Kutschker
+
+
+JS Directory Class API
+
+  dir.js
+
+Function List
+
+    create(aPermissions);      // permissions are optional
+
+    files();                   // returns an array listing all files of a dirs contents 
+    dirs();                    // returns an array listing all dirs of a dirs contents 
+    list(aDirPath);            // returns an array listing of a dirs contents 
+
+    // help!
+    help();                    // currently dumps a list of available functions 
+
+Instructions:
+
+
+*/
+
+if (typeof(JS_LIB_LOADED)=='boolean') {
+
+/************* INCLUDE FILESYSTEM *****************/
+if(typeof(JS_FILESYSTEM_LOADED)!='boolean')
+  include(jslib_filesystem);
+/************* INCLUDE FILESYSTEM *****************/
+
+
+/****************** Globals **********************/
+const JS_DIR_FILE                    = "dir.js";
+const JS_DIR_LOADED                  = true;
+
+const JS_DIR_LOCAL_CID               = "@mozilla.org/file/local;1";
+const JS_DIR_LOCATOR_PROGID          = '@mozilla.org/filelocator;1';
+const JS_DIR_CID                     = "@mozilla.org/file/directory_service;1";
+
+const JS_DIR_I_LOCAL_FILE            = "nsILocalFile";
+const JS_DIR_INIT_W_PATH             = "initWithPath";
+
+const JS_DIR_PREFS_DIR               = 65539;
+
+const JS_DIR_DIRECTORY               = 0x01;     // 1
+const JS_DIR_OK                      = true;
+
+const JS_DIR_DEFAULT_PERMS           = 0766;
+
+const JS_DIR_FilePath                = new C.Constructor(JS_DIR_LOCAL_CID, 
+                                                   JS_DIR_I_LOCAL_FILE, 
+                                                   JS_DIR_INIT_W_PATH);
+/****************** Globals **********************/
+
+/****************** Dir Object Class *********************/
+// constructor
+function Dir(aPath) {
+
+  if(!aPath) {
+    jslibError(null,
+              "Please enter a local file path to initialize",
+              "NS_ERROR_XPC_NOT_ENOUGH_ARGS", JS_DIR_FILE);
+    throw C.results.NS_ERROR_XPC_NOT_ENOUGH_ARGS;
+  }
+
+  return this.initPath(arguments);
+} // end constructor
+
+Dir.prototype = new FileSystem;
+Dir.prototype.fileInst = null;
+
+/********************* CREATE ****************************/
+Dir.prototype.create = function(aPermissions) 
+{
+  if(!this.mPath) {
+    jslibError(null, "create (no file path defined)", "NS_ERROR_NOT_INITIALIZED");
+    return C.results.NS_ERROR_NOT_INITIALIZED;
+  }
+
+  if(this.exists()) {
+    jslibError(null, "(Dir already exists", "NS_ERROR_FAILURE", JS_DIR_FILE+":create");
+    return null;
+  }
+
+  if (typeof(aPermissions) == "number") {
+    var checkPerms = this.validatePermissions(aPermissions);
+
+    if(!checkPerms) {
+      jslibError(null, "create (invalid permissions)", 
+                       "NS_ERROR_INVALID_ARG", JS_DIR_FILE+":create");
+      return C.results.NS_ERROR_INVALID_ARG;
+    }               
+  } else {
+    checkPerms = this.mFileInst.parent.permissions;
+  }
+
+  var rv = null;
+
+  try {
+    rv=this.mFileInst.create(JS_DIR_DIRECTORY, checkPerms);
+  } catch (e) { 
+    jslibError(e, "(unable to create)", "NS_ERROR_FAILURE", JS_DIR_FILE+":create");
+    rv=null;
+  }
+
+  return rv;
+};
+
+/********************* READDIR **************************/
+Dir.prototype.readDir = function ()
+{
+
+  if(!this.exists()) {
+    jslibError(null, "(Dir already exists", "NS_ERROR_FAILURE", JS_DIR_FILE+":readDir");
+    return null;
+  }
+
+  var rv=null;
+
+  try {
+    if(!this.isDir()) {
+      jslibError(null, "(file is not a directory)", "NS_ERROR_FAILURE", JS_DIR_FILE+":readDir");
+      return null;
+    }
+
+    var files     = this.mFileInst.directoryEntries;
+    var listings  = new Array();
+    var file;
+
+    if(typeof(JS_FILE_LOADED)!='boolean')
+      include(JS_LIB_PATH+'io/file.js');
+
+    while(files.hasMoreElements()) {
+      file = files.getNext().QueryInterface(C.interfaces.nsILocalFile);
+      if(file.isFile())
+        listings.push(new File(file.path));
+
+      if(file.isDirectory())
+        listings.push(new Dir(file.path));
+    }
+
+    rv=listings;
+  } catch(e) { 
+    jslibError(e, "(unexpected error)", "NS_ERROR_UNEXPECTED", JS_FILE_FILE+":readDir");
+    rv=null;
+  }
+
+  return rv;
+};
+
+/********************* REMOVE *******************************/
+Dir.prototype.remove = function (aRecursive)
+{
+
+  if(typeof(aRecursive)!='boolean')
+    aRecursive=false;
+
+  if(!this.checkInst())
+    throw C.results.NS_ERROR_NOT_INITIALIZED;
+
+  if(!this.mPath)
+  {
+    jslibError(null, "remove (no path defined)", 
+                     "NS_ERROR_INVALID_ARG", JS_DIR_FILE+":remove");
+    return null;
+  }
+
+  var rv=null
+
+  try { 
+    if(!this.exists()) {
+      jslibError(null, "(directory doesn't exist)", "NS_ERROR_FAILURE", JS_DIR_FILE+":remove");
+      return null;
+    }
+
+    if(!this.isDir()) {
+      jslibError(null, "(file is not a directory)", "NS_ERROR_FAILURE", JS_DIR_FILE+":remove");
+      return null;
+    }
+
+    rv=this.mFileInst.remove(aRecursive);
+  } catch (e) { 
+    jslibError(e, "(dir not empty, use 'remove(true)' for recursion)", "NS_ERROR_UNEXPECTED", 
+                  JS_DIR_FILE+":remove");
+    rv=null;
+  }
+
+  return rv;
+};
+
+/********************* HELP *****************************/
+Dir.prototype.super_help = FileSystem.prototype.help;
+
+Dir.prototype.__defineGetter__('help', 
+function() {
+  var help = this.super_help()              +
+
+    "   create(aPermissions);\n"            +
+    "   remove(aRecursive);\n"              +
+    "   readDir(aDirPath);\n";
+
+  return help;
+});
+
+jslibDebug('*** load: '+JS_DIR_FILE+' OK');
+
+} else {
+    dump("JSLIB library not loaded:\n"                                  +
+         " \tTo load use: chrome://jslib/content/jslib.js\n"            +
+         " \tThen: include(jslib_dir);\n\n");
+}
+

Added: trunk/extensions/firefox-extension/chrome/content/jslib/io/dirUtils.js
==============================================================================
--- (empty file)
+++ trunk/extensions/firefox-extension/chrome/content/jslib/io/dirUtils.js	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,185 @@
+/*** -*- Mode: Javascript; tab-width: 2;
+
+The contents of this file are subject to the Mozilla Public
+License Version 1.1 (the "License"); you may not use this file
+except in compliance with the License. You may obtain a copy of
+the License at http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS
+IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+implied. See the License for the specific language governing
+rights and limitations under the License.
+
+The Original Code is Collabnet code.
+The Initial Developer of the Original Code is Collabnet.
+
+Portions created by Collabnet are Copyright (C) 2000 Collabnet.
+All Rights Reserved.
+
+Contributor(s): Pete Collins, Doug Turner, Brendan Eich, Warren Harris,
+                Eric Plaster, Martin Kutschker
+
+
+JS Directory Class API
+
+  dirUtils.js
+
+Function List
+
+
+Instructions:
+
+
+*/
+
+if(typeof(JS_LIB_LOADED)=='boolean') {
+
+const JS_DIRUTILS_FILE              = "dirUtils.js";
+const JS_DIRUTILS_LOADED            = true;
+
+const JS_DIRUTILS_FILE_LOCAL_CID    = "@mozilla.org/file/local;1";
+const JS_DIRUTILS_FILE_DIR_CID      = "@mozilla.org/file/directory_service;1";
+
+const JS_DIRUTILS_FILE_I_LOCAL_FILE = "nsILocalFile";
+const JS_DIRUTILS_INIT_W_PATH       = "initWithPath";
+const JS_DIRUTILS_I_PROPS           = "nsIProperties";
+const JS_DIRUTILS_NSIFILE           = C.interfaces.nsIFile;
+
+const NS_APP_PREFS_50_DIR           = "PrefD"; // /root/.mozilla/Default User/k1m30xaf.slt
+const NS_APP_CHROME_DIR             = "AChrom"; // /usr/src/mozilla/dist/bin/chrome
+const NS_APP_USER_PROFILES_ROOT_DIR = "DefProfRt";  // /root/.mozilla
+const NS_APP_USER_PROFILE_50_DIR    = "ProfD";      // /root/.mozilla/Default User/k1m30xaf.slt
+
+const NS_APP_APPLICATION_REGISTRY_DIR  = "AppRegD"; // /root/.mozilla
+const NS_APP_APPLICATION_REGISTRY_FILE = "AppRegF"; // /root/.mozilla/appreg
+const NS_APP_DEFAULTS_50_DIR           = "DefRt";   // /usr/src/mozilla/dist/bin/defaults 
+const NS_APP_PREF_DEFAULTS_50_DIR      = "PrfDef";  // /usr/src/mozilla/dist/bin/defaults/pref
+const NS_APP_PROFILE_DEFAULTS_50_DIR   = "profDef"; // /usr/src/mozilla/dist/bin/defaults/profile/US
+const NS_APP_PROFILE_DEFAULTS_NLOC_50_DIR = "ProfDefNoLoc"; // /usr/src/mozilla/dist/bin/defaults/profile 
+const NS_APP_RES_DIR                      = "ARes"; // /usr/src/mozilla/dist/bin/res
+const NS_APP_PLUGINS_DIR                  = "APlugns"; // /usr/src/mozilla/dist/bin/plugins
+const NS_APP_SEARCH_DIR                   = "SrchPlugns"; // /usr/src/mozilla/dist/bin/searchplugins
+const NS_APP_PREFS_50_FILE                = "PrefF"; // /root/.mozilla/Default User/k1m30xaf.slt/prefs.js
+const NS_APP_USER_CHROME_DIR              = "UChrm"; // /root/.mozilla/Default User/k1m30xaf.slt/chrome
+const NS_APP_LOCALSTORE_50_FILE           = "LclSt"; // /root/.mozilla/Default User/k1m30xaf.slt/localstore.rdf
+const NS_APP_HISTORY_50_FILE              = "UHist"; // /root/.mozilla/Default User/k1m30xaf.slt/history.dat
+const NS_APP_USER_PANELS_50_FILE          = "UPnls"; // /root/.mozilla/Default User/k1m30xaf.slt/panels.rdf
+const NS_APP_USER_MIMETYPES_50_FILE       = "UMimTyp"; // /root/.mozilla/Default User/k1m30xaf.slt/mimeTypes.rdf
+const NS_APP_BOOKMARKS_50_FILE            = "BMarks"; // /root/.mozilla/Default User/k1m30xaf.slt/bookmarks.html 
+const NS_APP_SEARCH_50_FILE               = "SrchF"; // /root/.mozilla/Default User/k1m30xaf.slt/search.rdf
+const NS_APP_MAIL_50_DIR                  = "MailD"; // /root/.mozilla/Default User/k1m30xaf.slt/Mail
+const NS_APP_IMAP_MAIL_50_DIR             = "IMapMD"; // /root/.mozilla/Default User/k1m30xaf.slt/ImapMail
+const NS_APP_NEWS_50_DIR                  = "NewsD"; // /root/.mozilla/Default User/k1m30xaf.slt/News
+const NS_APP_MESSENGER_FOLDER_CACHE_50_DIR = "MFCaD"; // /root/.mozilla/Default User/k1m30xaf.slt/panacea.dat
+
+// Useful OS System Dirs
+const NS_OS_CURRENT_PROCESS_DIR = "CurProcD"; // /usr/src/mozilla/dist/bin
+const NS_OS_HOME_DIR = "Home"; // /root
+const NS_OS_TEMP_DIR = "TmpD"; // /tmp
+const NS_XPCOM_COMPONENT_DIR = "ComsD"; // /usr/src/mozilla/dist/bin/components
+
+const JS_DIRUTILS_FilePath  = new C.Constructor(JS_DIRUTILS_FILE_LOCAL_CID,
+                                                JS_DIRUTILS_FILE_I_LOCAL_FILE,
+                                                JS_DIRUTILS_INIT_W_PATH);
+
+const JS_DIRUTILS_DIR       = new C.Constructor(JS_DIRUTILS_FILE_DIR_CID,
+                                                JS_DIRUTILS_I_PROPS);
+
+// constructor
+function DirUtils(){}
+
+DirUtils.prototype = {
+
+getPath   : function (aAppID) {
+
+  if(!aAppID) {
+    jslibError(null, "(no arg defined)", "NS_ERROR_INVALID_ARG", JS_FILE_FILE+":getPath");
+    return null;
+  }
+
+  var rv;
+
+  try { 
+    rv=(new JS_DIRUTILS_DIR()).get(aAppID, JS_DIRUTILS_NSIFILE).path; 
+  } catch (e) {
+    jslibError(e, "(unexpected error)", "NS_ERROR_FAILURE", JS_DIRUTILS_FILE+":getPath");
+    rv=null;
+  }
+
+  return rv;
+},
+
+getPrefsDir : function () { return this.getPath(NS_APP_PREFS_50_DIR); },
+getChromeDir : function () { return this.getPath(NS_APP_CHROME_DIR); },
+getMozHomeDir : function () { return this.getPath(NS_APP_USER_PROFILES_ROOT_DIR); },
+getMozUserHomeDir : function () { return this.getPath(NS_APP_USER_PROFILE_50_DIR); },
+getAppRegDir : function () { return this.getPath(NS_APP_APPLICATION_REGISTRY_FILE); },
+getAppDefaultDir : function () { return this.getPath(NS_APP_DEFAULTS_50_DIR); },
+getAppDefaultPrefDir : function () { return this.getPath(NS_APP_PREF_DEFAULTS_50_DIR); },
+getProfileDefaultsLocDir : function () { return this.getPath(NS_APP_PROFILE_DEFAULTS_50_DIR); },
+getProfileDefaultsDir : function () { return this.getPath(NS_APP_PROFILE_DEFAULTS_NLOC_50_DIR); },
+getAppResDir : function () { return this.getPath(NS_APP_RES_DIR); },
+getAppPluginsDir : function () { return this.getPath(NS_APP_PLUGINS_DIR); },
+getSearchPluginsDir : function () { return this.getPath(NS_APP_SEARCH_DIR); },
+getPrefsFile : function () { return this.getPath(NS_APP_PREFS_50_FILE); },
+getUserChromeDir : function () { return this.getPath(NS_APP_USER_CHROME_DIR); },
+getLocalStore : function () { return this.getPath(NS_APP_LOCALSTORE_50_FILE); },
+getHistoryFile : function () { return this.getPath(NS_APP_HISTORY_50_FILE); },
+getPanelsFile : function () { return this.getPath(NS_APP_USER_PANELS_50_FILE); },
+getMimeTypes : function () { return this.getPath(NS_APP_USER_MIMETYPES_50_FILE); },
+getBookmarks : function () { return this.getPath(NS_APP_BOOKMARKS_50_FILE); },
+getSearchFile : function () { return this.getPath(NS_APP_SEARCH_50_FILE); },
+getUserMailDir : function () { return this.getPath(NS_APP_MAIL_50_DIR); },
+getUserImapDir : function () { return this.getPath(NS_APP_IMAP_MAIL_50_DIR); },
+getUserNewsDir : function () { return this.getPath(NS_APP_NEWS_50_DIR); },
+getMessengerFolderCache : function () { return this.getPath(NS_APP_MESSENGER_FOLDER_CACHE_50_DIR); },
+getCurProcDir : function () { return this.getPath(NS_OS_CURRENT_PROCESS_DIR); },
+getHomeDir : function () { return this.getPath(NS_OS_HOME_DIR); },
+getTmpDir : function () { return this.getPath(NS_OS_TEMP_DIR); },
+getComponentsDir : function () { return this.getPath(NS_XPCOM_COMPONENT_DIR); },
+
+get help() {
+  const help =
+
+    "\n\nFunction and Attribute List:\n"    +
+    "\n"                                    +
+    "    getPrefsDir()\n"                       +
+    "    getChromeDir()\n"                      +
+    "    getMozHomeDir()\n"                     +
+    "    getMozUserHomeDir()\n"                 +
+    "    getAppRegDir()\n"                      +
+    "    getAppDefaultDir()\n"                  +
+    "    getAppDefaultPrefDir()\n"              +
+    "    getProfileDefaultsLocDir()\n"          +
+    "    getProfileDefaultsDir()\n"             +
+    "    getAppResDir()\n"                      +
+    "    getAppPluginsDir()\n"                  +
+    "    getSearchPluginsDir()\n"               +
+    "    getPrefsFile()\n"                      +
+    "    getUserChromeDir()\n"                  +
+    "    getLocalStore()\n"                     +
+    "    getHistoryFile()\n"                    +
+    "    getPanelsFile()\n"                     +
+    "    getMimeTypes()\n"                      +
+    "    getBookmarks()\n"                      +
+    "    getSearchFile()\n"                     +
+    "    getUserMailDir()\n"                    +
+    "    getUserImapDir()\n"                    +
+    "    getUserNewsDir()\n"                    +
+    "    getMessengerFolderCache()\n\n";
+
+  return help;
+}
+
+}; //END CLASS 
+
+jslibDebug('*** load: '+JS_DIRUTILS_FILE+' OK');
+
+} // END BLOCK JS_LIB_LOADED CHECK
+
+else {
+    dump("JSLIB library not loaded:\n"                                  +
+         " \tTo load use: chrome://jslib/content/jslib.js\n"            +
+         " \tThen: include(jslib_dirutils);\n\n");
+}
+

Added: trunk/extensions/firefox-extension/chrome/content/jslib/io/file.js
==============================================================================
--- (empty file)
+++ trunk/extensions/firefox-extension/chrome/content/jslib/io/file.js	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,771 @@
+/*** -*- Mode: Javascript; tab-width: 2; 
+The contents of this file are subject to the Mozilla Public
+License Version 1.1 (the "License"); you may not use this file
+except in compliance with the License. You may obtain a copy of
+the License at http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS
+IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+implied. See the License for the specific language governing
+rights and limitations under the License.
+
+The Original Code is jslib code.
+The Initial Developer of the Original Code is jslib team.
+
+Portions created by jslib team are
+Copyright (C) 2000 jslib team.  All
+Rights Reserved.
+
+Contributor(s): Pete Collins, 
+                Doug Turner, 
+                Brendan Eich, 
+                Warren Harris, 
+                Eric Plaster,
+                Martin Kutschker
+
+The purpose of this file is to make it a little easier to use 
+xpcom nsIFile file IO library from js
+
+ File API 
+    file.js
+
+ Base Class:
+    FileSystem
+      filesystem.js
+
+ Function List:
+    // Constructor
+    File(aPath)                         creates the File object and sets the file path
+
+    // file stream methods
+    open(aMode, aPermissions);          open a file handle for reading,
+                                        writing or appending.  permissions are optional.
+    read();                             returns the contents of a file
+    readline();                         returns the next line in the file.
+    EOF;                                boolean check 'end of file' status
+    write(aContents);                   writes the contents out to file.
+    copy(aDest);                        copy the current file to a aDest
+    close();                            closes a file handle
+    create();                           creates a new file if one doesn't already exist
+    exists();                           check to see if a file exists
+
+    // file attributes
+    size;                               read only attribute gets the file size
+    ext;                                read only attribute gets a file extension if there is one
+    permissions;                        attribute gets or sets the files permissions
+    dateModified;                       read only attribute gets last modified date in locale string
+
+    // file path attributes
+    leaf;                               read only attribute gets the file leaf
+    path;                               read only attribute gets the path
+    parent;                             read only attribute gets parent dir part of a path
+
+    // direct manipulation
+    nsIFile                             returns an nsIFile obj
+
+    // utils
+    remove();                           removes the current file
+    append(aLeaf);                      appends a leaf name to the current file
+    appendRelativePath(aRelPath);       appends a relitave path the the current file
+
+    // help!
+    help;                               currently dumps a list of available functions
+
+ Instructions:
+
+       First include this js file in your xul file.  
+       Next, create an File object:
+
+          var file = new File("/path/file.ext");
+
+       To see if the file exists, call the exists() member.  
+       This is a good check before going into some
+       deep code to try and extract information from a non-existant file.
+
+       To open a file for reading<"r">, writing<"w"> or appending<"a">,
+       just call:
+
+          file.open("w", 0644);
+
+       where in this case you will be creating a new file called '/path/file.ext', 
+       with a mode of "w" which means you want to write a new file.
+
+       If you want to read from a file, just call:
+
+          file.open(); or
+          file.open("r");
+          var theFilesContents    = file.read();
+
+          ---- or ----
+
+          while(!file.EOF) {
+            var theFileContentsLine = file.readline();
+            dump("line: "+theFileContentsLine+"\n");
+          }
+
+       The file contents will be returned to the caller so you can do something usefull with it.
+
+          file.close();
+
+       Calling 'close()' destroys any created objects.  If you forget to use file.close() no probs
+       all objects are discarded anyway.
+
+       Warning: these API's are not for religious types
+
+************/
+
+// insure jslib base is loaded
+if (typeof(JS_LIB_LOADED)=='boolean') {
+
+// test to make sure filesystem base class is loaded
+if (typeof(JS_FILESYSTEM_LOADED)!='boolean')
+  include(jslib_filesystem);
+
+/****************** Globals **********************/
+const JS_FILE_LOADED           = true;
+const JS_FILE_FILE             = "file.js";
+
+const JS_FILE_IOSERVICE_CID    = "@mozilla.org/network/io-service;1";
+const JS_FILE_I_STREAM_CID     = "@mozilla.org/scriptableinputstream;1";
+const JS_FILE_OUTSTREAM_CID    = "@mozilla.org/network/file-output-stream;1";
+
+const JS_FILE_F_TRANSPORT_SERVICE_CID  = "@mozilla.org/network/file-transport-service;1";
+
+const JS_FILE_I_IOSERVICE              = C.interfaces.nsIIOService;
+const JS_FILE_I_SCRIPTABLE_IN_STREAM   = "nsIScriptableInputStream";
+const JS_FILE_I_FILE_OUT_STREAM        = C.interfaces.nsIFileOutputStream;
+
+const JS_FILE_READ          = 0x01;  // 1
+const JS_FILE_WRITE         = 0x08;  // 8
+const JS_FILE_APPEND        = 0x10;  // 16
+
+const JS_FILE_READ_MODE     = "r";
+const JS_FILE_WRITE_MODE    = "w";
+const JS_FILE_APPEND_MODE   = "a";
+
+const JS_FILE_FILE_TYPE     = 0x00;  // 0
+
+const JS_FILE_CHUNK         = 1024;  // buffer for readline => set to 1k
+
+const JS_FILE_DEFAULT_PERMS = 0644;
+
+const JS_FILE_OK            = true;
+
+try {
+  const JS_FILE_InputStream  = new C.Constructor
+  (JS_FILE_I_STREAM_CID, JS_FILE_I_SCRIPTABLE_IN_STREAM);
+
+  const JS_FILE_IOSERVICE    = C.classes[JS_FILE_IOSERVICE_CID].
+  getService(JS_FILE_I_IOSERVICE);
+
+} catch (e) {
+  jslibError(e);
+}
+
+/***
+ * Possible values for the ioFlags parameter 
+ * From: 
+ * http://lxr.mozilla.org/seamonkey/source/nsprpub/pr/include/prio.h#601
+ */
+
+
+// #define PR_RDONLY       0x01
+// #define PR_WRONLY       0x02
+// #define PR_RDWR         0x04
+// #define PR_CREATE_FILE  0x08
+// #define PR_APPEND       0x10
+// #define PR_TRUNCATE     0x20
+// #define PR_SYNC         0x40
+// #define PR_EXCL         0x80
+
+const JS_FILE_NS_RDONLY               = 0x01;
+const JS_FILE_NS_WRONLY               = 0x02;
+const JS_FILE_NS_RDWR                 = 0x04;
+const JS_FILE_NS_CREATE_FILE          = 0x08;
+const JS_FILE_NS_APPEND               = 0x10;
+const JS_FILE_NS_TRUNCATE             = 0x20;
+const JS_FILE_NS_SYNC                 = 0x40;
+const JS_FILE_NS_EXCL                 = 0x80;
+/****************** Globals **********************/
+
+
+/****************************************************************
+* void File(aPath)                                              *
+*                                                               *
+* class constructor                                             *
+* aPath is an argument of string local file path                *
+* returns NS_OK on success, exception upon failure              *
+*   Ex:                                                         *
+*     var p = '/tmp/foo.dat';                                   *
+*     var f = new File(p);                                      *
+*                                                               *
+*   outputs: void(null)                                         *
+****************************************************************/
+
+function File(aPath) {
+
+  if (!aPath)
+    return jslibErrorMsg("NS_ERROR_INVALID_ARG");
+
+  return this.initPath(arguments);
+} // constructor
+
+File.prototype = new FileSystem();
+
+// member vars
+File.prototype.mMode        = null;
+File.prototype.mFileChannel = null;
+File.prototype.mTransport   = null;
+File.prototype.mURI         = null;
+File.prototype.mOutStream   = null;
+File.prototype.mInputStream = null;
+File.prototype.mLineBuffer  = null;
+File.prototype.mPosition    = 0;
+
+/********************* OPEN *************************************
+* bool open(aMode, aPerms)                                      *
+*                                                               *
+* opens a file handle to read, write or append                  *
+* aMode is an argument of string 'w', 'a', 'r'                  *
+* returns true on success, null on failure                      *
+*   Ex:                                                         *
+*     var p='/tmp/foo.dat';                                     *
+*     var f=new File(p);                                        *
+*                                                               *
+*   outputs: void(null)                                         *
+****************************************************************/
+
+File.prototype.open = function (aMode, aPerms) 
+{
+
+  // close any existing file handles
+  this.close();
+
+  if (!this.checkInst())
+    return jslibErrorMsg("NS_ERROR_NOT_INITIALIZED");
+
+  if (!this.mPath) 
+    return jslibErrorMsg("NS_ERROR_NOT_INITIALIZED");
+
+  if (this.exists() && this.mFileInst.isDirectory()) 
+      return jslibErrorMsg("NS_ERROR_FILE_IS_DIRECTORY");
+
+  if (this.mMode) 
+    return jslibErrorMsg("NS_ERROR_NOT_INITIALIZED");
+
+  if (!this.mURI) {
+    if (!this.exists())
+      this.create();
+    this.mURI = JS_FILE_IOSERVICE.newFileURI(this.mFileInst);
+  }
+
+  if (!aMode)
+    aMode=JS_FILE_READ_MODE;
+
+  this.resetCache();
+  var rv;
+
+  switch (aMode) 
+  {
+    case JS_FILE_WRITE_MODE: 
+    case JS_FILE_APPEND_MODE: {
+      try {
+        if (!this.mFileChannel)
+          this.mFileChannel = JS_FILE_IOSERVICE.newChannelFromURI(this.mURI);
+      } catch (e)    {
+        return jslibError(e);
+      }    
+
+      if (aPerms && !this.validatePermissions(aPerms))
+        jslibErrorMsg("NS_ERROR_INVALID_ARG");
+
+      if (!aPerms)
+        aPerms=JS_FILE_DEFAULT_PERMS;
+
+      try {
+        var offSet=0;
+        if (aMode == JS_FILE_WRITE_MODE) {
+          this.mMode=JS_FILE_WRITE_MODE;
+          // create a filestream
+          var fs = jslibCreateInstance(JS_FILE_OUTSTREAM_CID, JS_FILE_I_FILE_OUT_STREAM);
+
+          fs.init(this.mFileInst, JS_FILE_NS_TRUNCATE | 
+                                  JS_FILE_NS_WRONLY, 00004, null); 
+          this.mOutStream = fs;
+        } else {
+          this.mMode=JS_FILE_APPEND_MODE;
+          // create a filestream
+          fs = jslibCreateInstance(JS_FILE_OUTSTREAM_CID, JS_FILE_I_FILE_OUT_STREAM);
+
+          fs.init(this.mFileInst, JS_FILE_NS_RDWR | 
+                                  JS_FILE_NS_APPEND, 00004, null); 
+          this.mOutStream = fs;
+        }
+      } catch (e) {
+        return jslibError(e);
+      }
+      rv = JS_LIB_OK;
+      break;
+    }
+
+    case JS_FILE_READ_MODE: {
+      if (!this.exists()) 
+        jslibErrorMsg("NS_ERROR_FILE_NOT_FOUND");
+
+      this.mMode=JS_FILE_READ_MODE;
+
+      try {
+        jslibDebug('****** '+this.mURI);
+        this.mFileChannel = JS_FILE_IOSERVICE.newChannelFromURI(this.mURI);
+        this.mInputStream = new JS_FILE_InputStream();    
+        this.mInputStream.init(this.mFileChannel.open());
+        this.mLineBuffer  = new Array();
+        rv = JS_LIB_OK;
+      } catch (e) {
+        rv = jslibError(e);
+      }
+      break;
+    }
+
+    default:
+      rv = jslibErrorMsg("NS_ERROR_INVALID_ARG");
+  }
+  return rv;
+}
+
+/********************* READ *************************************
+* string read()                                                 *
+*                                                               *
+* reads a file if the file is binary it will                    *
+* return type ex: ELF                                           *
+* takes no arguments needs an open read mode filehandle         *
+* returns string on success, null on failure                    *
+*   Ex:                                                         *
+*     var p='/tmp/foo.dat';                                     *
+*     var f=new File(p);                                        *
+*     f.open(p);                                                *
+*     f.read();                                                 *
+*                                                               *
+*   outputs: <string contents of foo.dat>                       *
+****************************************************************/
+
+File.prototype.read = function (aSize) 
+{
+
+  if (!this.checkInst())
+    return jslibErrorMsg("NS_ERROR_NOT_INITIALIZED");
+
+  if (this.mMode != JS_FILE_READ_MODE) {
+    this.close();
+    return jslibErrorMsg("NS_ERROR_NOT_AVAILABLE");
+  }
+
+  var rv = null;
+  try {
+    if (!this.mFileInst || !this.mInputStream) 
+      jslibErrorMsg("NS_ERROR_NOT_INITIALIZED");
+
+    rv = this.mInputStream.read(aSize != undefined ? aSize : this.mFileInst.fileSize);
+    this.mInputStream.close();
+  } catch (e) { 
+    rv = jslibError(e);
+  }
+  return rv;
+}
+
+/********************* READLINE**********************************
+* string readline()                                             *
+*                                                               *
+* reads a file if the file is binary it will                    *
+* return type string                                            *
+* takes no arguments needs an open read mode filehandle         *
+* returns string on success, null on failure                    *
+*   Ex:                                                         *
+*     var p='/tmp/foo.dat';                                     *
+*     var f=new File(p);                                        *
+*     f.open();                                                 *
+*     while(!f.EOF)                                             *
+*       dump("line: "+f.readline()+"\n");                       *
+*                                                               *
+*   outputs: <string line of foo.dat>                           *
+****************************************************************/
+
+File.prototype.readline = function ()
+{
+
+  if (!this.checkInst() || !this.mInputStream)
+    return jslibErrorMsg("NS_ERROR_NOT_INITIALIZED");
+
+  var rv      = null;
+  var buf     = null;
+  var tmp     = null;
+  try {
+    if (this.mLineBuffer.length < 2) {
+      buf = this.mInputStream.read(JS_FILE_CHUNK);
+      this.mPosition = this.mPosition + JS_FILE_CHUNK;
+      if (this.mPosition > this.mFileInst.fileSize) 
+        this.mPosition  = this.mFileInst.fileSize;
+      if (buf) {
+        if (this.mLineBuffer.length == 1) {
+          tmp = this.mLineBuffer.shift();
+          buf = tmp+buf;
+        }
+        this.mLineBuffer = buf.split(/[\n\r]/);
+      }
+    }
+    rv = this.mLineBuffer.shift();
+  } catch (e) {
+    rv = jslibError(e);
+  }
+  return rv;
+}
+
+/********************* EOF **************************************
+* bool getter EOF()                                             *
+*                                                               *
+* boolean check 'end of file' status                            *
+* return type boolean                                           *
+* takes no arguments needs an open read mode filehandle         *
+* returns true on eof, false when not at eof                    *
+*   Ex:                                                         *
+*     var p='/tmp/foo.dat';                                     *
+*     var f=new File(p);                                        *
+*     f.open();                                                 *
+*     while(!f.EOF)                                             *
+*       dump("line: "+f.readline()+"\n");                       *
+*                                                               *
+*   outputs: true or false                                      *
+****************************************************************/
+
+File.prototype.__defineGetter__('EOF', 
+function ()
+{
+  if (!this.checkInst() || !this.mInputStream)
+    throw jslibErrorThrow("NS_ERROR_NOT_INITIALIZED");
+
+  if ((this.mLineBuffer.length > 0) || (this.mInputStream.available() > 0)) 
+    return false;
+  
+  return true;
+})
+
+/********************* WRITE ************************************
+* write()                                                       *
+*                                                               *
+*  Write data to a file                                         *
+*                                                               *
+*   Ex:                                                         *
+*     var p='/tmp/foo.dat';                                     *
+*     var f=new File(p);                                        *
+*     f.open("w");                                              *
+*     f.write();                                                *
+*                                                               *
+*   outputs: JS_LIB_OK upon success                             *
+****************************************************************/
+
+File.prototype.write = function (aBuffer)
+{
+
+  if (!aBuffer)
+    return jslibErrorMsg("NS_ERROR_INVALID_ARG");
+
+  if (!this.checkInst())
+    return jslibErrorMsg("NS_ERROR_NOT_INITIALIZED");
+
+  if (this.mMode == JS_FILE_READ_MODE) {
+    this.close();
+    jslibErrorMsg("NS_ERROR_FILE_READ_ONLY");
+  }
+
+  if (!this.mFileInst)
+    return jslibErrorMsg("NS_ERROR_NOT_INITIALIZED")
+
+  var rv = null;
+  try {
+    this.mOutStream.write(aBuffer, aBuffer.length);
+    this.mOutStream.flush();
+    rv = JS_LIB_OK;
+  } catch (e) { 
+    rv = jslibError(e);
+  }
+
+  return rv;
+}
+
+/********************* COPY *************************************
+* void copy(aDest)                                              *
+*                                                               *
+* void file close                                               *
+* return type void(null)                                        *
+* takes no arguments closes an open file stream and             *
+* deletes member var instances of objects                       *
+*   Ex:                                                         *
+*     var p='/tmp/foo.dat';                                     *
+*     var f=new File(p);                                        *
+*     fopen();                                                  *
+*     f.close();                                                *
+*                                                               *
+*   outputs: JS_LIB_OK upon success                             *
+****************************************************************/
+
+File.prototype.copy = function (aDest)
+{
+
+  if (!aDest)
+    return jslibErrorMsg("NS_ERROR_INVALID_ARG");
+
+  if (!this.checkInst())
+    return jslibErrorMsg("NS_ERROR_NOT_INITIALIZED");
+
+  if (!this.exists()) 
+    return jslibErrorMsg("NS_ERROR_FILE_NOT_FOUND");
+
+  var rv = JS_LIB_OK;
+  try {
+    var dest = new JS_FS_File_Path(aDest);
+    var copyName, dir = null;
+
+    if (dest.equals(this.mFileInst)) 
+      return jslibErrorMsg("NS_ERROR_FILE_COPY_OR_MOVE_FAILED");
+
+    if (dest.exists()) 
+      return jslibErrorMsg("NS_ERROR_FILE_ALREADY_EXISTS");
+
+    if (this.mFileInst.isDirectory()) 
+      return jslibErrorMsg("NS_ERROR_FILE_IS_DIRECTORY");
+
+    if (!dest.exists()) {
+      copyName = dest.leafName;
+      dir = dest.parent;
+
+      if (!dir.exists()) 
+        return jslibErrorMsg("NS_ERROR_FILE_NOT_FOUND");
+
+      if (!dir.isDirectory()) 
+        return jslibErrorMsg("NS_ERROR_FILE_DESTINATION_NOT_DIR");
+    }
+
+    if (!dir) {
+      dir = dest;
+      if (dest.equals(this.mFileInst)) 
+        return jslibErrorMsg("NS_ERROR_FILE_COPY_OR_MOVE_FAILED");
+    }
+    this.mFileInst.copyTo(dir, copyName);
+    jslibDebug(JS_FILE_FILE+":copy successful!");
+  } catch (e) {
+    rv = jslibError(e);
+  }
+  return rv;
+}
+
+/********************* CLOSE ************************************
+* void close()                                                  *
+*                                                               *
+* void file close                                               *
+* return type void(null)                                        *
+* takes no arguments closes an open file stream and             *
+* deletes member var instances of objects                       *
+*   Ex:                                                         *
+*     var p='/tmp/foo.dat';                                     *
+*     var f=new File(p);                                        *
+*     fopen();                                                  *
+*     f.close();                                                *
+*                                                               *
+*   outputs: void(null)                                         *
+****************************************************************/
+
+File.prototype.close = function () 
+{
+  /***************** Destroy Instances *********************/
+  if (this.mFileChannel)   delete this.mFileChannel;
+  if (this.mInputStream)   delete this.mInputStream;
+  if (this.mTransport)     delete this.mTransport;
+  if (this.mMode)          this.mMode=null;
+  if (this.mOutStream) {
+    this.mOutStream.close();
+    delete this.mOutStream;
+  }
+  if (this.mLineBuffer)    this.mLineBuffer=null;
+  this.mPosition           = 0;
+  /***************** Destroy Instances *********************/
+
+  return void(null);
+}
+
+/********************* CREATE *****************************/
+File.prototype.create = function ()
+{
+
+  // We can probably implement this so that it can create a 
+  // file or dir if a long non-existent mPath is present
+
+  if (!this.checkInst())
+    return jslibErrorMsg("NS_ERROR_NOT_INITIALIZED");
+
+  if (this.exists()) 
+    return jslibErrorMsg("NS_ERROR_FILE_ALREADY_EXISTS");
+
+  var rv = null;
+  try { 
+    rv = this.mFileInst.create(JS_FILE_FILE_TYPE, JS_FILE_DEFAULT_PERMS); 
+  } catch (e) {
+    rv = jslibError(e);
+  }
+
+  return rv;
+}
+
+/********************* REMOVE *******************************/
+File.prototype.remove = function ()
+{
+  if (!this.checkInst())
+    return jslibErrorMsg("NS_ERROR_NOT_INITIALIZED");
+
+  if (!this.mPath) 
+    return jslibErrorMsg("NS_ERROR_FILE_INVALID_PATH");
+
+  this.close();
+  var rv;
+  try {
+    // this is a non recursive remove because we are only dealing w/ files.
+    rv = this.mFileInst.remove(false); 
+  } catch (e) {
+    rv = jslibError(e);
+  }
+
+  return rv;
+}
+
+/********************* POS **************************************
+* int getter POS()                                              *
+*                                                               *
+* int file position                                             *
+* return type int                                               *
+* takes no arguments needs an open read mode filehandle         *
+* returns current position, default is 0 set when               *
+* close is called                                               *
+*   Ex:                                                         *
+*     var p='/tmp/foo.dat';                                     *
+*     var f=new File(p);                                        *
+*     f.open();                                                 *
+*     while(!f.EOF){                                            *
+*       dump("pos: "+f.pos+"\n");                               *
+*       dump("line: "+f.readline()+"\n");                       *
+*     }                                                         *
+*                                                               *
+*   outputs: int pos                                            *
+****************************************************************/
+
+File.prototype.__defineGetter__('pos', function (){ return this.mPosition; })
+
+/********************* SIZE *************************************
+* int getter size()                                             *
+*                                                               *
+* int file size                                                 *
+* return type int                                               *
+* takes no arguments a getter only                              *
+*   Ex:                                                         *
+*     var p='/tmp/foo.dat';                                     *
+*     var f=new File(p);                                        *
+*     f.size;                                                   *
+*                                                               *
+*   outputs: int 16                                             *
+****************************************************************/
+
+File.prototype.__defineGetter__('size',
+function ()
+{
+
+  if (!this.checkInst())
+    return jslibErrorMsg("NS_ERROR_NOT_INITIALIZED");
+
+  if (!this.mPath) 
+    return jslibErrorMsg("NS_ERROR_FILE_INVALID_PATH");
+
+  if (!this.exists()) 
+    return jslibErrorMsg("NS_ERROR_FILE_NOT_FOUND");
+
+  var rv = null;
+  this.resetCache();
+  try { 
+    rv=this.mFileInst.fileSize; 
+  } catch(e) {
+    rv = jslibError(e);
+  }
+
+  return rv;
+}) // END size Getter
+
+/********************* EXTENSION ********************************
+* string getter ext()                                           *
+*                                                               *
+* string file extension                                         *
+* return type string                                            *
+* takes no arguments a getter only                              *
+*   Ex:                                                         *
+*     var p='/tmp/foo.dat';                                     *
+*     var f=new File(p);                                        *
+*     f.ext;                                                    *
+*                                                               *
+*   outputs: dat                                                *
+****************************************************************/
+
+File.prototype.__defineGetter__('ext', 
+function ()
+{
+
+  if (!this.checkInst())
+    return jslibErrorMsg("NS_ERROR_NOT_INITIALIZED");
+
+  if (!this.mPath) 
+    return jslibErrorMsg("NS_ERROR_FILE_INVALID_PATH");
+  
+  if (!this.exists()) 
+    return jslibErrorMsg("NS_ERROR_FILE_NOT_FOUND");
+
+  var rv = null;
+  try {
+    var leafName  = this.mFileInst.leafName;
+    var dotIndex  = leafName.lastIndexOf('.');
+    rv=(dotIndex >= 0) ? leafName.substring(dotIndex+1) : "";
+  } catch(e) {
+    rv = jslibError(e);
+  }
+
+  return rv;
+}) // END ext Getter
+
+File.prototype.super_help = FileSystem.prototype.help;
+
+/********************* HELP *****************************/
+File.prototype.__defineGetter__('help', 
+function ()
+{
+  const help = this.super_help()            +
+    "   open(aMode);\n"                     +
+    "   read();\n"                          +
+    "   readline();\n"                      +
+    "   EOF;\n"                             +
+    "   write(aContents, aPermissions);\n"  +
+    "   copy(aDest);\n"                     +
+    "   close();\n"                         +
+    "   create();\n"                        +
+    "   remove();\n"                        +
+    "   size;\n"                            +
+    "   ext;\n"                             +
+    "   help;\n";
+
+  return help;
+})
+
+jslibDebug('*** load: '+JS_FILE_FILE+' OK');
+
+} // END BLOCK JS_LIB_LOADED CHECK
+
+// If jslib base library is not loaded, dump this error.
+else
+{
+    dump("JS_FILE library not loaded:\n"                                +
+         " \tTo load use: chrome://jslib/content/jslib.js\n"            +
+         " \tThen: include(jslib_file);\n\n");
+}

Added: trunk/extensions/firefox-extension/chrome/content/jslib/io/fileUtils.js
==============================================================================
--- (empty file)
+++ trunk/extensions/firefox-extension/chrome/content/jslib/io/fileUtils.js	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,598 @@
+/*** -*- Mode: Javascript; tab-width: 2; -*-
+  
+  The contents of this file are subject to the Mozilla Public
+  License Version 1.1 (the "License"); you may not use this file
+  except in compliance with the License. You may obtain a copy of
+  the License at http://www.mozilla.org/MPL/
+  
+  Software distributed under the License is distributed on an "AS
+  IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+  implied. See the License for the specific language governing
+  rights and limitations under the License.
+  
+  The Original Code is Collabnet code.
+  The Initial Developer of the Original Code is Collabnet.
+  
+  Portions created by Collabnet are Copyright (C) 2000 Collabnet.
+  All Rights Reserved.
+  
+  Contributor(s): Pete Collins,
+                  Doug Turner,
+                  Brendan Eich,
+                  Warren Harris,
+                  Eric Plaster,
+                  Martin Kutschker
+                  Philip Lindsay
+  
+  
+  JS FileUtils IO API (The purpose of this file is to make it a little easier to do file IO from js) 
+  
+      fileUtils.js
+  
+  Function List
+  
+      chromeToPath(aPath)              // Converts a chrome://bob/content uri to a path.
+                                       // NOTE: although this gives you the
+                                       // path to a file in the chrome directory, you will
+                                       // most likely not have permisions
+                                       // to create or write to files there.
+      chromeToURL(aPath)               // Converts a chrome://bob/content file:// uri.
+      urlToPath(aPath)                 // Converts a file:// url to a path
+      exists(aPath);                   // check to see if a file exists
+      append(aDirPath, aFileName);     // append is for abstracting platform specific file paths
+      remove(aPath);                   // remove a file
+      copy(aSource, aDest);            // copy a file from source to destination
+      leaf(aPath);                     // leaf is the endmost file string
+                                       //  eg: foo.html in /myDir/foo.html
+      permissions(aPath);              // returns the files permissions
+      dateModified(aPath);             // returns the last modified date in locale string
+      size(aPath);                     // returns the file size
+      ext(aPath);                      // returns a file extension if there is one
+      parent(aPath)                    // returns the dir part of a path
+      dirPath(aPath)                   // *Depriciated* use parent 
+      spawn(aPath, aArgs)              // spawns another program 
+      nsIFile(aPath)                   // returns an nsIFile obj 
+      help;                            // currently returns a list of available functions 
+  
+    Deprecated
+  
+      chrome_to_path(aPath);           // synonym for chromeToPath
+      URL_to_path(aPath)               // synonym for use urlToPath
+      rm(aPath);                       // synonym for remove
+      extension(aPath);                // synonym for ext
+  
+  Instructions:
+  
+    First include this js file 
+  
+     var file = new FileUtils();
+  
+    Examples:
+  
+     var path='/usr/X11R6/bin/Eterm';
+     file.spawn(path, ['-e/usr/bin/vi']); 
+     *note* all args passed to spawn must be in the form of an array
+  
+     // to list help
+     dump(file.help);
+  
+    Warning: these API's are not for religious types
+  
+*******************************************/
+  
+  // Make sure jslib is loaded
+  if (typeof(JS_LIB_LOADED)=='boolean')
+  {
+  
+  /****************** Globals **********************/
+  
+  const JS_FILEUTILS_FILE                    = "fileUtils.js";
+  const JS_FILEUTILS_LOADED                  = true;
+  
+  const JS_FILEUTILS_LOCAL_CID               = "@mozilla.org/file/local;1";
+  const JS_FILEUTILS_FILESPEC_PROGID         = '@mozilla.org/filespec;1';
+  const JS_FILEUTILS_NETWORK_STD_CID         = '@mozilla.org/network/standard-url;1';
+  const JS_FILEUTILS_SIMPLEURI_PROGID        = "@mozilla.org/network/simple-uri;1";
+  const JS_FILEUTILS_CHROME_REG_PROGID       = '@mozilla.org/chrome/chrome-registry;1';
+  const JS_FILEUTILS_DR_PROGID               = "@mozilla.org/file/directory_service;1";
+  const JS_FILEUTILS_PROCESS_CID             = "@mozilla.org/process/util;1";
+  
+  const JS_FILEUTILS_I_LOCAL_FILE            = "nsILocalFile";
+  const JS_FILEUTILS_INIT_W_PATH             = "initWithPath";
+  const JS_FILEUTILS_I_PROPS                 = "nsIProperties";
+  
+  const JS_FILEUTILS_CHROME_DIR              = "AChrom";
+  
+  const JS_FILEUTILS_OK                      = true;
+  const JS_FILEUTILS_FilePath                = new C.Constructor(JS_FILEUTILS_LOCAL_CID, 
+                                                                 JS_FILEUTILS_I_LOCAL_FILE, 
+                                                                 JS_FILEUTILS_INIT_W_PATH);
+  
+  
+  /****************** FileUtils Object Class *********************/
+  function FileUtils () 
+  {
+    include (jslib_dirutils);
+    this.mDirUtils = new DirUtils();
+  } // constructor
+  
+  FileUtils.prototype  = 
+  {
+    mFileInst        : null,
+    mDirUtils        : null,
+  
+    /********************* CHROME_TO_PATH ***************************/
+    // this is here for backward compatability but is deprecated --pete
+    chrome_to_path : function (aPath) { return this.chromeToPath(aPath); },
+  
+    chromeToPath : function (aPath) 
+    {
+      if (!aPath)
+        return jslibErrorMsg("NS_ERROR_INVALID_ARG");
+  
+      return this.urlToPath(this.chromeToURL(aPath));
+    },
+  
+    chromeToURL : function (aPath) 
+    {
+      if (!aPath)
+        return jslibErrorMsg("NS_ERROR_INVALID_ARG");
+  
+      var uri = jslibCreateInstance(JS_FILEUTILS_SIMPLEURI_PROGID, "nsIURI");
+  
+      var rv = null;
+      if (/^chrome:/.test(aPath)) {
+        try {
+          var cr        = jslibGetService(JS_FILEUTILS_CHROME_REG_PROGID);
+          if (cr) {
+            cr          = jslibQI(cr, "nsIChromeRegistry");
+            uri.spec    = aPath;
+            uri.spec    = cr.convertChromeURL(uri);
+            rv          = uri.path;
+  
+            // deal w/ jar resource files
+            if (/.jar!/.test(rv)) {
+              rv = rv.replace(/resource:/, "");
+              rv =  "file://"+this.mDirUtils.getCurProcDir()+rv;
+            }
+          }
+        } catch (e) {}
+  
+        if (/^\/|\\|:chrome/.test(rv)) {
+          try {
+            // prepend the system path to this process dir
+            rv = "file:///"+(''+this.mDirUtils.getCurProcDir()+rv)
+                  .replace(/\\/g, "\/").replace(/^\s*\/?/, "").replace(/\ /g, "%20");
+          } catch (e) {
+            rv = jslibError(e);
+          }
+        }
+      } else if (/^file:/.test(aPath)) {
+        rv = this.urlToPath(aPath); 
+      } 
+
+      return rv;
+    },
+  
+    /********************* URL_TO_PATH ***************************/
+    URL_to_path : function (aPath) { return this.urlToPath(aPath); },
+  
+  urlToPath : function (aPath)
+  {
+    if (!aPath)
+      return jslibErrorMsg("NS_ERROR_INVALID_ARG");
+  
+    // xpcshell doesn't have unescape func
+    const hasUnescape = (typeof(unescape)=="function");
+    var path = aPath;
+    var rv;
+  
+    if (/^file:/.test(path)) {
+      try {
+        var uri = jslibCreateInstance(JS_FILEUTILS_NETWORK_STD_CID, "nsIURI");
+        uri.spec = path;
+        rv = uri.path; 
+  
+        var file = jslibCreateInstance(JS_FILEUTILS_LOCAL_CID, "nsILocalFile");
+  
+        // unix and friends
+        try {
+          file.initWithPath(rv);
+          rv = hasUnescape ? unescape(file.path) : file.path;
+          return rv;
+        } catch (e) {}
+  
+        // windows
+        try {
+          file.initWithPath(rv.replace(/^\//,"").replace(/\//g,"\\"));
+          rv = hasUnescape ? unescape(file.path) : file.path;
+          return rv;
+        } catch (e) {}
+  
+        // FIXME: add checking for Mac
+      
+      } catch (e) { 
+        rv = jslibError(e);
+      }
+    }
+  
+    return rv;
+  },
+  
+  /********************* EXISTS ***************************/
+  exists : function (aPath) 
+  {
+    if (!aPath)
+      return jslibErrorMsg("NS_ERROR_INVALID_ARG");
+  
+    var rv;
+    try { 
+      var file = new JS_FILEUTILS_FilePath(aPath);
+      rv = file.exists();
+    } catch (e) { 
+      rv = jslibError(e);
+    }
+  
+    return rv;
+  },
+  
+  /********************* RM *******************************/
+  rm : function (aPath) { return this.remove(aPath); },
+  
+  remove : function (aPath) 
+  {
+    if (!aPath) 
+      return jslibErrorMsg("NS_ERROR_INVALID_ARG");
+  
+    if (!this.exists(aPath))
+      return jslibErrorMsg("NS_ERROR_FILE_TARGET_DOES_NOT_EXIST");
+  
+    var rv;
+  
+    try { 
+      var fileInst = new JS_FILEUTILS_FilePath(aPath);
+      if (fileInst.isDirectory())
+        return jslibErrorMsg("NS_ERROR_INVALID_ARG");
+  
+      fileInst.remove(false);
+      rv = jslibRes.NS_OK;
+    } catch (e) { 
+      rv = jslibError(e);
+    }
+  
+    return rv;
+  },
+  
+  /********************* COPY *****************************/
+  copy  : function (aSource, aDest) 
+  {
+    if (!aSource || !aDest) 
+      return jslibErrorMsg("NS_ERROR_INVALID_ARG");
+  
+    if (!this.exists(aSource)) 
+      return jslibErrorMsg("NS_ERROR_UNEXPECTED");
+  
+    var rv;
+  
+    try { 
+      var fileInst = new JS_FILEUTILS_FilePath(aSource);
+      var dir      = new JS_FILEUTILS_FilePath(aDest);
+      var copyName = fileInst.leafName;
+  
+      if (fileInst.isDirectory())
+        return jslibErrorMsg("NS_ERROR_FILE_COPY_OR_MOVE_FAILED");
+  
+      if (!this.exists(aDest) || !dir.isDirectory()) {
+        copyName   = dir.leafName;
+        dir        = new JS_FILEUTILS_FilePath(dir.path.replace(copyName,''));
+  
+        if (!this.exists(dir.path)) 
+          return jslibErrorMsg("NS_ERROR_FILE_ALREADY_EXISTS");
+  
+        if (!dir.isDirectory())
+          return jslibErrorMsg("NS_ERROR_FILE_INVALID_PATH");
+      }
+  
+      if (this.exists(this.append(dir.path, copyName))) 
+        return jslibError("NS_ERROR_FILE_ALREADY_EXISTS");
+  
+      rv = fileInst.copyTo(dir, copyName);
+      rv = jslibRes.NS_OK;
+    } catch (e) { 
+      return jslibError(e);
+    }
+  
+    return rv;
+  },
+  
+  /********************* LEAF *****************************/
+  leaf  : function (aPath) 
+  {
+    if (!aPath)
+      return jslibErrorMsg("NS_ERROR_INVALID_ARG");
+  
+    if (!this.exists(aPath)) 
+      jslibErrorWarn("NS_ERROR_FILE_NOT_FOUND ["+aPath+"]")
+  
+    var rv;
+  
+    try {
+      var fileInst = new JS_FILEUTILS_FilePath(aPath);
+      rv=fileInst.leafName;
+    }
+  
+    catch (e) { 
+      return jslibError(e);
+    }
+  
+    return rv;
+  },
+  
+  /********************* APPEND ***************************/
+  append : function (aDirPath, aFileName) 
+  {
+    if (!aDirPath || !aFileName)
+      jslibErrorMsg("NS_ERROR_INVALID_ARG");
+  
+    if (!this.exists(aDirPath)) 
+      return jslibErrorMsg("NS_ERROR_FILE_NOT_FOUND");
+  
+    var rv;
+  
+    try { 
+      var fileInst = new JS_FILEUTILS_FilePath(aDirPath);
+      if (fileInst.exists() && !fileInst.isDirectory()) 
+        return jslibErrorMsg("NS_ERROR_INVALID_ARG");
+  
+      fileInst.append(aFileName);
+      rv=fileInst.path;
+      delete fileInst;
+    } catch (e) { 
+      return jslibError(e);
+    }
+  
+    return rv;
+  },
+  
+  /********************* VALIDATE PERMISSIONS *************/
+  validatePermissions : function(aNum) 
+  {
+    if ( parseInt(aNum.toString(10).length) < 3 ) 
+      return false;
+  
+    return JS_FILEUTILS_OK;
+  },
+  
+  /********************* PERMISSIONS **********************/
+  permissions : function (aPath) 
+  {
+    if (!aPath)
+      return jslibErrorMsg("NS_ERROR_INVALID_ARG");
+  
+    if (!this.exists(aPath)) 
+      return jslibErrorMsg("NS_ERROR_FILE_NOT_FOUND");
+  
+    var rv;
+  
+    try { 
+      rv=(new JS_FILEUTILS_FilePath(aPath)).permissions.toString(8);
+    } catch (e) { 
+      rv = jslibError(e);
+    }
+  
+    return rv;
+  },
+  
+  /********************* MODIFIED *************************/
+  dateModified  : function (aPath) 
+  {
+    if (!aPath)
+      return jslibErrorMsg("NS_ERROR_INVALID_ARG");
+  
+    if (!this.exists(aPath)) 
+      jslibErrorMsg("NS_ERROR_FILE_NOT_FOUND");
+  
+    var rv;
+  
+    try { 
+      var date = new Date((new JS_FILEUTILS_FilePath(aPath)).lastModifiedTime).toLocaleString();
+      rv=date;
+    } catch (e) { 
+      rv = jslibError(e);
+    }
+  
+    return rv;
+  },
+  
+  /********************* SIZE *****************************/
+  size  : function (aPath) 
+  {
+    if (!aPath)
+      return jslibErrorMsg("NS_ERROR_INVALID_ARG");
+  
+    if (!this.exists(aPath)) 
+      return jslibErrorMsg("NS_ERROR_FILE_NOT_FOUND");
+  
+    var rv;
+  
+    try { 
+      rv = (new JS_FILEUTILS_FilePath(aPath)).fileSize;
+    } catch (e) { 
+      jslibError(e);
+      rv=0;
+    }
+  
+    return rv;
+  },
+  
+  /********************* EXTENSION ************************/
+  extension  : function (aPath) { return this.ext(aPath); },
+  
+  ext  : function (aPath)
+  {
+    if (!aPath) 
+      return jslibErrorMsg("NS_ERROR_INVALID_ARG");
+  
+    if (!this.exists(aPath)) 
+      return jslibErrorMsg("NS_ERROR_FILE_NOT_FOUND");
+  
+    var rv;
+  
+    try { 
+      var leafName  = (new JS_FILEUTILS_FilePath(aPath)).leafName;
+      var dotIndex  = leafName.lastIndexOf('.'); 
+      rv=(dotIndex >= 0) ? leafName.substring(dotIndex+1) : ""; 
+    } catch (e) { 
+      return jslibError(e);
+    }
+  
+    return rv;
+  },
+  
+  /********************* DIRPATH **************************/
+  dirPath   : function (aPath) { return this.parent(aPath); }, 
+  
+  parent   : function (aPath) 
+  {
+    if (!aPath)
+      return jslibErrorMsg("NS_ERROR_INVALID_ARG");
+  
+    var rv;
+  
+    try { 
+      var fileInst = new JS_FILEUTILS_FilePath(aPath);
+  
+      if (!fileInst.exists()) 
+        return jslibErrorMsg("NS_ERROR_FILE_NOT_FOUND");
+  
+      if (fileInst.isFile())
+        rv = fileInst.parent.path;
+  
+      else if (fileInst.isDirectory())
+        rv = fileInst.path;
+  
+      else
+        rv = null;
+    }
+  
+    catch (e) { 
+      return jslibError(e);
+    }
+  
+    return rv;
+  },
+  
+  /********************* SPAWN ****************************/
+  spawn : function (aPath, aArgs) { this.run(aPath, aArgs); },
+  run : function (aPath, aArgs) 
+  /*
+   * Trys to execute the requested file as a separate *non-blocking* process.
+   * 
+   * Passes the supplied *array* of arguments on the command line if
+   * the OS supports it.
+   *
+   */
+  {
+    if (!aPath) 
+      return jslibErrorMsg("NS_ERROR_INVALID_ARG");
+  
+    if (!this.exists(aPath)) 
+      return jslibErrorMsg("NS_ERROR_FILE_NOT_FOUND");
+  
+    var len = 0;
+    if (aArgs)
+      len = aArgs.length;
+    else
+      aArgs = null;
+  
+    var rv;
+    try { 
+      var fileInst = new JS_FILEUTILS_FilePath(aPath);
+  
+      if (!fileInst.isExecutable()) 
+        return jslibErrorMsg("NS_ERROR_INVALID_ARG");
+  
+      if (fileInst.isDirectory())
+        return jslibErrorMsg("NS_ERROR_FILE_IS_DIRECTORY");
+        // Create and execute the process...
+        /*
+         * NOTE: The first argument of the process instance's 'run' method
+         *       below specifies the blocking state (false = non-blocking).
+         *       The last argument, in theory, contains the process ID (PID)
+         *       on return if a variable is supplied--not sure how to implement
+         *       this with JavaScript though.
+         */
+        try {
+          var theProcess = jslibCreateInstance(JS_FILEUTILS_PROCESS_CID, "nsIProcess");
+          
+          theProcess.init(fileInst);
+  
+          rv = theProcess.run(false, aArgs, len);
+          jslib_debug("rv="+rv);
+        } catch (e) {
+          rv = jslibError(e);
+        }
+    } catch (e) { 
+      rv = jslibError(e);
+    }
+  
+    return rv;
+  },
+  
+  /********************* nsIFILE **************************/
+  nsIFile : function (aPath) 
+  {
+    if (!aPath) 
+      return jslibErrorMsg("NS_ERROR_INVALID_ARG");
+  
+    var rv;
+    try {
+      rv = new JS_FILEUTILS_FilePath(aPath);
+    } catch (e) { 
+      rv = jslibError(e);
+    }
+  
+    return rv;
+  },
+  
+  /********************* HELP *****************************/
+  get help() 
+  {
+    var help =
+  
+      "\n\nFunction List:\n"                  +
+      "\n"                                    +
+      "   exists(aPath);\n"                   +
+      "   chromeToPath(aPath);\n"             +
+      "   chromeToURL(aPath);\n"              +
+      "   urlToPath(aPath);\n"                +
+      "   append(aDirPath, aFileName);\n"     +
+      "   remove(aPath);\n"                   +
+      "   copy(aSource, aDest);\n"            +
+      "   leaf(aPath);\n"                     +
+      "   permissions(aPath);\n"              +
+      "   dateModified(aPath);\n"             +
+      "   size(aPath);\n"                     +
+      "   ext(aPath);\n"                      +
+      "   parent(aPath);\n"                   + 
+      "   run(aPath, aArgs);\n"               + 
+      "   nsIFile(aPath);\n"                  + 
+      "   help;\n";
+  
+    return help;
+  }
+  
+};
+  
+jslibDebug('*** load: '+JS_FILEUTILS_FILE+' OK');
+  
+} // END BLOCK JS_LIB_LOADED CHECK
+  
+// If jslib base library is not loaded, dump this error.
+else
+{
+    dump("JS_FILE library not loaded:\n"                                +
+         " \tTo load use: chrome://jslib/content/jslib.js\n"            +
+         " \tThen: include(jslib_fileutils);\n\n");
+}
+  

Added: trunk/extensions/firefox-extension/chrome/content/jslib/io/filesystem.js
==============================================================================
--- (empty file)
+++ trunk/extensions/firefox-extension/chrome/content/jslib/io/filesystem.js	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,648 @@
+/*** -*- Mode: Javascript; tab-width: 2;
+
+The contents of this file are subject to the Mozilla Public
+License Version 1.1 (the "License"); you may not use this file
+except in compliance with the License. You may obtain a copy of
+the License at http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS
+IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+implied. See the License for the specific language governing
+rights and limitations under the License.
+
+The Original Code is Collabnet code.
+The Initial Developer of the Original Code is Collabnet.
+
+Portions created by Collabnet are
+Copyright (C) 2000 Collabnet.  All
+Rights Reserved.
+
+Contributor(s): Pete Collins, 
+                Doug Turner, 
+                Brendan Eich, 
+                Warren Harris, 
+                Eric Plaster,
+                Martin Kutschker
+***/
+
+if (typeof(JS_LIB_LOADED)=='boolean') {
+
+/***************************
+* Globals                  *
+***************************/
+
+const JS_FILESYSTEM_LOADED = true;
+const JS_FILESYSTEM_FILE   = "filesystem.js";
+const JS_FS_LOCAL_CID      = "@mozilla.org/file/local;1";
+const JS_FS_DIR_CID        = "@mozilla.org/file/directory_service;1";
+const JS_FS_NETWORK_CID    = '@mozilla.org/network/standard-url;1';
+const JS_FS_URL_COMP       = "nsIURL";
+const JS_FS_I_LOCAL_FILE   = "nsILocalFile";
+const JS_FS_DIR_I_PROPS    = "nsIProperties";
+const JS_FS_INIT_W_PATH    = "initWithPath";
+const JS_FS_CHROME_DIR     = "AChrom";
+const JS_FS_USR_DEFAULT    = "DefProfRt";
+const JS_FS_PREF_DIR       = "PrefD";
+const JS_FS_OK             = true;
+const JS_FS_File_Path      = new Components.Constructor
+  (JS_FS_LOCAL_CID, JS_FS_I_LOCAL_FILE, JS_FS_INIT_W_PATH);
+const JS_FS_Dir            = new Components.Constructor
+  (JS_FS_DIR_CID, JS_FS_DIR_I_PROPS);
+const JS_FS_URL            = new Components.Constructor
+  (JS_FS_NETWORK_CID, JS_FS_URL_COMP);
+
+/***************************
+* Globals                  *
+***************************/
+
+/***************************
+* FileSystem Object Class  *
+***************************/
+function FileSystem(aPath) 
+{
+  return (aPath?this.initPath(arguments):void(null));
+} // constructor
+
+/***************************
+* FileSystem Prototype     *
+***************************/
+FileSystem.prototype  = {
+
+  mPath           : null,
+  mFileInst       : null,
+
+/***************************
+* INIT PATH                *
+***************************/
+initPath : function(args)
+{
+   // check if the argument is a file:// url
+   var fileURL;
+   if(typeof(args)=='object') {
+      for (var i=0; i<args.length; i++) {
+         if(args[i].search(/^file:/) == 0) {
+            try {
+               fileURL= new JS_FS_URL();
+               fileURL.spec=args[i];
+               args[i] = fileURL.path;
+            } catch (e) { 
+               jslibError(e, "(problem getting file instance)", 
+                     "NS_ERROR_UNEXPECTED", JS_FILESYSTEM_FILE+":initPath");
+               rv=null;
+            }
+         }
+      }
+   } else {
+      if(args.search(/^file:/) == 0) {
+         try {
+            fileURL= new JS_FS_URL();
+            fileURL.spec=args;
+            args = fileURL.path;
+         } catch (e) { 
+            jslibError(e, "(problem getting file instance)", 
+                  "NS_ERROR_UNEXPECTED", JS_FILESYSTEM_FILE+":initPath");
+            rv=null;
+         }
+      }
+   }
+
+  /** 
+   * If you are wondering what all this extra cruft is, well
+   * this is here so you can reinitialize 'this' with a new path
+   */
+  var rv = null;
+  try {
+    if (typeof(args)=='object') {
+      this.mFileInst = new JS_FS_File_Path(args[0]?args[0]:this.mPath);
+      if (args.length>1)
+        for (i=1; i<args.length; i++)
+          this.mFileInst.append(args[i]);
+      (args[0] || this.mPath)?rv=this.mPath = this.mFileInst.path:rv=null;
+    } else {
+      this.mFileInst = new JS_FS_File_Path(args?args:this.mPath);
+      this.mFileInst.path?rv=this.mPath = this.mFileInst.path:rv=null;
+    }
+  } catch(e) {
+    jslibError(e?e:null, 
+      "initPath (nsILocalFile problem)", 
+      "NS_ERROR_UNEXPECTED", 
+      JS_FILESYSTEM_FILE+":initPath");
+    rv = null;
+  }
+  return rv;
+},
+
+/***************************
+*  CHECK INST              *
+***************************/
+checkInst : function () 
+{
+  if (!this.mFileInst) {
+    jslibError(null, 
+      "(no path defined)", 
+      "NS_ERROR_NOT_INITIALIZED", 
+      JS_FILESYSTEM_FILE+":checkInstance");
+    return false;
+  }
+  return true;
+},
+
+/***************************
+*  PATH                    *
+***************************/
+get path() 
+{ 
+  if (!this.checkInst())
+    throw jslibRes.NS_ERROR_NOT_INITIALIZED;
+  return this.mFileInst.path; 
+},
+
+/***************************
+*  EXISTS                  *
+***************************/
+exists : function ()
+{
+  if (!this.checkInst())
+    throw jslibRes.NS_ERROR_NOT_INITIALIZED;
+  var rv = false;
+  try { 
+    rv = this.mFileInst.exists(); 
+  } catch(e) {
+    jslibError(e, 
+      "exists (nsILocalFile problem)", 
+      "NS_ERROR_UNEXPECTED", 
+      JS_FILESYSTEM_FILE+":exists");
+    rv = false;
+  }
+  return rv;
+},
+
+/***************************
+*  GET LEAF                *
+***************************/
+get leaf()
+{
+  if (!this.checkInst())
+    throw jslibRes.NS_ERROR_NOT_INITIALIZED;
+  var rv = null;
+  try { 
+    rv = this.mFileInst.leafName; 
+  } catch(e) {
+    jslibError(e, 
+      "(problem getting file instance)", 
+      "NS_ERROR_FAILURE", 
+      JS_FILESYSTEM_FILE+":leaf");
+    rv = null;
+  }
+  return rv;
+},
+
+/***************************
+*  SET LEAF                *
+***************************/
+set leaf(aLeaf)
+{
+  if (!aLeaf) {
+    jslibError(null, 
+      "(missing argument)", 
+      "NS_ERROR_INVALID_ARG", 
+      JS_FILESYSTEM_FILE+":leaf");
+    return null;
+  }
+  if (!this.checkInst())
+    throw jslibRes.NS_ERROR_NOT_INITIALIZED;
+  var rv = null;
+  try { 
+    rv = (this.mFileInst.leafName=aLeaf); 
+  } catch(e) {
+    jslibError(e, 
+        "(problem getting file instance)", 
+        "NS_ERROR_FAILURE", 
+        JS_FILESYSTEM_FILE+":leaf");
+    rv = null;
+  }
+  return rv;
+},
+
+/***************************
+*  PARENT                  *
+***************************/
+get parent()
+{
+  if (!this.checkInst())
+    throw jslibRes.NS_ERROR_NOT_INITIALIZED;
+  var rv = null;
+  try { 
+    if (this.mFileInst.parent.isDirectory()) {
+      if (typeof(JS_DIR_LOADED)!='boolean')
+        include(JS_LIB_PATH+'io/dir.js');
+      rv = new Dir(this.mFileInst.parent.path);
+    }
+  } catch (e) {
+    jslibError(e, 
+      "(problem getting file parent)", 
+      "NS_ERROR_UNEXPECTED", 
+      JS_FILESYSTEM_FILE+":parent");
+    rv = null;
+  }
+  return rv;
+},
+
+/***************************
+*  GET PERMISSIONS         *
+***************************/
+get permissions()
+{
+  if (!this.checkInst())
+    throw jslibRes.NS_ERROR_NOT_INITIALIZED;
+  if (!this.exists()) {
+    jslibError(null, 
+      "(file doesn't exist)", 
+      "NS_ERROR_FAILURE", 
+      JS_FILESYSTEM_FILE+":permisions");
+    return null;
+  }
+  var rv = null;
+  try { 
+    rv = this.mFileInst.permissions.toString(8); 
+  } catch(e) {
+    jslibError(e, 
+                "(problem getting file instance)", 
+                "NS_ERROR_UNEXPECTED", 
+                JS_FILESYSTEM_FILE+":permissions");
+    rv = null;
+  }
+  return rv;
+},
+
+/***************************
+*  SET PERMISSIONS         *
+***************************/
+set permissions(aPermission)
+{
+  if (!this.checkInst())
+    throw jslibRes.NS_ERROR_NOT_INITIALIZED;
+
+  if (!aPermission) {
+    jslibError(null, 
+      "(no new permission defined)", 
+      "NS_ERROR_INVALID_ARG", 
+      JS_FILESYSTEM_FILE+":permissions");
+    return null;
+  }
+  if (!this.exists()) {
+    jslibError(null, 
+                "(file doesn't exist)", 
+                "NS_ERROR_FAILURE", 
+                JS_FILESYSTEM_FILE+":permisions");
+    return null;
+  }
+  if (!this.validatePermissions(aPermission)) {
+    jslibError(null, 
+                "(invalid permission argument)", 
+                "NS_ERROR_INVALID_ARG", 
+                JS_FILESYSTEM_FILE+":permissions");
+    return null;
+  }
+  var rv = null;
+  try { 
+    rv = this.mFileInst.permissions=aPermission; 
+  } catch(e) {
+    jslibError(e, 
+      "(problem getting file instance)", 
+      "NS_ERROR_UNEXPECTED", 
+      JS_FILESYSTEM_FILE+":permissions");
+    rv = null;
+  }
+  return rv;
+
+},
+
+/***************************
+*  VALIDATE PERMISSIONS    *
+***************************/
+validatePermissions : function (aNum)
+{
+  if (typeof(aNum)!='number')
+    return false;
+  if (parseInt(aNum.toString(10).length) < 3 )
+    return false;
+  return true;
+},
+
+/***************************
+*  MODIFIED                *
+***************************/
+get dateModified()
+{
+  if (!this.checkInst())
+    throw jslibRes.NS_ERROR_NOT_INITIALIZED;
+  if (!this.exists()) {
+    jslibError(null, 
+      "(file doesn't exist)", 
+      "NS_ERROR_FAILURE", 
+      JS_FILESYSTEM_FILE+":dateModified");
+    return null;
+  }
+  var rv = null;
+  try { 
+    rv = (new Date(this.mFileInst.lastModifiedTime)); 
+  } catch(e) {
+    jslibError(e, 
+      "(problem getting file instance)", 
+      "NS_ERROR_UNEXPECTED", 
+      JS_FILESYSTEM_FILE+":dateModified");
+    rv = null;
+  }
+  return rv;
+},
+
+/***************************
+*  RESET CACHE             *
+***************************/
+resetCache : function()
+{
+  if (!this.checkInst())
+    throw jslibRes.NS_ERROR_NOT_INITIALIZED;
+  var rv = false;
+  if (this.mPath) {
+    delete this.mFileInst;
+    try {
+      this.mFileInst=new JS_FS_File_Path(this.mPath);
+      rv = true;
+    } catch(e) {
+      jslibError(e, 
+        "(unable to get nsILocalFile)", 
+        "NS_ERROR_UNEXPECTED", 
+        JS_FILESYSTEM_FILE+":resetCache");
+      rv = false;
+    }
+  }
+  return rv;
+},
+
+/***************************
+*  nsIFILE                 *
+***************************/
+get nsIFile()
+{
+  if (!this.checkInst())
+    throw jslibRes.NS_ERROR_NOT_INITIALIZED;
+  var rv = null;
+  try { 
+    rv = this.mFileInst.clone(); 
+  } catch (e) {
+    jslibError(e, 
+      "(problem getting file instance)", 
+      "NS_ERROR_UNEXPECTED", 
+      JS_FILESYSTEM_FILE+":nsIFile");
+    rv = null;
+  }
+  return rv;
+},
+
+/***************************
+*  NOTE: after a move      *
+*  successful, 'this' will *
+*  be reinitialized        *
+*  to the moved file!      *
+***************************/
+move : function (aDest)
+{
+  if (!this.checkInst())
+    throw jslibRes.NS_ERROR_NOT_INITIALIZED;
+  if (!aDest) {
+    jslibError(null, 
+      "(no destination path defined)", 
+      "NS_ERROR_INVALID_ARG", 
+      JS_FILESYSTEM_FILE+":move");
+    return false;
+  }
+  if (!this.mPath) {
+    jslibError(null, 
+      "(no path defined)", 
+      "NS_ERROR_INVALID_ARG", 
+      JS_FILESYSTEM_FILE+":move");
+    return false;
+  }
+  var rv = null;
+  var newName=null;
+  try {
+    var f = new JS_FS_File_Path(aDest);
+    if (f.exists() && !f.isDirectory()) {
+      jslibError(null, 
+        "(destination file exists remove it)", 
+        "NS_ERROR_INVALID_ARG", 
+        JS_FILESYSTEM_FILE+":move");
+      return false;
+    }
+    if (f.equals(this.mFileInst)) {
+      jslibError(null, 
+        "(destination file is this file)", 
+        "NS_ERROR_INVALID_ARG", 
+        JS_FILESYSTEM_FILE+":move");
+      return false;
+    }
+    if (!f.exists() && f.parent.exists())
+      newName=f.leafName;
+    if (f.equals(this.mFileInst.parent) && !newName) {
+      jslibError(null,  
+        "(destination file is this file)", 
+        "NS_ERROR_INVALID_ARG", 
+        JS_FILESYSTEM_FILE+":move");
+      return false;
+    }
+    var dir=f.parent;
+    if (dir.exists() && dir.isDirectory()) {
+      jslibDebug(newName);
+      this.mFileInst.moveTo(dir, newName);
+      jslibDebug(JS_FILESYSTEM_FILE+':move successful!\n');
+      this.mPath=f.path;
+      this.resetCache();
+      delete dir;
+      rv = true;
+    } else {
+      jslibError(null, 
+        "(destination "+dir.parent.path+" doesn't exists)", 
+        "NS_ERROR_INVALID_ARG", 
+        JS_FILESYSTEM_FILE+":move");
+      return false;
+    }
+  } catch (e) {
+    jslibError(e, 
+      "(problem getting file instance)", 
+      "NS_ERROR_UNEXPECTED", 
+      JS_FILESYSTEM_FILE+":move");
+    rv = false;
+  }
+  return rv;
+},
+
+/***************************
+*  APPEND                  *
+***************************/
+append : function(aLeaf)
+{
+  if (!this.checkInst())
+    throw jslibRes.NS_ERROR_NOT_INITIALIZED;
+  if (!aLeaf) {
+    jslibError(null, 
+      "(no argument defined)", 
+      "NS_ERROR_INVALID_ARG", 
+      JS_FILESYSTEM_FILE+":append");
+    return null;
+  }
+  if (!this.mPath) {
+    jslibError(null, 
+      "(no path defined)", 
+      "NS_ERROR_INVALID_ARG", 
+      JS_FILESYSTEM_FILE+":append");
+    return null;
+  }
+  var rv = null;
+  try {
+    this.mFileInst.append(aLeaf);
+    rv = this.mPath=this.path;
+  } catch(e) {
+    jslibError(null, 
+      "(unexpected error)", 
+      "NS_ERROR_UNEXPECTED", 
+      JS_FILESYSTEM_FILE+":append");
+    rv = null;
+  }
+  return rv;
+},
+
+/***************************
+*  APPEND RELATIVE         *
+***************************/
+appendRelativePath : function(aRelPath)
+{
+  if (!this.checkInst())
+    throw jslibRes.NS_ERROR_NOT_INITIALIZED;
+  if (!aRelPath) {
+    jslibError(null, 
+      "(no argument defined)", 
+      "NS_ERROR_INVALID_ARG", 
+      JS_FILESYSTEM_FILE+":appendRelativePath");
+    return null;
+  }
+  if (!this.mPath) {
+    jslibError(null, 
+      "(no path defined)", 
+      "NS_ERROR_INVALID_ARG", 
+      JS_FILESYSTEM_FILE+":appendRelativePath");
+    return null;
+  }
+  var rv = null;
+  try {
+    this.mFileInst.appendRelativePath(aRelPath);
+    rv = this.mPath=this.path;
+  } catch(e) {
+    jslibError(null, 
+      "(unexpected error)", 
+      "NS_ERROR_UNEXPECTED", 
+      JS_FILESYSTEM_FILE+":appendRelativePath");
+    rv = null;
+  }
+  return rv;
+},
+
+/***************************
+*  GET URL                 *
+***************************/
+get URL()
+{
+  return (this.path?'file:///'+this.path.replace(/\\/g, "\/").replace(/^\s*\/?/, "").replace(/\ /g, "%20"):'');
+},
+
+/***************************
+*  ISDIR                   *
+***************************/
+isDir : function()
+{
+  var rv = false;
+  try {
+    rv = this.mFileInst.isDirectory();
+  } catch (e) { rv = false; }
+    
+  return rv;
+},
+
+/***************************
+*  ISFILE                  *
+***************************/
+isFile : function()
+{
+  var rv = false;
+  try {
+    rv = this.mFileInst.isFile();
+  } catch (e) { rv = false; }
+    
+  return rv;
+},
+
+/***************************
+*  ISEXEC                  *  
+***************************/
+isExec : function()
+{
+  var rv = false;
+  try {
+    rv = this.mFileInst.isExecutable();
+  } catch (e) { rv = false; }
+    
+  return rv;
+},
+
+/***************************
+*  ISSYMLINK               *
+***************************/
+isSymlink : function()
+{
+  var rv = false;
+  try {
+    rv = this.mFileInst.isSymlink();
+  } catch (e) { rv = false; }
+    
+  return rv;
+},
+
+/***************************
+*  HELP                    *
+***************************/
+help  : function()
+{
+  const help =
+    "\n\nFunction and Attribute List:\n"    +
+    "\n"                                    +
+    "   initPath(aPath);\n"                 +
+    "   path;\n"                            +
+    "   exists();\n"                        +
+    "   leaf;\n"                            +
+    "   parent;\n"                          +
+    "   permissions;\n"                     +
+    "   dateModified;\n"                    +
+    "   nsIFile;\n"                         +
+    "   move(aDest);\n"                     +
+    "   append(aLeaf);\n"                   +
+    "   appendRelativePath(aRelPath);\n"    +
+    "   URL;\n"                             +
+    "   isDir();\n"                         +
+    "   isFile();\n"                        +
+    "   isExec();\n"                        +
+    "   isSymlink();\n";
+  return help;
+} 
+
+}; // END FileSystem Class
+
+jslibDebug('*** load: '+JS_FILESYSTEM_FILE+' OK');
+
+} // END BLOCK JS_LIB_LOADED CHECK
+
+else {
+    dump("JS_FILE library not loaded:\n"                                +
+         " \tTo load use: chrome://jslib/content/jslib.js\n"            +
+         " \tThen: include(jslib_filesystem);\n\n");
+}

Added: trunk/extensions/firefox-extension/chrome/content/jslib/jslib.js
==============================================================================
--- (empty file)
+++ trunk/extensions/firefox-extension/chrome/content/jslib/jslib.js	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,264 @@
+/*** -*- Mode: Javascript; tab-width: 2;
+
+The contents of this file are subject to the Mozilla Public
+License Version 1.1 (the "License"); you may not use this file
+except in compliance with the License. You may obtain a copy of
+the License at http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS
+IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+implied. See the License for the specific language governing
+rights and limitations under the License.
+
+The Original Code is jslib team code.
+The Initial Developer of the Original Code is jslib team.
+
+Portions created by jslib team are
+Copyright (C) 2000 jslib team.  All
+Rights Reserved.
+
+Original Author: Pete Collins <pete mozdevgroup com>
+Contributor(s): Martin Kutschker <Martin T Kutschker blackbox net>
+
+***/
+
+/**
+ * insure jslib base is not already loaded
+ */
+if (typeof(JS_LIB_LOADED) != "boolean")
+{
+  try {
+
+  const JS_LIB_LOADED     = true;
+
+  const JS_LIBRARY        = "jslib";
+  const JS_LIB_FILE       = "jslib.js"
+  const JS_LIB_PATH       = "chrome://beagle/content/jslib/";
+  const JS_LIB_VERSION    = "0.1.184-modified";
+  const JS_LIB_AUTHORS    = "\tPete Collins       <pete mozdevgroup com>\n"
+                          + "\tNeil Deakin        <neil mozdevgroup com>\n"
+                          + "\tEric Plaster       <plaster urbanrage com>\n"
+                          + "\tMartin.T.Kutschker <Martin T Kutschker blackbox net>\n";
+  const JS_LIB_BUILD      = "mozilla 1.3+";
+  const JS_LIB_ABOUT      = "\tThis is an effort to provide a fully "
+                          + "functional js library\n"
+                          + "\tfor mozilla package authors to use "
+                          + "in their applications\n";
+  const JS_LIB_HOME       = "http://jslib.mozdev.org/";;
+
+  const ON                = true;
+  const OFF               = false;
+  const C                 = Components;
+  const jslibRes          = C.results;
+  const jslibI            = C.interfaces;
+
+  const JS_LIB_OK         = jslibRes.NS_OK;
+
+  // DEPRICATED
+  const jslib_results     = jslibRes;
+
+  if (typeof(JS_LIB_DEBUG) != "boolean")
+    var JS_LIB_DEBUG      = ON;
+
+  var JS_LIB_DEBUG_ALERT  = OFF;
+  var JS_LIB_ERROR        = ON;
+  var JS_LIB_ERROR_ALERT  = OFF;
+
+  const JS_LIB_HELP       = "\n\nWelcome to jslib version "+JS_LIB_VERSION+"\n\n"
+                          + "Global Constants:\n\n"
+                          + "JS_LIBRARY     \n\t"+JS_LIBRARY     +"\n"
+                          + "JS_LIB_FILE    \n\t"+JS_LIB_FILE    +"\n"
+                          + "JS_LIB_PATH    \n\t"+JS_LIB_PATH    +"\n"
+                          + "JS_LIB_VERSION \n\t"+JS_LIB_VERSION +"\n"
+                          + "JS_LIB_AUTHORS \n"  +JS_LIB_AUTHORS
+                          + "JS_LIB_BUILD   \n\t"+JS_LIB_BUILD   +"\n"
+                          + "JS_LIB_ABOUT   \n"  +JS_LIB_ABOUT
+                          + "JS_LIB_HOME    \n\t"+JS_LIB_HOME    +"\n\n"
+                          + "Global Variables:\n\n"
+                          + "  JS_LIB_DEBUG\n  JS_LIB_ERROR\n\n";
+
+
+  function jslibGetService (aURL, aInterface)
+  {
+    var rv;
+    try {
+      // determine how 'aInterface' is passed and handle accordingly
+      switch (typeof(aInterface))
+      {
+        case "object":
+          rv = C.classes[aURL].getService(aInterface);
+          break;
+
+        case "string":
+          rv = C.classes[aURL].getService(C.interfaces[aInterface]);
+          break;
+
+        default:
+          rv = C.classes[aURL].getService();
+          break;
+      }
+    } catch (e) {
+      rv = -1;
+    }
+    return rv;
+  }
+
+  function jslibCreateInstance (aURL, aInterface)
+  {
+    var rv;
+    try {
+      rv = C.classes[aURL].createInstance(C.interfaces[aInterface]);
+    } catch (e) {
+      rv = -1;
+    }
+    return rv;
+  }
+
+  function jslibGetInterface (aInterface)
+  {
+    var rv;
+    try {
+      rv = C.interfaces[aInterface];
+    } catch (e) {
+      rv = -1;
+    }
+    return rv;
+  }
+
+  function jslibQI (aObj, aInterface)
+  {
+    try {
+      return aObj.QueryInterface(C.interfaces[aInterface]);
+    } catch (e) {
+      throw ("Unable to QI " + aObj + " to " + aInterface);
+    }
+  }
+
+  /****************************************************************
+  * void include(aScriptPath)                                     *
+  * aScriptPath is an argument of string lib chrome path          *
+  * returns NS_OK on success, 1 if file is already loaded and     *
+  * - errorno or throws exception on failure                      *
+  *   eg:                                                         *
+  *       var path='chrome://jslib/content/io/file.js';           *
+  *       include(path);                                          *
+  *  Or:                                                          *
+  *       include(jslib_file);                                    *
+  *                                                               *
+  *   outputs: void(null)                                         *
+  ****************************************************************/
+
+  function include (aScriptPath)
+  {
+    if (!aScriptPath) {
+      dump("include: Missing file path argument\n");
+      throw - jslibRes.NS_ERROR_XPC_NOT_ENOUGH_ARGS;
+    }
+
+    if (aScriptPath == JS_LIB_PATH+JS_LIB_FILE) {
+      dump("include: "+aScriptPath+" is already loaded!\n");
+      throw - jslibRes.NS_ERROR_INVALID_ARG;
+    }
+
+    var start   = aScriptPath.lastIndexOf('/') + 1;
+    var end     = aScriptPath.lastIndexOf('.');
+    var slice   = aScriptPath.length - end;
+    var loadID  = aScriptPath.substring(start, (aScriptPath.length - slice));
+
+    if (typeof(this['JS_'+loadID.toUpperCase()+'_LOADED']) == "boolean")
+      return jslibRes.NS_OK;
+
+    var rv;
+    try {
+      if (jslibNeedsPrivs())
+        netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+      jslibGetService("@mozilla.org/moz/jssubscript-loader;1",
+                      "mozIJSSubScriptLoader").loadSubScript(aScriptPath);
+      rv = jslibRes.NS_OK;
+      dump("include: "+aScriptPath+"\n");
+    } catch (e) {
+      const msg = aScriptPath+" is not a valid path or is already loaded\n";
+      dump(e+"\n");
+      dump("include: "+msg+"\n");
+      rv = - jslibRes.NS_ERROR_INVALID_ARG;
+    }
+    return rv;
+  }
+
+  function jslibNeedsPrivs ()
+  {
+    var rv;
+    if (typeof(this.location) == "object") {
+      var proto = this.location.protocol;
+      rv = (proto == "file:")
+    }
+    return rv;
+  }
+
+  // include debug methods
+  const jslib_debug = JS_LIB_PATH+'debug/debug.js';
+  include(jslib_debug);
+
+  function jslibUninstall (aPackage, aCallback)
+  {
+    if (!aPackage)
+      throw - jslibRes.NS_ERROR_INVALID_ARG;
+
+    include (jslib_window);
+    var win = new CommonWindow(null, 400, 400);
+    win.position = JS_MIDDLE_CENTER;
+    win.openUninstallWindow(aPackage, aCallback);
+  }
+
+  /*********** Launch JSLIB Splash ***************/
+  function jslibLaunchSplash ()
+  {
+    include (jslib_window);
+    var win = new CommonWindow("chrome://jslib/content/splash.xul", 400, 220);
+    win.position = JS_MIDDLE_CENTER;
+    win.openSplash();
+  }
+
+  // DEPRICATED
+  function jslib_turnDumpOn () { jslibTurnDumpOn(); }
+
+  function jslibTurnDumpOn ()
+  {
+    include (jslib_prefs);
+    // turn on dump
+    var pref = new Prefs();
+    const prefStr = "browser.dom.window.dump.enabled"
+
+    // turn dump on if not enabled
+    if (!pref.getBool(prefStr)) {
+      pref.setBool(prefStr, true);
+      pref.save();
+    }
+    return;
+  }
+
+  // DEPRICATED
+  function jslib_turnDumpOff () { jslibTurnDumpOff(); }
+
+  function jslibTurnDumpOff ()
+  {
+    include (jslib_prefs);
+    // turn off dump
+    var pref = new Prefs();
+    const prefStr = "browser.dom.window.dump.enabled"
+
+    // turn dump off if enabled
+    if (pref.getBool(prefStr)) {
+      pref.setBool(prefStr, false);
+      pref.save();
+    }
+    return;
+  }
+
+  const jslib_modules = JS_LIB_PATH+'modules.js';
+  include (jslib_modules);
+
+  } catch (e) {}
+
+} // end jslib load test
+

Added: trunk/extensions/firefox-extension/chrome/content/jslib/modules.js
==============================================================================
--- (empty file)
+++ trunk/extensions/firefox-extension/chrome/content/jslib/modules.js	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,74 @@
+/** 
+ * jslib module identifiers
+ */
+
+const JS_MODULES_LOADED        = true;
+const JS_MODULES_FILE          = "modules.js";
+
+// insure jslib base is loaded
+if (typeof(JS_LIB_LOADED)=='boolean') 
+{
+
+// help identifier
+const jslib_help = "need to write some global help docs here\n";
+
+// Library Identifiers
+
+// io library modules
+const jslib_io         = JS_LIB_PATH+'io/io.js';
+const jslib_filesystem = JS_LIB_PATH+'io/filesystem.js'
+const jslib_file       = JS_LIB_PATH+'io/file.js';
+const jslib_fileutils  = JS_LIB_PATH+'io/fileUtils.js';
+const jslib_dir        = JS_LIB_PATH+'io/dir.js';
+const jslib_dirutils   = JS_LIB_PATH+'io/dirUtils.js';
+
+// data structures
+const jslib_dictionary       = JS_LIB_PATH+'ds/dictionary.js';
+const jslib_chaindictionary  = JS_LIB_PATH+'ds/chainDictionary.js';
+
+// RDF library modules
+const jslib_rdf           = JS_LIB_PATH+'rdf/rdf.js';
+const jslib_rdffile       = JS_LIB_PATH+'rdf/rdfFile.js';
+const jslib_rdfcontainer  = JS_LIB_PATH+'rdf/rdfContainer.js';
+const jslib_rdfresource   = JS_LIB_PATH+'rdf/rdfResource.js';
+const jslib_rdfmemory     = JS_LIB_PATH+'rdf/inMemoryRDF.js';
+
+// network library modules
+const jslib_remotefile  = JS_LIB_PATH+'network/remoteFile.js';
+const jslib_socket      = JS_LIB_PATH+'network/socket.js';
+
+// network - http
+const jslib_http                = JS_LIB_PATH+'network/http.js';
+const jslib_getrequest          = JS_LIB_PATH+'network/getRequest.js';
+const jslib_postrequest         = JS_LIB_PATH+'network/postRequest.js';
+const jslib_multipartrequest    = JS_LIB_PATH+'network/multipartRequest.js';
+const jslib_filepart            = JS_LIB_PATH+'network/parts/filePart.js';
+const jslib_textpart            = JS_LIB_PATH+'network/parts/textPart.js';
+const jslib_urlparameterspart   = JS_LIB_PATH+'network/parts/urlParametersPart.js';
+const jslib_bodyparameterspart  = JS_LIB_PATH+'network/parts/bodyParametersPart.js';
+
+// xul dom library modules
+const jslib_dialog      = JS_LIB_PATH+'xul/commonDialog.js';
+const jslib_filepicker  = JS_LIB_PATH+'xul/commonFilePicker.js';
+const jslib_window      = JS_LIB_PATH+'xul/commonWindow.js';
+const jslib_routines    = JS_LIB_PATH+'xul/appRoutines.js';
+
+// sound library modules
+const jslib_sound = JS_LIB_PATH+'sound/sound.js';
+
+// utils library modules
+const jslib_date     = JS_LIB_PATH+'utils/date.js';
+const jslib_prefs    = JS_LIB_PATH+'utils/prefs.js';
+const jslib_validate = JS_LIB_PATH+'utils/validate.js';
+const jslib_sax      = JS_LIB_PATH+'utils/sax.js';
+
+// zip
+const jslib_zip  = JS_LIB_PATH+'zip/zip.js';
+
+// install/uninstall
+const jslib_install    = JS_LIB_PATH+'install/install.js';
+const jslib_uninstall  = JS_LIB_PATH+'install/uninstall.js';
+const jslib_autoupdate = JS_LIB_PATH+'install/autoupdate.js';
+
+}
+

Added: trunk/extensions/firefox-extension/chrome/content/json.js
==============================================================================
--- (empty file)
+++ trunk/extensions/firefox-extension/chrome/content/json.js	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,287 @@
+/*
+    json.js
+    2007-04-30
+
+    Public Domain
+
+    This file adds these methods to JavaScript:
+
+        array.toJSONString()
+        boolean.toJSONString()
+        date.toJSONString()
+        number.toJSONString()
+        object.toJSONString()
+        string.toJSONString()
+            These methods produce a JSON text from a JavaScript value.
+            It must not contain any cyclical references. Illegal values
+            will be excluded.
+
+            The default conversion for dates is to an ISO string. You can
+            add a toJSONString method to any date object to get a different
+            representation.
+
+        string.parseJSON(filter)
+            This method parses a JSON text to produce an object or
+            array. It can throw a SyntaxError exception.
+
+            The optional filter parameter is a function which can filter and
+            transform the results. It receives each of the keys and values, and
+            its return value is used instead of the original value. If it
+            returns what it received, then structure is not modified. If it
+            returns undefined then the member is deleted.
+
+            Example:
+
+            // Parse the text. If a key contains the string 'date' then
+            // convert the value to a date.
+
+            myData = text.parseJSON(function (key, value) {
+                return key.indexOf('date') >= 0 ? new Date(value) : value;
+            });
+
+    It is expected that these methods will formally become part of the
+    JavaScript Programming Language in the Fourth Edition of the
+    ECMAScript standard in 2008.
+
+    This file will break programs with improper for..in loops. See
+    http://yuiblog.com/blog/2006/09/26/for-in-intrigue/
+
+    This is a reference implementation. You are free to copy, modify, or
+    redistribute.
+
+    Use your own copy. It is extremely unwise to load untrusted third party
+    code into your pages.
+*/
+
+/*jslint evil: true */
+
+/**
+rewirite by taofei 2007-8-22
+to avoid for-in loop problem.
+toJSONString(anything)
+parseJSON(anything)
+*/
+
+
+
+//help function xxxToJSONString
+
+
+    function arrayToJSON (arrayVal) {
+        var a = ['['],  // The array holding the text fragments.
+            b,          // A boolean indicating that a comma is required.
+            i,          // Loop counter.
+            l = arrayVal.length,
+            v;          // The value to be stringified.
+
+        function p(s) {
+
+// p accumulates text fragments in an array. It inserts a comma before all
+// except the first fragment.
+
+            if (b) {
+                a.push(',');
+            }
+            a.push(s);
+            b = true;
+        }
+
+// For each value in arrayVal array...
+
+        for (i = 0; i < l; i += 1) {
+            v = arrayVal[i];
+            p(toJSONString(v));
+        }
+
+// Join all of the fragments together and return.
+
+        a.push(']');
+        return a.join('');
+    };
+
+
+
+
+    function objectToJSON(objectVal) {
+        var a = ['{'],  // The array holding the text fragments.
+            b,          // A boolean indicating that a comma is required.
+            k,          // The current key.
+            v;          // The current value.
+
+        function p(s) {
+
+// p accumulates text fragment pairs in an array. It inserts a comma before all
+// except the first fragment pair.
+
+            if (b) {
+                a.push(',');
+            }
+            a.push(toJSONString(k), ':', s);
+            b = true;
+        }
+
+// Iterate through all of the keys in the object, ignoring the proto chain.
+
+        for (k in objectVal) {
+            if (objectVal.hasOwnProperty(k)) {
+                v = objectVal[k];
+                p(toJSONString(v));
+            }
+        }
+
+// Join all of the fragments together and return.
+
+        a.push('}');
+        return a.join('');
+    };
+
+function strToJSON(str)
+{
+         var m = {
+            '\b': '\\b',
+            '\t': '\\t',
+            '\n': '\\n',
+            '\f': '\\f',
+            '\r': '\\r',
+            '"' : '\\"',
+            '\\': '\\\\'
+        };
+
+   // If the string contains no control characters, no quote characters, and no
+    // backslash characters, then we can simply slap some quotes around it.
+    // Otherwise we must also replace the offending characters with safe
+    // sequences.
+
+    if (/["\\\x00-\x1f]/.test(str)) {
+        return '"' + str.replace(/([\x00-\x1f\\"])/g, function (a, b) {
+            var c = m[b];
+            if (c) {
+                return c;
+            }
+            c = b.charCodeAt();
+            return '\\u00' +
+                Math.floor(c / 16).toString(16) +
+                (c % 16).toString(16);
+        }) + '"';
+    }
+    return '"' + str + '"';
+}
+
+function boolToJSON(bool)
+{
+    return String(bool);
+}
+
+function dateToJSON(dateVal)
+{
+
+// Ultimately, this method will be equivalent to the date.toISOString method.
+
+        function f(n) {
+
+// Format integers to have at least two digits.
+
+            return n < 10 ? '0' + n : n;
+        }
+
+        return '"' + dataVal.getFullYear() + '-' +
+                f(dataVal.getMonth() + 1) + '-' +
+                f(dataVal.getDate()) + 'T' +
+                f(dataVal.getHours()) + ':' +
+                f(dataVal.getMinutes()) + ':' +
+                f(dataVal.getSeconds()) + '"';
+};
+
+function numberToJSON(numberVal) 
+{
+
+// JSON numbers must be finite. Encode non-finite numbers as null.
+
+        return isFinite(numberVal) ? String(numberVal) : "null";
+};
+
+function toJSONString(anything)
+{
+     switch (typeof anything) {
+
+// Serialize a JavaScript object value. Ignore objects thats lack the
+// toJSONString method. Due to a specification error in ECMAScript,
+// typeof null is 'object', so watch out for that case.
+    case 'array':
+        return arrayToJSON(anything);
+    case 'object':
+        if (anything) {
+            if (anything instanceof Array)
+                return arrayToJSON(anything);
+            else
+                return objectToJSON(anything);
+        } else {
+            return "null";
+        }
+        break;
+
+    case 'string':
+        return strToJSON(anything);
+    case 'number':
+        return numberToJSON(anything);
+    case 'boolean':
+        return boolToJSON(anything);
+    default:
+        return String(anything);
+    }
+}
+
+
+
+function parseJSON(str,filter) {
+    var j;
+
+    function walk(k, v) {
+        var i;
+        if (v && typeof v === 'object') {
+            for (i in v) {
+                if (v.hasOwnProperty(i)) {
+                    v[i] = walk(i, v[i]);
+                }
+            }
+        }
+        return filter(k, v);
+    }
+
+
+// Parsing happens in three stages. In the first stage, we run the text against
+// a regular expression which looks for non-JSON characters. We are especially
+// concerned with '()' and 'new' because they can cause invocation, and '='
+// because it can cause mutation. But just to be safe, we will reject all
+// unexpected characters.
+
+    if (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.
+            test(str)) {
+
+// In the second stage we use the eval function to compile the text into a
+// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
+// in JavaScript: it can begin a block or an object literal. We wrap the text
+// in parens to eliminate the ambiguity.
+
+        try {
+            j = eval('(' + str + ')');
+        } catch (e) {
+            throw new SyntaxError("parseJSON");
+        }
+    } else {
+        throw new SyntaxError("parseJSON");
+    }
+
+// In the optional third stage, we recursively walk the new structure, passing
+// each name/value pair to a filter function for possible transformation.
+
+    if (typeof filter === 'function') {
+        j = walk('', j);
+    }
+    dump(toJSONString(j));
+    return j;
+};
+
+
+
+

Added: trunk/extensions/firefox-extension/chrome/content/md5.js
==============================================================================
--- (empty file)
+++ trunk/extensions/firefox-extension/chrome/content/md5.js	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,256 @@
+/*
+ * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
+ * Digest Algorithm, as defined in RFC 1321.
+ * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
+ * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
+ * Distributed under the BSD License
+ * See http://pajhome.org.uk/crypt/md5 for more info.
+ */
+
+/*
+ * Configurable variables. You may need to tweak these to be compatible with
+ * the server-side, but the defaults work in most cases.
+ */
+var hexcase = 0;  /* hex output format. 0 - lowercase; 1 - uppercase        */
+var b64pad  = ""; /* base-64 pad character. "=" for strict RFC compliance   */
+var chrsz   = 8;  /* bits per input character. 8 - ASCII; 16 - Unicode      */
+
+/*
+ * These are the functions you'll usually want to call
+ * They take string arguments and return either hex or base-64 encoded strings
+ */
+function hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));}
+function b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));}
+function str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));}
+function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); }
+function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); }
+function str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); }
+
+/*
+ * Perform a simple self-test to see if the VM is working
+ */
+function md5_vm_test()
+{
+  return hex_md5("abc") == "900150983cd24fb0d6963f7d28e17f72";
+}
+
+/*
+ * Calculate the MD5 of an array of little-endian words, and a bit length
+ */
+function core_md5(x, len)
+{
+  /* append padding */
+  x[len >> 5] |= 0x80 << ((len) % 32);
+  x[(((len + 64) >>> 9) << 4) + 14] = len;
+
+  var a =  1732584193;
+  var b = -271733879;
+  var c = -1732584194;
+  var d =  271733878;
+
+  for(var i = 0; i < x.length; i += 16)
+  {
+    var olda = a;
+    var oldb = b;
+    var oldc = c;
+    var oldd = d;
+
+    a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
+    d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
+    c = md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);
+    b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
+    a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
+    d = md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);
+    c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
+    b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
+    a = md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
+    d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
+    c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
+    b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
+    a = md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);
+    d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
+    c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
+    b = md5_ff(b, c, d, a, x[i+15], 22,  1236535329);
+
+    a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
+    d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
+    c = md5_gg(c, d, a, b, x[i+11], 14,  643717713);
+    b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
+    a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
+    d = md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);
+    c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
+    b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
+    a = md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
+    d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
+    c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
+    b = md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);
+    a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
+    d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
+    c = md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);
+    b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
+
+    a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
+    d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
+    c = md5_hh(c, d, a, b, x[i+11], 16,  1839030562);
+    b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
+    a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
+    d = md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);
+    c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
+    b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
+    a = md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);
+    d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
+    c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
+    b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
+    a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
+    d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
+    c = md5_hh(c, d, a, b, x[i+15], 16,  530742520);
+    b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
+
+    a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
+    d = md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);
+    c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
+    b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
+    a = md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);
+    d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
+    c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
+    b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
+    a = md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
+    d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
+    c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
+    b = md5_ii(b, c, d, a, x[i+13], 21,  1309151649);
+    a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
+    d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
+    c = md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);
+    b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
+
+    a = safe_add(a, olda);
+    b = safe_add(b, oldb);
+    c = safe_add(c, oldc);
+    d = safe_add(d, oldd);
+  }
+  return Array(a, b, c, d);
+
+}
+
+/*
+ * These functions implement the four basic operations the algorithm uses.
+ */
+function md5_cmn(q, a, b, x, s, t)
+{
+  return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
+}
+function md5_ff(a, b, c, d, x, s, t)
+{
+  return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
+}
+function md5_gg(a, b, c, d, x, s, t)
+{
+  return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
+}
+function md5_hh(a, b, c, d, x, s, t)
+{
+  return md5_cmn(b ^ c ^ d, a, b, x, s, t);
+}
+function md5_ii(a, b, c, d, x, s, t)
+{
+  return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
+}
+
+/*
+ * Calculate the HMAC-MD5, of a key and some data
+ */
+function core_hmac_md5(key, data)
+{
+  var bkey = str2binl(key);
+  if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz);
+
+  var ipad = Array(16), opad = Array(16);
+  for(var i = 0; i < 16; i++)
+  {
+    ipad[i] = bkey[i] ^ 0x36363636;
+    opad[i] = bkey[i] ^ 0x5C5C5C5C;
+  }
+
+  var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);
+  return core_md5(opad.concat(hash), 512 + 128);
+}
+
+/*
+ * Add integers, wrapping at 2^32. This uses 16-bit operations internally
+ * to work around bugs in some JS interpreters.
+ */
+function safe_add(x, y)
+{
+  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
+  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
+  return (msw << 16) | (lsw & 0xFFFF);
+}
+
+/*
+ * Bitwise rotate a 32-bit number to the left.
+ */
+function bit_rol(num, cnt)
+{
+  return (num << cnt) | (num >>> (32 - cnt));
+}
+
+/*
+ * Convert a string to an array of little-endian words
+ * If chrsz is ASCII, characters >255 have their hi-byte silently ignored.
+ */
+function str2binl(str)
+{
+  var bin = Array();
+  var mask = (1 << chrsz) - 1;
+  for(var i = 0; i < str.length * chrsz; i += chrsz)
+    bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
+  return bin;
+}
+
+/*
+ * Convert an array of little-endian words to a string
+ */
+function binl2str(bin)
+{
+  var str = "";
+  var mask = (1 << chrsz) - 1;
+  for(var i = 0; i < bin.length * 32; i += chrsz)
+    str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);
+  return str;
+}
+
+/*
+ * Convert an array of little-endian words to a hex string.
+ */
+function binl2hex(binarray)
+{
+  var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
+  var str = "";
+  for(var i = 0; i < binarray.length * 4; i++)
+  {
+    str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
+           hex_tab.charAt((binarray[i>>2] >> ((i%4)*8  )) & 0xF);
+  }
+  return str;
+}
+
+/*
+ * Convert an array of little-endian words to a base-64 string
+ */
+function binl2b64(binarray)
+{
+  var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+  var str = "";
+  for(var i = 0; i < binarray.length * 4; i += 3)
+  {
+    var triplet = (((binarray[i   >> 2] >> 8 * ( i   %4)) & 0xFF) << 16)
+                | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 )
+                |  ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);
+    for(var j = 0; j < 4; j++)
+    {
+      if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
+      else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
+    }
+  }
+  return str;
+}

Added: trunk/extensions/firefox-extension/chrome/content/utils.js
==============================================================================
--- (empty file)
+++ trunk/extensions/firefox-extension/chrome/content/utils.js	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,51 @@
+/*
+ * Add A Row to a list 
+ * Param is string . Might be more than one .
+ * Every param is a considered to be a cell .
+ */
+function appendRow (){
+    var list = arguments[0];
+    var listitem = document.createElement('listitem');
+    for(var i = 1; i< arguments.length; i++)
+    {
+        var listcell = document.createElement('listcell');
+        listcell.setAttribute('label',arguments[i]);
+        listcell.setAttribute('value',arguments[i]);
+        listitem.appendChild(listcell);
+    }
+    list.appendChild(listitem);
+}
+
+
+/*
+ * check weather a string is end with another 
+ * usage: somestirng.endWith(antoherString)
+ * @param {string} subfix  
+ */
+String.prototype.isEndWith = function(subfix)
+{
+    var index = this.lastIndexOf(subfix);
+    return index != -1 && index + subfix.length == this.length;
+}
+
+/**
+ * convert  a wildcard expression to regular expression
+ * usage: wildcard.wildcard2RE
+ * @return the re string ( not RegExp Object)
+ */
+String.prototype.wildcard2RE = function()
+{
+    return this.replace(/([\\\+\[\]\{\}\^])/g,"\\$1").replace(/\?/g,".?").replace(/\*/g,".*");
+}
+
+Function.prototype.bind = function(f,obj) {
+    var temp = function() {
+        return f.apply(obj, arguments);
+    };
+    return temp;
+}
+
+function log(msg)
+{
+   dump("[xesam] " + msg + "\n"); 
+}

Added: trunk/extensions/firefox-extension/chrome/locale/en-US/beagle.dtd
==============================================================================
--- (empty file)
+++ trunk/extensions/firefox-extension/chrome/locale/en-US/beagle.dtd	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,38 @@
+<!ENTITY beagle.context.search.link.label           "Find References to this Link">
+<!ENTITY beagle.context.search.page.label           "Find References to this Page">
+
+<!ENTITY beagle.pref.label                          "Xesam Preferences">
+<!ENTITY beagle.pref.security.active.label          "Index pages with secure content">
+<!ENTITY beagle.pref.bookmark.active.label          "Index bookmarks when close the window">
+<!ENTITY beagle.pref.prompt.keywords.active.label   "Prompt for keywords when indexing on demand">
+<!ENTITY beagle.pref.default.action.label           "Default Action">
+<!ENTITY beagle.pref.default.action.tooltip         "What to do When the URL doesn't match any rule">
+<!ENTITY beagle.pref.conflict.action.label          "Conflict Action">
+<!ENTITY beagle.pref.conflict.action.tooltip        "What to do When the URL matches both rules in include list and exclude list">
+<!ENTITY beagle.pref.action.index.label             "Index">
+<!ENTITY beagle.pref.action.noindex.label           "No Index">
+<!ENTITY beagle.pref.groupbox.general.label         "General">
+<!ENTITY beagle.pref.groupbox.include.label         "Include">
+<!ENTITY beagle.pref.groupbox.exclude.label         "Exclude">
+<!ENTITY beagle.pref.filter.name.label              "Name">
+<!ENTITY beagle.pref.filter.pattern.label           "Pattern">
+<!ENTITY beagle.pref.filter.patterntype.label       "PatternType">
+<!ENTITY beagle.pref.filter.add.label		        "Add">
+<!ENTITY beagle.pref.filter.remove.label            "Remove">
+<!ENTITY beagle.pref.filter.add.dlg.title           "Add Rule">
+<!ENTITY beagle.pref.help.label                     "Help">
+<!ENTITY beagle.pref.save.label                     "Save">
+<!ENTITY beagle.pref.cancel.label                   "Cancel">
+
+<!ENTITY beagle.run.index.this.label                "Index This Page">
+<!ENTITY beagle.run.always.index.label              "Always Index This Site">
+<!ENTITY beagle.run.never.index.label               "Never  Index This Site">
+<!ENTITY beagle.run.preferences.label               "Preferences">
+<!ENTITY beagle.run.context.menu.label              "Search">
+<!ENTITY beagle.run.index.link.label                "Index This Link">
+<!ENTITY beagle.run.index.image.label               "Index This Image">
+<!ENTITY beagle.index.link.start.label              "Start">
+<!ENTITY beagle.index.link.stop.label               "Stop">
+
+<!ENTITY beagle.bookmark.index.modified.label       "Index The Modified Bookmarks">
+

Added: trunk/extensions/firefox-extension/chrome/locale/en-US/beagle.properties
==============================================================================
--- (empty file)
+++ trunk/extensions/firefox-extension/chrome/locale/en-US/beagle.properties	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,22 @@
+beagle_context_search_text=Search for "%S"
+beagle_tooltip_actived=Xesam auto index active. Click to disable.
+beagle_tooltip_disabled=Xesam auto index disabled. Click to active.
+beagle_tooltip_error=Xesam indexing error: %S
+beagle_not_found=beagle not found
+beagle_run_error=beagle run error
+beagle_check_env_error=~/.beagle Not Found.This extension will not work.
+beagle_write_error_confirm=Fail to write content/metadata.\n Would you like to disable beagle now ?
+beagle_statuslabel_indexing=xesam is indexing %S
+beagle_index_link_connect=Connecting ... %S
+beagle_index_link_start=Loading ... %S
+beagle_index_link_progress=Transferring Data (%S) %S
+beagle_index_link_saving=Saving ... %S
+beagle_index_link_stop=Stopped
+beagle_index_link_invalid_url=Invalid URL
+beagle_index_link_http_403=Access Denied (403 Forbidden)
+beagle_index_link_http_404=File Not Found (404 Not Found)
+beagle_index_link_http_500=500 Internal Server Error
+beagle_prompt_keywords_title=Keywords
+beagle_prompt_keywords_text=Extra keywords to index
+beagle_index_bookmark_finish=%S bookmarks have been indexed!
+beagle_quick_add_rule_error=Error! Not Found a hostname for this page!

Added: trunk/extensions/firefox-extension/chrome/locale/en-US/contents.rdf
==============================================================================
--- (empty file)
+++ trunk/extensions/firefox-extension/chrome/locale/en-US/contents.rdf	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+         xmlns:chrome="http://www.mozilla.org/rdf/chrome#";>
+
+<!-- list all the packages being supplied by this jar -->
+<RDF:Seq about="urn:mozilla:locale:root">
+  <RDF:li resource="urn:mozilla:locale:en-US" />
+</RDF:Seq>
+
+<!-- locale information -->
+<RDF:Description about="urn:mozilla:locale:en-US"
+  chrome:displayName="English(US)"
+  chrome:author="Filia Tao"
+  chrome:name="en-US">
+  <chrome:packages>
+    <RDF:Seq about="urn:mozilla:locale:en-US:packages">
+      <RDF:li resource="urn:mozilla:locale:en-US:beagle" />
+    </RDF:Seq>
+  </chrome:packages>
+</RDF:Description>
+
+<!-- version information -->
+<RDF:Description about="urn:mozilla:locale:en-US:beagle"
+  chrome:localeVersion="1.4" />
+
+</RDF:RDF>

Added: trunk/extensions/firefox-extension/chrome/skin/classic/beagle-big.png
==============================================================================
Binary file. No diff available.

Added: trunk/extensions/firefox-extension/chrome/skin/classic/beagle-disabled.png
==============================================================================
Binary file. No diff available.

Added: trunk/extensions/firefox-extension/chrome/skin/classic/beagle-error.png
==============================================================================
Binary file. No diff available.

Added: trunk/extensions/firefox-extension/chrome/skin/classic/beagle.png
==============================================================================
Binary file. No diff available.

Added: trunk/extensions/firefox-extension/chrome/skin/classic/contents.rdf
==============================================================================
--- (empty file)
+++ trunk/extensions/firefox-extension/chrome/skin/classic/contents.rdf	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+         xmlns:chrome="http://www.mozilla.org/rdf/chrome#";>
+
+  <RDF:Seq about="urn:mozilla:skin:root">
+    <RDF:li resource="urn:mozilla:skin:classic/1.0" />
+  </RDF:Seq>
+
+  <RDF:Description about="urn:mozilla:skin:classic/1.0">
+    <chrome:packages>
+      <RDF:Seq about="urn:mozilla:skin:classic/1.0:packages">
+        <RDF:li resource="urn:mozilla:skin:classic/1.0:beagle" />
+      </RDF:Seq>
+    </chrome:packages>
+  </RDF:Description>
+
+</RDF:RDF>

Added: trunk/extensions/firefox-extension/chrome/skin/classic/overlay.css
==============================================================================
--- (empty file)
+++ trunk/extensions/firefox-extension/chrome/skin/classic/overlay.css	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,28 @@
+statusbarpanel#beagle-notifier-status[status="000"] {
+  list-style-image: url("chrome://beagle/skin/beagle.png");
+  padding-left: 8px;
+  padding-right: 8px;
+  padding-top: 2px;
+}
+
+statusbarpanel#beagle-notifier-status[status="00f"] {
+  list-style-image: url("chrome://beagle/skin/beagle-disabled.png");
+  padding-left: 8px;
+  padding-right: 8px;
+  padding-top: 2px;
+}
+
+statusbarpanel#beagle-notifier-status[status="f00"] {
+  list-style-image: url("chrome://beagle/skin/beagle-error.png");
+  padding-left: 8px;
+  padding-right: 8px;
+  padding-top: 2px;
+}
+
+#beagle-button {
+  list-style-image: url("chrome://beagle/skin/beagle.png");
+}
+
+toolbar[iconsize="small"] #beagle-button {
+  list-style-image: url("chrome://beagle/skin/beagle.png");
+}

Added: trunk/extensions/firefox-extension/firefox-extension-xesam.xpi
==============================================================================
Binary file. No diff available.

Added: trunk/extensions/firefox-extension/install.rdf
==============================================================================
--- (empty file)
+++ trunk/extensions/firefox-extension/install.rdf	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"; 
+     xmlns:em="http://www.mozilla.org/2004/em-rdf#";>
+
+  <Description about="urn:mozilla:install-manifest">
+
+    <em:id>{fda00e13-8c62-4f63-9d19-d168115b11ca}</em:id>
+    <em:name>Firefox Extension for Xesam search enginer(beagle,tracker)</em:name>
+    <em:version>1.0.1</em:version>
+    <em:description>
+      Index webpages you visit using Beagle or Tracker
+    </em:description>
+    <em:creator>Tao Fei (Filia Tao gmail com) </em:creator>
+    <em:contributor>Alex Graveley  (Original Code)</em:contributor>
+    <em:contributor>Abby Coffin (Icon Design)</em:contributor>
+    <em:homepageURL>http://beagle-project.org/Browser_Extension#Firefox_Extension</em:homepageURL>
+    <em:iconURL>chrome://beagle/skin/beagle-big.png</em:iconURL>
+    <em:optionsURL>chrome://beagle/content/beaglePrefs.xul</em:optionsURL>
+
+    <em:targetApplication>
+      <Description>
+        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
+        <em:minVersion>0.8</em:minVersion>
+        <em:maxVersion>3.0.*</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+
+  </Description>
+
+</RDF>

Added: trunk/extensions/thunderbird-extension/Makefile
==============================================================================
--- (empty file)
+++ trunk/extensions/thunderbird-extension/Makefile	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,48 @@
+PROJECT = tracker
+VERSION = 0.1.3
+
+all: $(PROJECT).xpi
+
+JAR_FILES =					\
+	content/tracker.css			\
+	content/trackerIndexer.js		\
+	content/trackerMailWindow.xul		\
+	content/trackerMessenger.xul		\
+	content/trackerPrefs.xul			\
+	content/trackerQueue.js			\
+	content/trackerService.js		\
+	content/trackerSettings.js		\
+	content/trackerUnindex.js		\
+	content/trackerUnindex.xul		\
+	content/trackerUtils.js			\
+	content/contents.rdf			\
+	locale/en-US/tracker.dtd			\
+	locale/en-US/strings.properties		\
+	locale/en-US/contents.rdf		\
+	skin/classic/overlay.css		\
+	skin/classic/tracker.png			\
+	skin/classic/tracker-disabled.png	\
+	skin/classic/tracker-error.png		\
+	skin/classic/contents.rdf
+
+XPI_FILES =					\
+	chrome.manifest				\
+	install.rdf				\
+	chrome/$(PROJECT).jar			\
+	defaults/preferences/default.js		\
+	components/TrackerIndexer.js		\
+	components/TrackerQueue.js		\
+	components/TrackerSettings.js		\
+	components/TrackerCommandLine.js
+
+chrome/$(PROJECT).jar: $(JAR_FILES)
+	- mkdir -p chrome
+	zip -q9 $@ $^
+
+$(PROJECT).xpi: $(XPI_FILES)
+	zip -q9 $@ $^
+
+
+clean:
+	rm -f chrome/$(PROJECT).jar
+	rm -f $(PROJECT).xpi				

Added: trunk/extensions/thunderbird-extension/chrome.manifest
==============================================================================
--- (empty file)
+++ trunk/extensions/thunderbird-extension/chrome.manifest	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,5 @@
+content 	tracker 	jar:chrome/tracker.jar!/content/
+overlay 	chrome://messenger/content/messenger.xul chrome://tracker/content/trackerMessenger.xul
+overlay		chrome://messenger/content/mailWindowOverlay.xul chrome://tracker/content/trackerMailWindow.xul
+locale 		tracker 	en-US 		jar:chrome/tracker.jar!/locale/en-US/
+skin 		tracker 	classic/1.0 	jar:chrome/tracker.jar!/skin/classic/

Added: trunk/extensions/thunderbird-extension/components/TrackerCommandLine.js
==============================================================================
--- (empty file)
+++ trunk/extensions/thunderbird-extension/components/TrackerCommandLine.js	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,186 @@
+//
+// tracker.xul: Overlay for basic main window GUI items
+//
+// Copyright (C) 2007 Pierre Ãstlund
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+//
+
+// Original source: http://developer.mozilla.org/en/docs/Chrome:_Command_Line
+
+const nsIAppShellService    = Components.interfaces.nsIAppShellService;
+const nsISupports           = Components.interfaces.nsISupports;
+const nsICategoryManager    = Components.interfaces.nsICategoryManager;
+const nsIComponentRegistrar = Components.interfaces.nsIComponentRegistrar;
+const nsICommandLine        = Components.interfaces.nsICommandLine;
+const nsICommandLineHandler = Components.interfaces.nsICommandLineHandler;
+const nsIFactory            = Components.interfaces.nsIFactory;
+const nsIModule             = Components.interfaces.nsIModule;
+const nsIWindowWatcher      = Components.interfaces.nsIWindowWatcher;
+
+const clh_contractID = "@mozilla.org/commandlinehandler/general-startup;1?type=tracker";
+const clh_CID = Components.ID("{679f520a-b062-45f9-a02c-482cfebf3b77}");
+const clh_category = "m-tracker";
+
+var loaded = false;
+var last_uri = null;
+
+const startupObserver = {
+
+	Observer: Components.classes['@mozilla.org/observer-service;1']
+		.getService(Components.interfaces.nsIObserverService),
+
+	RegisterSelf: function ()
+	{
+		this.Observer.addObserver (this, 'tracker-loaded', false);
+		this.Observer.addObserver (this, 'quit-application', false);
+	},
+	
+	UnregisterSelf: function ()
+	{
+		this.Observer.removeObserver (this, 'tracker-loaded');
+		this.Observer.removeObserver (this, 'quit-application');
+	},
+	
+	notify: function ()
+	{
+		if (!loaded || (loaded && !last_uri))
+			return;
+		
+		var uri = last_uri;
+		last_uri = null;
+		this.Observer.notifyObservers (this, 'tracker-open-uri', uri);
+	},
+
+	observe: function (subject, topic, data)
+	{
+		if (topic == 'tracker-loaded') {
+			// Each message will send this so we better not respond to all of them
+			if (loaded)
+				return;
+			
+			loaded = true;
+			this.notify ();
+		} else if (topic == 'quit-application')
+			this.UnregisterSelf ();
+	}
+};
+ 
+const myAppHandler = {
+
+	QueryInterface : function clh_QI(iid)
+	{
+		if (iid.equals(nsICommandLineHandler) || iid.equals(nsIFactory) || iid.equals(nsISupports))
+			return this;
+
+		throw Components.results.NS_ERROR_NO_INTERFACE;
+	},
+
+	handle : function clh_handle(cmdLine)
+	{
+		try {
+			var uristr = cmdLine.handleFlagWithParam("viewtracker", false);
+			if (uristr) {
+				last_uri = uristr;
+				cmdLine.preventDefault = false;
+				startupObserver.notify ();
+			}
+		} catch (e) {
+			Components.utils.reportError("incorrect parameter passed to -viewtracker on the command line.");
+		}
+	},
+
+	helpInfo : "  -viewtracker <uri>       Open specified URI,\n",
+
+	createInstance : function clh_CI(outer, iid)
+	{
+		if (outer != null)
+			throw Components.results.NS_ERROR_NO_AGGREGATION;
+
+		return this.QueryInterface(iid);
+	},
+
+	lockFactory : function clh_lock(lock)
+	{
+	}
+};
+
+
+const myAppHandlerModule = {
+
+	QueryInterface : function mod_QI(iid)
+	{
+		if (iid.equals(nsIModule) || iid.equals(nsISupports))
+			return this;
+
+		throw Components.results.NS_ERROR_NO_INTERFACE;
+	},
+
+	getClassObject : function mod_gch(compMgr, cid, iid)
+	{
+		if (cid.equals(clh_CID))
+			return myAppHandler.QueryInterface(iid);
+
+		throw Components.results.NS_ERROR_NOT_REGISTERED;
+	},
+
+	registerSelf : function mod_regself(compMgr, fileSpec, location, type)
+	{
+		compMgr.QueryInterface(nsIComponentRegistrar);
+
+		compMgr.registerFactoryLocation(clh_CID,
+			"myAppHandler",
+			clh_contractID,
+			fileSpec,
+			location,
+			type);
+
+		var catMan = Components.classes["@mozilla.org/categorymanager;1"]
+			.getService(nsICategoryManager);
+		catMan.addCategoryEntry("command-line-handler",
+			clh_category,
+			clh_contractID, true, true);
+	},
+
+	unregisterSelf : function mod_unreg(compMgr, location, type)
+	{
+		compMgr.QueryInterface(nsIComponentRegistrar);
+		compMgr.unregisterFactoryLocation(clh_CID, location);
+
+		var catMan = Components.classes["@mozilla.org/categorymanager;1"]
+			.getService(nsICategoryManager);
+		catMan.deleteCategoryEntry("command-line-handler", clh_category);
+	},
+
+	canUnload : function (compMgr)
+	{
+		return true;
+	}
+};
+
+function NSGetModule(comMgr, fileSpec)
+{
+	return myAppHandlerModule;
+}
+
+startupObserver.RegisterSelf ();
+
+

Added: trunk/extensions/thunderbird-extension/components/TrackerIndexer.js
==============================================================================
--- (empty file)
+++ trunk/extensions/thunderbird-extension/components/TrackerIndexer.js	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,97 @@
+//
+// TrackerIndexer.js: Base code for indexer component
+//
+// Copyright (C) 2007 Pierre Ãstlund
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+//
+
+// Source: http://hyperstruct.net/2006/8/10/your-first-javascript-xpcom-component-in-10-minutes
+
+const CLASS_ID = Components.ID('{a7b17c1b-0346-4c52-b52b-6ee858a10f53}');
+const CLASS_NAME = 'Tracker indexer component';
+const CONTRACT_ID = '@tracker-project.org/services/indexer;1';
+const SOURCE = 'chrome://tracker/content/trackerIndexer.js';
+const INTERFACE = Components.interfaces.nsISupports;
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+const loader = Cc['@mozilla.org/moz/jssubscript-loader;1']
+    .getService(Ci.mozIJSSubScriptLoader);
+
+function Component() {
+    this.wrappedJSObject = this;
+}
+
+function GetJsService (component)
+{
+	var comp = Components.classes [component].getService (Components.interfaces.nsISupports);
+	return comp.wrappedJSObject;
+}
+
+loader.loadSubScript(SOURCE, Component.prototype);
+
+var Factory = {
+    createInstance: function(aOuter, aIID) {
+        if(aOuter != null)
+            throw Cr.NS_ERROR_NO_AGGREGATION;
+        var component = new Component();
+        if(typeof(component.wrappedJSObject.init) == 'function')
+            component.init();
+
+        return component.QueryInterface(aIID);
+    }
+};
+
+var Module = {
+    _firstTime: true,
+
+    registerSelf: function(aCompMgr, aFileSpec, aLocation, aType) {
+        if (this._firstTime) {
+            this._firstTime = false;
+            throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN;
+        };
+        aCompMgr = aCompMgr.QueryInterface(Ci.nsIComponentRegistrar);
+        aCompMgr.registerFactoryLocation(
+            CLASS_ID, CLASS_NAME, CONTRACT_ID, aFileSpec, aLocation, aType);
+    },
+
+    unregisterSelf: function(aCompMgr, aLocation, aType) {
+        aCompMgr = aCompMgr.QueryInterface(Ci.nsIComponentRegistrar);
+        aCompMgr.unregisterFactoryLocation(CLASS_ID, aLocation);
+    },
+
+    getClassObject: function(aCompMgr, aCID, aIID) {
+        if (!aIID.equals(Ci.nsIFactory))
+            throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+
+        if (aCID.equals(CLASS_ID))
+            return Factory;
+
+        throw Cr.NS_ERROR_NO_INTERFACE;        
+    },
+
+    canUnload: function(aCompMgr) { return true; }
+};
+
+function NSGetModule(aCompMgr, aFileSpec) { return Module; }
+

Added: trunk/extensions/thunderbird-extension/components/TrackerQueue.js
==============================================================================
--- (empty file)
+++ trunk/extensions/thunderbird-extension/components/TrackerQueue.js	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,97 @@
+//
+// TrackerQueue.js: Base code for queue component
+//
+// Copyright (C) 2007 Pierre Ãstlund
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+//
+
+// Source: http://hyperstruct.net/2006/8/10/your-first-javascript-xpcom-component-in-10-minutes
+
+const CLASS_ID = Components.ID('{d413d0af-29c4-4d47-bfa8-cc585075603d}');
+const CLASS_NAME = 'Tracker queue component';
+const CONTRACT_ID = '@tracker-project.org/services/queue;1';
+const SOURCE = "chrome://tracker/content/trackerQueue.js";
+const INTERFACE = Components.interfaces.nsISupports;
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+const loader = Cc['@mozilla.org/moz/jssubscript-loader;1']
+    .getService(Ci.mozIJSSubScriptLoader);
+
+function Component () {
+    this.wrappedJSObject = this;
+}
+
+function GetJsService (component)
+{
+	var comp = Components.classes [component].getService (Components.interfaces.nsISupports);
+	return comp.wrappedJSObject;
+}
+
+loader.loadSubScript(SOURCE, Component.prototype);
+
+var Factory = {
+    createInstance: function(aOuter, aIID) {
+        if(aOuter != null)
+            throw Cr.NS_ERROR_NO_AGGREGATION;
+        var component = new Component();
+        if(typeof(component.wrappedJSObject.init) == 'function')
+            component.init();
+
+        return component.QueryInterface(aIID);
+    }
+};
+
+var Module = {
+    _firstTime: true,
+
+    registerSelf: function(aCompMgr, aFileSpec, aLocation, aType) {
+        if (this._firstTime) {
+            this._firstTime = false;
+            throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN;
+        };
+        aCompMgr = aCompMgr.QueryInterface(Ci.nsIComponentRegistrar);
+        aCompMgr.registerFactoryLocation(
+            CLASS_ID, CLASS_NAME, CONTRACT_ID, aFileSpec, aLocation, aType);
+    },
+
+    unregisterSelf: function(aCompMgr, aLocation, aType) {
+        aCompMgr = aCompMgr.QueryInterface(Ci.nsIComponentRegistrar);
+        aCompMgr.unregisterFactoryLocation(CLASS_ID, aLocation);
+    },
+
+    getClassObject: function(aCompMgr, aCID, aIID) {
+        if (!aIID.equals(Ci.nsIFactory))
+            throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+
+        if (aCID.equals(CLASS_ID))
+            return Factory;
+
+        throw Cr.NS_ERROR_NO_INTERFACE;        
+    },
+
+    canUnload: function(aCompMgr) { return true; }
+};
+
+function NSGetModule(aCompMgr, aFileSpec) { return Module; }
+

Added: trunk/extensions/thunderbird-extension/components/TrackerSettings.js
==============================================================================
--- (empty file)
+++ trunk/extensions/thunderbird-extension/components/TrackerSettings.js	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,97 @@
+//
+// TrackerSettings.js: Base code for settings component
+//
+// Copyright (C) 2007 Pierre Ãstlund
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+//
+
+// Source: http://hyperstruct.net/2006/8/10/your-first-javascript-xpcom-component-in-10-minutes
+
+const CLASS_ID = Components.ID('{bde6ce3b-f79d-4cea-8db2-a26d69616536}');
+const CLASS_NAME = 'Tracker settings component';
+const CONTRACT_ID = '@tracker-project.org/services/settings;1';
+const SOURCE = 'chrome://tracker/content/trackerSettings.js';
+const INTERFACE = Components.interfaces.nsISupports;
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+const loader = Cc['@mozilla.org/moz/jssubscript-loader;1']
+    .getService(Ci.mozIJSSubScriptLoader);
+
+function Component() {
+    this.wrappedJSObject = this;
+}
+
+function GetJsService (component)
+{
+	var comp = Components.classes [component].getService (Components.interfaces.nsISupports);
+	return comp.wrappedJSObject;
+}
+
+loader.loadSubScript(SOURCE, Component.prototype);
+
+var Factory = {
+    createInstance: function(aOuter, aIID) {
+        if(aOuter != null)
+            throw Cr.NS_ERROR_NO_AGGREGATION;
+        var component = new Component();
+        if(typeof(component.wrappedJSObject.init) == 'function')
+            component.init();
+
+        return component.QueryInterface(aIID);
+    }
+};
+
+var Module = {
+    _firstTime: true,
+
+    registerSelf: function(aCompMgr, aFileSpec, aLocation, aType) {
+        if (this._firstTime) {
+            this._firstTime = false;
+            throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN;
+        };
+        aCompMgr = aCompMgr.QueryInterface(Ci.nsIComponentRegistrar);
+        aCompMgr.registerFactoryLocation(
+            CLASS_ID, CLASS_NAME, CONTRACT_ID, aFileSpec, aLocation, aType);
+    },
+
+    unregisterSelf: function(aCompMgr, aLocation, aType) {
+        aCompMgr = aCompMgr.QueryInterface(Ci.nsIComponentRegistrar);
+        aCompMgr.unregisterFactoryLocation(CLASS_ID, aLocation);
+    },
+
+    getClassObject: function(aCompMgr, aCID, aIID) {
+        if (!aIID.equals(Ci.nsIFactory))
+            throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+
+        if (aCID.equals(CLASS_ID))
+            return Factory;
+
+        throw Cr.NS_ERROR_NO_INTERFACE;        
+    },
+
+    canUnload: function(aCompMgr) { return true; }
+};
+
+function NSGetModule(aCompMgr, aFileSpec) { return Module; }
+

Added: trunk/extensions/thunderbird-extension/content/contents.rdf
==============================================================================
--- (empty file)
+++ trunk/extensions/thunderbird-extension/content/contents.rdf	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"; 
+	 xmlns:chrome="http://www.mozilla.org/rdf/chrome#";>
+
+  <RDF:Seq RDF:about="urn:mozilla:package:root">
+    <RDF:li RDF:resource="urn:mozilla:package:tracker"/>
+  </RDF:Seq>
+    
+  <RDF:Seq RDF:about="urn:mozilla:overlays">
+    <RDF:li RDF:resource="chrome://messenger/content/messenger.xul"/>
+    <RDF:li RDF:resource="chrome://messenger/content/mailWindowOverlay.xul"/>
+  </RDF:Seq>
+     
+  <RDF:Seq RDF:about="chrome://messenger/content/messenger.xul">
+    <RDF:li>chrome://tracker/content/tracker.xul</RDF:li>
+  </RDF:Seq>
+  
+  <RDF:Seq about="chrome://messenger/content/mailWindowOverlay.xul">
+    <RDF:li>chrome://tracker/content/trackerMailWindow.xul</RDF:li>
+  </RDF:Seq>  
+  
+  <RDF:Description 
+    RDF:about="urn:mozilla:package:tracker"
+    chrome:displayName="Tracker Indexer 0.1.3"
+    chrome:author="Pierre Ãstlund"
+    chrome:authorURL="mailto:pierre ostlund gmail com"
+    chrome:name="tracker"
+    chrome:extension="true"
+    chrome:description="Index mails, RSS and more using Tracker."
+    chrome:settingsURL="chrome://tracker/content/trackerPrefs.xul">
+  </RDF:Description>
+
+</RDF:RDF>

Added: trunk/extensions/thunderbird-extension/content/tracker.css
==============================================================================
--- (empty file)
+++ trunk/extensions/thunderbird-extension/content/tracker.css	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,12 @@
+
+statusbarpanel#tracker-icon[status="enabled"] {
+  list-style-image: url("chrome://tracker/skin/tracker.png");
+}
+
+statusbarpanel#tracker-icon[status="disabled"] {
+  list-style-image: url("chrome://tracker/skin/tracker-disabled.png");
+}
+
+statusbarpanel#tracker-icon[status="error"] {
+  list-style-image: url("chrome://tracker/skin/tracker-error.png");
+}

Added: trunk/extensions/thunderbird-extension/content/tracker.js
==============================================================================
--- (empty file)
+++ trunk/extensions/thunderbird-extension/content/tracker.js	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,122 @@
+//
+// tracker.js: Starting point for the Thunderbird extension
+//
+// Copyright (C) 2007 Pierre Ãstlund
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+//
+
+var gTrackerSettings = Components.classes ['@tracker-project.org/services/settings;1']
+	.getService (Components.interfaces.nsITrackerSettings);
+
+function trackerInit ()
+{
+	dump ("Tracker init started\n");
+	
+	// Load settings
+	gTrackerSettings.init ();
+	
+	dump ('Adding settings observer...');
+	var prefbranch = Components.classes ['@mozilla.org/preferences-service;1']
+		.getService (Components.interfaces.nsIPrefBranch2);
+	prefbranch.addObserver ('tracker', gSettingObserver, false);
+	dump ("Done.\n");
+	
+	var enabled = gTrackerSettings.getBoolPref ('Enabled');
+	if (!enabled) {
+		dump ("Tracker backend is now disabled\n");
+	} else {
+		// Make sure we catch changes
+		gTrackerDataTracker.RegisterSelf ();
+		
+		// The following timeout handler will initiate the indexing process by locating data that
+		// needs to be indexed. We delay a while go give Thunderbird some time to settle.
+		dump ("Tracker extension is now enabled\n");
+		window.setTimeout (function () { gTrackerMainloop.Start (); }, 3000);
+	}
+	
+	updateStatus (enabled);
+	dump ("Tracker init ended\n");
+}
+
+function onShowSettings (event)
+{
+	window.openDialog ('chrome://tracker/content/trackerPrefs.xul',
+						'PreferenceWindow',
+						'chrome,toolbar,modal=yes,resizable=no',
+						'pref-indexing');
+}
+
+function onStatusbarClick ()
+{
+	// We invert current running mode
+	var enabled = !gTrackerSettings.getBoolPref ('Enabled');
+	gTrackerSettings.setBoolPref ('Enabled', enabled);
+}
+
+// Update status of the little dog in the corner
+function updateStatus (enabled)
+{
+	var elem = document.getElementById ('tracker-icon');
+	var bundle = document.getElementById ('bundle_tracker');
+	
+	if (enabled) {
+		elem.setAttribute ('status', 'enabled');
+		elem.setAttribute ('tooltiptext', bundle.getString ('indexingEnabledTooltip'));
+	} else {
+		elem.setAttribute ('status', 'disabled');
+		elem.setAttribute ('tooltiptext', bundle.getString ('indexingDisabledTooltip'));
+	}
+}
+
+// We use this observer to check if we have been enabled or disabled and if we should restart
+// the main loop in case indexing speed changed
+var gSettingObserver = {
+
+	observe: function (subject, topic, data)
+	{
+		var branch = subject.QueryInterface (Components.interfaces.nsIPrefBranch);
+		
+		if (data == 'tracker.enabled') {
+			var enabled = branch.getBoolPref (data);
+			
+			// Enable or disabled depending on new status
+			if (enabled) {
+				gTrackerDataTracker.RegisterSelf ();
+				gTrackerMainloop.Restart (3);
+				dump ("Tracker extension is now enabled\n");
+			} else {
+				gTrackerDataTracker.UnregisterSelf ();
+				gTrackerMainloop.Stop ();
+				dump ("Tracker extension is now disabled\n");
+			}
+			
+			updateStatus (enabled);
+		} else if (data == 'tracker.index.delay') {
+			// In case delay time changed, restart mainloop to get immediate effect. We need to get
+			// the new value from included branch as it may not be updated in gTrackerSettings yet.
+			gTrackerMainloop.Restart (branch.getIntPref ('tracker.index.delay'));
+		}
+	}
+};
+
+window.addEventListener ('load', trackerInit, false);
+

Added: trunk/extensions/thunderbird-extension/content/tracker.xul
==============================================================================
--- (empty file)
+++ trunk/extensions/thunderbird-extension/content/tracker.xul	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,60 @@
+<?xml version="1.0"?>
+
+<!--
+//
+// tracker.xul: Overlay for basic main window GUI items
+//
+// Copyright (C) 2007 Pierre Ãstlund
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+//
+-->
+
+<?xml-stylesheet href="chrome://tracker/skin/overlay.css" type="text/css"?>
+<?xml-stylesheet href="chrome://tracker/content/tracker.css" type="text/css"?>
+<!DOCTYPE overlay SYSTEM "chrome://tracker/locale/tracker.dtd">
+
+<overlay id="tracker-overlay" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";>
+
+	<stringbundleset id="stringbundleset">
+		<stringbundle id="bundle_tracker" src="chrome://tracker/locale/strings.properties"/>
+	</stringbundleset>
+
+	<script src="trackerUnindex.js"/>
+	<script src="tracker.js"/>
+
+	<!-- This will appear in the tools menu -->
+	<menupopup id="taskPopup">
+		<menuitem id="tracker-settings" 
+				label="&trackermain.settings;" 
+				oncommand="onShowSettings(event);"/>
+	</menupopup>
+	
+	<!-- This gives the small icon in the bottom right corner -->
+	<statusbar id="status-bar">
+		<statusbarpanel id="tracker-icon" 
+						class="statusbarpanel-iconic" 
+						status="disabled" 
+						oncommand="onStatusbarClick ();"/>
+	</statusbar>
+	
+</overlay>
+

Added: trunk/extensions/thunderbird-extension/content/trackerIndexer.js
==============================================================================
--- (empty file)
+++ trunk/extensions/thunderbird-extension/content/trackerIndexer.js	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,452 @@
+//
+// trackerIndexer.js: Indexer component implementation
+//
+// Copyright (C) 2007 Pierre Ãstlund
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+//
+
+const TRACKER_INDEX_PROPERTY = 'trackerIndex';
+const TRACKER_SHOULDNOTINDEX_PROPERTY = 'trackerNoIndex';
+
+var object_count = -1;
+
+// This function taks a hashtable as argument with key-value pairs and writes to the next 
+// output file. This should be considered an internal function and should only be called
+// from within this instance. The type variable should reflect the content and will be the
+// document element name (we generate some sort of xml here for easy parsing).
+function writeHashTableToNextFile (hashtable, type)
+{
+	var file = Components.classes ['@mozilla.org/file/local;1']
+		.createInstance (Components.interfaces.nsILocalFile);
+	file.initWithPath (newOutputFilename ())
+	
+	// Components.resultseate a stream so that data can be written to file. The various modes were found
+	// on this page: http://developer.mozilla.org/en/docs/Code_snippets:File_I/O
+	var stream = Components.classes ['@mozilla.org/network/file-output-stream;1']
+		.createInstance (Components.interfaces.nsIFileOutputStream);
+	try {
+		stream.init (file, 0x20 | 0x08 | 0x02, 0664, 0);
+	} catch (ex) {
+		dump ('Failed to write index file: ' + ex + "\n");
+		return;
+	}
+	
+	// HOWTO write unicode-encoded strings :
+	// http://developer.mozilla.org/en/docs/Writing_textual_data
+	var ustream = Components.classes["@mozilla.org/intl/converter-output-stream;1"]
+    .createInstance(Components.interfaces.nsIConverterOutputStream);
+  	try {
+		ustream.init (stream, 'UTF-8', 0, 0x0000);
+	} catch (ex) {
+		dump ('Failed to write unicode index file: ' + ex + "\n");
+		return;
+	}
+	
+	ustream.writeString ('<' + type + ">\n");
+	for (var key in hashtable) {
+		var line = '<' + key + '><![CDATA[' + hashtable [key] + ']]></' + key + ">\n";
+		ustream.writeString (line);
+	}
+	ustream.writeString ("</" + type + ">\n");
+
+	ustream.close ();
+	stream.close ();
+}
+
+function findLastOutputFile ()
+{
+	var settings = GetJsService ('@tracker-project.org/services/settings;1');
+	var dir = Components.classes ['@mozilla.org/file/local;1']
+		.getService (Components.interfaces.nsILocalFile);
+	
+	try {
+		dir.initWithPath (settings.getCharPref ('DestinationDirectory') + "/ToIndex");
+		
+		var enumerator = dir.directoryEntries;
+		while (enumerator.hasMoreElements ()) {
+			var file = enumerator.getNext ().QueryInterface (Components.interfaces.nsIFile);
+			if (!file || !file.isFile ())
+				continue;
+			
+			var number = parseInt (file.leafName.substring(0,file.leafName.length-4));
+			if (object_count == -1 || object_count > number)
+				object_count = number;
+		}
+		
+		if (object_count == -1)
+			object_count = 0;
+	} catch (ex) {
+		// We default to 0 (zero)
+		object_count = 0;
+	}
+}
+
+// Figure out what our next object name is (we don't overwrite existing files)
+function newOutputFilename () 
+{
+	if (object_count == -1)
+		findLastOutputFile ();
+		
+	var settings = GetJsService ('@tracker-project.org/services/settings;1');
+	var file = Components.classes ["@mozilla.org/file/local;1"]
+		.createInstance (Components.interfaces.nsILocalFile);
+	if (!file)
+		throw Components.results.NS_ERROR_FAILURE;
+	
+	do {
+		file.initWithPath (settings.getCharPref ('DestinationDirectory') +
+			"/ToIndex/" + object_count++ +".tms");	
+	} while (file.exists ());
+
+	return file.path;
+}
+
+
+Component.prototype = {
+
+	reload: function() {
+		loader.loadSubScript(SOURCE, this.__proto__);
+	},
+
+	QueryInterface: function(aIID) {
+		if(!aIID.equals(INTERFACE) && !aIID.equals(Ci.nsISupports))
+			throw Cr.NS_ERROR_NO_INTERFACE;
+		return this;
+	},
+
+	// Checks if an account should be indexed by pulling its type and checks if that type is enabled
+	// for indexing.
+	shouldIndexAccount: function (account)
+	{
+		var settings = GetJsService ('@tracker-project.org/services/settings;1');
+
+		if (account instanceof Components.interfaces.nsIMsgAccount)
+			account.QueryInterface (Components.interfaces.nsIMsgAccount);
+		else
+			return false;
+		
+		switch (account.incomingServer.type) {
+		case 'imap':
+			return settings.getBoolPref ('EnableImap');
+		case 'pop3':
+			return settings.getBoolPref ('EnablePop');
+		case 'rss':
+			return settings.getBoolPref ('EnableRss');
+		case 'nntp':
+			return settings.getBoolPref ('Enable.News');
+		case 'movemail':
+			return settings.getBoolPref ('EnableMailspool');
+		case 'none':
+			return settings.getBoolPref ('EnableLocal');
+		}
+		
+		return false;
+	},
+
+	// A folder or a message should only be indexed in case TRACKER_SHOULDNOTINDEX_PROPERTY does not
+	// exist or if it exists with a value other than '1'
+	shouldIndexFolder: function (folder)
+	{
+		if (folder instanceof Components.interfaces.nsIMsgFolder) {
+			var prop = folder.getStringProperty (TRACKER_SHOULDNOTINDEX_PROPERTY);
+			return !prop || (prop && prop != '1');
+		}
+		
+		return false;
+	},
+
+	shouldIndexHdr: function (hdr)
+	{
+		if (hdr instanceof Components.interfaces.nsIMsgDBHdr) {
+			var prop = hdr.getStringProperty (TRACKER_SHOULDNOTINDEX_PROPERTY);
+			return !prop || (prop && prop != '1');
+		}
+		
+		throw Components.results.NS_ERROR_NO_INTERFACE;
+	},
+
+	// A folder or message is indexed if it has the TRACKER_INDEX_PROPERTY set with value "1".
+	isFolderIndexed: function (folder)
+	{
+		if (folder instanceof Components.interfaces.nsIMsgFolder) 
+			return (folder && folder.getStringProperty (TRACKER_INDEX_PROPERTY) == '1');
+
+		throw Components.results.NS_ERROR_NO_INTERFACE;
+	},
+
+	isHdrIndexed: function (hdr)
+	{
+		if (hdr instanceof Components.interfaces.nsIMsgDBHdr) 
+			return (hdr && hdr.getStringProperty (TRACKER_INDEX_PROPERTY) == '1');
+		
+		throw Components.results.NS_ERROR_NO_INTERFACE;
+	},
+
+	isFolderUserMarked: function (folder)
+	{
+		if (folder instanceof Components.interfaces.nsIMsgFolder)
+			return (folder && folder.getStringProperty (TRACKER_SHOULDNOTINDEX_PROPERTY) == '1');
+		
+		throw Components.results.NS_ERROR_NO_INTERFACE;
+	},
+
+	isHdrUserMarked: function (hdr)
+	{
+		if (hdr instanceof Components.interfaces.nsIMsgDBHdr)
+			return (hdr && hdr.getStringProperty (TRACKER_SHOULDNOTINDEX_PROPERTY) == '1');
+		
+		throw Components.results.NS_ERROR_NO_INTERFACE;
+	},
+
+	markFolderAsIndexed: function (folder)
+	{
+		if (folder instanceof Components.interfaces.nsIMsgFolder)
+			folder.setStringProperty (TRACKER_INDEX_PROPERTY, '1');
+		else 
+			throw Components.results.NS_ERROR_NO_INTERFACE;
+	},
+
+	markHdrAsIndexed: function (hdr)
+	{
+		if (hdr instanceof Components.interfaces.nsIMsgDBHdr)
+			hdr.setStringProperty (TRACKER_INDEX_PROPERTY, '1');
+		else	
+			throw Components.results.NS_ERROR_NO_INTERFACE;
+	},
+
+	markFolderAsUserMarked: function (folder)
+	{
+		if (folder instanceof Components.interfaces.nsIMsgFolder) 
+			folder.setStringProperty (TRACKER_SHOULDNOTINDEX_PROPERTY, '1');
+		else
+			throw Components.results.NS_ERROR_NO_INTERFACE;
+	},
+
+	markHdrAsUserMarked: function (hdr)
+	{
+		if (hdr instanceof Components.interfaces.nsIMsgDBHdr)
+			hdr.setStringProperty (TRACKER_SHOULDNOTINDEX_PROPERTY, '1');
+		else
+			throw Components.results.NS_ERROR_NO_INTERFACE;
+	},
+
+	resetFolderUserMarked: function (folder)
+	{
+		if (folder instanceof Components.interfaces.nsIMsgFolder) { 
+			if (folder.getStringProperty (TRACKER_SHOULDNOTINDEX_PROPERTY) != '')
+				folder.setStringProperty (TRACKER_SHOULDNOTINDEX_PROPERTY, '');
+		} else
+			throw Components.results.NS_ERROR_NO_INTERFACE;
+	},
+
+	resetHdrUserMarked: function (hdr)
+	{
+		if (hdr instanceof Components.interfaces.nsIMsgDBHdr) {
+			if (hdr.getStringProperty (TRACKER_SHOULDNOTINDEX_PROPERTY) != '')
+				hdr.setStringProperty (TRACKER_SHOULDNOTINDEX_PROPERTY, '');
+		} else
+			throw Components.results.NS_ERROR_NO_INTERFACE;
+	},
+
+	resetFolder: function (folder, userMarked, recursive, content)
+	{
+		if (folder instanceof Components.interfaces.nsIMsgFolder) {
+			// We only update with a new value if we already have one
+			if (folder.getStringProperty (TRACKER_INDEX_PROPERTY) != '') 
+				folder.setStringProperty (TRACKER_INDEX_PROPERTY, '');
+
+			if (userMarked)
+				this.resetFolderUserMarked (folder);
+		} else 
+			throw Components.results.NS_ERROR_NO_INTERFACE;
+		
+		// Should we mark content as well?
+		if (content && folder.getTotalMessages (false) > 0) {
+			var enumerator = folder.getMessages (null);
+			while (enumerator.hasMoreElements ()) {
+				var hdr = enumerator.getNext ().QueryInterface (Components.interfaces.nsIMsgDBHdr);
+				if (!hdr)
+					continue;
+				this.resetHdr (hdr);
+			}
+		}
+		
+		// Should we go recursive too?
+		if (recursive) {
+			var allFolders = Components.classes ['@mozilla.org/supports-array;1']
+				.createInstance (Components.interfaces.nsISupportsArray);
+			folder.ListDescendents (allFolders);
+			for (var i = 0; i < allFolders.Count (); i++) {
+				var subFolder = allFolders.QueryElementAt (i, Components.interfaces.nsIMsgFolder);
+				if (!subFolder) 
+					continue;
+				
+				// Using ListDescendents above will "plain out" the folder list and give us all folders.
+				// This is why we should not go recursive from here.
+				this.resetFolder (subFolder, userMarked, false, content);
+			}
+		}
+	},
+
+	resetHdr: function (hdr, userMarked)
+	{
+		if (hdr instanceof Components.interfaces.nsIMsgDBHdr) {
+			// We only update with a new value if we already have one
+			if (hdr.getStringProperty (TRACKER_INDEX_PROPERTY) != '')
+				hdr.setStringProperty (TRACKER_INDEX_PROPERTY, '');
+			
+			if (userMarked) 
+				this.resetHdrUserMarked (hdr);
+		} else 
+			throw Components.results.NS_ERROR_NO_INTERFACE;
+	},
+
+	resetEverything: function (userMarked)
+	{
+		var gAccountManager = Components.classes ['@mozilla.org/messenger/account-manager;1']
+			.getService (Components.interfaces.nsIMsgAccountManager);
+		var accounts = gAccountManager.accounts;
+		
+		for (var i = 0; i < accounts.Count (); i++) {
+			var account = accounts.QueryElementAt (i, Components.interfaces.nsIMsgAccount)
+			if (!account)
+				continue;
+			
+			// We reset everything recursively
+			this.resetFolder (account.incomingServer.rootFolder, userMarked, true, true);
+		}
+	},
+
+	addToIndex: function (hdr)
+	{
+		try {
+			hdr.QueryInterface (Components.interfaces.nsIMsgDBHdr);
+		} catch (ex) {
+		}
+		
+		if (!hdr)
+			throw Components.interfaces.NS_ERROR_NO_INTERFACE;
+			
+		var properties = new Array ();
+		
+		var serverType = hdr.folder.server.type, type = null;
+		
+		// We must ensure that all elements exist. Some of them might throw an exception in various
+		// set-ups, so we have to catch them and default to something.
+		properties ['Author'] = hdr.mime2DecodedAuthor;
+		properties ['Date'] = hdr.dateInSeconds;
+		properties ['Folder'] = hdr.folder.name;
+		properties ['FolderFile'] = hdr.folder.path.unixStyleFilePath;
+		
+		try {
+			properties ['HasOffline'] = hdr.folder.hasMsgOffline (hdr.messageKey);
+		} catch (ex) {
+			dump ('Failed to parse HasOffline: ' + ex + "\n");
+			properties ['HasOffline'] = 'false';
+		}
+		
+		properties ['MessageId'] = hdr.messageId;
+		properties ['MessageSize'] = hdr.messageSize;
+		properties ['MessageOffset'] = hdr.messageOffset;
+		properties ['OfflineSize'] = hdr.offlineMessageSize;
+		properties ['Recipients'] = hdr.mime2DecodedRecipients;
+		properties ['Subject'] = hdr.mime2DecodedSubject;
+		properties ['MessageKey'] = hdr.messageKey;
+		
+		try {
+			properties ['Uri'] = hdr.folder.getUriForMsg (hdr);
+		} catch (ex) { 
+			properties ['Uri'] = null;
+			dump ('Failed to parse uri: ' + ex + "\n");
+		}
+		
+		switch (serverType) {
+		case 'none': // local account
+		case 'pop3':
+		case 'imap':
+		case 'nntp':
+		case 'movemail':
+			type = 'MailMessage';
+			break;
+		case 'rss':
+			// We usually have the content body available but hasMsgOffline still reports false. Just
+			// default to true until this mess has been cleared out.
+			properties ['HasOffline'] = true;
+			
+			try {
+				var db = hdr.folder.getMsgDatabase (null);
+				var urls = db.dBFolderInfo.getCharPtrProperty ('feedUrl');
+				properties ['FeedURL'] = urls.substring (urls.lastIndexOf ('|')+1);
+			} catch (ex) {
+				properties ['FeedURL'] = 'Unknown';
+			}
+			type = 'FeedItem';
+			break;
+		}
+		
+		// Write everything to file
+		if (type) {
+			dump ('Writing ' + type + ' to file. Subject: ' + properties ['Subject'] + "\n");
+			writeHashTableToNextFile (properties, type);
+		}
+	},
+
+	dropFolderFromIndex: function (folder)
+	{
+		try {
+			folder.QueryInterface (Components.interfaces.nsIMsgFolder);
+		} catch (ex) {
+		}
+		
+		if (!folder)
+			throw Components.interfaces.NS_ERROR_NO_INTERFACE;
+
+		// We don't even bother if no messages exist
+		if (folder.getTotalMessages (false) < 1)
+			return;
+
+		var properties = new Array ();
+		properties ['FolderFile'] = folder.path.unixStyleFilePath;
+		
+		writeHashTableToNextFile (properties, 'DeleteFolder');
+	},
+
+	dropHdrFromIndex: function (hdr)
+	{
+		try {
+			hdr.QueryInterface (Components.interfaces.nsIMsgDBHdr);
+		} catch (ex) {
+		}
+		
+		if (!hdr)
+			throw Components.interfaces.NS_ERROR_NO_INTERFACE;
+		
+		var properties = new Array ();
+		properties ['Uri'] = hdr.folder.getUriForMsg (hdr);
+
+		properties ['FolderFile'] = hdr.folder.path.unixStyleFilePath;
+		properties ['MessageKey'] = hdr.messageKey;
+
+		writeHashTableToNextFile (properties, 'DeleteHdr');
+	}
+};
+

Added: trunk/extensions/thunderbird-extension/content/trackerMailWindow.xul
==============================================================================
--- (empty file)
+++ trunk/extensions/thunderbird-extension/content/trackerMailWindow.xul	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,397 @@
+<?xml version="1.0"?>
+
+<!--
+//
+// trackerMailWindow.xul: Overlay for folder and message menu
+//
+// Copyright (C) 2007 Pierre Ãstlund
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+//
+-->
+
+<?xml-stylesheet href="chrome://tracker/skin/overlay.css" type="text/css"?>
+<?xml-stylesheet href="chrome://tracker/content/tracker.css" type="text/css"?>
+<!DOCTYPE overlay SYSTEM "chrome://tracker/locale/tracker.dtd">
+
+<overlay id="tracker-window-overlay" 
+		xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";>
+
+	<stringbundleset id="stringbundleset">
+		<stringbundle id="bundle_tracker" src="chrome://tracker/locale/strings.properties"/>
+	</stringbundleset>
+
+	<script src="trackerUtils.js"/>
+
+	<script type="application/x-javascript">
+	<![CDATA[
+		var indexer = GetJsService ('@tracker-project.org/services/indexer;1');
+		var settings = GetJsService ('@tracker-project.org/services/settings;1');
+		var messenger = Components.classes ['@mozilla.org/messenger;1'].getService (Components.interfaces.nsIMessenger);
+
+		function getSelectedFolder ()
+		{
+			var selectedFolders = GetSelectedMsgFolders ();
+			if (selectedFolders == null || selectedFolders.length < 1)
+				return null;
+			
+			return selectedFolders [0];
+		}
+		
+		function getSelectedMessages ()
+		{
+			try {
+				var view = GetDBView ();
+				var uris = {};
+				var count = {};
+				view.getURIsForSelection (uris, count);
+				var msgs = Components.classes ['@mozilla.org/supports-array;1']
+					.createInstance (Components.interfaces.nsISupportsArray);
+				
+				for (var i = 0; i < count.value; i++) {
+					var uri = uris.value [i];
+					msgs.AppendElement (messenger.msgHdrFromURI (uri));
+				}
+				
+				return msgs;
+			} catch (ex) {
+			}
+			
+			return null;
+		}
+
+		function onIndexFillFolderMenu ()
+		{
+			// We don't generate the menu if we are disabled
+			if (!settings.getBoolPref ('Enabled'))
+				return;
+			
+			var folder = getSelectedFolder ();
+			if (!folder)
+				return;
+				
+			try {
+				var noindexing = document.getElementById ('folderPaneContext-noindexing');
+				var removefolder = document.getElementById ('folderPaneContext-removeIndexedFolder');
+				var removefolderrecursive = 
+					document.getElementById ('folderPaneContext-removeIndexedFolderRecursive');
+				var shouldNotIndex = indexer.isFolderUserMarked (folder);
+				
+				// Show a nice little "checked mark" if item is not supposed to be indexed
+				if (shouldNotIndex) 
+					noindexing.setAttribute ('checked', 'true');
+				else
+					noindexing.setAttribute ('checked', 'false');
+				noindexing.hidden = false;
+					
+				// Show the "remove" option if current folder is indexed
+				if (indexer.isFolderIndexed (folder)) {
+					removefolder.hidden = false;
+					removefolderrecursive.hidden = false;
+				} else {
+					removefolder.hidden = true;
+					removefolderrecursive.hidden = true;
+				}
+				
+				// Show the separator
+				var sep = document.getElementById ('folderPaneContext-trackersep-folder');
+				sep.hidden = false;
+			} catch (ex) {
+			}
+		}
+		
+		function allMarkedForExclusion (msgs)
+		{
+			var enum = msgs.Enumerate ();
+			
+			while (1) {
+				var hdr = enum.currentItem ();
+				if (!indexer.isHdrUserMarked (hdr))
+					break;
+			
+				try {
+					enum.next ();
+				} catch (ex) {
+					return true;
+				}
+			}
+			
+			return false;
+		}
+		
+		function allMarkedAsNotIndexed (msgs)
+		{
+			var enum = msgs.Enumerate ();
+			
+			while (1) {
+				var hdr = enum.currentItem ();
+				if (indexer.isHdrIndexed (hdr))
+					break;
+				
+				try {
+					enum.next ();
+				} catch (ex) {
+					return true;
+				}
+			}
+			
+			return false;
+		}
+
+		function onIndexFillMessageMenu ()
+		{
+			// We don't generate the menu if we are disabled
+			if (!settings.getBoolPref ('Enabled'))
+				return;
+			
+			var messages = getSelectedMessages ();
+			if (!messages)
+				return;
+			
+			try {
+				var removemessages = document.getElementById ('threadPaneContext-removeIndexedMessage');
+				var noindexing = document.getElementById ('threadPaneContext-noindexing');
+				
+				// If all selected messages has the "do not index" flag, show the check mark. Otherwise don't.
+				if (allMarkedForExclusion (messages)) 
+					noindexing.setAttribute ('checked', 'true');
+				else
+					noindexing.setAttribute ('checked', 'false');
+				noindexing.hidden = false;
+				
+				// We only show the "remove"-item if we have at least one indexed message
+				removemessages.hidden = allMarkedAsNotIndexed (messages);
+			} catch (ex) {
+			}
+		}
+		
+		function onRemoveIndexedFolder ()
+		{
+			try {
+				var folder = getSelectedFolder ();
+				if (!folder)
+					return;
+				
+				// We remove folder non-recursive and ask if user marks should be removed as well
+				gTrackerUnindex.RemoveFolder (folder, false, true);
+			} catch (ex) {
+			}
+		}
+
+		function onRemoveIndexedFolderRecursive ()
+		{			
+			try {
+				var folder = getSelectedFolder ();
+				if (!folder)
+					return;
+							
+				// We remove folder recursively and ask if user marks should be removed as well
+				gTrackerUnindex.RemoveFolder (folder, true, true);
+			} catch (ex) {
+			}
+		}
+		
+		function onRemoveIndexedMessage ()
+		{
+			try {
+				var msgs = getSelectedMessages ();
+				if (!msgs)
+					return;
+					
+				// We remove messages and ask is user marks should be removed as well
+				gTrackerUnindex.RemoveHdrs (msgs, true);
+			} catch (ex) {
+			}
+		}
+		
+		function onNoFolderIndexing ()
+		{
+			try {
+				var folder = getSelectedFolder ();
+				if (!folder)
+					return;
+					
+				folder.QueryInterface (Components.interfaces.nsIMsgFolder);
+				var userMarked = indexer.isFolderUserMarked (folder);
+				var queue = GetJsService ('@tracker-project.org/services/queue;1');
+				if (userMarked) {
+					indexer.resetFolder (folder, true, false, false);
+					queue.addFolder (folder);
+				} else
+					indexer.markFolderAsUserMarked (folder);
+			} catch (ex) {
+			}
+		}
+		
+		function setUserMarked (msgs, userMarked)
+		{
+			var done = false;
+			var enum = msgs.Enumerate ();
+			
+			while (!done) {
+				var hdr = enum.currentItem ();
+				
+				if (userMarked) 
+					indexer.markHdrAsUserMarked (hdr);
+				else
+					indexer.resetHdrUserMarked (hdr);
+			
+				try {
+					enum.next ();
+				} catch (ex) {
+					done = true;
+				}
+			}
+		}
+		
+		function onNoMessageIndexing ()
+		{
+			try {
+				var msgs = getSelectedMessages ();
+				if (!msgs)
+					return;
+				
+				var userMarked = !allMarkedForExclusion (msgs);
+				setUserMarked (msgs, userMarked);
+				
+				if (!userMarked) {
+					var hdr = msgs.QueryElementAt (0, Components.interfaces.nsIMsgDBHdr);
+					var queue = GetJsService ('@tracker-project.org/services/queue;1');
+					queue.addHdr (hdr);
+				}
+					
+			} catch (ex) {
+			}
+		}
+		
+		// The code below belongs to the "open-message-from-command-line"-code. The reason
+		// why this code is placed here is because of the MsgOpenNewWindowForMessage-function,
+		// which will do everything we want. This code will be removed once we have proper
+		// support for opening from command line. This code is ugly...
+		var observer = Components.classes['@mozilla.org/observer-service;1']
+			.getService(Components.interfaces.nsIObserverService);
+		
+		var gTrackerCommandLineObserver = {
+			observe: function (subject, topic, data)
+			{
+				if (!data)
+					return;
+				
+				var messenger = Components.classes ['@mozilla.org/messenger;1']
+					.getService (Components.interfaces.nsIMessenger);
+				var hdr = messenger.msgHdrFromURI (data);
+				var tree = null, currentIndex = -1;
+				
+				dump ("Opening message: " + hdr.folder.getUriForMsg (hdr) + "\n");
+				
+				// Save the index of the currently selected folder and switch to folder indexed
+				// 0 (zero), which will always be a root folder.
+				if(gFolderTree) {
+					tree = GetFolderTree();
+					currentIndex = tree.view.selection.currentIndex;
+					tree.view.selection.select(0);
+				}
+
+				// Open the message
+				MsgOpenNewWindowForMessage (data, hdr.folder.URI);
+				
+				// Switch back to the old folder (if any)
+				if(currentIndex != -1) {
+					tree.view.selection.select(currentIndex);
+				}
+			}
+		};
+		
+		function getWindowCount ()
+		{
+			var ww = Components.classes ['@mozilla.org/embedcomp/window-watcher;1']
+				.getService (Components.interfaces.nsIWindowWatcher);
+			var enumerator = ww.getWindowEnumerator ();
+			var count = 0;
+			
+			while (enumerator.hasMoreElements ()) {
+				enumerator.getNext ();
+				count++;
+			}
+			
+			return count;
+		}
+		
+		// Each window opened (including the windows displaying a message) will load the code in
+		// this file due to the way they are overlayed. If each window is listening for new 
+		// messages that the user wants to display, then we'll get one new message window per 
+		// already opened window. For instance, if the user has a main window open as well as 
+		// writing a message and decides to display a message - two equal windows will pop-up. 
+		// We only listen in case we have one windows open to work around this issue.
+		if (getWindowCount () == 1)
+			observer.addObserver (gTrackerCommandLineObserver, 'tracker-open-uri', false);
+		
+		// Tell the world that we are done (mainly to make the "open from command line"-hack to work)
+		observer.notifyObservers (null, 'tracker-loaded', null);
+	]]>
+	</script>
+	
+	<!-- These menu options will show up when right-clicking a message -->
+	<popup id="threadPaneContext" 
+			onpopupshowing="onIndexFillMessageMenu (); return fillThreadPaneContextMenu();">
+		<menuseparator id="threadPaneContext-trackersep-message"/>
+		<menuitem id="threadPaneContext-removeIndexedMessage"
+				label="&trackermessageview.removemessage;"
+				oncommand="onRemoveIndexedMessage ();"
+				insertbefore="threadPaneContext-trackersep-message"
+				hidden="true"/>
+		<menuitem id="threadPaneContext-noindexing"
+				label="&trackermessageview.noindexing;"
+				oncommand="onNoMessageIndexing ();"
+				insertafter="threadPaneContext-trackersep-message"
+				type="checkbox"
+				autocheck="false"
+				hidden="true"/>
+	</popup>
+	
+	<!-- These menu options will show up when right-clicking a folder -->
+	<popup id="folderPaneContext" 
+			onpopupshowing="onIndexFillFolderMenu (); return fillFolderPaneContextMenu ();">
+			
+		<menuseparator id="folderPaneContext-trackersep-folder" 
+				insertafter="folderPaneContext-sep2"
+				hidden="true"/> 
+		<menuitem id="folderPaneContext-removeIndexedFolder" 
+				label="&trackerfoldertree.removefolder;" 
+				oncommand="onRemoveIndexedFolder ();"
+				insertafter="folderPaneContext-sep2"
+				hidden="true"/>
+		<menuitem id="folderPaneContext-removeIndexedFolderRecursive" 
+				label="&trackerfoldertree.removefolderrecursive;" 
+				oncommand="onRemoveIndexedFolderRecursive ();"
+				insertafter="folderPaneContext-sep2"
+				hidden="true"/>
+		<menuitem id="folderPaneContext-noindexing"
+				label="&trackerfoldertree.noindexing;"
+				oncommand="onNoFolderIndexing ();"
+				insertafter="folderPaneContext-sep2"
+				hidden="true"
+				type="checkbox"
+				autocheck="false"/>
+	</popup>
+
+</overlay>
+

Added: trunk/extensions/thunderbird-extension/content/trackerMessenger.xul
==============================================================================
--- (empty file)
+++ trunk/extensions/thunderbird-extension/content/trackerMessenger.xul	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,187 @@
+<?xml version="1.0"?>
+
+<!--
+//
+// tracker.xul: Overlay for basic main window GUI items
+//
+// Copyright (C) 2007 Pierre Ãstlund
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+//
+-->
+
+<?xml-stylesheet href="chrome://tracker/skin/overlay.css" type="text/css"?>
+<?xml-stylesheet href="chrome://tracker/content/tracker.css" type="text/css"?>
+<!DOCTYPE overlay SYSTEM "chrome://tracker/locale/tracker.dtd">
+
+<overlay id="tracker-overlay" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";>
+
+	<stringbundleset id="stringbundleset">
+		<stringbundle id="bundle_tracker" src="chrome://tracker/locale/strings.properties"/>
+	</stringbundleset>
+	
+	<script src="trackerUtils.js"/>
+	<script src="trackerUnindex.js"/>
+	<script src="trackerService.js"/>
+
+	<script type="application/x-javascript">
+	<![CDATA[
+		var observer = Components.classes['@mozilla.org/observer-service;1']
+			.getService(Components.interfaces.nsIObserverService);
+		var gTrackerSettings = GetJsService ('@tracker-project.org/services/settings;1');
+
+		function onTrackerLoad ()
+		{
+			observer.addObserver (gServiceObserver, 'tracker-service', false);
+			observer.addObserver (gQueueObserver, 'tracker-queue', false);
+			initService ();
+		}
+
+		function onTrackerUnload ()
+		{
+			observer.removeObserver (gServiceObserver, 'tracker-service');
+			observer.removeObserver (gQueueObserver, 'tracker-queue');
+		}
+
+		function onShowSettings (event)
+		{
+			window.openDialog ('chrome://tracker/content/trackerPrefs.xul',
+				'PreferenceWindow',
+				'chrome,toolbar,modal=no,resizable=no',
+				'pref-indexing');
+		}
+
+		function onStatusbarClick (t)
+		{
+			var elem = document.getElementById ('tracker-icon');
+			
+			// We need to check if we are in error mode. If we are, we show a message box with warnings.
+			// Otherwise we invert the running mode.
+			if (elem.getAttribute  ('status') == 'error') {
+				var bundle = document.getElementById ('bundle_tracker');
+				
+				window.alert (getLastWarning ());
+				setWarningStatus ();
+			} else {
+				// We invert current running mode
+				var enabled = !gTrackerSettings.getBoolPref ('Enabled');
+				gTrackerSettings.setBoolPref ('Enabled', enabled);
+			}
+		}
+
+		// Update status of the little dog in the corner
+		function setIndexingStatus (enabled)
+		{
+			var elem = document.getElementById ('tracker-icon');
+			var bundle = document.getElementById ('bundle_tracker');
+			
+			// We do not change the icon in case it displays the "error" dog. The user needs to check
+			// warnings in case they appear.
+			if (elem.getAttribute ('status') == 'error' && getWarningCount () > 0)
+				return;
+				
+			if (enabled) {
+				elem.setAttribute ('status', 'enabled');
+				elem.setAttribute ('tooltiptext', bundle.getString ('indexingEnabledTooltip'));
+			} else {
+				elem.setAttribute ('status', 'disabled');
+				elem.setAttribute ('tooltiptext', bundle.getString ('indexingDisabledTooltip'));
+			}
+		}
+
+		function setWarningStatus ()
+		{
+			var count = getWarningCount ();
+			
+			// Make sure that we have any warnings at all
+			if (count == 0) 
+				return;
+			
+			var elem = document.getElementById ('tracker-icon');
+			var bundle = document.getElementById ('bundle_tracker');
+			
+			elem.setAttribute ('status', 'error');
+			elem.setAttribute ('tooltiptext', bundle.getFormattedString ('warningCountDescription', [count]));
+		}
+
+		var gServiceObserver = {
+
+			observe: function (subject, topic, data)
+			{
+				// Act upon various interesting events
+				if (data == 'update-enabled') // Indexing was enabled
+					setIndexingStatus (true);
+				else if (data == 'update-disabled') // Indexing was disabled
+					setIndexingStatus (false);
+				else if (data == 'new-warning') // A warning was added
+					setWarningStatus ();
+				else if (data == 'warnings-empty') // All warnings has been seen by the user {
+					setIndexingStatus (gTrackerSettings.getBoolPref ('Enabled'));
+			}
+		};
+		
+		// This observer will listen for changes made to the queue. Why is this important?
+		// Well... if main loop isn't running and something new ends up in the queue,
+		// we must start the main loop to get that data out of the queue and into tracker.
+		var gQueueObserver = {
+			
+			observe: function (subject, topic, data)
+			{
+				// We can safely exit if we are already running (and save some
+				// cycles since we don't have to check if we are enabled every time
+				// something is added to the queue).
+				if (running)
+					return;
+			
+				// Check if indexing is enabled
+				if (!gTrackerSettings.getBoolPref ('Enabled'))
+					return;
+			
+				// Enable main loop in case it's disabled
+				if (!running)
+					start ();
+			}
+		};
+		
+		// We need to ensure that the user interface has initialized before we do anything
+		window.addEventListener ('load', function () { onTrackerLoad (); }, false);
+		window.addEventListener ('unload', function () { onTrackerUnload (); }, false);
+
+	]]>
+	</script>
+
+	<!-- This will appear in the tools menu -->
+	<menupopup id="taskPopup">
+		<menuitem id="tracker-settings" 
+			label="&trackermain.settings;" 
+			oncommand="onShowSettings(event);"/>
+	</menupopup>
+	
+	<!-- This gives the small icon in the bottom right corner -->
+	<statusbar id="status-bar">
+		<statusbarpanel id="tracker-icon" 
+			class="statusbarpanel-iconic" 
+			status="disabled" 
+			oncommand="onStatusbarClick (this);"/>
+	</statusbar>
+	
+</overlay>
+

Added: trunk/extensions/thunderbird-extension/content/trackerPrefs.xul
==============================================================================
--- (empty file)
+++ trunk/extensions/thunderbird-extension/content/trackerPrefs.xul	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,332 @@
+<?xml version="1.0"?>
+
+<!--
+//
+// trackerPrefs.xul: Preference dialog
+//
+// Copyright (C) 2007 Pierre Ãstlund
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+//
+-->
+
+<!DOCTYPE window SYSTEM "chrome://tracker/locale/tracker.dtd">
+
+<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
+
+<prefwindow id="preference-window"
+			title="&trackerprefs.title;"
+			style="width: 350px"
+			onload="load (); refreshStats ();"
+			xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";>
+
+	<prefpane id="pref-indexing" 
+		label="&trackerprefs.indexing;" 
+		image="chrome://messenger/skin/preferences/advanced.png">
+		
+		<preferences>
+			<preference id="pref-enabled" name="tracker.enabled" type="bool"/>
+			<preference id="pref-batchcount" name="tracker.index.batch_count" type="int"/>
+			<preference id="pref-queuecount" name="tracker.index.queue_count" type="int"/>
+			<preference id="pref-delay" name="tracker.index.delay" type="int"/>
+		</preferences>
+		<checkbox label="&trackerprefs.enabled;" preference="pref-enabled"/>
+		<spacer height="5"/>
+		<groupbox flex="0">
+			<caption>
+				<label value="&trackerprefs.speedcaption;"/>
+			</caption>
+			<radiogroup id="speed-group">
+				<radio id="0" label="&trackerprefs.veryslow;" oncommand="onSpeedChange (this);"/>
+				<radio id="1" label="&trackerprefs.slow;" oncommand="onSpeedChange (this);"/>
+				<radio id="2" label="&trackerprefs.normal; &trackerprefs.recommended;" oncommand="onSpeedChange (this);"/>
+				<radio id="3" label="&trackerprefs.fast;" oncommand="onSpeedChange (this);"/>
+				<radio id="4" label="&trackerprefs.veryfast;" oncommand="onSpeedChange (this);"/>
+				<radio id="5" label="&trackerprefs.instant; &trackerprefs.notrecommended;" oncommand="onSpeedChange (this);"/>
+				<radio id="6" label="&trackerprefs.custom;" oncommand="onSpeedChange (this);"/>
+			</radiogroup>
+		</groupbox>
+		<groupbox id='custom-settings' flex="0" style="visibility: hidden;">
+			<caption>
+				<label value="&trackerprefs.customcaption;"/>
+			</caption>
+			<grid>
+				<columns>
+					<column flex="1"/>
+					<column flex="0"/>
+				</columns>
+				
+				<rows>
+					<row>
+						<label value="&trackerprefs.batchcount;"/>
+						<textbox preference="pref-batchcount"/>
+					</row>
+					<row>
+						<label value="&trackerprefs.queuecount;"/>
+						<textbox preference="pref-queuecount"/>
+					</row>
+					<row>
+						<label value="&trackerprefs.delay;"/>
+						<textbox preference="pref-delay"/>
+					</row>
+				</rows>
+			</grid>
+		</groupbox>
+	</prefpane>
+	<prefpane id="pref-privacy" 
+		label="&trackerprefs.privacy;"
+		image="chrome://messenger/skin/preferences/privacy.png">
+		
+		<preferences>
+			<preference id="pref-local" name="tracker.enable.local" type="bool"/>
+			<preference id="pref-imap" name="tracker.enable.imap" type="bool"/>
+			<preference id="pref-pop" name="tracker.enable.pop" type="bool"/>
+			<preference id="pref-mailspool" name="tracker.enable.mailspool" type="bool"/>
+			<preference id="pref-news" name="tracker.enable.news" type="bool"/>
+			<preference id="pref-rss" name="tracker.enable.rss" type="bool"/>
+		</preferences>
+		<groupbox flex="0">
+			<caption>
+				<label value="&trackerprefs.sourcescaption;"/>
+			</caption>
+			<vbox flex="1">
+				<checkbox label="&trackerprefs.local;" preference="pref-local"/>
+				<checkbox label="&trackerprefs.imap;" preference="pref-imap"/>
+				<checkbox label="&trackerprefs.pop;" preference="pref-pop"/>
+				<checkbox label="&trackerprefs.mailspool;" preference="pref-mailspool"/>
+				<checkbox label="&trackerprefs.news;" preference="pref-news"/>
+				<checkbox label="&trackerprefs.rss;" preference="pref-rss"/>
+			</vbox>
+		</groupbox>
+		<groupbox flex="0">
+			<caption>
+				<label value="&trackerprefs.contentcaption;"/>
+			</caption>
+			<vbox flex="1">
+				<button label="&trackerprefs.delindex;" oncommand="onDelIndex ();"/>
+				<button label="&trackerprefs.unindex;" oncommand="onUnindex ();"/>
+			</vbox>
+		</groupbox>
+	</prefpane>
+	<prefpane id="pref-stats" 
+		label="&trackerprefs.status;" 
+		image="chrome://messenger/skin/preferences/general.png">
+		<groupbox flex="0">
+			<caption>
+				<label value="&trackerprefs.stats;"/>
+			</caption>
+			<grid>
+				<columns>
+					<column flex="0"/>
+					<column flex="1"/>
+				</columns>
+				<rows>
+					<row>
+						<label value="&trackerprefs.indexingstatus;"/>
+						<vbox id="indexingstatus"/>
+					</row>
+					<row>
+						<label value="&trackerprefs.itemsadded;"/>
+						<label id="itemsadded"/>
+					</row>
+					<row>
+						<label value="&trackerprefs.itemsremoved;"/>
+						<label id="itemsremoved"/>
+					</row>
+					<row>
+						<label value="&trackerprefs.itemsqueued;"/>
+						<label id="itemsqueued"/>
+					</row>
+				</rows>
+			</grid>
+		</groupbox>
+		<hbox flex="0" pack="end">
+			<button label="&trackerprefs.refresh;" oncommand="refreshStats ();"/>
+		</hbox>
+	</prefpane>
+	
+	<stringbundleset id="stringbundleset">
+		<stringbundle id="bundle_tracker" src="chrome://tracker/locale/strings.properties"/>
+	</stringbundleset>
+	
+	<script src="trackerUtils.js"/>
+	<script src="trackerUnindex.js"/>
+	
+	<script type="application/x-javascript">
+	<![CDATA[
+		const Cc = Components.classes;
+		const Ci = Components.interfaces;
+
+		// These are arbitrary values. We should really tweak these values.
+		const TRACKER_INDEX_SPEED = new Array (
+			// [Batch count, index delay, queue count]
+			[1,			60,		10],	// Very slow 
+			[5,			30,		5],		// Slow
+			[10,		10,		10],	// Normal
+			[20,		5,		20],	// Fast
+			[100,		1,		100],	// Very fast
+			[9999999,	1,		1]		// Instant
+		);
+		
+		var settings = GetJsService ('@tracker-project.org/services/settings;1');
+		
+		function load ()
+		{
+			
+			var batch = settings.getIntPref ('IndexBatchCount');
+			var queue = settings.getIntPref ('IndexQueueCount');
+			var delay = settings.getIntPref ('IndexDelay');
+			var group = document.getElementById ('speed-group');
+
+			window.setTimeout ('autoUpdate ()', 1000);
+
+			for (var i = 0; i < TRACKER_INDEX_SPEED.length; i++) {
+				var speed = TRACKER_INDEX_SPEED [i];
+				
+				if (batch == speed [0] && delay == speed [1] && queue == speed [2]) {
+					group.selectedIndex = i;
+					return;
+				}
+			}
+			
+			group.selectedIndex = TRACKER_INDEX_SPEED.length;
+			var custom = document.getElementById ('custom-settings');
+			custom.setAttribute ('style', 'visibility: visible;');
+		}
+		
+		function autoUpdate ()
+		{
+			refreshStats ();
+			window.setTimeout ('autoUpdate ()', 1000);
+		}
+
+		function onSpeedChange (radio)
+		{
+			var element = document.getElementById ('custom-settings');
+			var pref = Cc ['@mozilla.org/preferences-service;1'].getService (Ci.nsIPrefBranch);
+			var batchCount = settings.getIntPref ('IndexBatchCount');
+			var queueCount = settings.getIntPref ('IndexQueueCount');
+			var delay = settings.getIntPref ('IndexDelay');
+			
+			if (parseInt (radio.id) >= 0 && parseInt (radio.id) <= 5) {
+				var speed = TRACKER_INDEX_SPEED [radio.id];
+				batchCount = speed [0];
+				delay = speed [1];
+				queueCount = speed [2];
+				element.setAttribute ('style', 'visibility: hidden;');
+			} else 
+				element.setAttribute ('style', 'visibility: visible;');
+			
+			// Update with new values
+			pref.setIntPref ('tracker.index.batch_count', batchCount);
+			pref.setIntPref ('tracker.index.queue_count', queueCount);
+			pref.setIntPref ('tracker.index.delay', delay);
+		}
+		
+		function onDelIndex ()
+		{
+			gTrackerUnindex.RemoveEverything (true);
+		}
+		
+		function onUnindex ()
+		{
+			gTrackerUnindex.UnindexEverything (true);
+		}
+		
+		function clearIndexingStatus ()
+		{
+			var elem = document.getElementById ('indexingstatus');
+			
+			while (elem.hasChildNodes ()) 
+				elem.removeChild (elem.firstChild);
+		}
+		
+		function createDescription (text)
+		{
+			var desc = document.createElement ('description');
+			var text = document.createTextNode (text);
+			desc.appendChild (text);
+			return desc;
+		}
+		
+		function refreshStats ()
+		{
+			var bundle = document.getElementById ('bundle_tracker');
+			var status = document.getElementById ('indexingstatus');
+			var added = document.getElementById ('itemsadded');
+			var removed = document.getElementById ('itemsremoved');
+			var queued = document.getElementById ('itemsqueued');
+			var trackerQueue = GetJsService ('@tracker-project.org/services/queue;1');
+			var remaining = new Array ();
+			
+			added.value = trackerQueue.totalAdded;
+			removed.value = trackerQueue.totalRemoved;
+			queued.value = trackerQueue.getQueueCount ();
+			
+			// Figure out if all mailboxes have been indexed
+			try {
+				var indexedFolders = 0;
+				var totalFolders = 0;
+				var indexer = GetJsService ('@tracker-project.org/services/indexer;1');
+				var accountmanager = Cc ['@mozilla.org/messenger/account-manager;1']
+					.getService (Ci.nsIMsgAccountManager);
+				var accounts = accountmanager.accounts;
+				
+				clearIndexingStatus ();
+				for (var i = 0; i < accounts.Count (); i++) {
+					var account = accounts.QueryElementAt (i, Ci.nsIMsgAccount);
+					var allFolders = Cc ['@mozilla.org/supports-array;1']
+						.createInstance (Ci.nsISupportsArray);
+					account.incomingServer.rootFolder.ListDescendents (allFolders);
+					
+					for (var j = 0; j < allFolders.Count (); j++) {
+						var folder = allFolders.QueryElementAt (j, Ci.nsIMsgFolder);
+						
+						if (indexer.isFolderIndexed (folder)
+							|| !indexer.shouldIndexFolder (folder) 
+							|| folder.getTotalMessages (false) == 0)
+							indexedFolders++;
+						else
+							remaining [remaining.length] = folder.prettyName;
+							
+						totalFolders++;
+					}
+				}
+				
+				if (indexedFolders == totalFolders)
+					status.appendChild (createDescription (bundle.getString ('indexIdle')));
+				else {
+					var status_str = bundle.getFormattedString 
+						('indexWorking', [ totalFolders - indexedFolders ]);
+					status.appendChild (createDescription (status_str));
+					
+					for (var i = 0; i < (remaining.length > 5 ? 5 : remaining.length); i++) 
+						status.appendChild (createDescription (remaining [i]));
+				}
+				
+			} catch (ex) {
+				status.value = bundle.getString ('failedGetStatus');
+			}
+		}
+	]]>
+	</script>
+</prefwindow>
+

Added: trunk/extensions/thunderbird-extension/content/trackerQueue.js
==============================================================================
--- (empty file)
+++ trunk/extensions/thunderbird-extension/content/trackerQueue.js	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,256 @@
+//
+// trackerQueue.js: Queue component implementation
+//
+// Copyright (C) 2007 Pierre Ãstlund
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+//
+
+var queueAdd = Components.classes ['@mozilla.org/supports-array;1']
+	.createInstance (Components.interfaces.nsISupportsArray);
+var queueRemove = Components.classes ['@mozilla.org/supports-array;1']
+	.createInstance (Components.interfaces.nsISupportsArray);
+var observerService = Components.classes ['@mozilla.org/observer-service;1']
+	.getService(Components.interfaces.nsIObserverService);
+
+function notify (data)
+{
+	var self = GetJsService ('@tracker-project.org/services/queue;1');
+	observerService.notifyObservers (self, "tracker-queue", data);
+}
+
+Component.prototype = {
+
+	reload: function() {
+		loader.loadSubScript(SOURCE, this.__proto__);
+	},
+
+	QueryInterface: function(aIID) {
+		if(!aIID.equals(INTERFACE) && !aIID.equals(Ci.nsISupports))
+			throw Cr.NS_ERROR_NO_INTERFACE;
+		return this;
+	},
+
+	totalAdded: 0,
+	totalRemoved: 0,
+
+	init: function ()
+	{
+		observerService.addObserver (gTrackerQueueObserver, 'quit-application', null);
+	},
+
+	// obj is either nsIMsgDBHdr or nsIMsgFolder
+	add: function (obj)
+	{
+		if (queueAdd.GetIndexOf (obj) != -1)
+			return;
+		
+		var indexer = GetJsService ('@tracker-project.org/services/indexer;1');
+		
+		if (obj instanceof Components.interfaces.nsIMsgDBHdr)
+			indexer.markHdrAsIndexed (obj);
+		else if (obj instanceof Components.interfaces.nsIMsgFolder)
+			indexer.markFolderAsIndexed (obj);
+		else 
+			return;
+		
+		queueAdd.AppendElement (obj);
+		this.totalAdded++;
+		notify ('add');
+	},
+
+	remove: function (obj)
+	{
+		if (queueRemove.GetIndexOf (obj) != -1)
+			return;
+		
+		var indexer = GetJsService ('@tracker-project.org/services/indexer;1');
+		
+		if (obj instanceof Components.interfaces.nsIMsgDBHdr)
+			indexer.resetHdr (obj, false);
+		else if (obj instanceof Components.interfaces.nsIMsgFolder)
+			indexer.resetFolder (obj, false, false, false);
+		else 
+			return;
+		
+		queueRemove.AppendElement (obj);
+		this.totalRemoved++;
+		notify ('remove');
+	},
+
+	// add, remove* and move* all return true if the object was added to the queue. If the object was
+	// rejected by a filter, then they will return false. A filter in this sense is if a mail is marked
+	// to be indexed or not (by the user).
+
+	// Add a new header for inclusion in the tracker index
+	addHdr: function (hdr)
+	{
+		var indexer = GetJsService ('@tracker-project.org/services/indexer;1');
+		
+		// Check if we should index this
+		if (!indexer.shouldIndexHdr (hdr) || !indexer.shouldIndexFolder (hdr.folder)) 
+			return false;
+
+		this.add (hdr);
+		
+		this.process ();
+		
+		return true;
+	},
+
+	removeHdr: function (hdr)
+	{
+		var indexer = GetJsService ('@tracker-project.org/services/indexer;1');
+		
+		if (hdr instanceof Components.interfaces.nsIMsgDBHdr) {
+			this.remove (hdr);
+
+			this.process ();
+
+			return true;
+		}
+		
+		return false;
+	},
+
+	// Basic purpose of this function is to make the main loop run which eventually will pick it up
+	addFolder: function (folder)
+	{
+		var indexer = GetJsService ('@tracker-project.org/services/indexer;1');
+		
+		if (indexer.isFolderIndexed (folder))
+			return;
+		
+		notify ('add-folder');
+	},
+
+	removeFolder: function (folder)
+	{
+		var indexer = GetJsService ('@tracker-project.org/services/indexer;1');
+		
+		if (folder instanceof Components.interfaces.nsIMsgFolder) {
+			this.remove (folder);
+			this.process ();
+
+			return true;
+		}
+		
+		return false;
+	},
+
+	moveHdr: function (oldHdr, newHdr)
+	{
+		var indexer = GetJsService ('@tracker-project.org/services/indexer;1');
+		
+		if (!indexer.shouldIndexHdr (oldHdr) || !indexer.shouldIndexHdr (newHdr))
+			return false;
+		
+		this.remove (oldHdr);
+		this.add (newHdr);
+		this.processs ();
+		
+		return true;
+	},
+
+	moveFolder: function (oldFolder, newFolder)
+	{
+		var indexer = GetJsService ('@tracker-project.org/services/indexer;1');
+		
+		if (!indexer.shouldIndexFolder (oldFolder) || !indexer.shouldIndexFolder (newFolder))
+			return false;
+			
+		this.remove (oldFolder);
+		this.add (newFolder);
+		this.process ();
+			
+		return true;
+	},
+
+	// This process function will make sure that we have enough objects in the queue before processing
+	process: function ()
+	{
+		var settings = GetJsService ('@tracker-project.org/services/settings;1');
+		
+		if (this.getQueueCount () < settings.getIntPref ('IndexQueueCount'))
+			return;
+		
+		this.forceProcess ();
+	},
+
+	// No object count is done here, mainly so that the queue can be processed at any given time
+	forceProcess: function ()
+	{
+		var count = this.getQueueCount ();
+		if (count == 0)
+			return;
+		
+		var indexer = GetJsService ('@tracker-project.org/services/indexer;1');
+
+		// Add new items to the tracker database
+		for (var i = 0; i < queueAdd.Count (); i++) {
+			var msg = queueAdd.GetElementAt (i).QueryInterface (Components.interfaces.nsIMsgDBHdr);
+			if (!msg)
+				continue;
+			indexer.addToIndex (msg);
+		}
+		
+		// Remove old items from the tracker database
+		for (var i = 0; i < queueRemove.Count (); i++) {
+			var obj = queueRemove.GetElementAt (i);
+			
+			if (obj instanceof Components.interfaces.nsIMsgDBHdr) {
+				obj.QueryInterface (Components.interfaces.nsIMsgDBHdr);
+				indexer.dropHdrFromIndex (obj);
+			} else if (obj instanceof Components.interfaces.nsIMsgFolder) {
+				obj.QueryInterface (Components.interfaces.nsIMsgFolder);
+				indexer.dropFolderFromIndex (obj);
+			}
+		}
+		
+		queueAdd.Clear ();
+		queueRemove.Clear ();
+		
+		dump ("Done processing " + count + " items\n");
+	},
+
+	getQueueCount: function ()
+	{
+		return queueAdd.Count () + queueRemove.Count ();
+	}
+
+};
+
+// This observer will check if the application is about to quit and process any remaining
+// items in the queue when it does
+var gTrackerQueueObserver = {
+
+	observe: function (subject, topic, data)
+	{
+		// Just process whatever is left in the queue
+		try {
+			var queue = GetJsService ('@tracker-project.org/services/queue;1');
+			queue.forceProcess ();
+			observerService.removeObserver (gTrackerQueueObserver, 'quit-application');
+		} catch (ex) {
+		}
+	}
+};
+

Added: trunk/extensions/thunderbird-extension/content/trackerService.js
==============================================================================
--- (empty file)
+++ trunk/extensions/thunderbird-extension/content/trackerService.js	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,521 @@
+//
+// trackerService.js: This is the entry point of the extension
+//
+// Copyright (C) 2007 Pierre Ãstlund
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+//
+
+var tracker_init = false;
+var gTrackerSettings = null;
+var gTrackerIndexer = null;
+var gTrackerQueue = null;
+var gTrackerTimer = Components.classes ["@mozilla.org/timer;1"]
+	.createInstance (Components.interfaces.nsITimer);
+
+var running = false;
+var warnings = new Array (); // We will only hold javascript strings here
+
+function notify (data)
+{
+	var observer = Components.classes['@mozilla.org/observer-service;1']
+		.getService(Components.interfaces.nsIObserverService);
+	observer.notifyObservers (null, "tracker-service", data);
+}
+
+function initService ()
+{
+	if (tracker_init)
+		return;
+	
+	dump ("Tracker init started\n");
+	
+	// Set our globally needed variables
+	gTrackerSettings = GetJsService ('@tracker-project.org/services/settings;1');
+	gTrackerIndexer = GetJsService ('@tracker-project.org/services/indexer;1');
+	gTrackerQueue = GetJsService ('@tracker-project.org/services/queue;1');
+	tracker_init = true;
+	
+	// Load settings
+	gTrackerSettings.init ();
+	
+	dump ('Adding settings observer...');
+	var prefbranch = Components.classes ['@mozilla.org/preferences-service;1']
+		.getService (Components.interfaces.nsIPrefBranch2);
+	prefbranch.addObserver ('tracker', gSettingObserver, false);
+	dump ("Done.\n");
+	
+	var enabled = gTrackerSettings.getBoolPref ('Enabled');
+	if (!enabled) {
+		dump ("Tracker backend is now disabled\n");
+	} else {
+		// Make sure we catch changes
+		gTrackerDataTracker.RegisterSelf ();
+		
+		// The following timeout handler will initiate the indexing process by locating data that
+		// needs to be indexed. We delay a while go give Thunderbird some time to settle.
+		dump ("Tracker extension is now enabled\n");
+		restart (3);
+	}
+	
+	notify (running ? 'update-enabled' : 'update-disabled');
+	dump ("Tracker init ended\n");
+}
+
+function start ()
+{
+	if (!tracker_init)
+		initService ();
+	
+	gTrackerTimer.cancel ();
+	gTrackerTimer.initWithCallback (gTrackerMainLoop,
+		gTrackerSettings.getIntPref ('IndexDelay') * 1000,
+		Components.interfaces.nsITimer.TYPE_REPEATING_SLACK);
+	running = true;
+}
+
+function stop (disable)
+{
+	if (!tracker_init)
+		initService ();
+	
+	gTrackerTimer.cancel ();
+	running = false;
+	
+	if (disable) {
+		try {
+			gTrackerSettings.setBoolPref ('Enabled', false);
+		} catch (ex) {
+			dump ("Failed to disable tracker extension!\n");
+		}
+	}
+}
+
+function restart (seconds)
+{
+	if (!tracker_init)
+		initService ();
+		
+	gTrackerTimer.cancel ();
+	gTrackerTimer.initWithCallback (gTrackerMainLoop, 
+		seconds * 1000,
+		Components.interfaces.nsITimer.TYPE_REPEATING_SLACK);
+	running = true;
+}
+
+function addWarning (text, params, length)
+{
+	if (!text)
+		return;
+
+	var bundleService = Components.classes ['@mozilla.org/intl/stringbundle;1']
+		.getService (Components.interfaces.nsIStringBundleService);
+	var bundle = bundleService.createBundle ('chrome://tracker/locale/strings.properties');
+	
+	try {
+		if (params == null)
+			warnings [warnings.length] = bundle.GetStringFromName (text);
+		else 
+			warnings [warnings.length] = bundle.formatStringFromName (text, params, length);
+	} catch (ex) {
+		warnings [warnings.length] = "Failed to add error message! You should report this as a bug. Details: " + ex;
+	}
+	
+	notify ('new-warning');
+}
+
+function getLastWarning ()
+{
+	var str = null;
+
+	for (var i = 0; i < warnings.length; i++) {
+		if (warnings [i] != null) {
+			str = warnings [i];
+			warnings [i] = null;
+			break;
+		}
+	}
+
+	// Make sure we notify if we have no more warnings
+	if (getWarningCount () == 0) {
+		notify ('warnings-empty');
+		warnings = new Array ();
+	}
+	
+	return str;
+}
+
+function getWarningCount ()
+{
+	var count = 0;
+	
+	for (var i = 0; i < warnings.length; i++) {
+		if (warnings [i] != null)
+			count++;
+	}
+	
+	return count;
+}
+
+function clearWarnings ()
+{
+	warnings = new Array ();
+	notify ('warnings-empty');
+}
+
+var gTrackerMainLoop = {
+
+	notify: function (timer)
+	{
+		// Make sure we have a destination directory. If it doesn't exists, create it and mark 
+		// everything as not indexed.
+		try {
+			// Check if destination directory exists
+			var dir = Components.classes ["@mozilla.org/file/local;1"]
+				.createInstance (Components.interfaces.nsILocalFile);
+			dir.initWithPath (gTrackerSettings.getCharPref ('DestinationDirectory'));
+			if (dir.exists ()) {
+				if (!dir.isDirectory ()) {
+					dump ("Destination directory exists but is not a directory! Bailing out!\n");
+					addWarning ('destinationDirInvalidType', null, 0);
+					stop (true);
+					return;
+				}
+                        }
+				
+				// We need to create the ToIndex directory in case it doesn't exist
+				dir.initWithPath (gTrackerSettings.getCharPref ('DestinationDirectory') + '/ToIndex');
+				if (!dir.exists ()) {
+					// We create this directory and mark all content as not indexed
+					dir.create (Components.interfaces.nsIFile.DIRECTORY_TYPE, 0755);
+					gTrackerUnindex.UnindexEverything (false);
+				} else if (dir.isFile ()) {
+					dump ("The ToIndex directory exists but is not a directory!\n");
+					addWarning ('toIndexDirInvalidType', null, 0);
+					stop (true);
+					return;
+				} else if (!dir.isWritable ()) {
+					dump ("The ToIndex directory exists but is not writable!\n");
+					addWarning ('toIndexDirectoryLackPermission', null, 0);
+					stop (true);
+					return;
+				}
+               // Commented, if the directory does not exists it will be created by plugin
+		//	} else {
+		//		dump ("Destination directory does not exist!\n");
+		//		addWarning ('destinationDirectoryNoExist', null, 0);
+		//		stop (true);
+		//		return;
+		//	}
+		} catch (ex) {
+			dump ("Failed to create ToIndex directory! Bailing out! (" + ex + ")\n");
+			addWarning ('failedCreateToIndexDir', [ex], 1);
+			stop (true);
+			return;
+		}
+
+		// Index next set
+		try {
+			gTrackerDataCollector.Process ();
+		} catch (ex) {
+			dump ("Error while indexing: " + ex + "\n");
+			addWarning ('errorWhileIndexing', [ex], 1);
+		}
+		
+		gTrackerTimer.delay = gTrackerSettings.getIntPref ('IndexDelay') * 1000;
+	}
+};
+
+// We use this observer to check if we have been enabled or disabled and if we should restart
+// the main loop in case indexing speed changed. We also make sure the GUI part gets to know this
+// so that the little dog icon can be updated.
+var gSettingObserver = {
+
+	observe: function (subject, topic, data)
+	{
+		var branch = subject.QueryInterface (Components.interfaces.nsIPrefBranch);
+		
+		if (data == 'tracker.enabled') {
+			var enabled = branch.getBoolPref (data);
+			
+			// Enable or disabled depending on new status
+			if (enabled) {
+				gTrackerDataTracker.RegisterSelf ();
+				restart (3);
+				dump ("Tracker extension is now enabled\n");
+			} else {
+				gTrackerDataTracker.UnregisterSelf ();
+				stop (true);
+				dump ("Tracker extension is now disabled\n");
+			}
+			
+			notify (running ? 'update-enabled' : 'update-disabled');
+		} else if (data == 'tracker.index.delay') {
+			// In case delay time changed, restart mainloop to get immediate effect. We need to get
+			// the new value from included branch as it may not be updated in gTrackerSettings yet.
+			restart (branch.getIntPref ('tracker.index.delay'));
+		}
+	}
+};
+
+var gTrackerDataTracker = {
+
+	Notifications: Components.classes ['@mozilla.org/messenger/msgnotificationservice;1']
+		.getService(Components.interfaces.nsIMsgFolderNotificationService),
+
+	RegisterSelf: function ()
+	{
+		dump ('Registering tracker data tracker...');
+		this.Notifications.removeListener (this);
+		this.Notifications.addListener (this);
+		dump ("Done.\n");
+	},
+	
+	UnregisterSelf: function ()
+	{
+		dump ('Unregistering tracker data tracker...');
+		this.Notifications.removeListener (this);
+		dump ("Done.\n");
+	},
+	
+	//
+	//	Below are the functios that will get all updates
+	//
+	
+	// Message added
+	itemAdded: function (item)
+	{
+		if (!item)
+			return;
+		
+		dump ("Adding new messages and restarting loop\n");
+		restart (10);
+		
+		// New approach here: mark folder as not indexed and let main loop find new messages.
+		// Doing it this way will make sure we don't hammer the system since we take
+		// advantage of the indexing queue.
+		try {
+			var hdr = item.QueryInterface (Components.interfaces.nsIMsgDBHdr);
+			if (!hdr)
+				return;
+			
+			gTrackerIndexer.resetFolder (hdr.folder, false, false, false);
+		} catch (ex) {
+		}
+	},
+	
+	// Message _or_ folder removed
+	itemDeleted: function (item)
+	{
+		dump ("Removing message(s) or folder and restarting loop\n");
+		restart (10);
+		if (item instanceof Components.interfaces.nsIMsgDBHdr) {
+			gTrackerQueue.removeHdr (item)
+		} else if (item instanceof Components.interfaces.nsIMsgFolder) {
+			gTrackerQueue.removeFolder (item)
+		}
+	},
+	
+	itemMoveCopyCompleted: function (move, items, dest)
+	{
+		dump ("Moving/copying message(s)/folder and restarting loop\n");
+		restart (10);
+		
+		// There can be at most one folder in "items", so we check for that
+		var folder = items.GetElementAt (0);
+		if (folder instanceof Components.interfaces.nsIMsgFolder) {
+			
+			// We have a folder. This is an ugly solution...
+			var finished = false;
+			var enumerator = dest.GetSubFolders ();
+			while (!finished) {
+				var f = enumerator.currentItem ().QueryInterface (Components.interfaces.nsIMsgFolder);
+				if (f.name == folder.name) {
+					// We do this recursively to ensure all sub-content are re-indexed too
+					gTrackerIndexer.resetFolder (f, false, true, true);
+					break;
+				}
+			
+				try { enumerator.next (); }
+				catch (ex) { finished = true; }
+			}
+			
+			// If we moved the folder, then we also have to remove the source folder. Otherwise
+			// we'll end up with messages and folders that doesn't exist
+			if (move)
+				gTrackerQueue.removeFolder (folder);
+		} else {
+			// We have a bunch of messages. Reset all messages.
+			for (var i = 0; i < items.Count (); i++) {
+				var message = items.QueryElementAt (i, Components.interfaces.nsIMsgDBHdr);
+				gTrackerIndexer.resetHdr (message, false);
+			}
+			
+			// Also make sure we reset the destination folder, otherwise the new messages
+			// won't be caught by the main loop
+			gTrackerIndexer.resetFolder (dest, false, false, true);
+		}
+	},
+	
+	folderRenamed: function (oldFolder, newFolder)
+	{
+		dump ("Renaming folder and restarting loop\n");
+		restart (10);
+		gTrackerQueue.moveFolder (oldFolder, newFolder);
+	},
+	
+	itemEvent: function (item, event, data)
+	{
+	}
+};
+
+var gTrackerDataCollector = {
+
+	GetNextFolder: function ()
+	{
+		var gAccountManager = Components.classes ['@mozilla.org/messenger/account-manager;1']
+			.getService (Components.interfaces.nsIMsgAccountManager);
+		var accounts = gAccountManager.accounts;
+		
+		for (var i = 0; i < accounts.Count (); i++) {
+			var account = accounts.QueryElementAt (i, Components.interfaces.nsIMsgAccount);
+			
+			// This check the overall type
+			if (!gTrackerIndexer.shouldIndexAccount (account)) 
+				continue;
+			else if (!account.incomingServer) { // Invalid accounts might exist for some reason
+				var email = "Unknown";
+				
+				if (account.defaultIdentity)
+					email = account.defaultIdentity.email;
+				
+				dump (account.key + ' does not have an incoming server! Sender address: ' + email + "\n");
+				continue;
+			}
+			
+			var allFolders = Components.classes ['@mozilla.org/supports-array;1']
+				.createInstance (Components.interfaces.nsISupportsArray);
+			account.incomingServer.rootFolder.ListDescendents (allFolders);
+			
+			for (var j = 0; j < allFolders.Count (); j++) {
+				var folder = allFolders.QueryElementAt (j, Components.interfaces.nsIMsgFolder);
+				
+				// We don't bother if there's nothing to index
+				if (folder.getTotalMessages (false) == 0)
+					continue;
+
+				// We only need to index a folder if it isn't already indexed and if the user
+				// hasn't explicitly excluded it
+				if (!gTrackerIndexer.isFolderIndexed (folder) && gTrackerIndexer.shouldIndexFolder (folder))
+					return folder;
+			}
+		}
+		
+		return null;
+	},
+	
+	// Some times Thunderbird lists "invalid" folders. An invalid folder is a folder where all messages have
+	// been downloaded but the mork database is not available. This probably only happen if a user is messing
+	// around with the file structure manually.
+	ValidFolder: function ()
+	{
+		var filePath = this.CurrentFolder.path.unixStyleFilePath + '.msf';
+
+		try {
+			var file = Components.classes ['@mozilla.org/file/local;1']
+				.createInstance (Components.interfaces.nsILocalFile);
+			if (!file)
+				return false;
+			
+			file.initWithPath (filePath);
+		} catch (ex) {
+			return false;
+		}
+		
+		return file.exists ();
+	},
+
+	// Add new mails to the indexing queue
+	Process: function ()
+	{
+		// If we don't have a folder available at this time, get next available
+		if (!this.CurrentFolder)
+			this.CurrentFolder = this.GetNextFolder ();
+			
+		// Note that we don't have any folders left to index if GetNextFolder returned null
+		if (!this.CurrentFolder) {
+			gTrackerQueue.forceProcess ();
+			stop (false);
+			return;
+		}
+		
+		dump ('Processing messages in ' + this.CurrentFolder.prettyName + "\n");
+		
+		// We have a valid folder to enumerate over, make sure we have a valid enumerator as well
+		if (this.CurrentEnumerator == null) {
+			try {
+				this.CurrentEnumerator = this.CurrentFolder.getMessages (null);
+			} catch (ex) {
+				// Only display the error message to the user in case the folder is valid and
+				// messages could not be listed
+				if (this.ValidFolder ()) {
+					dump ('Failed to list messages in ' + this.CurrentFolder.prettyName + ': ' + ex + "\n");
+					addWarning ('failedListingMessages', [this.CurrentFolder.prettyName, ex], 2);
+				}
+				gTrackerIndexer.markFolderAsIndexed (this.CurrentFolder);
+				this.CurrentFolder = null;
+				return;
+			}
+		}
+		
+		// Process items. We skip already indexed items.
+		var batchCounter = gTrackerSettings.getIntPref ('IndexBatchCount');
+		while (batchCounter > 0 && this.CurrentEnumerator.hasMoreElements ()) {
+			var hdr = this.CurrentEnumerator.getNext ().QueryInterface (Components.interfaces.nsIMsgDBHdr);
+			if (!hdr || gTrackerIndexer.isHdrIndexed (hdr))
+				continue;
+			
+			// We only count down the counter in case we actually did add the message (since a
+			// filter could have picked this up)		
+			if (gTrackerQueue.addHdr (hdr)) 
+				batchCounter--;
+		}
+		
+		// We might have missed items in case the database content changed, so we set the enumerator
+		// to null to make sure we enumerate the same database again. We keep doing this until
+		// batchCounter does not change, this way we'll know that everything in this mailbox has
+		// been indexed and that we can move on.
+		if (!this.CurrentEnumerator.hasMoreElements ()) {
+			// We are done and mark this folder to reflect this. Doing so will make it a lot faster
+			// finding not already indexed folders and we don't have to keep track of this somewhere
+			// else. It's also stored across sessions.
+			gTrackerIndexer.markFolderAsIndexed (this.CurrentFolder);
+			this.CurrentFolder.getMsgDatabase (null).Commit (1);
+			dump ('Finished indexing ' + this.CurrentFolder.prettyName + "\n");
+			this.CurrentFolder = null;
+		}
+		this.CurrentEnumerator = null;
+	},
+	
+	CurrentFolder: null,
+	CurrentEnumerator: null
+};
+

Added: trunk/extensions/thunderbird-extension/content/trackerSettings.js
==============================================================================
--- (empty file)
+++ trunk/extensions/thunderbird-extension/content/trackerSettings.js	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,207 @@
+//
+// trackerSettings.js: A convenient way for accessing all tracker settings
+//
+// Copyright (C) 2007 Pierre Ãstlund
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+//
+
+var prefs = Components.classes ['@mozilla.org/preferences-service;1']
+	.getService (Components.interfaces.nsIPrefBranch);
+var environment = Components.classes ['@mozilla.org/process/environment;1']
+	.getService (Components.interfaces.nsIEnvironment);
+
+// We store all available settings here together with access key, default values and type
+const TRACKER_SETTINGS = new Array (
+	['tracker.enabled',					'Enabled',					true,	Components.interfaces.nsIPrefBranch.PREF_BOOL],
+	['tracker.destination.directory',	'DestinationDirectory',		getDestinationDirectory (),	Components.interfaces.nsIPrefBranch.PREF_STRING],
+	['tracker.index.batch_count',		'IndexBatchCount',			10,		Components.interfaces.nsIPrefBranch.PREF_INT],
+	['tracker.index.queue_count',		'IndexQueueCount',			10,		Components.interfaces.nsIPrefBranch.PREF_INT],
+	['tracker.index.delay',				'IndexDelay',				1,		Components.interfaces.nsIPrefBranch.PREF_INT],
+	['tracker.enable.imap',				'EnableImap',				true,	Components.interfaces.nsIPrefBranch.PREF_BOOL],
+	['tracker.enable.pop',				'EnablePop',				true,	Components.interfaces.nsIPrefBranch.PREF_BOOL],
+	['tracker.enable.rss',				'EnableRss',				true,	Components.interfaces.nsIPrefBranch.PREF_BOOL],
+	['tracker.enable.contacts',			'EnableContacts',			true,	Components.interfaces.nsIPrefBranch.PREF_BOOL],
+	['tracker.enable.news',				'EnableNews',				true,	Components.interfaces.nsIPrefBranch.PREF_BOOL],
+	['tracker.enable.mailspool',			'EnableMailspool',			true,	Components.interfaces.nsIPrefBranch.PREF_BOOL],
+	['tracker.enable.local',				'EnableLocal',				true,	Components.interfaces.nsIPrefBranch.PREF_BOOL]
+);
+
+var loadedPrefs = new Array ();
+
+// The .tracker directory is normally stored in $HOME, but this can be overriden by using the
+// $TRACKER_STORAGE environment variable. $HOME can also be overriden by $TRACKER_HOME.
+function getDestinationDirectory ()
+{
+	var directory = '/ThunderbirdEmails';
+	var trackerStorage = environment.get ('TRACKER_STORAGE');
+	
+	if (trackerStorage)
+		directory = trackerStorage + directory;
+	else {
+		var trackerHome = environment.get ('TRACKER_HOME');
+		if (trackerHome)
+			directory = trackerHome + '/.tracker/' + directory;
+		else
+			directory = environment.get ('HOME') + '/.xesam/' + directory;
+	}
+	
+	return directory;
+}
+
+Component.prototype = {
+
+	reload: function() {
+		loader.loadSubScript(SOURCE, this.__proto__);
+	},
+
+	QueryInterface: function(aIID) {
+		if(!aIID.equals(INTERFACE) && !aIID.equals(Ci.nsISupports))
+			throw Cr.NS_ERROR_NO_INTERFACE;
+		return this;
+	},
+	
+	init: function ()
+	{
+		// Load settings
+		this.forceLoad ();
+		
+		// Make sure we catch updates as we should
+		var prefbranch = Components.classes ['@mozilla.org/preferences-service;1']
+			.getService (Components.interfaces.nsIPrefBranch2);
+		prefbranch.removeObserver ('tracker', gObserver);
+		prefbranch.addObserver ('tracker', gObserver, false);
+	},
+
+	// Use this to force a preference read
+	forceLoad: function ()
+	{
+		for (var i = 0; i < TRACKER_SETTINGS.length; i++) {
+			var val = TRACKER_SETTINGS [i] [2];
+			
+			try {
+				var type = TRACKER_SETTINGS [i] [3], domain = TRACKER_SETTINGS [i] [0];
+				if (type == Components.interfaces.nsIPrefBranch.PREF_BOOL)
+					val = prefs.getBoolPref (domain);
+				else if (type == Components.interfaces.nsIPrefBranch.PREF_INT)
+					val = prefs.getIntPref (domain);
+				else if (type == Components.interfaces.nsIPrefBranch.PREF_STRING)
+					val = prefs.getCharPref (domain);
+			} catch (ex) {
+			}
+			
+			loadedPrefs [TRACKER_SETTINGS [i] [1]] = val;
+		}
+	},
+
+	getBoolPref: function (prefName)
+	{
+		return loadedPrefs [prefName];
+	},
+
+	getCharPref: function (prefName)
+	{
+		return loadedPrefs [prefName];
+	},
+
+	getIntPref: function (prefName)
+	{
+		return loadedPrefs [prefName];
+	},
+
+	getDomain: function (prefName)
+	{
+		for (var i = 0; i < TRACKER_SETTINGS.length; i++) {
+			var setting = TRACKER_SETTINGS [i];
+			if (setting [1] == prefName)
+				return setting [0];
+		}
+		
+		return null;
+	},
+
+	setBoolPref: function (prefName, value)
+	{
+		var domain = this.getDomain (prefName);
+		if (!domain)
+			throw Components.results.NS_ERROR_FAILURE;
+		
+		prefs.setBoolPref (domain, value);
+	},
+
+	setCharPref: function (prefName, value)
+	{
+		var domain = this.getDomain (prefName);
+		if (!domain)
+			throw Components.results.NS_ERROR_FAILURE;
+		
+		prefs.setCharPref (domain, value);
+	},
+
+	setIntPref: function (prefName, value)
+	{
+		var domain = this.getDomain (prefName);
+		if (!domain)
+			throw Components.results.NS_ERROR_FAILURE;
+		
+		prefs.setIntPref (domain, value);
+	}
+
+};
+
+// We use this to catch updates
+var gObserver = {
+
+	observe: function (subject, topic, data)
+	{
+		var branch = subject.QueryInterface (Components.interfaces.nsIPrefBranch);
+		
+		// Find the correct setting so that we can update
+		for (var i = 0; i < TRACKER_SETTINGS.length; i++) {
+			var domain = TRACKER_SETTINGS [i] [0];
+			
+			if (domain != data)
+				continue;
+			
+			var key = TRACKER_SETTINGS [i][1], val = loadedPrefs [key];
+			
+			try {
+				switch (branch.getPrefType (data)) {
+				case Components.interfaces.nsIPrefBranch.PREF_BOOL:
+					val = branch.getBoolPref (data);
+					break;
+				case Components.interfaces.nsIPrefBranch.PREF_INT:
+					val = branch.getIntPref (data);
+					break;
+				case Components.interfaces.nsIPrefBranch.PREF_STRING:
+					val = Branch.getCharPref (data);
+					break;
+				}
+			} catch (ex) {
+			}
+			
+			loadedPrefs [key] = val;
+			
+			break;
+		}
+	}
+};
+

Added: trunk/extensions/thunderbird-extension/content/trackerUnindex.js
==============================================================================
--- (empty file)
+++ trunk/extensions/thunderbird-extension/content/trackerUnindex.js	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,394 @@
+//
+// trackerUnindex.js: Unindexing code, used when data needs to be removed from index or unindexed
+//
+// Copyright (C) 2007 Pierre Ãstlund
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+//
+
+// FIXME: This entire implementation should somehow be threaded (or similar) to update GUI correctly
+
+const FOLDER_MODE_REMOVE = 0;
+const FOLDER_MODE_UNINDEX = 1;
+const HDR_MODE_REMOVE = 2;
+const HDR_MODE_UNINDEX = 3;
+const EVERYTHING_MODE_REMOVE = 4;
+const EVERYTHING_MODE_UNINDEX = 5;
+
+var gTrackerIndexer = GetJsService ('@tracker-project.org/services/indexer;1');
+var gTrackerQueue = GetJsService ('@tracker-project.org/services/queue;1');
+
+//
+//	Call one of the following functions to remove or unindex content
+//
+
+var gTrackerUnindex = {
+
+	RemoveFolder: function (folder, recursive, askUserMarked)
+	{
+		var userMarked = false;
+		if (askUserMarked) {
+			var ret = getUserMarkedChoice ();
+			if (ret == 0)
+				userMarked = true;
+			else if (ret == 2)
+				return;
+		}
+		
+		window.openDialog ('chrome://tracker/content/trackerUnindex.xul',
+							'UnindexWindow',
+							'chrome,modal=yes,resizable=no',
+							FOLDER_MODE_REMOVE, folder, recursive, userMarked);
+	},
+
+	RemoveHdrs: function (hdrs, askUserMarked)
+	{
+		var userMarked = false;
+		if (askUserMarked) {
+			var ret = getUserMarkedChoice ();
+			if (ret == 0)
+				userMarked = true;
+			else if (ret == 2)
+				return;
+		}
+			
+		window.openDialog ('chrome://tracker/content/trackerUnindex.xul',
+							'UnindexWindow',
+							'chrome,modal=yes,resizable=no',
+							HDR_MODE_REMOVE, hdrs, userMarked);
+	},
+
+	RemoveEverything: function (askUserMarked)
+	{
+		var userMarked = false;
+		if (askUserMarked) {
+			var ret = getUserMarkedChoice ();
+			if (ret == 0)
+				userMarked = true;
+			else if (ret == 2)
+				return;
+		}
+		
+		window.openDialog ('chrome://tracker/content/trackerUnindex.xul',
+							'UnindexWindow',
+							'chrome,modal=yes,resizable=no',
+							EVERYTHING_MODE_REMOVE, userMarked);
+	},
+
+	UnindexFolder: function (folder, recursive, askUserMarked)
+	{
+		var userMarked = false;
+		if (askUserMarked) {
+			var ret = getUserMarkedChoice ();
+			if (ret == 0)
+				userMarked = true;
+			else if (ret == 2)
+				return;
+		}
+			
+		window.openDialog ('chrome://tracker/content/trackerUnindex.xul',
+							'UnindexWindow',
+							'chrome,modal=yes,resizable=no',
+							FOLDER_MODE_UNINDEX, folder, recursive, userMarked);
+	},
+
+	UnindexHdrs: function (hdrs, askUserMarked)
+	{
+		var userMarked = false;
+		if (askUserMarked) {
+			var ret = getUserMarkedChoice ();
+			if (ret == 0)
+				userMarked = true;
+			else if (ret == 2)
+				return;
+		}
+		
+		window.openDialog ('chrome://tracker/content/trackerUnindex.xul',
+							'UnindexWindow',
+							'chrome,modal=yes,resizable=no',
+							HDR_MODE_UNINDEX, hdrs, userMarked);
+	},
+
+	UnindexEverything: function (askUserMarked)
+	{
+		var userMarked = false;
+		if (askUserMarked) {
+			var ret = getUserMarkedChoice ();
+			if (ret == 0)
+				userMarked = true;
+			else if (ret == 2)
+				return;
+		}
+		
+		window.openDialog ('chrome://tracker/content/trackerUnindex.xul',
+							'UnindexWindow',
+							'chrome,modal=yes,resizable=no',
+							EVERYTHING_MODE_UNINDEX, userMarked);
+	}
+};
+
+//
+//	Various handlers and functions
+//
+
+function onLoad ()
+{
+	window.setTimeout (function () { startProcessing () }, 0);
+}
+
+function startProcessing ()
+{
+	// Make sure we have arguments (we should always have that)
+	if (!window.arguments || window.arguments.length < 1) {
+		window.close (); 
+		return;
+	}
+
+	var remove = false;
+	switch (window.arguments [0]) {
+	case FOLDER_MODE_REMOVE:
+		remove = true;
+	case FOLDER_MODE_UNINDEX:
+		prepareFolder (window.arguments [1], window.arguments [2], true, window.arguments [3]);
+		handleFolder (window.arguments [1], window.arguments [2], remove, window.arguments [3]);
+		break;
+	case HDR_MODE_REMOVE:
+		remove = true;
+	case HDR_MODE_UNINDEX:
+		prepareHdrs (window.arguments [1], window.arguments [2]);
+		handleHdrs (window.arguments [1], window.arguments [2], remove);
+		break;
+	case EVERYTHING_MODE_REMOVE:
+		remove = true;
+	case EVERYTHING_MODE_UNINDEX:
+		prepareEverything ();
+		handleEverything (remove, window.arguments [1]);
+		break;
+	}
+	
+	// Force a process
+	gTrackerQueue.forceProcess ();
+
+	window.close ();
+}
+
+// Returns 0 when true, 1 when false and 2 when cancel
+function getUserMarkedChoice ()
+{
+	var bundle = document.getElementById ('bundle_tracker');
+	var prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
+		.getService(Components.interfaces.nsIPromptService);
+	var flags = prompts.BUTTON_POS_0 * prompts.BUTTON_TITLE_YES + 
+				prompts.BUTTON_POS_1 * prompts.BUTTON_TITLE_NO + 
+				prompts.BUTTON_POS_2 * prompts.BUTTON_TITLE_CANCEL;
+	var check = {value: true}
+	var button = prompts.confirmEx (window, 
+		bundle.getString ('userMarkedContent'),
+		bundle.getString ('removeUserMarkedContent'),
+		flags,
+		null, null, null, null, check);
+		
+	return button;
+}
+
+var folder_count = 0;
+var current_folder = 0;
+var message_count = 0;
+var current_message = 0;
+
+// Updates window with current statistics
+function updateWindow ()
+{
+	// Everything folder related goes here
+	var folderStatus = document.getElementById ('folder-status');
+	var folderMeter = document.getElementById ('folder-status-meter');
+	folderStatus.value = current_folder + '/' + folder_count;
+	folderMeter.value = 100 * (current_folder / folder_count);
+	
+	// Next are message related things
+	var messageStatus = document.getElementById ('message-status');
+	var messageMeter = document.getElementById ('message-status-meter');
+	messageStatus.value = current_message + '/' + message_count;
+	messageMeter.value = 100 * (current_message / message_count);
+}
+
+function prepareDone ()
+{
+	// Take GUI from preparation mode to unindex/remove mode
+	var statusElem = document.getElementById ('unindex-mainstatus');
+	var prepareElem = document.getElementById ('unindex-preparestatus');
+	var currentElem = document.getElementById ('current-item')
+	currentElem.value = '';
+	prepareElem.setAttribute ('style', 'display: none;');
+	statusElem.setAttribute ('style', '');
+	updateWindow ();
+}
+
+// Preapares the GUI when removing/unindexing a folder
+function prepareFolder (folder, recursive, gui)
+{
+	if (!folder)
+		return;
+	
+	// Update our statistics
+	if (recursive) {
+		var allFolders = Components.classes ['@mozilla.org/supports-array;1']
+			.createInstance (Components.interfaces.nsISupportsArray);
+		folder.ListDescendents (allFolders);
+		folder_count += allFolders.Count () + 1;
+		message_count += folder.getTotalMessages (true);
+		current_folder = 0;
+		current_message = 0;
+	} else {
+		folder_count += 1;
+		message_count += folder.getTotalMessages (false);
+		current_folder = 0;
+		current_message = 0;
+	}
+	
+	if (gui)
+		prepareDone ();
+}
+
+function prepareHdrs (hdrs)
+{
+	// Pretty easy, huh?
+	folder_count = 1;
+	message_count = hdrs.Count ();
+	current_folder = 0;
+	current_message = 0;
+	prepareDone ();
+}
+
+function prepareEverything ()
+{
+	var gAccountManager = Components.classes ['@mozilla.org/messenger/account-manager;1']
+		.getService (Components.interfaces.nsIMsgAccountManager);
+	var accounts = gAccountManager.accounts;
+	
+	for (var i = 0; i < accounts.Count (); i++) {
+		var account = accounts.QueryElementAt (i, Components.interfaces.nsIMsgAccount);
+		if (!account)
+			continue;
+		
+		prepareFolder (account.incomingServer.rootFolder, true, false);
+	}
+	
+	prepareDone ();
+}
+
+function handleFolder (folder, recursive, remove, userMarked)
+{
+	if (!folder)
+		return;
+
+	var currentItem = document.getElementById ('current-item');
+		
+	if (recursive) {
+		var allFolders = Components.classes ['@mozilla.org/supports-array;1']
+			.createInstance (Components.interfaces.nsISupportsArray);
+		folder.ListDescendents (allFolders);
+		allFolders.AppendElement (folder);
+		for (var i = 0; i < allFolders.Count (); i++) {
+			var currentFolder = allFolders.QueryElementAt (i, Components.interfaces.nsIMsgFolder);
+			if (!currentFolder)
+				continue;
+		
+			handleFolder (currentFolder, false, remove, userMarked);
+		}
+	} else {
+	    try {
+			currentItem.value = folder.prettyName;
+			
+			// Update user interface
+			current_folder++;
+			updateWindow ();
+
+			gTrackerIndexer.resetFolder (folder, userMarked, false, false);
+			if (remove)
+				gTrackerQueue.removeFolder (folder);
+			
+			// Only process content if we have any content (getMessages will throw an exception otherwise)
+			if (folder.getTotalMessages (false) > 0) {		
+				var enumerator = folder.getMessages (null);
+				while (enumerator.hasMoreElements ()) {
+					var hdr = enumerator.getNext ().QueryInterface (Components.interfaces.nsIMsgDBHdr);
+					if (!hdr)
+						continue;
+					current_message++;
+					updateWindow ();
+					gTrackerIndexer.resetHdr (hdr, userMarked);
+				}
+				folder.getMsgDatabase (null).Commit (1);
+			}
+		} catch (ex) {
+		}
+	}
+}
+
+function handleHdrs (hdrs, userMarked, remove)
+{
+	var folder = null;
+	
+	for (var i = 0; i < hdrs.Count (); i++) {
+		var hdr = hdrs.QueryElementAt (i, Components.interfaces.nsIMsgDBHdr);
+		if (!hdr)
+			continue;
+		
+		// We save the folder so we can mark it as "unindexed". Otherwise the extension won't try to re-index
+		// the content of the folder.
+		if (!folder)
+			folder = hdr.folder;
+		
+		// We only remove in case the message is indexed
+		var isIndexed = gTrackerIndexer.isHdrIndexed (hdr);
+		if (isIndexed) {
+			if (remove)
+				gTrackerQueue.removeHdr (hdr);
+			else
+				gTrackerIndexer.resetHdr (hdr, false);
+		}
+		if (userMarked)
+			gTrackerIndexer.resetHdrUserMarked (hdr);
+		
+		current_message++;
+		updateWindow ();
+	}
+	
+	// Reset folder
+	if (folder) 
+		gTrackerIndexer.resetFolder (folder, false, false, false);
+}
+
+function handleEverything (remove, userMarked)
+{
+	var gAccountManager = Components.classes ['@mozilla.org/messenger/account-manager;1']
+		.getService (Components.interfaces.nsIMsgAccountManager);
+	var accounts = gAccountManager.accounts;
+	
+	for (var i = 0; i < accounts.Count (); i++) {
+		var account = accounts.QueryElementAt (i, Components.interfaces.nsIMsgAccount);
+		if (!account)
+			continue;
+		handleFolder (account.incomingServer.rootFolder, true, remove, userMarked);
+	}
+}
+

Added: trunk/extensions/thunderbird-extension/content/trackerUnindex.xul
==============================================================================
--- (empty file)
+++ trunk/extensions/thunderbird-extension/content/trackerUnindex.xul	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,77 @@
+<?xml version="1.0"?>
+
+<!--
+//
+// trackerUnindex.xul: A window displaying unindexing/removal status
+//
+// Copyright (C) 2007 Pierre Ãstlund
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+//
+-->
+
+<!DOCTYPE window SYSTEM "chrome://tracker/locale/tracker.dtd">
+
+<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
+
+<window id="tracker-unindex-window"
+		title="&trackerunindex.title;"
+		onload="onLoad ();"
+		xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";>
+	
+	<script src="trackerUtils.js"/>
+	<script src="trackerUnindex.js"/>
+	
+	<vbox flex="0">
+		<spacer height="5"/>
+		<label value="&trackerunindex.topic;" style="font-weight: bold;"/>
+		<spacer height="5"/>
+		<hbox flex="1">
+			<label value="&trackerunindex.currentItem;"/>
+			<label id="current-item" value="&trackerunindex.preparing;"/>
+		</hbox>
+		<spacer height="5"/>
+		<grid id="unindex-mainstatus" style="visibility: hidden;">
+			<columns>
+				<column flex="0"/>
+				<column flex="1"/>
+				<column flex="0"/>
+			</columns>
+			<rows>
+				<row>
+					<label value="&trackerunindex.folderdesc;"/>
+					<progressmeter id="folder-status-meter" mode="determined" value="0%"/>
+					<label id="folder-status"/>
+				</row>
+				<row>
+					<label value="&trackerunindex.messagedesc;"/>
+					<progressmeter id="message-status-meter" mode="determined" value="0%"/>
+					<label id="message-status"/> 
+				</row>
+			</rows>
+		</grid>
+		<hbox flex="1" id="unindex-preparestatus">
+			<progressmeter id="prepare-meter" mode="undetermined" flex="1"/>
+		</hbox>-
+		<spacer height="5"/>
+	</vbox>
+</window>
+

Added: trunk/extensions/thunderbird-extension/content/trackerUtils.js
==============================================================================
--- (empty file)
+++ trunk/extensions/thunderbird-extension/content/trackerUtils.js	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,32 @@
+//
+// TrackerUtils.js: This is the base of various utility functions
+//
+// Copyright (C) 2007 Pierre Ãstlund
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+//
+
+function GetJsService (component)
+{
+	var comp = Components.classes [component].getService (Components.interfaces.nsISupports);
+	return comp.wrappedJSObject;
+}
+

Added: trunk/extensions/thunderbird-extension/defaults/preferences/default.js
==============================================================================
--- (empty file)
+++ trunk/extensions/thunderbird-extension/defaults/preferences/default.js	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,11 @@
+pref("tracker.enable.contacts", true);
+pref("tracker.enable.imap", true);
+pref("tracker.enable.local", true);
+pref("tracker.enable.mailspool", true);
+pref("tracker.enable.news", true);
+pref("tracker.enable.pop", true);
+pref("tracker.enable.rss", true);
+pref("tracker.enabled", true);
+pref("tracker.index.batch_count", 10);
+pref("tracker.index.delay", 10);
+pref("tracker.index.queue_count", 10);

Added: trunk/extensions/thunderbird-extension/install.rdf
==============================================================================
--- (empty file)
+++ trunk/extensions/thunderbird-extension/install.rdf	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,36 @@
+<?xml version="1.0"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"; 
+     xmlns:em="http://www.mozilla.org/2004/em-rdf#";>
+
+  <Description about="urn:mozilla:install-manifest">
+
+    <em:id>{b656ef18-fd76-45e6-95cc-8043f26361e7}</em:id>
+    <em:name>Tracker Indexer</em:name>
+    <em:version>0.1.3</em:version>
+    <em:description>
+      Index mails, RSS and more using Tracker.
+    </em:description>
+    <em:creator>Pierre Èstlund, Michal Pryc</em:creator>
+    <em:homepageURL>http://www.gnome.org/projects/tracker</em:homepageURL>
+    <em:optionsURL>chrome://tracker/content/trackerPrefs.xul</em:optionsURL>
+
+    <em:file>
+      <Description about="urn:mozilla:extension:file:tracker.jar">
+        <em:package>content/</em:package>
+        <em:locale>locale/en-US/</em:locale>
+        <em:skin>skin/classic/</em:skin>
+      </Description>
+    </em:file>
+
+    <em:targetApplication>
+      <Description>
+        <em:id>{3550f703-e582-4d05-9a08-453d09bdfdc6}</em:id>
+        <em:minVersion>2.0</em:minVersion>
+        <em:maxVersion>3.0.*</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+
+  </Description>
+
+</RDF>

Added: trunk/extensions/thunderbird-extension/locale/en-US/contents.rdf
==============================================================================
--- (empty file)
+++ trunk/extensions/thunderbird-extension/locale/en-US/contents.rdf	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+         xmlns:chrome="http://www.mozilla.org/rdf/chrome#";>
+
+<RDF:Seq about="urn:mozilla:locale:root">
+  <RDF:li resource="urn:mozilla:locale:en-US" />
+</RDF:Seq>
+
+<RDF:Description about="urn:mozilla:locale:en-US"
+  chrome:displayName="English(US)"
+  chrome:author="Pierre Èstlund, Michal Pryc"
+  chrome:name="en-US">
+  <chrome:packages>
+    <RDF:Seq about="urn:mozilla:locale:en-US:packages">
+      <RDF:li resource="urn:mozilla:locale:en-US:tracker" />
+    </RDF:Seq>
+  </chrome:packages>
+</RDF:Description>
+
+<RDF:Description about="urn:mozilla:locale:en-US:tracker"
+  chrome:localeVersion="1.4" />
+
+</RDF:RDF>

Added: trunk/extensions/thunderbird-extension/locale/en-US/strings.properties
==============================================================================
--- (empty file)
+++ trunk/extensions/thunderbird-extension/locale/en-US/strings.properties	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,16 @@
+indexingEnabledTooltip=Indexing is enabled. Click to disable.
+indexingDisabledTooltip=Indexing is diabled. Click to enable.
+selectedFolderFailed=Failed to get selected folder!
+userMarkedContent=User marked content
+removeUserMarkedContent=Do you want this operation to affect objects you have manually excluded from indexing?
+indexIdle=Idle
+indexWorking=%1$S folders remaining
+failedGetStatus=Failed to get status!
+errorWhileIndexing=An error occurred while indexing. Error description: %1$S
+warningCountDescription=You have %1$S unread warnings. Click to read next.
+destinationDirInvalidType=Destination directory exists but is not a directory!
+toIndexDirInvalidType=The ToIndex directory exists but is not a directory!
+toIndexDirectoryLackPermission=The ToIndex directory exists but is not writable!
+destinationDirectoryNoExist=Destination directory does not exist! Start tracker with the Thunderbird backend enabled and try enabling indexing again.
+failedCreateToIndexDir=An error occurred while creating the ToIndex directory: %1$S
+failedListingMessages=Failed listing messages in %1$S. Ignoring folder. Error description: %2$S

Added: trunk/extensions/thunderbird-extension/locale/en-US/tracker.dtd
==============================================================================
--- (empty file)
+++ trunk/extensions/thunderbird-extension/locale/en-US/tracker.dtd	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,50 @@
+<!ENTITY trackermain.settings "Tracker indexing settings">
+<!ENTITY trackerprefs.title "Tracker indexing settings">
+<!ENTITY trackerprefs.indexing "Indexing">
+<!ENTITY trackerprefs.privacy "Privacy">
+<!ENTITY trackerprefs.sourcescaption "Sources">
+<!ENTITY trackerprefs.contentcaption "Content">
+<!ENTITY trackerprefs.enabled "Enable indexing">
+<!ENTITY trackerprefs.destdir "Destination directory">
+<!ENTITY trackerprefs.batchcount "Batch count">
+<!ENTITY trackerprefs.delay "Batch delay">
+<!ENTITY trackerprefs.local "Local account">
+<!ENTITY trackerprefs.imap "IMAP accounts">
+<!ENTITY trackerprefs.pop "POP3 accounts">
+<!ENTITY trackerprefs.mailspool "Mailspool accounts">
+<!ENTITY trackerprefs.news "Newsgroup accounts">
+<!ENTITY trackerprefs.rss "RSS feeds">
+<!ENTITY trackerprefs.contentdesc "To empty index, press the button">
+<!ENTITY trackerprefs.delindex "Drop everything">
+<!ENTITY trackerprefs.unindex "Reset index status">
+<!ENTITY trackerprefs.speedcaption "Indexing speed">
+<!ENTITY trackerprefs.recommended "(Recommended)">
+<!ENTITY trackerprefs.notrecommended "(Not recommended)">
+<!ENTITY trackerprefs.veryslow "Very slow">
+<!ENTITY trackerprefs.slow "Slow">
+<!ENTITY trackerprefs.normal "Normal">
+<!ENTITY trackerprefs.fast "Fast">
+<!ENTITY trackerprefs.veryfast "Very fast">
+<!ENTITY trackerprefs.instant "Instant">
+<!ENTITY trackerprefs.custom "Custom">
+<!ENTITY trackerprefs.customcaption "Custom settings">
+<!ENTITY trackerprefs.status "Status">
+<!ENTITY trackerprefs.queuecount "Queue count">
+<!ENTITY trackerprefs.stats "Statistics">
+<!ENTITY trackerprefs.indexingstatus "Indexing status:">
+<!ENTITY trackerprefs.itemsadded "Items added:">
+<!ENTITY trackerprefs.itemsremoved "Items removed:">
+<!ENTITY trackerprefs.itemsqueued "Items queued:">
+<!ENTITY trackerprefs.refresh "Refresh">
+<!ENTITY trackerunindex.title "Processing index data">
+<!ENTITY trackerunindex.topic "Data is is currently being processed. Please wait...">
+<!ENTITY trackerunindex.currentItem "Processing:">
+<!ENTITY trackerunindex.preparing "Preparing data...">
+<!ENTITY trackerunindex.folderdesc "Folders:">
+<!ENTITY trackerunindex.messagedesc "Messages:">
+<!ENTITY trackerunindex.cancel "Cancel">
+<!ENTITY trackerfoldertree.removefolder "Remove folder from index">
+<!ENTITY trackerfoldertree.removefolderrecursive "Recursively remove folder from index">
+<!ENTITY trackerfoldertree.noindexing "Never index this folder">
+<!ENTITY trackermessageview.removemessage "Remove message from index">
+<!ENTITY trackermessageview.noindexing "Never index this message">

Added: trunk/extensions/thunderbird-extension/skin/classic/contents.rdf
==============================================================================
--- (empty file)
+++ trunk/extensions/thunderbird-extension/skin/classic/contents.rdf	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+         xmlns:chrome="http://www.mozilla.org/rdf/chrome#";>
+
+  <RDF:Seq about="urn:mozilla:skin:root">
+    <RDF:li resource="urn:mozilla:skin:classic/1.0" />
+  </RDF:Seq>
+
+  <RDF:Description about="urn:mozilla:skin:classic/1.0">
+    <chrome:packages>
+      <RDF:Seq about="urn:mozilla:skin:classic/1.0:packages">
+        <RDF:li resource="urn:mozilla:skin:classic/1.0:tracker" />
+      </RDF:Seq>
+    </chrome:packages>
+  </RDF:Description>
+
+</RDF:RDF>

Added: trunk/extensions/thunderbird-extension/skin/classic/overlay.css
==============================================================================

Added: trunk/extensions/thunderbird-extension/skin/classic/tracker-disabled.png
==============================================================================
Binary file. No diff available.

Added: trunk/extensions/thunderbird-extension/skin/classic/tracker-error.png
==============================================================================
Binary file. No diff available.

Added: trunk/extensions/thunderbird-extension/skin/classic/tracker.png
==============================================================================
Binary file. No diff available.

Added: trunk/extensions/thunderbird-extension/tracker.xpi
==============================================================================
Binary file. No diff available.

Added: trunk/filters/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/filters/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,3 @@
+include $(top_srcdir)/Makefile.decl
+
+SUBDIRS = application text

Added: trunk/filters/application/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/filters/application/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,28 @@
+include $(top_srcdir)/Makefile.decl
+
+thumbappbindir = $(libdir)/tracker/filters/application
+
+thumbappbin_SCRIPTS = 						\
+	pdf_filter 						\
+	msword_filter 						\
+	vnd.oasis.opendocument.presentation_filter 		\
+	vnd.oasis.opendocument.presentation-template_filter 	\
+	vnd.oasis.opendocument.spreadsheet_filter 		\
+	vnd.oasis.opendocument.spreadsheet-template_filter 	\
+	vnd.oasis.opendocument.text_filter 			\
+	vnd.oasis.opendocument.text-template_filter 		\
+	vnd.stardivision.writer_filter 				\
+	vnd.sun.xml.calc_filter 				\
+	vnd.sun.xml.calc.template_filter 			\
+	vnd.sun.xml.impress_filter 				\
+	vnd.sun.xml.impress.template_filter 			\
+	vnd.sun.xml.writer_filter 				\
+	vnd.sun.xml.writer.template_filter 			\
+	x-abiword_filter 					\
+	x-gnumeric_filter 					\
+	vnd.sun.xml.draw_filter 				\
+	csv_filter 						\
+	vnd.ms-excel_filter 					\
+	tab-separated-values_filter
+
+EXTRA_DIST = $(thumbappbin_SCRIPTS)

Added: trunk/filters/application/csv_filter
==============================================================================
--- (empty file)
+++ trunk/filters/application/csv_filter	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+tmpfile=`mktemp` || exit 1
+
+nice -n19 ssindex -i "$1" > "$tmpfile"
+
+nice -n19 xsltproc - "$tmpfile" <<EOF
+<?xml version="1.0"?>
+<xsl:stylesheet 
+   xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
+   version="1.0">
+
+  <xsl:output method="text"/>
+  <xsl:output omit-xml-declaration="yes"/>
+
+  <xsl:template match="*">
+    <xsl:value-of select="."/>
+  </xsl:template>
+
+</xsl:stylesheet>
+EOF
+
+rm "$tmpfile"

Added: trunk/filters/application/msword_filter
==============================================================================
--- (empty file)
+++ trunk/filters/application/msword_filter	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+tmpdir=${TMPDIR-/tmp}
+tmpdir="$tmpdir"/tmpdir.$$
+
+tmptxtfile=`mktemp`
+
+(umask 077 && mkdir "$tmpdir") || exit 1
+
+tmpfile="$tmpdir"/tmpfile.$$
+
+cp "$1" "$tmpfile"
+
+# Change the working directory to $tmpdir which we can safely deleter later.
+# Some distributions ship a version of wvText which extracts the image files
+# from the word document and places them in the current working directory.
+
+cd "$tmpdir"
+
+nice -n19 wvText "$tmpfile" "$tmptxtfile"
+
+rm -rf "$tmpdir"
+rm "$tmptxtfile"
+

Added: trunk/filters/application/pdf_filter
==============================================================================
--- (empty file)
+++ trunk/filters/application/pdf_filter	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+nice -n19 pdftotext -enc UTF-8 -q -nopgbrk  "$1" -

Added: trunk/filters/application/tab-separated-values_filter
==============================================================================
--- (empty file)
+++ trunk/filters/application/tab-separated-values_filter	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+tmpfile=`mktemp` || exit 1
+
+nice -n19 ssindex -i "$1" > "$tmpfile"
+
+nice -n19 xsltproc - "$tmpfile" <<EOF
+<?xml version="1.0"?>
+<xsl:stylesheet 
+   xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
+   version="1.0">
+
+  <xsl:output method="text"/>
+  <xsl:output omit-xml-declaration="yes"/>
+
+  <xsl:template match="*">
+    <xsl:value-of select="."/>
+  </xsl:template>
+
+</xsl:stylesheet>
+EOF
+
+rm "$tmpfile"

Added: trunk/filters/application/vnd.ms-excel_filter
==============================================================================
--- (empty file)
+++ trunk/filters/application/vnd.ms-excel_filter	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+tmpfile=`mktemp` || exit 1
+
+nice -n19 ssindex -i "$1" > "$tmpfile"
+
+nice -n19 xsltproc - "$tmpfile" <<EOF
+<?xml version="1.0"?>
+<xsl:stylesheet 
+   xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
+   version="1.0">
+
+  <xsl:output method="text"/>
+  <xsl:output omit-xml-declaration="yes"/>
+
+  <xsl:template match="*">
+    <xsl:value-of select="."/>
+  </xsl:template>
+
+</xsl:stylesheet>
+EOF
+
+rm "$tmpfile"

Added: trunk/filters/application/vnd.oasis.opendocument.presentation-template_filter
==============================================================================
--- (empty file)
+++ trunk/filters/application/vnd.oasis.opendocument.presentation-template_filter	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+nice -n19 odt2txt "$1"

Added: trunk/filters/application/vnd.oasis.opendocument.presentation_filter
==============================================================================
--- (empty file)
+++ trunk/filters/application/vnd.oasis.opendocument.presentation_filter	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+nice -n19 odt2txt "$1"

Added: trunk/filters/application/vnd.oasis.opendocument.spreadsheet-template_filter
==============================================================================
--- (empty file)
+++ trunk/filters/application/vnd.oasis.opendocument.spreadsheet-template_filter	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+nice -n19 odt2txt "$1"

Added: trunk/filters/application/vnd.oasis.opendocument.spreadsheet_filter
==============================================================================
--- (empty file)
+++ trunk/filters/application/vnd.oasis.opendocument.spreadsheet_filter	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+nice -n19 odt2txt "$1"

Added: trunk/filters/application/vnd.oasis.opendocument.text-template_filter
==============================================================================
--- (empty file)
+++ trunk/filters/application/vnd.oasis.opendocument.text-template_filter	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+nice -n19 odt2txt "$1"

Added: trunk/filters/application/vnd.oasis.opendocument.text_filter
==============================================================================
--- (empty file)
+++ trunk/filters/application/vnd.oasis.opendocument.text_filter	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+nice -n19 odt2txt "$1"

Added: trunk/filters/application/vnd.stardivision.writer_filter
==============================================================================
--- (empty file)
+++ trunk/filters/application/vnd.stardivision.writer_filter	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+nice -n19 odt2txt "$1"

Added: trunk/filters/application/vnd.sun.xml.calc.template_filter
==============================================================================
--- (empty file)
+++ trunk/filters/application/vnd.sun.xml.calc.template_filter	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+nice -n19 odt2txt "$1"

Added: trunk/filters/application/vnd.sun.xml.calc_filter
==============================================================================
--- (empty file)
+++ trunk/filters/application/vnd.sun.xml.calc_filter	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+nice -n19 odt2txt "$1"

Added: trunk/filters/application/vnd.sun.xml.draw_filter
==============================================================================
--- (empty file)
+++ trunk/filters/application/vnd.sun.xml.draw_filter	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+nice -n19 odt2txt "$1"

Added: trunk/filters/application/vnd.sun.xml.impress.template_filter
==============================================================================
--- (empty file)
+++ trunk/filters/application/vnd.sun.xml.impress.template_filter	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+nice -n19 odt2txt "$1"

Added: trunk/filters/application/vnd.sun.xml.impress_filter
==============================================================================
--- (empty file)
+++ trunk/filters/application/vnd.sun.xml.impress_filter	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+nice -n19 odt2txt "$1"

Added: trunk/filters/application/vnd.sun.xml.writer.template_filter
==============================================================================
--- (empty file)
+++ trunk/filters/application/vnd.sun.xml.writer.template_filter	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+nice -n19 odt2txt "$1"

Added: trunk/filters/application/vnd.sun.xml.writer_filter
==============================================================================
--- (empty file)
+++ trunk/filters/application/vnd.sun.xml.writer_filter	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+nice -n19 odt2txt "$1"

Added: trunk/filters/application/x-abiword_filter
==============================================================================
--- (empty file)
+++ trunk/filters/application/x-abiword_filter	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+nice -n19 xsltproc - "$1" <<EOF
+<?xml version="1.0"?>
+<xsl:stylesheet 
+   xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
+   version="1.0">
+
+  <xsl:output method="text"/>
+  <xsl:output omit-xml-declaration="yes"/>
+
+  <xsl:template match="*">
+    <xsl:value-of select="."/>
+  </xsl:template>
+
+</xsl:stylesheet>
+EOF

Added: trunk/filters/application/x-gnumeric_filter
==============================================================================
--- (empty file)
+++ trunk/filters/application/x-gnumeric_filter	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+tmpfile=`mktemp` || exit 1
+
+nice -n19 ssindex -i "$1" > "$tmpfile"
+
+nice -n19 xsltproc - "$tmpfile" <<EOF
+<?xml version="1.0"?>
+<xsl:stylesheet 
+   xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
+   version="1.0">
+
+  <xsl:output method="text"/>
+  <xsl:output omit-xml-declaration="yes"/>
+
+  <xsl:template match="*">
+    <xsl:value-of select="."/>
+  </xsl:template>
+
+</xsl:stylesheet>
+EOF
+
+rm "$tmpfile"

Added: trunk/filters/text/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/filters/text/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,15 @@
+include $(top_srcdir)/Makefile.decl
+
+filterbindir = $(libdir)/tracker/filters/text
+
+filterbin_SCRIPTS = 			\
+	html_filter 			\
+	xml_filter 			\
+	x-tex_filter 			\
+	djvu_filter 			\
+	csv_filter 			\
+	spreadsheet_filter 		\
+	tab-separated-values_filter 	\
+	x-comma-separated-values_filter
+			
+EXTRA_DIST = $(filterbin_SCRIPTS)

Added: trunk/filters/text/csv_filter
==============================================================================
--- (empty file)
+++ trunk/filters/text/csv_filter	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+tmpfile=`mktemp` || exit 1
+
+nice -n19 ssindex -i "$1" > "$tmpfile"
+
+nice -n19 xsltproc - "$tmpfile" <<EOF
+<?xml version="1.0"?>
+<xsl:stylesheet 
+   xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
+   version="1.0">
+
+  <xsl:output method="text"/>
+  <xsl:output omit-xml-declaration="yes"/>
+
+  <xsl:template match="*">
+    <xsl:value-of select="."/>
+  </xsl:template>
+
+</xsl:stylesheet>
+EOF
+
+rm "$tmpfile"

Added: trunk/filters/text/djvu_filter
==============================================================================
--- (empty file)
+++ trunk/filters/text/djvu_filter	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+nice -n19 djvused "$1" -e 'print-pure-txt'

Added: trunk/filters/text/html_filter
==============================================================================
--- (empty file)
+++ trunk/filters/text/html_filter	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+nice -n19 w3m \
+    -o indent_incr=0 \
+    -o multicol=false \
+    -o no_cache=true \
+    -o use_cookie=false \
+    -o display_charset=utf8 \
+    -o system_charset=utf8 \
+    -o follow_locale=false \
+    -o use_language_tag=true \
+    -o ucs_conv=true \
+    -T text/html \
+    -dump \
+    "$1"

Added: trunk/filters/text/spreadsheet_filter
==============================================================================
--- (empty file)
+++ trunk/filters/text/spreadsheet_filter	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+tmpfile=`mktemp` || exit 1
+
+nice -n19 ssindex -i "$1" > "$tmpfile"
+
+nice -n19 xsltproc - "$tmpfile" <<EOF
+<?xml version="1.0"?>
+<xsl:stylesheet 
+   xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
+   version="1.0">
+
+  <xsl:output method="text"/>
+  <xsl:output omit-xml-declaration="yes"/>
+
+  <xsl:template match="*">
+    <xsl:value-of select="."/>
+  </xsl:template>
+
+</xsl:stylesheet>
+EOF
+
+rm "$tmpfile"

Added: trunk/filters/text/tab-separated-values_filter
==============================================================================
--- (empty file)
+++ trunk/filters/text/tab-separated-values_filter	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+tmpfile=`mktemp` || exit 1
+
+nice -n19 ssindex -i "$1" > "$tmpfile"
+
+nice -n19 xsltproc - "$tmpfile" <<EOF
+<?xml version="1.0"?>
+<xsl:stylesheet 
+   xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
+   version="1.0">
+
+  <xsl:output method="text"/>
+  <xsl:output omit-xml-declaration="yes"/>
+
+  <xsl:template match="*">
+    <xsl:value-of select="."/>
+  </xsl:template>
+
+</xsl:stylesheet>
+EOF
+
+rm "$tmpfile"

Added: trunk/filters/text/x-comma-separated-values_filter
==============================================================================
--- (empty file)
+++ trunk/filters/text/x-comma-separated-values_filter	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+tmpfile=`mktemp` || exit 1
+
+nice -n19 ssindex -i "$1" > "$tmpfile"
+
+nice -n19 xsltproc - "$tmpfile" <<EOF
+<?xml version="1.0"?>
+<xsl:stylesheet 
+   xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
+   version="1.0">
+
+  <xsl:output method="text"/>
+  <xsl:output omit-xml-declaration="yes"/>
+
+  <xsl:template match="*">
+    <xsl:value-of select="."/>
+  </xsl:template>
+
+</xsl:stylesheet>
+EOF
+
+rm "$tmpfile"

Added: trunk/filters/text/x-tex_filter
==============================================================================
--- (empty file)
+++ trunk/filters/text/x-tex_filter	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,5 @@
+#!/bin/sh
+UNTEX=`which untex`
+if [ -n "$UNTEX" ]; then nice -n19 $UNTEX "$1";
+else cat "$1";
+fi

Added: trunk/filters/text/xml_filter
==============================================================================
--- (empty file)
+++ trunk/filters/text/xml_filter	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+nice -n19 xsltproc - "$1" <<EOF
+<?xml version="1.0"?>
+<xsl:stylesheet 
+   xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
+   version="1.0">
+
+  <xsl:output method="text"/>
+  <xsl:output omit-xml-declaration="yes"/>
+
+  <xsl:template match="*">
+    <xsl:value-of select="."/>
+  </xsl:template>
+
+</xsl:stylesheet>
+EOF

Added: trunk/po/LINGUAS
==============================================================================
--- (empty file)
+++ trunk/po/LINGUAS	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,38 @@
+# tracker translations
+#
+# please keep this list sorted alphabetically
+#
+ar
+be
+be latin
+ca
+cs
+de
+dz
+el
+en_GB
+es
+et
+fi
+fr
+gl
+he
+hu
+it
+ja
+ko
+lt
+mk
+nb
+nl
+oc
+pl
+pt
+pt_BR
+ru
+sk
+sl
+sv
+th
+zh_CN
+

Added: trunk/po/POTFILES.in
==============================================================================
--- (empty file)
+++ trunk/po/POTFILES.in	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,33 @@
+# List of source files containing translatable strings.
+# Please keep this file sorted alphabetically.
+[encoding: UTF-8]
+
+data/trackerd.desktop.in.in
+python/deskbar-handler/tracker-handler.py
+python/deskbar-handler/tracker-handler-static.py
+python/deskbar-handler/tracker-module.py
+src/libtracker-common/tracker-dbus.c
+src/libtracker-common/tracker-utils.c
+src/libtracker-gtk/tracker-metadata-tile.c
+src/libtracker-gtk/tracker-tag-bar.c
+src/tracker-applet/tracker-applet.c
+src/tracker-applet/tracker-applet.desktop.in.in
+src/tracker-applet/tracker-applet-prefs.glade
+src/tracker-indexer/tracker-indexer.c
+src/tracker-indexer/tracker-main.c
+src/tracker-preferences/tracker-preferences.c
+src/tracker-preferences/tracker-preferences.desktop.in.in
+src/tracker-preferences/tracker-preferences.glade
+src/tracker-search-tool/tracker-search-tool.c
+src/tracker-search-tool/tracker-search-tool-callbacks.c
+src/tracker-search-tool/tracker-search-tool.desktop.in.in
+src/tracker-search-tool/tracker-search-tool-support.c
+src/tracker-utils/tracker-files.c
+src/tracker-utils/tracker-meta-folder.c
+src/tracker-utils/tracker-query.c
+src/tracker-utils/tracker-search.c
+src/tracker-utils/tracker-stats.c
+src/tracker-utils/tracker-status.c
+src/tracker-utils/tracker-tag.c
+src/tracker-utils/tracker-unique.c
+src/trackerd/tracker-main.c

Added: trunk/po/POTFILES.skip
==============================================================================
--- (empty file)
+++ trunk/po/POTFILES.skip	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,7 @@
+# List of source files that should NOT be translated.
+# Please keep this file sorted alphabetically.
+
+data/trackerd.desktop.in
+src/tracker-applet/tracker-applet.desktop.in
+src/tracker-preferences/tracker-preferences.desktop.in
+src/tracker-search-tool/tracker-search-tool.desktop.in

Added: trunk/python/FUSE/trackerfs.py
==============================================================================
--- (empty file)
+++ trunk/python/FUSE/trackerfs.py	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,279 @@
+#!/usr/bin/env python
+#    Created by Eugenio Cutolo  me at eugesoftware dot com
+#
+#    This program can be distributed under the terms of the GNU GPL.
+#    See the file COPYING.
+#
+
+from fuse import Fuse
+from optparse import OptionParser
+from errno import *
+from stat import *
+import os,statvfs,logging,dbus,re
+
+#Simple class to interface with tracker dbus function
+class TrackerClient:
+
+	def __init__(self):
+		#Initialize dbus session and tracker interfaces
+		bus = dbus.SessionBus()
+		obj = bus.get_object('org.freedesktop.Tracker','/org/freedesktop/tracker')
+		
+		self.tracker_iface = dbus.Interface(obj, 'org.freedesktop.Tracker')
+		self.keywords_iface = dbus.Interface(obj, 'org.freedesktop.Tracker.Keywords')
+		self.search_iface = dbus.Interface(obj, 'org.freedesktop.Tracker.Search')
+		self.files_iface = dbus.Interface(obj, 'org.freedesktop.Tracker.Files')
+		
+		self.version = self.tracker_iface.GetVersion()
+		self.query_id = -1
+
+	def search(self,text,service='Files',offset=0,max_hits=-1):
+		self.resultlist = self.search_iface.Text(self.query_id, service, text, offset, max_hits)
+		self.resultlist = map(lambda x: str(x),self.resultlist)
+		
+	def search_by_tag(self,tag,service='Files',offset=0,max_hits=-1):
+		self.resultlist = self.keywords_iface.Search(self.query_id, service,tag,offset,max_hits)
+		self.resultlist = map(lambda x: str(x),self.resultlist)
+
+	def add_db_files(self,path):
+		self.files_iface.Exists(path,True)
+		self.files_iface.Exists(path,False)
+		return True
+
+	def add_tag(self,path,tags,service='Files'):
+		self.keywords_iface.Add(service,path,tags)
+		return
+
+	#In future will be implemented live_query support
+	def on_tracker_reply(self, results):
+		print results
+		
+	def on_tracker_error(self, e):
+		print "Error:"+e
+
+#This class it's an extension of Fuse and TrackerClient
+class TrackerFs (Fuse,TrackerClient):
+
+	def __init__(self):
+		#Initialize tracker client
+		TrackerClient.__init__(self)
+		
+		#Shell inteface
+		usage = "usage: %prog mountpoint [options]"
+		
+		self.parser = OptionParser(usage)
+		self.parser.add_option("-a", "--auto",action="store_true",help="Mount point will be populated with the rdf query in ~/.Tracker/fs", dest="automatic", default=True)
+		self.parser.add_option("-s", "--search", dest="keys",help="Use a key to find the contents of mounted dir", metavar="key")
+		self.parser.add_option("-t", "--tag",dest="tag",help="Use a tag/s to find the contents of mounted dir", metavar="tag")
+		self.parser.add_option("-q", "--query",dest="query",help="Use a rdf file to find the contents of mounted dir", metavar="path")
+		self.parser.add_option("-v", "--verbose",action="store_true",help="Verbose the output", dest="verbose", default=False)
+		self.params, args = self.parser.parse_args()
+
+		if os.path.exists(args[0]) == False:
+			print "The mount point doesen't exist make it?"
+			#self.log.debug("Create target directory")
+			os.mkdir(args[0])		
+
+		#check old files
+		#save_dir = open(args[0], O_RDONLY);
+		#fchdir(save_dir);
+		#close(save_dir);
+
+		#Init fuse
+		Fuse.__init__(self,args,{})
+
+		if self.params.verbose:
+			verbmode = logging.DEBUG
+		else:
+			verbmode = logging.WARNING
+		
+		#Setup logger
+		self.log = logging.getLogger("trackerfs");self.log.setLevel(verbmode)
+		fh = logging.StreamHandler();fh.setLevel(verbmode)
+		formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
+		fh.setFormatter(formatter)
+		self.log.addHandler(fh)
+		
+		#This is the path when file will be "really" created/moved/edited
+		self.realdir = "/media/Dati/.data/"
+		self.rdfdir = os.environ["HOME"]+"/.Tracker/fs/"
+
+		if os.path.exists(self.realdir) == False:
+			self.log.debug("Create target directory")
+			os.mkdir(self.realdir)
+
+		self.log.debug("mountpoint: %s" % repr(self.mountpoint))
+
+		#Get list of rdf query
+		#files = os.listdir(self.rdfdir)
+		#self.queryfiles = [f for f in files if f[-4:]]
+		#print self.queryfiles
+		self._refresh_filelist()
+		pass
+
+	def mythread(self):
+		self.log.debug("Start Thread")
+
+
+#Refresh file list calling the TrackerClient function
+	def _refresh_filelist(self):
+		if self.params.tag != None:
+			self.log.debug("Use Tag")
+			self.search_by_tag(self.params.tag.split("+"))
+		#Command Line support for query soon will be move to d-bus
+		elif self.params.query != None:
+			self.log.debug("Use Query")
+			self.resultlist = os.popen("/usr/bin/tracker-query "+self.params.query,"r").readlines();
+			self.resultlist = map(lambda x:(x.split(' : ')),self.resultlist)
+			self.resultlist = map(lambda x:(x[0].strip(' \t\r\n')),self.resultlist)
+		elif self.params.keys != None:
+			self.log.debug("Use Search")
+			self.search(self.params.keys)#.search,self.params.service)
+		else:
+			print 'You must specify an options'
+			return 0
+		self.log.debug("Refresh Filelist")
+
+	def _get_file_path(self,filename):
+		if os.path.dirname(filename) == "/":
+			for file in self.resultlist:
+				if filename== "/"+os.path.basename(file):
+					return file
+		else:
+			path = filename.split("/")
+			relpath = filename.replace("/"+path[1],"")
+			for file in self.resultlist:
+				if path[1] == os.path.basename(file):
+					return file+relpath
+		return filename
+
+	def create_file_or_dir(self,type,path,mode):
+		if os.path.dirname(path) == "/" and self.params.tag != None:
+			newpath = self.realdir+os.path.basename(path)
+		elif os.path.dirname(path) != "/":
+			newpath = self._get_file_path(path)
+		else:
+			self.log.error("Fs based on Search and Query doesen't have write access")
+			return 0
+		if S_ISREG(mode) and type == 0:
+			self.log.debug("MkFile:"+newpath)
+			res = os.open(newpath, os.O_CREAT | os.O_WRONLY,mode);
+		elif type == 1:	
+			self.log.debug("MkDir:"+newpath)
+			res = os.mkdir(newpath,mode)
+		else:
+			return -EINVAL
+		print "path:"+os.path.dirname(path)
+		if os.path.dirname(path) == "/":
+			print "Add to fs"
+			self.add_to_fs(newpath)
+		self._refresh_filelist()
+		self.getdir("/")
+		return res
+
+#Add file/dir to the tracker database and tag it with used tag
+	def add_to_fs(self,path):
+		if self.add_db_files(path):
+			self.log.debug("Added "+os.path.basename(path)+" to tracker database")
+			self.add_tag(path,self.params.tag.split("+"))
+			self.log.debug("Added "+os.path.basename(path)+" to fs")
+		else:
+			self.log.debug("Falied to add "+path+"to tracker database")
+
+	def getattr(self,path):
+		self.log.debug("GetAttr:"+self._get_file_path(path))
+		return os.lstat(self._get_file_path(path))
+
+	def readlink(self, path):
+		self.log.debug("ReadLink:"+self._get_file_path(path))
+		return os.readlink(self._get_file_path(path))
+
+	def getdir(self, path):
+		if path == "/":
+			self._refresh_filelist()
+			return map(lambda x: (os.path.basename(x),0),self.resultlist)
+		else:
+			return map(lambda x: (os.path.basename(x),0),os.listdir(self._get_file_path(path)))
+
+
+	def unlink(self, path):
+		return os.unlink(path)
+
+	def rmdir(self, path):
+		return os.rmdir(path)
+
+	def symlink(self, path, path1):
+		return os.symlink(path, path1)
+
+	def rename(self, path, path1):
+		return os.rename(path, path1)
+
+	def link(self, path, path1):
+		return os.link(path, path1)
+
+	def chmod(self, path, mode):
+		return os.chmod(self._get_file_path(path), mode)
+
+	def chown(self, path, user, group):
+		return os.chown(self._get_file_path(path), user, group)
+
+	def truncate(self, path, size):
+		f = open(self._get_file_path(path), "w+")
+		return f.truncate(size)
+
+	def mknod(self, path, mode, dev):
+		res = self.create_file_or_dir(0,path,mode)
+
+	def mkdir(self, path, mode):
+		return self.create_file_or_dir(1,path,mode)
+
+	def utime(self, path, times):
+		return os.utime(self._get_file_path(path), times)
+
+	def open(self, path, flags):
+		path = self._get_file_path(path)
+		res = os.open(path,flags)
+		self.log.debug("open"+path)
+		os.close(res)
+		return 0
+
+	def read(self, path, length, offset):
+		path = self._get_file_path(path)
+		self.log.debug("read:"+ path)
+		f = open(self._get_file_path(path), "r")
+		f.seek(offset)
+		return f.read(length)
+
+	def write(self, path, buf, off):
+		path = self._get_file_path(path)
+		self.log.debug(":write:"+path)
+		f = open(path, "w")
+		f.seek(off)
+		f.write(buf)
+		return len(buf)
+		return 0
+
+	def release(self, path, flags):
+		self.log.debug("release: %s %s" % (self._get_file_path(path), flags))
+		return 0
+
+	def statfs(self):
+		self.sysconst = os.statvfs(self.realdir)
+		self.log.debug("statfs: returning user's home value ")
+		block_size = self.sysconst[statvfs.F_BSIZE]
+		blocks = self.sysconst[statvfs.F_BLOCKS]
+		blocks_free = self.sysconst[statvfs.F_BFREE]
+		blocks_avail = self.sysconst[statvfs.F_BAVAIL]
+		files = self.sysconst[statvfs.F_FILES]
+		files_free = self.sysconst[statvfs.F_FFREE]
+		namelen = self.sysconst[statvfs.F_NAMEMAX]
+		return (block_size, blocks, blocks_free, blocks_avail, files, files_free, namelen)
+
+	def fsync(self, path, isfsyncfile):
+		self.log.debug("fsync: path=%s, isfsyncfile=%s" % (path, isfsyncfile))
+		return 0
+
+if __name__ == '__main__':	
+	fs = TrackerFs()
+	fs.multithreaded = 0;
+	fs.main()

Added: trunk/python/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/python/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,4 @@
+include $(top_srcdir)/Makefile.decl
+
+SUBDIRS = deskbar-handler
+

Added: trunk/python/SearchTool/COPYING
==============================================================================
--- (empty file)
+++ trunk/python/SearchTool/COPYING	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  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
+this service 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 make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  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.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+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
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the 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 a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE 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.
+
+		     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
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision 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, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This 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 Library General
+Public License instead of this License.

Added: trunk/python/SearchTool/README
==============================================================================
--- (empty file)
+++ trunk/python/SearchTool/README	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,19 @@
+TrackerQt it's a tracker front-end writtend with qt and python
+
+----------Requirements----------
+The qtgui requires recent versions of:
+* Python (>= 2.3)
+* Tracker(>= 5.2)
+* Python Qt3 (can possibly use 3.16)
+* Python Kde3 (>= 3.10)
+* Python DBus 
+
+If you use Ubuntu these can be installed via apt-get with this command:
+sudo apt-get install python-qt3 python-kde3 python-dbus
+
+----------Installation-----------
+
+cp ./*.py /usr/local/bin
+
+----------Todo-----------
+-Thumbail support in result list

Added: trunk/python/SearchTool/mainform.py
==============================================================================
--- (empty file)
+++ trunk/python/SearchTool/mainform.py	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,191 @@
+#!/usr/bin/env python
+#    Created by Eugenio Cutolo  <me eugesoftware com> using QtDesinger
+#
+#    This program can be distributed under the terms of the GNU GPL.
+#    See the file COPYING.
+#
+
+#QtTracker Relase 0.1
+
+from qt import *
+
+class QRichListViewItem(QListViewItem):
+	def __init__(self,*args):
+		QListViewItem.__init__(self,*args)
+		self.richtext = 0
+		self.indent = 0
+		self.rText = QString()
+		self.recreateRichText();
+
+	def setText(self,column,text):
+		if column == 1:
+			self.rText = text
+			self.recreateRichText();
+		else:
+			QListViewItem.setText(self,column,text)
+
+	def recreateRichText(self):
+		if  self.richtext != 0:
+			del(self.richtext)
+			self.richtext = 0
+		self.richtext = QSimpleRichText("<small>"+self.rText+"</small>", self.listView().font())
+
+	def widthChanged(self,c):
+		if c == -1 or c == 1:
+			self.richtext.setWidth(self.listView().columnWidth(1)-15)
+		QListViewItem.widthChanged(self,c)
+
+	def paintCell(self, p, cg, column, width, align):
+		if column == 1:
+			paper = QBrush()
+		
+			palette = self.listView().viewport().palette()
+			itemRectangle = self.listView().itemRect(self)
+			itemRectangle.setX(self.listView().columnWidth(0))
+			self.listView().viewport().erase(itemRectangle)
+			colourGroup = QColorGroup(cg)
+			
+			if  self.isSelected() == 1:
+				paper = QBrush(cg.highlight())
+			else:
+				txtcolor = QColor(100,100,100)
+				colourGroup.setColor(QColorGroup.Text,txtcolor)
+			width = self.listView().width() - self.listView().columnWidth(0)
+			self.listView().setColumnWidth(1,width)
+			self.richtext.draw( p,self.listView().itemMargin(), 0, QRect( 0, 0, width, self.height() ), colourGroup, paper );
+			self.widthChanged(1)
+		else:
+			QListViewItem.paintCell(self, p, cg, column,width, align)
+				
+class MainForm(QDialog):
+	def __init__(self,parent = None,name = None,modal = 0,fl = 0):
+		QDialog.__init__(self,parent,name,modal,fl)
+
+		if not name:
+			self.setName("MainForm")
+
+		self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding,QSizePolicy.Expanding,0,0,self.sizePolicy().hasHeightForWidth()))
+		self.setModal(1)
+
+		MainFormLayout = QGridLayout(self,1,1,11,6,"MainFormLayout")
+
+		self.mainframe = QFrame(self,"mainframe")
+		self.mainframe.setSizePolicy(QSizePolicy(QSizePolicy.Expanding,QSizePolicy.Expanding,0,0,self.mainframe.sizePolicy().hasHeightForWidth()))
+		self.mainframe.setFrameShape(QFrame.StyledPanel)
+		self.mainframe.setFrameShadow(QFrame.Raised)
+		mainframeLayout = QGridLayout(self.mainframe,1,1,11,6,"mainframeLayout")
+
+		self.nextbtn = QPushButton(self.mainframe,"nextbtn")
+		self.nextbtn.setEnabled(0)
+
+		mainframeLayout.addWidget(self.nextbtn,2,5)
+
+		self.prevbtn = QPushButton(self.mainframe,"prevbtn")
+		self.prevbtn.setEnabled(0)
+
+		mainframeLayout.addWidget(self.prevbtn,2,4)
+
+		self.mode_combo = QComboBox(0,self.mainframe,"mode_combo")
+
+		mainframeLayout.addWidget(self.mode_combo,0,2)
+
+		self.searchinp = QLineEdit(self.mainframe,"searchinp")
+
+		mainframeLayout.addWidget(self.searchinp,0,1)
+
+		self.pagen_display = QLabel(self.mainframe,"pagen_display")
+
+		mainframeLayout.addMultiCellWidget(self.pagen_display,2,2,0,3)
+
+		self.services_combo = QComboBox(0,self.mainframe,"services_combo")
+
+		mainframeLayout.addMultiCellWidget(self.services_combo,0,0,3,4)
+
+		self.findbtn = QPushButton(self.mainframe,"findbtn")
+
+		mainframeLayout.addWidget(self.findbtn,0,5)
+
+		self.textLabel1 = QLabel(self.mainframe,"textLabel1")
+
+		mainframeLayout.addWidget(self.textLabel1,0,0)
+
+		self.result_list = QListView(self.mainframe,"result_list")
+		self.result_list.addColumn(self.__tr("Info"))
+		self.result_list.header().setResizeEnabled(0,self.result_list.header().count() - 1)
+		self.result_list.addColumn(self.__tr("Text"))
+		self.result_list.setSizePolicy(QSizePolicy(QSizePolicy.Expanding,QSizePolicy.Expanding,0,0,self.result_list.sizePolicy().hasHeightForWidth()))
+		self.result_list.setLineWidth(1)
+		self.result_list.setResizeMode(QListView.LastColumn)
+		self.result_list.setMargin(0)
+		self.result_list.setMidLineWidth(0)
+		self.result_list.setResizePolicy(QScrollView.Manual)
+		self.result_list.setHScrollBarMode(QListView.AlwaysOff)
+		self.result_list.header().hide()
+		self.result_list.setAllColumnsShowFocus(0)
+		self.result_list.setShowSortIndicator(0)
+		self.result_list.setItemMargin(5)
+		self.result_list.setRootIsDecorated(0)
+
+		mainframeLayout.addMultiCellWidget(self.result_list,1,1,0,5)
+
+		MainFormLayout.addWidget(self.mainframe,0,0)
+
+		self.languageChange()
+
+		self.resize(QSize(629,520).expandedTo(self.minimumSizeHint()))
+		self.clearWState(Qt.WState_Polished)
+
+		self.connect(self.searchinp,SIGNAL("returnPressed()"),self.searchinp_returnPressed)
+		self.connect(self.services_combo,SIGNAL("activated(int)"),self.services_combo_textChanged)
+		self.connect(self.findbtn,SIGNAL("clicked()"),self.findbtn_clicked)
+		self.connect(self.nextbtn,SIGNAL("clicked()"),self.nextbtn_clicked)
+		self.connect(self.prevbtn,SIGNAL("clicked()"),self.prevbtn_clicked)
+		self.connect(self.result_list,SIGNAL("doubleClicked(QListViewItem*)"),self.result_list_doubleClicked)
+		self.connect(self.result_list,SIGNAL("contextMenuRequested(QListViewItem*,const QPoint&,int)"),self.result_list_contextMenuRequested)
+
+
+	def languageChange(self):
+		self.setCaption(self.__tr("Search Tool"))
+		self.nextbtn.setText(self.__tr("Next"))
+		self.prevbtn.setText(self.__tr("Previous"))
+		self.mode_combo.clear()
+		self.mode_combo.insertItem(self.__tr("key"))
+		self.mode_combo.insertItem(self.__tr("tag"))
+		self.pagen_display.setText(QString.null)
+		self.services_combo.clear()
+		self.services_combo.insertItem(self.__tr("All Files"))
+		self.services_combo.insertItem(self.__tr("Development"))
+		self.services_combo.insertItem(self.__tr("Documents"))
+		self.services_combo.insertItem(self.__tr("Images"))
+		self.services_combo.insertItem(self.__tr("Music"))
+		self.services_combo.insertItem(self.__tr("Plain Text"))
+		self.services_combo.insertItem(self.__tr("Videos"))
+		self.findbtn.setText(self.__tr("Find"))
+		self.textLabel1.setText(self.__tr("Search:"))
+		self.result_list.header().setLabel(0,self.__tr("Info"))
+		self.result_list.header().setLabel(1,self.__tr("Text"))
+		QToolTip.add(self.result_list,self.__tr("Lista Risultati"))
+
+	def services_combo_textChanged(self,int):
+		self.findbtn_clicked()
+
+	def findbtn_clicked(self):
+		print "MainForm.findbtn_clicked(): Not implemented yet"
+
+	def prevbtn_clicked(self):
+		print "MainForm.prevbtn_clicked(): Not implemented yet"
+
+	def nextbtn_clicked(self):
+		print "MainForm.nextbtn_clicked(): Not implemented yet"
+
+	def result_list_doubleClicked(self,a0):
+		print "MainForm.result_list_doubleClicked(QListViewItem*): Not implemented yet"
+
+	def result_list_contextMenuRequested(self,a0,a1,a2):
+		print "MainForm.result_list_contextMenuRequested(QListViewItem*,const QPoint&,int): Not implemented yet"
+
+	def searchinp_returnPressed(self):
+		print "MainForm.searchinp_returnPressed(): Not implemented yet"
+
+	def __tr(self,s,c = None):
+		return qApp.translate("MainForm",s,c)

Added: trunk/python/SearchTool/trackergui.py
==============================================================================
--- (empty file)
+++ trunk/python/SearchTool/trackergui.py	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,163 @@
+#!/usr/bin/env python
+#    Created by Eugenio Cutolo  <me eugesoftware com>
+#
+#    This program can be distributed under the terms of the GNU GPL.
+#    See the file COPYING.
+#
+
+#QtTracker Relase 0.1
+
+import sys,os,dbus,re
+from kdecore import KApplication, KCmdLineArgs, KURL,KIconTheme,KIcon
+from kio import KMimeType, KServiceTypeProfile
+from mainform import *
+
+class TrackerClient:
+
+	def __init__(self):
+		bus = dbus.SessionBus()
+		obj = bus.get_object('org.freedesktop.Tracker','/org/freedesktop/tracker')
+		#self.tracker = dbus.Interface(obj, 'org.freedesktop.Tracker')
+		self.keywords_iface = dbus.Interface(obj, 'org.freedesktop.Tracker.Keywords')
+		self.search_iface = dbus.Interface(obj, 'org.freedesktop.Tracker.Search')
+		self.files_iface = dbus.Interface(obj, 'org.freedesktop.Tracker.Files')
+		
+		#self.version = self.tracker.GetVersion()
+
+		#Self service eheheh
+		#self.services = self.tracker.GetServices(True)
+		self.services = ['Files','Development Files','Documents','Images','Music','Text Files','Videos']
+		self.query_id = 0
+
+	def search(self,text,service='Files',offset=0,max_hits=-1):
+		self.returnedfiles = self.search_iface.TextDetailed(1,service,text,offset,max_hits)
+		if len(self.returnedfiles) > 0:
+			return self.returnedfiles
+		else:
+			self.on_tracker_error("Nothing files found")
+			return 0
+	
+	def search_by_tag(self,tag,service='Files',offset=0,max_hits=-1):
+		self.returnedfiles = self.keywords_iface.Search(-1,service,[tag,] ,offset,max_hits)
+		output = []
+		for path in self.returnedfiles:
+			output.append([path,self.files_iface.GetServiceType(path),'mime'])
+		self.returnedfiles = output
+		return output
+
+#Thank you Mikkel
+	def text_snippet(self, text, result, service='Files'):
+		snippet = self.search_iface.GetSnippet(service, result, text)
+		snippet = snippet.replace('<!--', '&lt;!--').strip()
+		return snippet
+
+	def on_tracker_error(self, e):
+		print "Error:",e
+
+class TrackerGui(MainForm,TrackerClient):
+	
+	def __init__(self):
+		MainForm.__init__(self)
+		TrackerClient.__init__(self)
+		self.searchinp.setFocus();
+		self.fatalerr = 0
+		self.mip = 6#Max item for page
+
+	def refresh_page(self):
+		self.result_list.clear()
+		
+		start = (self.mip*self.pagen)
+		end = (self.mip*self.pagen)+self.mip + 1
+
+		self.setCursor(QCursor(3))
+		if self.mode_combo.currentItem() == 0:
+			self.search(self.input,self.service,start,self.mip+1)
+		elif self.mode_combo.currentItem() == 1:
+			self.search_by_tag(self.input,self.service,start,self.mip+1)
+		
+		if self.fatalerr:
+			return
+		
+		if self.pagen > 0:
+			self.prevbtn.setEnabled(1)
+		elif self.pagen == 0:
+			self.prevbtn.setEnabled(0)
+		if len(self.returnedfiles) > self.mip:
+			self.nextbtn.setEnabled(1)
+			self.returnedfiles.pop()
+		else:
+			self.nextbtn.setEnabled(0)
+
+		self.pagen_display.setText("Results "+str(start+1)+" - "+str(end-1))
+		self.show_result(self.returnedfiles)
+
+	def show_result(self,result):
+		self.result_list.setSorting(-1)
+		self.returnedfiles.reverse()
+		for (path,service,mime) in result:
+			item = QRichListViewItem(self.result_list,None)
+			item.setMultiLinesEnabled(1)
+			if len(os.path.dirname(path)) > 40:
+				dirname = os.path.dirname(path)[0:40]+"..."
+			else:
+				dirname = os.path.dirname(path)
+			item.setText(0,os.path.basename(path)+"\n"+dirname+"\n"+service)
+			item.setPixmap(0,self._get_iconc(path))
+			item.setText(1,self.text_snippet(self.input,path))
+		self.setCursor(QCursor(0))
+		self.returnedfiles.reverse()
+
+	def _get_iconc(self,path):
+		mobj = KMimeType.findByPath(path)
+		return mobj.pixmap(KURL(""),KIcon .Desktop,48)
+	
+	def on_tracker_error(self, e):
+		print "Error:",e
+		self.result_list.clear()
+		self.pagen_display.setText(e)
+		self.fatalerr = 1
+
+	def exec_file(self):
+		item = self.result_list.currentItem()
+		npos = (item.itemPos() / item.totalHeight())
+		mime = KMimeType.findByPath(self.returnedfiles[npos][0]).name()
+		offer = KServiceTypeProfile.preferredService(mime, "Application")
+		offer = re.sub('%.','',str(offer.exec_ ()))
+		print offer+" "+self.returnedfiles[npos][0]
+		os.system(offer+" '"+self.returnedfiles[npos][0]+"' &")
+		
+#----------------Qt Events----------------------------
+	
+	def findbtn_clicked(self):
+		self.pagen = 0
+		self.input = str(self.searchinp.text())
+		self.service = self.services[self.services_combo.currentItem()]
+		self.prevbtn.setEnabled(0)
+		self.nextbtn.setEnabled(0)
+		self.refresh_page()
+
+	def nextbtn_clicked(self):
+		self.pagen = self.pagen + 1
+		self.refresh_page()
+
+	def prevbtn_clicked(self):
+		self.pagen = self.pagen - 1
+		self.refresh_page()
+
+	def result_list_doubleClicked(self,item):
+		self.exec_file()
+
+	def result_list_contextMenuRequested(self,item):
+		if item != self.result_list:
+			contextMenu = QPopupMenu(self)
+			contextMenu.insertItem( "&Open", self.exec_file, Qt.CTRL+Qt.Key_O )
+			contextMenu.insertItem( "&Open with...", self.exec_file, Qt.CTRL+Qt.Key_S )
+			contextMenu.exec_loop( QCursor.pos() )
+
+if __name__ == "__main__":
+	KCmdLineArgs.init(sys.argv, "qttrackergui", "qtgui", "0.1")
+	app = KApplication()
+	gui = TrackerGui()
+	gui.show()
+	app.setMainWidget(gui)
+	app.exec_loop()
\ No newline at end of file

Added: trunk/python/applet/applet.py
==============================================================================
--- (empty file)
+++ trunk/python/applet/applet.py	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,131 @@
+#!/usr/bin/env python
+
+import os
+import gtk
+import dbus
+import gobject
+
+POPUP_TIMEOUT_MILLIS = 3000
+POLL_MILLIS = 5000
+
+class Popup (gtk.Window):
+	def __init__ (self, status, widget):
+		gtk.Window.__init__ (self)
+		self.set_decorated (False)
+		self.set_skip_taskbar_hint (True)
+		self.set_skip_pager_hint (True)
+		self.set_keep_above (True)
+		self.set_resizable (False)
+		label = gtk.Label ("MetaTracker is " + status)
+		label.show ()
+		ebox = gtk.EventBox ()
+		ebox.set_visible_window (True)
+		ebox.set_above_child (True)
+		ebox.add (label)
+		ebox.modify_bg (gtk.STATE_NORMAL, gtk.gdk.Color (65535, 65535, 56576))
+		ebox.set_border_width (1)
+		ebox.show ()
+		self.add (ebox)
+		self.show ()
+		scr, rect, orient = widget.get_geometry ()
+		wdir = (rect.x > scr.get_width () / 2) and -1 or 1
+		hdir = (rect.y > scr.get_height () / 2) and -1 or 1
+		width, height = self.get_size ()
+		self.modify_bg (gtk.STATE_NORMAL, gtk.gdk.Color (0,0,0))
+		self.move (rect.x + (width / 2) * wdir, rect.y + (height / 2) * hdir)
+		#self.style.paint_flat_box(self.window, gtk.STATE_NORMAL, gtk.SHADOW_OUT, None, self, 'tooltip', 0, 0, width, height)
+
+class TrackerStatusIcon(gtk.StatusIcon):
+	def __init__(self):
+		gtk.StatusIcon.__init__(self)
+		menu = '''
+		<ui>
+			<menubar name="Menubar">
+				<menu action="Menu">
+					<menuitem action="Search"/>
+					<menuitem action="Preferences"/>
+					<separator/>
+					<menuitem action="About"/>
+					<separator/>
+					<menuitem action="Quit"/>
+				</menu>
+			</menubar>
+		</ui>
+		'''
+		actions = [
+			('Menu',	None, 'Menu'),
+			('Search', None, '_Search...', None, 'Search files with MetaTracker', self.on_activate),
+			('Preferences', gtk.STOCK_PREFERENCES, '_Preferences...', None, 'Change MetaTracker preferences', self.on_preferences),
+			('About', gtk.STOCK_ABOUT, '_About...', None, 'About MetaTracker', self.on_about),
+			('Quit', gtk.STOCK_QUIT, '_Quit...', None, 'Quit Status Applet', gtk.main_quit)]
+		ag = gtk.ActionGroup('Actions')
+		ag.add_actions(actions)
+		self.manager = gtk.UIManager()
+		self.manager.insert_action_group(ag, 0)
+		self.manager.add_ui_from_string(menu)
+		self.menu = self.manager.get_widget('/Menubar/Menu/About').props.parent
+		search = self.manager.get_widget('/Menubar/Menu/Search')
+		search.get_children()[0].set_markup('<b>_Search...</b>')
+		search.get_children()[0].set_use_underline(True)
+		search.get_children()[0].set_use_markup(True)
+		pixbuf = gtk.gdk.pixbuf_new_from_file_at_size('applet.svg', 16, 16)
+		search.get_children()[1].set_from_pixbuf(pixbuf)
+		self.set_from_file('applet.svg')
+		self.set_tooltip('MetaTracker Desktop Search')
+		self.set_visible(True)
+		self.connect('activate', self.on_activate)
+		self.connect('popup-menu', self.on_popup_menu)
+		self.old_status = ""
+		self.connectToDBus ()
+		gobject.timeout_add (0, self.check_tracker_state)
+
+	def connectToDBus (self):
+		self.bus = dbus.SessionBus ()
+		self.connectToTracker ()
+
+	def connectToTracker (self):
+		self.obj = self.bus.get_object('org.freedesktop.Tracker','/org/freedesktop/tracker')
+		self.tracker = dbus.Interface(self.obj, 'org.freedesktop.Tracker')
+
+	def getTrackerStatus (self):
+		st = ""
+		try: st = str (self.tracker.GetStatus ())
+		except:
+			st = "Unreachable!"
+			try: self.connectToTracker ()
+			except: pass
+		return st
+
+	def check_tracker_state (self):
+		stat = self.getTrackerStatus ()
+		if stat != self.old_status:
+			p = Popup (stat, self)
+			gobject.timeout_add (POPUP_TIMEOUT_MILLIS, p.destroy)
+			self.old_status = stat
+		self.set_tooltip ("MetaTracker is " + stat)
+		gobject.timeout_add (POLL_MILLIS, self.check_tracker_state)
+
+	def on_activate(self, data):
+		tst="tracker-search-tool"
+		os.spawnlp (os.P_NOWAIT, tst, tst)
+
+	def on_popup_menu(self, status, button, time):
+		self.menu.popup(None, None, None, button, time)
+
+	def on_preferences(self, data):
+		tp="tracker-preferences"
+		os.spawnlp (os.P_NOWAIT, tp, tp)
+
+	def on_about(self, data):
+		dialog = gtk.AboutDialog()
+		dialog.set_name('MetaTracker')
+		dialog.set_version('0.6.2')
+		dialog.set_comments('A desktop indexing and search tool')
+		dialog.set_website('http://www.tracker-project.org/')
+		dialog.set_logo(gtk.gdk.pixbuf_new_from_file_at_size('applet.svg', 64, 64))
+		dialog.run()
+		dialog.destroy()
+
+if __name__ == '__main__':
+	TrackerStatusIcon()
+	gtk.main()

Added: trunk/python/applet/applet.svg
==============================================================================
--- (empty file)
+++ trunk/python/applet/applet.svg	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,453 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://web.resource.org/cc/";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:xlink="http://www.w3.org/1999/xlink";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   sodipodi:docname="system-search.svg"
+   sodipodi:docbase="/home/sciyoshi/Build/tracker"
+   inkscape:version="0.44"
+   sodipodi:version="0.32"
+   id="svg11300"
+   height="48px"
+   width="48px">
+  <defs
+     id="defs3">
+    <linearGradient
+       id="linearGradient2846">
+      <stop
+         id="stop2848"
+         offset="0.0000000"
+         style="stop-color:#8a8a8a;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2850"
+         offset="1.0000000"
+         style="stop-color:#484848;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2366">
+      <stop
+         id="stop2368"
+         offset="0"
+         style="stop-color:#ffffff;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0.21904762;"
+         offset="0.50000000"
+         id="stop2374" />
+      <stop
+         id="stop2370"
+         offset="1.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4487">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop4489" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop4491" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4477">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop4479" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop4481" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4467">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop4469" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0.24761905;"
+         offset="1.0000000"
+         id="stop4471" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4454">
+      <stop
+         style="stop-color:#729fcf;stop-opacity:0.20784314;"
+         offset="0.0000000"
+         id="stop4456" />
+      <stop
+         style="stop-color:#729fcf;stop-opacity:0.67619050;"
+         offset="1.0000000"
+         id="stop4458" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4440">
+      <stop
+         style="stop-color:#7d7d7d;stop-opacity:1;"
+         offset="0"
+         id="stop4442" />
+      <stop
+         id="stop4448"
+         offset="0.50000000"
+         style="stop-color:#b1b1b1;stop-opacity:1.0000000;" />
+      <stop
+         style="stop-color:#686868;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop4444" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4440"
+       id="linearGradient4446"
+       x1="30.656250"
+       y1="34.000000"
+       x2="33.218750"
+       y2="31.062500"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.334593,0,0,1.291292,-6.973842,-7.460658)" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4454"
+       id="radialGradient4460"
+       cx="18.240929"
+       cy="21.817987"
+       fx="18.240929"
+       fy="21.817987"
+       r="8.3085051"
+       gradientUnits="userSpaceOnUse" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4467"
+       id="radialGradient4473"
+       cx="15.414371"
+       cy="13.078408"
+       fx="15.414371"
+       fy="13.078408"
+       r="6.6562500"
+       gradientTransform="matrix(2.592963,0,0,2.252104,-25.05975,-18.941)"
+       gradientUnits="userSpaceOnUse" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4487"
+       id="radialGradient4493"
+       cx="24.130018"
+       cy="37.967922"
+       fx="24.130018"
+       fy="37.967922"
+       r="16.528622"
+       gradientTransform="matrix(1,0,0,0.237968,0,28.93278)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       gradientUnits="userSpaceOnUse"
+       y2="25.743469"
+       x2="17.500893"
+       y1="13.602121"
+       x1="18.292673"
+       id="linearGradient2372"
+       xlink:href="#linearGradient2366"
+       inkscape:collect="always" />
+    <radialGradient
+       r="16.528622"
+       fy="37.967922"
+       fx="24.130018"
+       cy="37.967922"
+       cx="24.130018"
+       gradientTransform="matrix(1,0,0,0.237968,0,28.93278)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2842"
+       xlink:href="#linearGradient4477"
+       inkscape:collect="always" />
+    <linearGradient
+       gradientUnits="userSpaceOnUse"
+       y2="30.557772"
+       x2="31.335964"
+       y1="26.580296"
+       x1="27.366341"
+       id="linearGradient2852"
+       xlink:href="#linearGradient2846"
+       inkscape:collect="always" />
+    <linearGradient
+       gradientUnits="userSpaceOnUse"
+       y2="42.5"
+       x2="31.5"
+       y1="35.75"
+       x1="33"
+       id="linearGradient2246"
+       xlink:href="#linearGradient2240"
+       inkscape:collect="always" />
+    <linearGradient
+       gradientUnits="userSpaceOnUse"
+       y2="42.5"
+       x2="31.5"
+       y1="35.75"
+       x1="33"
+       id="linearGradient2238"
+       xlink:href="#linearGradient2232"
+       inkscape:collect="always" />
+    <radialGradient
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-1.290127e-2,1.685197,1.713082,1.311475e-2,-1.041499,-10.11571)"
+       r="19.0625"
+       fy="11.132236"
+       fx="16.563837"
+       cy="11.132236"
+       cx="16.563837"
+       id="radialGradient4997"
+       xlink:href="#linearGradient4991"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="12.583769"
+       x2="12.624337"
+       y1="11.39502"
+       x1="17.060806"
+       gradientTransform="matrix(0,-1.171926,1.171926,0,1.782801,54.10111)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient1764"
+       xlink:href="#linearGradient2187"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient2187">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop2189" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop2191" />
+    </linearGradient>
+    <radialGradient
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,0.536723,0,16.87306)"
+       r="15.644737"
+       fy="36.421127"
+       fx="24.837126"
+       cy="36.421127"
+       cx="24.837126"
+       id="radialGradient8668"
+       xlink:href="#linearGradient8662"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient8662"
+       inkscape:collect="always">
+      <stop
+         id="stop8664"
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;" />
+      <stop
+         id="stop8666"
+         offset="1"
+         style="stop-color:#000000;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4991"
+       inkscape:collect="always">
+      <stop
+         id="stop4993"
+         offset="0"
+         style="stop-color:#ffffff;stop-opacity:1;" />
+      <stop
+         id="stop4995"
+         offset="1"
+         style="stop-color:#ffffff;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2232"
+       inkscape:collect="always">
+      <stop
+         id="stop2234"
+         offset="0"
+         style="stop-color:#788600;stop-opacity:1;" />
+      <stop
+         id="stop2236"
+         offset="1"
+         style="stop-color:#788600;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2240"
+       inkscape:collect="always">
+      <stop
+         id="stop2242"
+         offset="0"
+         style="stop-color:#99b00b;stop-opacity:1;" />
+      <stop
+         id="stop2244"
+         offset="1"
+         style="stop-color:#99b00b;stop-opacity:0;" />
+    </linearGradient>
+  </defs>
+  <sodipodi:namedview
+     stroke="#3465a4"
+     inkscape:window-y="0"
+     inkscape:window-x="0"
+     inkscape:window-height="754"
+     inkscape:window-width="1280"
+     inkscape:showpageshadow="false"
+     inkscape:document-units="px"
+     inkscape:grid-bbox="true"
+     showgrid="false"
+     inkscape:current-layer="layer1"
+     inkscape:cy="23.756772"
+     inkscape:cx="25.937787"
+     inkscape:zoom="15.030623"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     borderopacity="0.25490196"
+     bordercolor="#666666"
+     pagecolor="#ffffff"
+     id="base"
+     fill="#729fcf" />
+  <metadata
+     id="metadata4">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Jakub Steiner</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:source>http://jimmac.musichall.cz</dc:source>
+        <cc:license
+           rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/"; />
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/licenses/by-sa/2.0/";>
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction"; />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution"; />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/Notice"; />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/Attribution"; />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks"; />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/ShareAlike"; />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     inkscape:label="Layer 1"
+     id="layer1">
+    <g
+       id="g1772"
+       transform="matrix(0.826429,0,0,0.826429,8.249162,4.375344)">
+      <path
+         sodipodi:type="arc"
+         style="opacity:0.17112301;color:black;fill:url(#radialGradient2842);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         id="path4475"
+         sodipodi:cx="24.130018"
+         sodipodi:cy="37.967922"
+         sodipodi:rx="16.528622"
+         sodipodi:ry="3.9332814"
+         d="M 40.65864 37.967922 A 16.528622 3.9332814 0 1 1  7.6013966,37.967922 A 16.528622 3.9332814 0 1 1  40.65864 37.967922 z"
+         transform="matrix(1.446431,0,0,1.51999,-10.97453,-17.75168)" />
+      <path
+         sodipodi:nodetypes="csscccscccscczzzz"
+         id="path2844"
+         d="M 18.627569,3.1435548 C 10.488439,3.1435548 3.8827682,9.7492259 3.8827682,17.888356 C 3.8827682,26.027486 10.488439,32.633158 18.627569,32.633158 C 22.107124,32.633158 25.17857,31.248765 27.701292,29.230511 C 27.495915,30.237392 27.623257,31.265879 28.457436,31.990436 L 39.42152,41.517846 C 40.654936,42.589175 42.508982,42.448806 43.58031,41.215389 C 44.651638,39.981971 44.511269,38.127927 43.277853,37.056599 L 32.313769,27.529188 C 31.642242,26.945909 30.820891,26.773219 30.007531,26.886466 C 31.994231,24.374044 33.37237,21.337663 33.37237,17.888356 C 33.37237,9.7492259 26.766699,3.1435548 18.627569,3.1435548 z M 18.551954,4.3697381 C 26.191413,4.3697381 31.843729,9.1586886 31.843729,17.661513 C 31.843729,26.336626 26.027039,30.953288 18.551954,30.953288 C 11.249005,30.953288 5.2601806,25.475196 5.2601806,17.661513 C 5.2601806,9.6774061 11.084819,4.369738 18.551954,4.3697381 z "
+         style="opacity:1;color:black;fill:#dcdcdc;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient2852);stroke-width:2.00000095;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+      <path
+         style="opacity:1;color:black;fill:#dcdcdc;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.00000036;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 18.602905,3.0803551 C 10.437465,3.0803551 3.8104408,9.7073791 3.8104408,17.872819 C 3.8104408,26.038259 10.437465,32.665283 18.602905,32.665283 C 22.093708,32.665283 25.175082,31.276416 27.70596,29.251638 C 27.499919,30.261774 27.627672,31.293585 28.464547,32.020484 L 39.464073,41.578691 C 40.701476,42.653483 42.561515,42.512661 43.636306,41.275256 C 44.711097,40.037852 44.570274,38.177814 43.332871,37.103023 L 32.333346,27.544815 C 31.659648,26.959651 30.835642,26.786402 30.019653,26.900016 C 32.012775,24.379472 33.395369,21.333276 33.395369,17.872819 C 33.395369,9.7073791 26.768345,3.0803551 18.602905,3.0803551 z M 18.527046,6.2664243 C 24.808154,6.2664245 29.905864,11.364135 29.905864,17.645243 C 29.905864,23.926351 24.808154,29.024061 18.527046,29.024061 C 12.245938,29.024061 7.1482276,23.926351 7.1482276,17.645243 C 7.1482278,11.364135 12.245938,6.2664243 18.527046,6.2664243 z "
+         id="path4430" />
+      <path
+         style="opacity:1;color:black;fill:url(#linearGradient4446);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 39.507004,41.57769 C 39.028332,39.304503 40.904334,36.766268 43.091057,36.789315 C 43.091057,36.789315 32.33069,27.531204 32.33069,27.531204 C 29.385899,27.474498 28.061188,29.80382 28.553876,32.131126 L 39.507004,41.57769 z "
+         id="path4438"
+         sodipodi:nodetypes="ccccc" />
+      <path
+         sodipodi:type="arc"
+         style="opacity:1;color:black;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient2372);stroke-width:0.8027336;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         id="path4450"
+         sodipodi:cx="17.500893"
+         sodipodi:cy="18.920233"
+         sodipodi:rx="11.048544"
+         sodipodi:ry="11.048544"
+         d="M 28.549437 18.920233 A 11.048544 11.048544 0 1 1  6.4523487,18.920233 A 11.048544 11.048544 0 1 1  28.549437 18.920233 z"
+         transform="matrix(1.245743,0,0,1.245743,-3.425346,-6.177033)" />
+      <path
+         transform="matrix(0.497764,0,0,0.609621,8.973526,15.61929)"
+         d="M 40.65864 37.967922 A 16.528622 3.9332814 0 1 1  7.6013966,37.967922 A 16.528622 3.9332814 0 1 1  40.65864 37.967922 z"
+         sodipodi:ry="3.9332814"
+         sodipodi:rx="16.528622"
+         sodipodi:cy="37.967922"
+         sodipodi:cx="24.130018"
+         id="path4485"
+         style="opacity:1;color:black;fill:url(#radialGradient4493);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         sodipodi:type="arc" />
+      <rect
+         style="opacity:0.43315507;color:black;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:white;stroke-width:1.00003111;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         id="rect4495"
+         width="19.048439"
+         height="4.4404783"
+         x="40.373337"
+         y="0.14086054"
+         rx="2.1366608"
+         ry="1.8879365"
+         transform="matrix(0.752986,0.658037,-0.648902,0.760872,0,0)" />
+      <path
+         sodipodi:type="arc"
+         style="color:black;fill:url(#radialGradient4460);fill-opacity:1;fill-rule:evenodd;stroke:#3063a3;stroke-width:0.71499395;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dashoffset:0;stroke-opacity:1;visibility:visible"
+         id="path4452"
+         sodipodi:cx="17.589281"
+         sodipodi:cy="18.478292"
+         sodipodi:rx="8.3085051"
+         sodipodi:ry="8.3085051"
+         d="M 25.897786 18.478292 A 8.3085051 8.3085051 0 1 1  9.280776,18.478292 A 8.3085051 8.3085051 0 1 1  25.897786 18.478292 z"
+         transform="matrix(1.398614,0,0,1.398614,-6.224338,-8.298958)" />
+      <path
+         style="opacity:0.83422457;color:black;fill:url(#radialGradient4473);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 18.156915,7.3966938 C 12.949325,7.3966938 8.7323681,11.613651 8.7323681,16.821241 C 8.7323681,18.325216 9.1526753,19.709014 9.77954,20.971144 C 11.03192,21.432757 12.362297,21.746827 13.774307,21.746827 C 19.945262,21.746827 24.873589,16.88519 25.254413,10.809698 C 23.523449,8.7641668 21.044374,7.3966938 18.156915,7.3966938 z "
+         id="path4462" />
+    </g>
+    <g
+       inkscape:label="Layer 1"
+       id="g2194"
+       transform="matrix(0.651446,0,0,0.651446,3.41175,11.0539)"
+       style="opacity:0.84795323">
+      <path
+         sodipodi:type="arc"
+         style="opacity:0.14117647;color:black;fill:url(#radialGradient8668);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         id="path8660"
+         sodipodi:cx="24.837126"
+         sodipodi:cy="36.421127"
+         sodipodi:rx="15.644737"
+         sodipodi:ry="8.3968935"
+         d="M 40.481863 36.421127 A 15.644737 8.3968935 0 1 1  9.1923885,36.421127 A 15.644737 8.3968935 0 1 1  40.481863 36.421127 z"
+         transform="matrix(1.489736,0,0,-1.001252,-12.64716,75.3126)" />
+      <path
+         sodipodi:nodetypes="ccccccc"
+         id="path1432"
+         d="M 38.37476,45.034369 C -1.6510486,46.355509 4.6747954,12.29355 25.49479,12.49765 L 25.49479,3.1222396 L 42.143271,17.708819 L 25.49479,33.006349 C 25.49479,33.006349 25.49479,23.337969 25.49479,23.337969 C 11.43168,22.751999 7.3172614,44.770549 38.37476,45.034369 z "
+         style="opacity:1;color:black;fill:url(#linearGradient2246);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient2238);stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         style="opacity:0.69886361;color:black;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient1764);stroke-width:0.9999997;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+         d="M 16.92492,39.315519 C 5.2018204,33.235892 8.7371274,13.087489 26.5085,13.549959 L 26.5085,5.4508678 C 26.5085,5.4508678 40.556238,17.714589 40.556238,17.714589 L 26.5085,30.658617 C 26.5085,30.658617 26.5085,22.380979 26.5085,22.380979 C 11.66865,22.032709 12.34859,35.138579 16.92492,39.315519 z "
+         id="path2177"
+         sodipodi:nodetypes="ccccccc" />
+      <path
+         sodipodi:nodetypes="ccccccc"
+         id="path4989"
+         d="M 26.036989,4.5686095 L 36.723727,14.798241 C 29.786227,14.79824 32.036989,23.735424 25.911989,26.610424 L 25.974489,22.943609 C 10.786989,22.881109 11.661989,38.443609 22.724489,42.693609 C 3.6363414,37.811681 6.2869904,13.381109 25.911989,12.88111 L 26.036989,4.5686095 z "
+         style="opacity:0.49431817;color:black;fill:url(#radialGradient4997);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.9999997;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+    </g>
+  </g>
+</svg>

Added: trunk/python/deskbar-handler/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/python/deskbar-handler/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,19 @@
+include $(top_srcdir)/Makefile.decl
+
+if USING_DESKBAR_HANDLER
+handlerdir = $(DESKBAR_HANDLER_DIR)
+handler_DATA = 				\
+	tracker-handler.py		\
+	 tracker-handler-static.py
+endif
+
+if USING_DESKBAR_MODULE
+moduledir = $(DESKBAR_MODULE_DIR)
+module_DATA = tracker-module.py
+endif
+
+EXTRA_DIST = 				\
+	tracker-handler.py 		\
+	tracker-handler-static.py 	\
+	tracker-module.py 		\
+	README

Added: trunk/python/deskbar-handler/README
==============================================================================

Added: trunk/python/deskbar-handler/tracker-handler-static.py
==============================================================================
--- (empty file)
+++ trunk/python/deskbar-handler/tracker-handler-static.py	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+#    This handler was originaly created by Mikkel Kamstrup (c) 2006 and updated by Eugenio Cutolo (eulin)
+#
+#    The static search Handler was splitted to a separate file by Marcus Fritzsch
+#
+#    This program can be distributed under the terms of the GNU GPL version 2 or later.
+#    See the file COPYING.
+#
+
+import sys
+import os.path
+import gnome
+import gobject
+
+import gettext
+gettext.install('tracker')
+
+import deskbar.Handler
+import deskbar.Match
+
+
+
+
+class TrackerSearchToolMatch (deskbar.Match.Match):
+
+	def __init__(self, backend, **args):
+		deskbar.Match.Match.__init__(self, backend, **args)
+		self._icon = deskbar.Utils.load_icon ('tracker')
+
+	def action(self, text=None):
+		try:
+			gobject.spawn_async(['tracker-search-tool', self.name], flags=gobject.SPAWN_SEARCH_PATH)
+		except gobject.GError, e:
+			print >> sys.stderr, "*** Error when executing tracker-search-tool:", e
+
+	def get_verb(self):
+		return _('Search for %s with Tracker Search Tool') % ('<b>%(name)s</b>')
+
+	def get_category (self):
+		return 'actions'
+
+	def get_hash (self, text=None):
+		return 'tst-more-hits-action-'+self.name
+
+
+
+
+class TrackerSearchToolHandler(deskbar.Handler.Handler):
+
+	def __init__(self):
+		deskbar.Handler.Handler.__init__(self, 'tracker')
+
+	def query(self, query):
+		return [TrackerSearchToolMatch(self, name=query)]
+
+	@staticmethod
+	def requirements ():
+		if deskbar.Utils.is_program_in_path ('tracker-search-tool'):
+			return (deskbar.Handler.HANDLER_IS_HAPPY, None, None)
+		return (deskbar.Handler.HANDLER_IS_NOT_APPLICABLE, 'tracker-search-tool seems not to be installed properly.', None)
+
+
+
+
+HANDLERS = {
+	'TrackerSearchToolHandler': {
+		'name': 'Search for files using Tracker Search Tool',
+		'description': _('Search all of your documents with Tracker Search Tool'),
+		'requirements': TrackerSearchToolHandler.requirements, # XXX makes deskbar 2.18.1 not load the handler!!
+	},
+}

Added: trunk/python/deskbar-handler/tracker-handler.py
==============================================================================
--- (empty file)
+++ trunk/python/deskbar-handler/tracker-handler.py	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,402 @@
+# -*- coding: utf-8 -*-
+#    This handler was originaly created by Mikkel Kamstrup (c) 2006 and updated by Eugenio Cutolo (eulin)
+#
+#    The handler was rewritten and splitted into live and static search by Marcus Fritzsch
+#
+#    This program can be distributed under the terms of the GNU GPL version 2 or later.
+#    See the file COPYING.
+#
+
+# Notes on URL escaping and quoting:
+#  * Fields displayed in the deskbar applet should be escaped
+#  * There _MUST_ be an unescaped 'uri' field in order to open it
+#    (see also 'escqped_uri' - this URI however should urllib quoted
+#                 - Marcus Fritzsch, fritschy googlemail com, 2007-08-13
+
+import re
+import cgi
+import sys
+import os.path
+import time
+import urllib
+import string
+import gnome
+import gobject
+
+import gettext
+gettext.install('tracker')
+
+import deskbar, deskbar.Utils, deskbar.gnomedesktop
+import deskbar.Handler
+import deskbar.Match
+
+# For now description param it's not used
+TYPES = {
+	'Applications': {
+		'description': (_('Launch %s (%s)') % ('<b>%(name)s</b>', '%(app_name)s')),
+		'category': 'actions',
+	},
+
+	'GaimConversations': {
+		'description': (_('See %s conversation\n%s %s\nfrom %s') % ('<b>%(proto)s</b>', '%(channel)s', '<b>%(conv_to)s</b>', '<i>%(time)s</i>')),
+		'category': 'conversations',
+		'icon': 'stock_people',
+	},
+
+	'Emails': {
+		'description': (_('Email from %s') % '<i>%(publisher)s</i>' ) + '\n<b>%(title)s</b>',
+		'category': 'emails',
+		'action': { # more actions for different MUAs
+			'key': 'mua', # see TrackerLiveSearchMatch.action for a demo
+			'Evolution/Email':    'evolution %(uri)s',
+			'Modest/Email':    'modest-open %(uri)s',
+			'Thunderbird/Email':  'thunderbird -viewtracker %(uri)s',
+			'KMail/Email':        'kmail --view %(uri)s',
+		},
+		'icon': 'stock_mail',
+	},
+
+	'Music': {
+		'description': _('Listen to music %s\nin %s')	% ('<b>%(base)s</b>', '<i>%(dir)s</i>'),
+		'category': 'music',
+	},
+
+	'Documents': {
+		'description': _('See document %s\nin %s')	% ('<b>%(base)s</b>', '<i>%(dir)s</i>'),
+		'category': 'documents',
+	},
+
+	'Development': {
+		'description': _('Open file %s\nin %s')	% ('<b>%(base)s</b>', '<i>%(dir)s</i>'),
+		'category': 'develop',
+	},
+
+	'Images': {
+		'description': _('View image %s\nin %s')	% ('<b>%(base)s</b>', '<i>%(dir)s</i>'),
+		'category': 'images',
+		'icon': 'image',
+	},
+
+	'Videos': {
+		'description': _('Watch video  %s\nin %s')	% ('<b>%(base)s</b>', '<i>%(dir)s</i>'),
+		'category': 'videos',
+		'icon': 'video',
+	},
+
+	'Files': {
+		'description': _('Open file %s\nin %s')	% ('<b>%(base)s</b>', '<i>%(dir)s</i>'),
+		'category': 'files',
+	},
+
+	'Folders': {
+		'description': _('Open folder %s\n%s') % ('<b>%(name)s</b>', '<i>%(dir)s/%(name)s</i>'),
+		'category': 'places',
+		'icon': 'stock_folder',
+	},
+}
+
+
+
+
+class TrackerLiveSearchMatch (deskbar.Match.Match):
+
+	def __init__(self, handler,result=None, **args):
+		deskbar.Match.Match.__init__ (self, handler,name=result['name'], **args)
+		self.result = result
+		self.init_names(result['escaped_uri'])
+
+		# Set the match icon
+		try:
+			self._icon = deskbar.Utils.load_icon(TYPES[result['type']]['icon'])
+		except:
+			if self.result.has_key ('icon'):
+				self._icon = deskbar.Utils.load_icon_for_desktop_icon (result ['icon'])
+			else:
+				self._icon = deskbar.Utils.load_icon_for_file(result['uri'])
+
+	def get_name(self, text=None):
+		return self.result
+
+	def get_verb(self):
+		try:
+			return TYPES[self.result['type']]['description']
+		except:
+			return _('Open file %s\nin %s')	% ('<b>%(base)s</b>', '<i>%(dir)s</i>')
+
+	def get_hash(self, text=None):
+		if self.result ['type'] == 'Applications':
+			# return a name that matches the one returned by the Program handler of deskbar
+			return 'generic_' + self.result ['app_basename']
+		return self.result['uri']
+
+	def action(self, text=None):
+		try:
+			if TYPES[self.result['type']].has_key('action'):
+				if isinstance (TYPES[self.result['type']]['action'], dict):
+					try:
+						key = TYPES[self.result['type']]['action']['key']
+						cmd = TYPES[self.result['type']]['action'][self.result[key]]
+					except:
+						print >> sys.stderr, "Unknown action for URI %s (Error: %s)" % (self.result['uri'], sys.exc_info()[1])
+						return
+				else:
+					cmd = TYPES[self.result['type']]['action']
+				cmd = map(lambda arg : arg % self.result, cmd.split()) # we need this to handle spaces correctly
+
+				print 'Opening Tracker hit with command:', cmd
+				try:
+					# deskbar >= 2.17
+					deskbar.Utils.spawn_async(cmd)
+				except AttributeError:
+					# deskbar <= 2.16
+					gobject.spawn_async(cmd, flags=gobject.SPAWN_SEARCH_PATH)
+			else:
+				if 'desktop' in self.result:
+					self.result['desktop'].launch([])
+				else:
+					try:
+						# deskbar >= 2.17
+						deskbar.Utils.url_show ('file://'+self.result['uri'])
+					except AttributeError:
+						gnome.url_show('file://'+self.result['uri'])
+					print 'Opening Tracker hit:', self.result['uri']
+		except:
+			print >> sys.stderr, '*** Could not open URL %s: %s' % (self.result['uri'], sys.exc_info ()[1])
+
+	def get_category (self):
+		try:
+			return TYPES[self.result['type']]['category']
+		except:
+			return 'files'
+
+	def init_names (self, fullpath):
+		dirname, filename = os.path.split(fullpath)
+		if filename == '': #We had a trailing slash
+			dirname, filename = os.path.split(dirname)
+
+		#Reverse-tilde-expansion
+		home = os.path.normpath(os.path.expanduser('~'))
+		regexp = re.compile(r'^%s(/|$)' % re.escape(home))
+		dirname = re.sub(regexp, r'~\1', dirname)
+
+		self.result['base'] = filename
+		self.result['dir'] = dirname
+
+
+
+
+class TrackerLiveSearchHandler(deskbar.Handler.SignallingHandler):
+
+	def __init__(self):
+		deskbar.Handler.SignallingHandler.__init__(self, 'tracker')
+		# initing on search request, see self.query
+		self.tracker = self.search_iface = self.keywords_iface = self.files_iface = None
+		self.set_delay (500)
+		self.conv_re = re.compile (r'^.*?/logs/([^/]+)/([^/]+)/([^/]+)/(.+?)\.(:?txt|html)$') # all, proto, account, to-whom, time
+
+	def handle_email_hits (self, info, output):
+		if len (info) < 5:
+			print >> sys.stderr, "*** Hit for Service Emails had incomplete data, ignoring (%s)" % info[0]
+			return 0
+		output['title'] = cgi.escape(info[3])
+		output['publisher'] = cgi.escape(info[4])
+		output['mua'] = info[2]
+		if output['mua'] == 'Thunderbird/Email':
+			output['uri'] = info[0]
+		return 1
+
+	def handle_conversation_hits (self, info, output):
+		m = self.conv_re.match (output['escaped_uri'])
+		output['channel']=_('with')
+		output['proto']=output['conv_from']=output['conv_to']=output['time']='' # XXX, never happened during tests
+		if m:
+			output['proto'] = cgi.escape (m.group (1))
+			output['conv_from'] = urllib.unquote (cgi.escape (m.group (2)))
+			output['conv_to'] = urllib.unquote (cgi.escape (m.group (3)))
+			output['time'] = cgi.escape (time_from_purple_log (m.group (4)))
+		if output['conv_to'].endswith ('.chat'):
+			output['channel'] = _('in channel')
+			output['conv_to'] = output['conv_to'].replace ('.chat','')
+		if output['proto'] == 'irc':
+			nick_server = output['conv_from'].split ('@')
+			if len (nick_server) > 1:
+				output['conv_to'] = '%s on %s' % (output['conv_to'], nick_server[1])
+
+	def handle_application_hits (self, info, output):
+		# print info
+		#   dbus.Array(
+		#   [
+		#     dbus.String(u'/usr/share/applications/gksu.desktop'), # TrackerUri  0
+		#     dbus.String(u'Applications'),                         # TrackerType 1
+		#     dbus.String(u'Application'),                          # DesktopType 2
+		#     dbus.String(u'Root Terminal'),                        # DesktopName 3
+		#     dbus.String(u'gksu /usr/bin/x-terminal-emulator'),    # DesktopExec 4
+		#     dbus.String(u'gksu-root-terminal')                    # DesktopIcon 5
+		#   ],
+		#   signature=dbus.Signature('s'))
+		# Strip %U or whatever arguments in Exec field
+		if len (info) < 6:
+			print >> sys.stderr, "*** Hit for Service Applications had incomplete data, ignoring (%s)" % info[0]
+			return 0
+		output['app_name'] = re.sub(r'%\w+', '', info [4]).strip ()
+		output['app_basename'] = cgi.escape (os.path.basename (output['app_name']))
+		output['app_name'] = cgi.escape (output['app_name'])
+		if output['app_basename'] == '': # strange // in app_name, e.g. nautilus burn:///
+			output['app_basename'] = output['app_name']
+		output['name'] = cgi.escape (info [3])
+		output['icon'] = info [5] # no escaping, as it is not displayed as a string
+
+		desktop = parse_desktop_file (info[0])
+		if not desktop:
+			print >> sys.stderr, '*** Could not read .desktop file: %s' % info[0]
+		else:
+			output['desktop'] = desktop
+		return 1
+
+	def receive_hits (self, qstring, hits, max):
+		matches = []
+
+		for info in hits:
+			output = {}
+
+			if len (info) < 2:
+				print >> sys.stderr, "*** Hit had incomplete data, ignoring"
+				continue
+
+			info = [str (i) for i in info]
+
+			output['escaped_uri'] = cgi.escape (info[0])
+			output['uri'] = url_quote (info[0], ';?:@&=+$,./')
+			output['name'] = os.path.basename(output['escaped_uri'])
+			output['type'] = info[1]
+
+			if not TYPES.has_key(output['type']):
+				output['type'] = 'Files'
+
+			if output['type'] == 'Emails':
+				if not self.handle_email_hits (info, output):
+					continue
+
+			elif output['type'] in ('GaimConversations', 'Conversations'):
+				if not self.handle_conversation_hits (info, output):
+					continue
+
+			elif output['type'] == 'Applications':
+				if not self.handle_application_hits (info, output):
+					continue
+
+			# applications are launched by .desktop file, if not readable: exclude
+			if output['type'] != 'Applications' or output.has_key ('desktop'):
+				matches.append(TrackerLiveSearchMatch (self, output))
+
+		if len (matches):
+			self.emit_query_ready(qstring, matches)
+			print 'Tracker response for query "%s" (service %s); %s hits returned, %s shown' % (qstring, hits[0][1], len(hits), len(matches))
+
+	def recieve_error (self, error):
+		print >> sys.stderr, '*** Tracker dbus error:', error
+
+	def query (self, qstring, max):
+		if self.tracker:
+			try: self.tracker.GetStatus ()
+			except: self.tracker = None # reconnect
+		if not self.tracker:
+			try:
+				print "Connecting to Tracker (first search or trackerd restarted)"
+				import dbus
+				bus = dbus.SessionBus()
+				self.tracker = bus.get_object('org.freedesktop.Tracker','/org/freedesktop/tracker')
+				self.search_iface = dbus.Interface(self.tracker, 'org.freedesktop.Tracker.Search')
+				self.keywords_iface = dbus.Interface(self.tracker, 'org.freedesktop.Tracker.Keywords')
+				self.files_iface = dbus.Interface(self.tracker, 'org.freedesktop.Tracker.Files')
+			except:
+				print >> sys.stderr, '*** DBus connection to tracker failed, check your settings.'
+				return
+		for service in TYPES.iterkeys ():
+			self.search_iface.TextDetailed (-1, service, qstring, 0, max, \
+					reply_handler = lambda hits: self.receive_hits (qstring, hits, max), \
+					error_handler = self.recieve_error)
+		print 'Tracker query:', qstring
+
+	@staticmethod
+	def requirements ():
+		try:
+			import dbus
+			try :
+				if getattr(dbus, 'version', (0,0,0)) >= (0,41,0):
+					import dbus.glib
+
+				# Check that Tracker can be started via dbus activation, we will have trouble if it's not
+				bus = dbus.SessionBus()
+				proxy_obj = bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
+				dbus_iface = dbus.Interface(proxy_obj, 'org.freedesktop.DBus')
+				activatables = dbus_iface.ListActivatableNames()
+				if not 'org.freedesktop.Tracker' in activatables:
+					return (deskbar.Handler.HANDLER_IS_NOT_APPLICABLE, 'Tracker is not activatable via dbus', None)
+			except:
+				return (deskbar.Handler.HANDLER_IS_NOT_APPLICABLE, 'Python dbus.glib bindings not found.', None)
+			return (deskbar.Handler.HANDLER_IS_HAPPY, None, None)
+		except:
+			return (deskbar.Handler.HANDLER_IS_NOT_APPLICABLE, 'Python dbus bindings not found.', None)
+
+
+
+
+# this code is stolen from the programs handler of deskbar
+def parse_desktop_file(desktop, only_if_visible=False):
+	try:
+		desktop = deskbar.gnomedesktop.item_new_from_file(desktop, deskbar.gnomedesktop.LOAD_ONLY_IF_EXISTS)
+	except Exception, e:
+		print 'Couldn\'t read desktop file:%s:%s' % (desktop, e)
+		return None
+	if desktop == None or desktop.get_entry_type() != deskbar.gnomedesktop.TYPE_APPLICATION:
+		return None
+	if only_if_visible and desktop.get_boolean(deskbar.gnomedesktop.KEY_NO_DISPLAY):
+		return None
+	return desktop
+
+
+
+
+def time_from_purple_log (instr):
+	try:
+		if instr.find ('+') != -1: # new kind of log timestamp...
+			return time.strftime ('%c', time.strptime (re.sub (r'\+\d{4}', '', instr), '%Y-%m-%d.%H%M%S%Z'))
+		else: # ...from ancient times
+			return time.strftime ('%c', time.strptime (instr, '%Y-%m-%d.%H%M%S'))
+	except:
+		print >> sys.stderr, '*** time parsing for purple chat log failed: %s' % sys.exc_info ()[1]
+	return instr
+
+
+
+
+def url_quote (instr, safe = '/'):
+	"""A unicode capable quote, see http://bugs.python.org/issue1712522""";
+	return ''.join (map (lambda x: x in (safe+string.letters+string.digits) and x or ('%%%02X' % ord(x)), instr.encode ('utf-8')))
+
+
+
+
+
+HANDLERS = {
+	'TrackerLiveSearchHandler': {
+		'name': 'Search for files using Tracker',
+		'description': _('Search all of your documents, <b>as you type</b>'),
+		'requirements': TrackerLiveSearchHandler.requirements,
+		'categories': {
+			'develop': {
+				'name': _('Development Files'),
+			},
+			'music': {
+				'name': _('Music'),
+			},
+			'images': {
+				'name': _('Images'),
+			},
+			'videos': {
+				'name': _('Videos'),
+			},
+		},
+	},
+}

Added: trunk/python/deskbar-handler/tracker-module.py
==============================================================================
--- (empty file)
+++ trunk/python/deskbar-handler/tracker-module.py	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,492 @@
+# This deskbar module was ported from deskbar <= 2.18 handler by Marcus Fritzsch
+
+import gnome
+import gobject
+import re
+import sys
+import urllib
+import string
+import time
+import cgi
+import os.path
+import deskbar
+import deskbar.core.Utils
+import deskbar.core.gnomedesktop
+import deskbar.interfaces.Module
+import deskbar.interfaces.Match
+import deskbar.interfaces.Action
+from deskbar.core.Utils import is_program_in_path, spawn_async
+from deskbar.handlers.actions.OpenWithApplicationAction import \
+		OpenWithApplicationAction
+from deskbar.handlers.actions.OpenDesktopFileAction import \
+		OpenDesktopFileAction
+from deskbar.handlers.actions.ShowUrlAction import \
+		ShowUrlAction
+from deskbar.handlers.actions.ActionsFactory import \
+		get_actions_for_uri
+
+import gettext
+gettext.install('tracker')
+
+
+MAX_RESULTS = 10
+HANDLERS = ['TrackerSearchToolHandler', 'TrackerLiveSearchHandler']
+
+
+class TrackerSearchToolMatch (deskbar.interfaces.Match):
+
+	def __init__(self, **kwargs):
+		deskbar.interfaces.Match.__init__(self, **kwargs)
+		self.add_action (TrackerSearchToolAction (self.get_name ()))
+		self._pixbuf = deskbar.core.Utils.load_icon ('tracker')
+
+	def get_hash (self, text=None):
+		return 'tst-more-hits-action-'+self.get_name ()
+
+	def get_category (self):
+		return 'actions'
+
+
+
+
+class TrackerSearchToolAction (deskbar.interfaces.Action):
+	def __init__(self, name):
+		deskbar.interfaces.Action.__init__ (self, name)
+		self.name = name
+
+	def activate(self, text=None):
+		try:
+			gobject.spawn_async(['tracker-search-tool', self.name], \
+					flags=gobject.SPAWN_SEARCH_PATH)
+		except gobject.GError, e:
+			print >> sys.stderr, "*** Error when executing tracker-search-tool:", e
+
+	def get_verb(self):
+		return _('Search for %s with Tracker Search Tool') % '<b>%(name)s</b>'
+
+	def get_hash (self):
+		return 't-s-t:'+self.name
+
+	def get_category (self):
+		return 'actions'
+
+
+
+
+class TrackerSearchToolHandler(deskbar.interfaces.Module):
+	INFOS = {
+			'icon': deskbar.core.Utils.load_icon ('tracker'),
+			'name': _('Tracker Search'),
+			'description': _('Search with Tracker Search Tool'),
+			'version': '0.6.4',
+	}
+
+	def __init__(self):
+		deskbar.interfaces.Module.__init__(self)
+
+	def query(self, query):
+		self._emit_query_ready (query, [TrackerSearchToolMatch(name=query, priority=self.get_priority ())])
+
+	@staticmethod
+	def has_requirements ():
+		return is_program_in_path ('tracker-search-tool')
+
+
+
+
+#For now description param it's not used
+TYPES = {
+	'Applications': {
+		'description': (_('Launch %s (%s)') % ('<b>%(name)s</b>', '%(app_name)s') ),
+		'category': 'actions',
+	},
+
+	'GaimConversations': {
+		'description': (_('See %s conversation\n%s %s\nfrom %s') % ('<b>%(proto)s</b>', '%(channel)s', '<b>%(conv_to)s</b>', '<i>%(time)s</i>')),
+		'category': 'conversations',
+	},
+
+	'Emails': {
+		'description': (_('Email from %s') % '<i>%(publisher)s</i>' ) + '\n<b>%(title)s</b>',
+		'category': 'emails',
+		'action': { # more actions for different MUAs
+			'key': 'mua', # see TrackerLiveSearchAction.action for a demo
+			'Evolution/Email':    'evolution %(uri)s',
+			'Modest/Email':    'modest-open %(uri)s',
+			'Thunderbird/Email':  'thunderbird -viewtracker %(uri)s',
+			'KMail/Email':        'kmail --view %(uri)s',
+		},
+	},
+
+	'Music': {
+		'description': _('Listen to music %s\nin %s')	% ('<b>%(base)s</b>', '<i>%(dir)s</i>'),
+		'category': 'music',
+		#'icon': 'audio-x-generic',
+	},
+
+	'Documents': {
+		'description': _('See document %s\nin %s')	% ('<b>%(base)s</b>', '<i>%(dir)s</i>'),
+		'category': 'documents',
+	},
+
+	'Development': {
+		'description': _('Open file %s\nin %s')	% ('<b>%(base)s</b>', '<i>%(dir)s</i>'),
+		'category': 'develop',
+	},
+
+	'Images': {
+		'description': _('View image %s\nin %s')	% ('<b>%(base)s</b>', '<i>%(dir)s</i>'),
+		'category': 'images',
+		'icon': 'image',
+	},
+
+	'Videos': {
+		'description': _('Watch video  %s\nin %s')	% ('<b>%(base)s</b>', '<i>%(dir)s</i>'),
+		'category': 'videos',
+		#'icon': 'video-x-generic',
+	},
+
+	'Folders': {
+		'description': _('Open folder %s\n%s') % ('<b>%(name)s</b>', '<i>%(dir)s/%(name)s</i>'),
+		'category': 'places',
+	},
+
+	'Files': {
+		'description': _('Open file %s\nin %s')	% ('<b>%(base)s</b>', '<i>%(dir)s</i>'),
+		'category': 'files',
+	},
+
+	'Extra': {
+		'description': _('Search for %s with Tracker Search Tool') % ('<b>%(name)s</b>'),
+	},
+}
+
+
+
+
+class TrackerLiveSearchMatch (deskbar.interfaces.Match):
+
+	def __init__(self, result, **args):
+		deskbar.interfaces.Match.__init__ (self)
+		self.result = result
+		try:
+			desktop = result['desktop']
+			del result['desktop']
+		except:
+			desktop = None
+
+		# Set the match icon
+		try:
+			self._pixbuf = deskbar.core.Utils.load_icon(TYPES[result['type']]['icon'])
+		except:
+			if self.result.has_key ('icon'):
+				self._pixbuf = deskbar.core.Utils.load_icon_for_desktop_icon (result ['icon'])
+			else:
+				if not self.result['type'] in ('GaimConversations', 'Emails'):
+					try:
+						self._pixbuf = deskbar.core.Utils.load_icon ('file://'+result['quoted_uri'])
+					except:
+						pass # some icons cannot be loaded... (e.g. for non existent file or illegal URI)
+
+		self.add_action (TrackerLiveSearchAction (result, desktop))
+
+		# Add extra default actions where it makes sense
+		if not result['type'] in ["Emails", "Applications", "GaimConversations"]:
+			try:
+				self.add_all_actions (get_actions_for_uri(result['quoted_uri']))
+			except:
+				print >> sys.stderr, "*** Error when adding all actions for hit %s: %s" % (self.result['uri'], sys.exc_info()[1])
+
+	def get_name (self, text = None):
+		return self.get_verb() % self.result
+
+	def get_verb(self):
+		try:
+			return TYPES[self.result['type']]['description']
+		except:
+			return _('Open file %s\nin %s')	% ('<b>%(base)s</b>', '<i>%(dir)s</i>')
+
+	def get_hash(self, text=None):
+		return self.result['uri']
+
+	def get_category (self):
+		try:
+			return TYPES[self.result['type']]['category']
+		except:
+			return 'files'
+
+
+
+
+class TrackerLiveSearchAction (deskbar.interfaces.Action):
+
+	def __init__ (self, result, desktop):
+		deskbar.interfaces.Action (self)
+		self.name = result['name']
+		self.desktop = desktop
+		self.result = result
+		self.init_names (result['uri'])
+
+	def get_name(self, text=None):
+		return self.result
+
+	def get_hash(self, text=None):
+		if self.result ['type'] == 'Applications':
+			# return a name that matches the one returned by the Program handler of deskbar
+			return 'generic_' + self.result ['app_basename']
+		return self.result['uri']
+
+	def get_verb(self):
+		try:
+			return TYPES[self.result['type']]['description']
+		except:
+			return _('Open file %s\nin %s')	% ('<b>%(base)s</b>', '<i>%(dir)s</i>')
+
+	def activate (self, text=None):
+		try:
+			if TYPES[self.result['type']].has_key('action'):
+				if isinstance (TYPES[self.result['type']]['action'], dict):
+					try:
+						key = TYPES[self.result['type']]['action']['key']
+						cmd = TYPES[self.result['type']]['action'][self.result[key]]
+					except:
+						print >> sys.stderr, "Unknown action for URI %s (Error: %s)" % (self.result['uri'], sys.exc_info()[1])
+						return
+				else:
+					cmd = TYPES[self.result['type']]['action']
+				cmd = map(lambda arg : arg % self.result, cmd.split()) # we need this to handle spaces correctly
+
+				print 'Opening Tracker hit with command:', cmd
+				deskbar.core.Utils.spawn_async(cmd)
+			else:
+				if self.desktop:
+					self.desktop.launch ([])
+				else:
+					deskbar.core.Utils.url_show ('file://'+self.result['quoted_uri'])
+				print 'Opening Tracker hit:', self.result['quoted_uri']
+		except:
+			print >> sys.stderr, "*** Could not activate Hit %s: %s" % (self.result['uri'], sys.exc_info()[1])
+
+	def init_names (self, fullpath):
+		dirname, filename = os.path.split(fullpath)
+		if filename == '': #We had a trailing slash
+			dirname, filename = os.path.split(dirname)
+
+		#Reverse-tilde-expansion
+		home = os.path.normpath(os.path.expanduser('~'))
+		regexp = re.compile(r'^%s(/|$)' % re.escape(home))
+		dirname = re.sub(regexp, r'~\1', dirname)
+
+		self.result ['base'] = filename
+		self.result ['dir'] = dirname
+
+
+
+
+class TrackerLiveSearchHandler(deskbar.interfaces.Module):
+
+	INFOS = {
+			'icon': deskbar.core.Utils.load_icon ('tracker'),
+			'name': _('Tracker Live Search'),
+			'description': _('Search with Tracker, as you type'),
+			'version': '0.6.4',
+			'categories': {
+			'develop': {
+				'name': _('Development Files'),
+			},
+			'music': {
+				'name': _('Music'),
+			},
+			'images': {
+				'name': _('Images'),
+			},
+			'videos': {
+				'name': _('Videos'),
+			},
+		},
+	}
+
+	@staticmethod
+	def has_prerequisites ():
+		try:
+			import dbus
+			try :
+				if getattr(dbus, 'version', (0,0,0)) >= (0,41,0):
+					import dbus.glib
+
+				# Check that Tracker can be started via dbus activation, we will have trouble if it's not
+				bus = dbus.SessionBus()
+				proxy_obj = bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
+				dbus_iface = dbus.Interface(proxy_obj, 'org.freedesktop.DBus')
+				activatables = dbus_iface.ListActivatableNames()
+				if not 'org.freedesktop.Tracker' in activatables:
+					TrackerLiveSearchHandler.INSTRUCTIONS = ('Tracker is not activatable via dbus')
+					return False
+			except:
+				TrackerLiveSearchHandler.INSTRUCTIONS = ('Python dbus.glib bindings not found.')
+				return False
+			return True
+		except:
+			TrackerLiveSearchHandler.INSTRUCTIONS = ('Python dbus bindings not found.')
+			return False
+
+	def __init__(self):
+		deskbar.interfaces.Module.__init__(self)
+		# initing on search request, see self.query
+		self.tracker = self.search_iface = self.keywords_iface = self.files_iface = None
+		self.conv_re = re.compile (r'^.*?/logs/([^/]+)/([^/]+)/([^/]+)/(.+?)\.(:?txt|html)$') # all, proto, account, to-whom, time
+
+	def handle_email_hits (self, info, output):
+		if len (info) < 5:
+			print >> sys.stderr, "*** Hit for Service Emails had incomplete data, ignoring (%s)" % info[0]
+			return 0
+		output['title'] = info[3]
+		output['publisher'] = info[4]
+		output['mua'] = info[2]
+		return 1
+
+	def handle_conversation_hits (self, info, output):
+		output ['uri'] = info [0]
+		m = self.conv_re.match (output['uri'])
+		output['channel']=_('with')
+		output['proto']=output['conv_from']=output['conv_to']=output['time']='' # XXX, never happened during tests
+		if m:
+			output['proto'] = m.group (1)
+			output['conv_from'] = urllib.unquote (m.group (2))
+			output['conv_to'] = urllib.unquote (m.group (3))
+			output['time'] = time_from_purple_log (m.group (4))
+		if output['conv_to'].endswith ('.chat'):
+			output['channel'] = _('in channel')
+			output['conv_to'] = output['conv_to'].replace ('.chat','')
+		if output['proto'] == 'irc':
+			nick_server = output['conv_from'].split ('@')
+			if len (nick_server) > 1:
+				output['conv_to'] = '%s on %s' % (output['conv_to'], nick_server[1])
+		return 1
+
+	def handle_application_hits (self, info, output):
+		# print info
+		#   dbus.Array(
+		#   [
+		#     dbus.String(u'/usr/share/applications/gksu.desktop'), # TrackerUri  0
+		#     dbus.String(u'Applications'),                         # TrackerType 1
+		#     dbus.String(u'Application'),                          # DesktopType 2
+		#     dbus.String(u'Root Terminal'),                        # DesktopName 3
+		#     dbus.String(u'gksu /usr/bin/x-terminal-emulator'),    # DesktopExec 4
+		#     dbus.String(u'gksu-root-terminal')                    # DesktopIcon 5
+		#   ],
+		#   signature=dbus.Signature('s'))
+		# Strip %U or whatever arguments in Exec field
+		if len (info) < 6:
+			print >> sys.stderr, "*** Hit for Service Applications had incomplete data, ignoring (%s)" % info[0]
+			return 0
+		output['app_name'] = re.sub(r'%\w+', '', info [4]).strip ()
+		output['app_basename'] = os.path.basename (output['app_name'])
+		output['app_name'] = output['app_name']
+		if output['app_basename'] == '': # strange // in app_name, e.g. nautilus burn:///
+			output['app_basename'] = output['app_name']
+		output['name'] = info [3]
+		output['icon'] = info [5]
+		desktop = parse_desktop_file (output['uri'])
+		if desktop:
+			output['desktop'] = desktop
+		return 1
+
+	def recieve_hits (self, qstring, hits, max):
+		matches = []
+
+		for info in hits:
+			output = {}
+
+			if len (info) < 2:
+				print >> sys.stderr, "*** Hit had incomplete data, ignoring"
+				continue
+
+			info = [str (i) for i in info]
+
+			output['uri'] = info[0]
+			output['name'] = os.path.basename(output['uri'])
+			output['type'] = info[1]
+			output['quoted_uri'] = url_quote (info[0], ';?@&=+$,./')
+
+			if not TYPES.has_key(output['type']):
+				output['type'] = 'Files'
+
+			if output['type'] == 'Emails':
+				if not self.handle_email_hits (info, output):
+					continue
+
+			elif output['type'] == 'GaimConversations':
+				if not self.handle_conversation_hits (info, output):
+					continue
+
+			elif output['type'] == 'Applications':
+				if not self.handle_application_hits (info, output):
+					continue
+
+			# applications are launched by .desktop file, if not readable: exclude
+			if output['type'] != 'Applications' or output.has_key ('desktop'):
+				matches.append(TrackerLiveSearchMatch (output))
+
+		if len (matches):
+			self._emit_query_ready (qstring, matches)
+			print 'Tracker response for %s; %d hits returned, %d shown' % \
+					(qstring, len(hits), len(matches))
+
+	def recieve_error (self, error):
+		print >> sys.stderr, '*** Tracker dbus error:', error
+
+	def query (self, qstring):
+		max = MAX_RESULTS
+
+		if self.tracker:
+			try: self.tracker.GetStatus ()
+			except: self.tracker = None # reconnect
+		if not self.tracker:
+			try:
+				print "Connecting to Tracker (first search or trackerd restarted)"
+				import dbus
+				bus = dbus.SessionBus()
+				self.tracker = bus.get_object('org.freedesktop.Tracker', '/org/freedesktop/tracker')
+				self.search_iface = dbus.Interface(self.tracker, 'org.freedesktop.Tracker.Search')
+				self.keywords_iface = dbus.Interface(self.tracker, 'org.freedesktop.Tracker.Keywords')
+				self.files_iface = dbus.Interface(self.tracker, 'org.freedesktop.Tracker.Files')
+			except:
+				print >> sys.stderr, 'DBus connection to tracker failed, check your settings.'
+				return
+		for service in [key for key in TYPES.iterkeys () if key != 'Extra']:
+			print 'Searching %s' % service
+			self.search_iface.TextDetailed (-1, service, qstring, 0, max, \
+					reply_handler = lambda hits: self.recieve_hits(qstring, hits, max),
+					error_handler = self.recieve_error)
+		print 'Tracker query:', qstring
+
+
+
+
+# this code is stolen from the programs handler of deskbar
+def parse_desktop_file(desktop, only_if_visible=False):
+	try:
+		desktop = deskbar.core.gnomedesktop.item_new_from_file(desktop, deskbar.core.gnomedesktop.LOAD_ONLY_IF_EXISTS)
+	except Exception, e:
+		print 'Couldn\'t read desktop file:%s:%s' % (desktop, e)
+		return None
+	if desktop == None or desktop.get_entry_type() != deskbar.core.gnomedesktop.TYPE_APPLICATION:
+		return None
+	if only_if_visible and desktop.get_boolean(deskbar.core.gnomedesktop.KEY_NO_DISPLAY):
+		return None
+	return desktop
+
+def time_from_purple_log (instr):
+	try:
+		if instr.find ('+') != -1: # new kind of log timestamp...
+			return time.strftime ('%c', time.strptime (re.sub (r'\+\d{4}', '', instr), '%Y-%m-%d.%H%M%S%Z'))
+		else: # ...from ancient times
+			return time.strftime ('%c', time.strptime (instr, '%Y-%m-%d.%H%M%S'))
+	except:
+		print >> sys.stderr, '*** time parsing for purple chat log failed: %s' % sys.exc_info ()[1]
+	return instr
+
+def url_quote (instr, safe = '/'):
+	"""A unicode capable quote, see http://bugs.python.org/issue1712522""";
+	return ''.join (map (lambda x: x in (safe+string.letters+string.digits) and x or ('%%%02X' % ord(x)), instr.encode ('utf-8')))

Added: trunk/python/music/lyrics.py
==============================================================================
--- (empty file)
+++ trunk/python/music/lyrics.py	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,70 @@
+import os 
+import urllib
+import sys
+import re
+from xml.dom import minidom
+
+LYRIC_TITLE_STRIP = ["\(live[^\)]*\)", "\(acoustic[^\)]*\)", "\([^\)]*mix\)", "\([^\)]*version\)", "\([^\)]*edit\)", "\(feat[^\)]*\)"]
+LYRIC_TITLE_REPLACE = [("/", "-"), (" & ", " and ")]
+LYRIC_ARTIST_REPLACE = [("/", "-"), (" & ", " and ")]
+
+def get_lyrics(artist, title):
+	# replace ampersands and the like
+	for exp in LYRIC_ARTIST_REPLACE:
+		p = re.compile (exp[0])
+		artist = p.sub(exp[1], artist)
+	for exp in LYRIC_TITLE_REPLACE:
+		p = re.compile (exp[0])
+		title = p.sub(exp[1], title)
+
+	# strip things like "(live at Somewhere)", "(accoustic)", etc
+	for exp in LYRIC_TITLE_STRIP:
+		p = re.compile (exp)
+		title = p.sub ('', title)
+
+	# compress spaces
+	title = title.strip()
+	artist = artist.strip()
+
+	url = "http://api.leoslyrics.com/api_search.php?auth=Rhythmbox&artist=%s&songtitle=%s"; % (
+		urllib.quote(artist.encode('utf-8')),
+		urllib.quote(title.encode('utf-8')))
+
+	data = urllib.urlopen(url).read()
+	xmldoc = minidom.parseString(data).documentElement
+
+	result_code = xmldoc.getElementsByTagName('response')[0].getAttribute('code')
+	if result_code != '0':
+		xmldoc.unlink()
+		return
+
+	matches = xmldoc.getElementsByTagName('result')[:10]
+	hids = map(lambda x: x.getAttribute('hid'), matches)
+	if len(hids) == 0:
+		self.callback("Unable to find lyrics for this track.")
+		xmldoc.unlink()
+		return
+	
+	xmldoc.unlink()
+
+	url = "http://api.leoslyrics.com/api_lyrics.php?auth=Rhythmbox&hid=%s"; % (urllib.quote(hids[0].encode('utf-8')))
+	data = urllib.urlopen(url).read()
+	xmldoc = minidom.parseString(data).documentElement
+
+	text = xmldoc.getElementsByTagName('title')[0].firstChild.nodeValue
+	text += ' - ' + xmldoc.getElementsByTagName('artist')[0].getElementsByTagName('name')[0].firstChild.nodeValue + '\n\n'
+	text += xmldoc.getElementsByTagName('text')[0].firstChild.nodeValue
+
+	xmldoc.unlink()
+	return text
+
+
+if __name__ == '__main__':
+	if len(sys.argv) != 4:
+		print 'usage: %s artist title output' % (sys.argv[0])
+		sys.exit(1)
+
+	f = open(sys.argv[3], 'w')
+	lyrics = get_lyrics(sys.argv[1], sys.argv[2])
+	f.write(lyrics.encode('utf-8'))
+	f.close()

Added: trunk/python/nautilus/README
==============================================================================
--- (empty file)
+++ trunk/python/nautilus/README	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,15 @@
+tracker-tags-tab.py is a nautilus extension providing document tagging interface powered by tracker
+
+----------Requirements----------
+* python-dev (>= 2.3)
+* tracker(>= 5.0)
+* python-nautilus (>=0.4.3)
+
+If you use Ubuntu these can be installed via apt-get with this command:
+sudo apt-get install python-dev python-nautilus
+
+----------Installation-----------
+
+cp ./*.py ~/.nautilus/python-extensions/
+
+

Added: trunk/python/nautilus/tracker-tags-tab.py
==============================================================================
--- (empty file)
+++ trunk/python/nautilus/tracker-tags-tab.py	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,192 @@
+#  Copyright (C) 2006/7, Edward B. Duffy <eduffy gmail com>
+#  tracker-tags-tab.py:  Tag your files in your Tracker database
+#                        via Nautilus's property dialog.
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+if __name__ == '__main__':
+   import os
+   import sys
+
+   print 'This is nautilus extension, not a standalone application.'
+   print 'Please copy this file into your nautulis extensions directory:'
+   print
+   print '\t# cp %s %s/.nautilus/python-extensions' % \
+               (__file__,os.path.expanduser('~'))
+
+   sys.exit(1)
+
+
+import gtk
+import dbus
+import urllib
+import operator
+import nautilus
+
+class TrackerTagsPage(nautilus.PropertyPageProvider):
+
+   def __init__(self):
+      bus = dbus.SessionBus()
+      obj = bus.get_object('org.freedesktop.Tracker',
+                           '/org/freedesktop/tracker')
+      self.tracker = dbus.Interface(obj, 'org.freedesktop.Tracker')
+      self.keywords = dbus.Interface(obj, 'org.freedesktop.Tracker.Keywords')
+
+   def _on_toggle(self, cell, path, files):
+      on = not self.store.get_value(self.store.get_iter(path), 0)
+      self.store.set_value(self.store.get_iter(path), 0, on)
+      tag = self.store.get_value(self.store.get_iter(path), 1)
+      if on: func = self.keywords.Add
+      else:  func = self.keywords.Remove
+      for f in files:
+         func('Files', f, [tag])
+
+   def _on_add_tag(self, button):
+      self.store.append([False, ''])
+
+   def _on_edit_tag(self, cell, path, text, files):
+      old_text = self.store.get_value(self.store.get_iter(path), 1)
+      on = self.store.get_value(self.store.get_iter(path), 0)
+      if on:
+         for f in files:
+            self.keywords.Remove('Files', f, [old_text])
+            self.keywords.Add('Files', f, [text])
+      self.store.set_value(self.store.get_iter(path), 1, text)
+
+   def _on_update_tag_summary(self, store, path, iter):
+      tags = [ ]
+      for row in store:
+         if row[0]:
+            tags.append(row[1])
+      self.entry_tag.handler_block(self.entry_changed_id)
+      self.entry_tag.set_text(','.join(tags))
+      self.entry_tag.handler_unblock(self.entry_changed_id)
+
+   def _on_tag_summary_changed(self, entry, files):
+      new_tags = set(entry.get_text().split(','))
+      new_tags.discard('') # remove the empty string
+      for f in files:
+         old_tags = set(self.keywords.Get('Files', f))
+         tbr = list(old_tags.difference(new_tags))
+         tba = list(new_tags.difference(old_tags))
+         if tbr:
+            self.keywords.Remove('Files', f, tbr)
+         if tba:
+            self.keywords.Add('Files', f, tba)
+
+      # update check-box list (remove outdated tags, add the new ones)
+      self.store.handler_block(self.store_changed_id)
+      all_tags = [ t for t,c in self.keywords.GetList('Files') ]
+      i = 0
+      while i < len(self.store):
+         if self.store[i][1] in all_tags:
+            self.store[i][0] = (self.store[i][1] in new_tags)
+            all_tags.remove(self.store[i][1])
+            i += 1
+         else:
+            del self.store[i]
+      #  assert len(all_tags) == 1 ???
+      for t in all_tags:
+         self.store.append([True, t])
+      self.store.handler_unblock(self.store_changed_id)
+
+   def get_property_pages(self, files):
+      property_label = gtk.Label('Tags')
+      property_label.show()
+
+      # get the list of tags
+      all_tags = self.keywords.GetList('Files')
+      # convert usage count to an integer
+      all_tags = [ (t,int(c)) for t,c in all_tags ]
+      # sort by usage count
+      all_tags = sorted(all_tags, key=operator.itemgetter(1))
+      all_tags.reverse()
+      # strip away usage count
+      all_tags = [ t for t,c in all_tags ]
+
+      files = [ urllib.url2pathname(f.get_uri()[7:]) for f in files ]
+      indiv_count = dict([ (t,0) for t in all_tags ])
+      tags = { }
+      for f in files:
+         tags[f] = self.keywords.Get('Files', f)
+         for t in tags[f]:
+            indiv_count[t] += 1
+
+      main = gtk.VBox()
+
+      hbox = gtk.HBox()
+      hbox.set_border_width(6)
+      hbox.set_spacing(12)
+      label = gtk.Label('Tags: ')
+      self.entry_tag = gtk.Entry()
+      # self.entry_tag.props.editable = False
+      hbox.pack_start(label, False, False)
+      hbox.pack_start(self.entry_tag, True, True)
+      main.pack_start(hbox, False, False)
+      self.entry_changed_id = self.entry_tag.connect(
+                  'changed', self._on_tag_summary_changed, files)
+
+      sw = gtk.ScrolledWindow()
+      sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+      sw.set_shadow_type(gtk.SHADOW_ETCHED_OUT)
+
+      self.store = gtk.ListStore(bool, str)
+      self.store_changed_id = self.store.connect(
+                  'row-changed', self._on_update_tag_summary)
+      for tag in all_tags:
+         iter = self.store.append([False, tag])
+         if indiv_count[tag] == len(files):
+            self.store.set_value(iter, 0, True)
+         elif indiv_count[tag] == 0:
+            self.store.set_value(iter, 0, False)
+         else:
+            print 'inconsistant'
+      tv = gtk.TreeView(self.store)
+      tv.set_headers_visible(False)
+
+      column = gtk.TreeViewColumn()
+      tv.append_column(column)
+      cell = gtk.CellRendererToggle()
+      column.pack_start(cell, True)
+      column.add_attribute(cell, 'active', 0)
+      # column.add_attribute(cell, 'inconsistent', 0)
+      cell.connect('toggled', self._on_toggle, files)
+      cell.set_property('activatable', True)
+      # cell.set_property('inconsistent', True)
+
+      column = gtk.TreeViewColumn()
+      tv.append_column(column)
+      cell = gtk.CellRendererText()
+      column.pack_start(cell, True)
+      column.add_attribute(cell, 'text', 1)
+      cell.connect('edited', self._on_edit_tag, files)
+      cell.set_property('editable', True)
+
+      sw.add(tv)
+      main.pack_start(sw)
+
+      hbox = gtk.HBox()
+      hbox.set_border_width(6)
+      btn = gtk.Button(stock='gtk-add')
+      btn.get_child().get_child().get_children()[1].props.label = '_Add Tag'
+      btn.connect('clicked', self._on_add_tag)
+      hbox.pack_end(btn, False, False)
+      main.pack_start(hbox, False, False)
+      
+      main.show_all()
+
+      return nautilus.PropertyPage("NautilusPython::tags", property_label, main),
+

Added: trunk/rdf-query-examples/80s-music.rdf
==============================================================================
--- (empty file)
+++ trunk/rdf-query-examples/80s-music.rdf	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,15 @@
+<rdfq:Condition>
+  <rdfq:and>	
+
+    <rdfq:greaterThan>
+      <rdfq:Property name="Audio:ReleaseDate" />
+      <rdf:Integer>1979</rdf:Integer> 
+    </rdfq:greaterThan>
+
+    <rdfq:lessThan>
+      <rdfq:Property name="Audio:ReleaseDate" />
+      <rdf:Integer>1990</rdf:Integer> 
+    </rdfq:lessThan>
+    
+  </rdfq:and>
+</rdfq:Condition>

Added: trunk/rdf-query-examples/90s-music.rdf
==============================================================================
--- (empty file)
+++ trunk/rdf-query-examples/90s-music.rdf	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,15 @@
+<rdfq:Condition>
+  <rdfq:and>	
+
+    <rdfq:greaterThan>
+      <rdfq:Property name="Audio:ReleaseDate" />
+      <rdf:Integer>1989</rdf:Integer> 
+    </rdfq:greaterThan>
+
+    <rdfq:lessThan>
+      <rdfq:Property name="Audio:ReleaseDate" />
+      <rdf:Integer>2000</rdf:Integer> 
+    </rdfq:lessThan>
+    
+  </rdfq:and>
+</rdfq:Condition>

Added: trunk/rdf-query-examples/all-documents.rdf
==============================================================================
--- (empty file)
+++ trunk/rdf-query-examples/all-documents.rdf	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,8 @@
+<rdfq:Condition>
+
+  <rdfq:inSet>
+    <rdfq:Property name="File:Mime" />
+    <rdf:String>application/msword,application/pdf,application/vnd.ms-excel,application/vnd.oasis.opendocument.text,application/vnd.sun.xml.writer</rdf:String> 
+  </rdfq:inSet>
+
+</rdfq:Condition>

Added: trunk/rdf-query-examples/big-documents.rdf
==============================================================================
--- (empty file)
+++ trunk/rdf-query-examples/big-documents.rdf	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,15 @@
+<rdfq:Condition>
+  <rdfq:and>
+
+    <rdfq:inSet>
+      <rdfq:Property name="File:Mime" />
+      <rdf:String>application/msword,application/pdf,application/vnd.ms-excel,application/vnd.oasis.opendocument.text,application/vnd.sun.xml.writer</rdf:String> 
+    </rdfq:inSet>
+
+    <rdfq:greaterThan>
+      <rdfq:Property name="Doc:PageCount" />
+      <rdf:Integer>10</rdf:Integer> 
+    </rdfq:greaterThan>
+
+  </rdfq:and>
+</rdfq:Condition>

Added: trunk/rdf-query-examples/wallpapers.rdf
==============================================================================
--- (empty file)
+++ trunk/rdf-query-examples/wallpapers.rdf	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,16 @@
+<rdfq:Condition>
+  <rdfq:and>	
+
+    <rdfq:greaterThan>
+      <rdfq:Property name="Image:Width" />
+      <rdf:Integer>1023</rdf:Integer> 
+    </rdfq:greaterThan>
+
+    <rdfq:greaterThan>
+      <rdfq:Property name="Image:Height" />
+      <rdf:Integer>767</rdf:Integer> 
+    </rdfq:greaterThan>
+
+
+  </rdfq:and>
+</rdfq:Condition>

Added: trunk/src/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,65 @@
+include $(top_srcdir)/Makefile.decl
+
+if HAVE_GNOME
+build_tracker_search_tool = tracker-search-tool
+endif
+
+if HAVE_INOTIFY
+build_libinotify = libinotify
+endif
+
+if !USING_EXTERNAL_QDBM
+build_qdbm = qdbm
+endif
+
+if ENABLE_SQLITE_FTS
+build_sqlite_fts = tracker-fts
+endif
+
+if ENABLE_PREFERENCES
+build_tracker_preferences = tracker-preferences
+endif
+
+if ENABLE_LIBTRACKERGTK
+build_libtrackergtk = libtracker-gtk
+endif
+
+if ENABLE_TRACKERAPPLET
+build_trackerapplet = tracker-applet
+endif
+
+SUBDIRS = 					\
+	libstemmer				\
+	$(build_qdbm)				\
+	$(build_libinotify)			\
+	libtracker-common 			\
+	libtracker-db				\
+	libtracker				\
+	$(build_sqlite_fts)			\
+	trackerd				\
+	tracker-utils				\
+	tracker-extract				\
+	tracker-thumbnailer			\
+	tracker-indexer				\
+	$(build_libtrackergtk)			\
+	$(build_trackerapplet) 			\
+	$(build_tracker_search_tool)		\
+	$(build_tracker_preferences)
+
+DIST_SUBDIRS = 					\
+	libstemmer				\
+	qdbm					\
+	libinotify				\
+	libtracker-common 			\
+	libtracker-db				\
+	libtracker				\
+	tracker-fts				\
+	trackerd				\
+	tracker-utils				\
+	tracker-extract				\
+	tracker-thumbnailer			\
+	tracker-indexer				\
+	libtracker-gtk				\
+	tracker-applet	 			\
+	tracker-search-tool			\
+	tracker-preferences

Added: trunk/src/libinotify/.cvsignore
==============================================================================
--- (empty file)
+++ trunk/src/libinotify/.cvsignore	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,5 @@
+Makefile
+Makefile.in
+config.h
+config.h.in
+stamp-h1

Added: trunk/src/libinotify/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/libinotify/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,23 @@
+include $(top_srcdir)/Makefile.decl
+
+INCLUDES =						\
+	-DG_LOG_DOMAIN=\"libinotify\"			\
+	-I$(top_srcdir)/src				\
+	$(GLIB2_CFLAGS)
+
+noinst_LTLIBRARIES = libinotify.la
+
+libinotify_la_SOURCES = 				\
+	inotify-monitor.c 				\
+	inotify-handle.c 				\
+	inotify-listhash.c 				\
+	inotify-monitor.h				\
+	inotify-handle.h 				\
+	inotify-listhash.h 				\
+	inotify-log.h 					\
+	linux-inotify-syscalls.h			\
+	libinotify.h
+
+libinotify_la_LDFLAGS = -version-info 0:0:0
+libinotify_la_LIBADD = 					\
+	$(GLIB2_LIBS)

Added: trunk/src/libinotify/inotify-handle.c
==============================================================================
--- (empty file)
+++ trunk/src/libinotify/inotify-handle.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,274 @@
+/*
+ * inotify-handle.c - a structure to represent a client-side watch
+ * Copyright  2005 Ryan Lortie <desrt desrt ca>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of version 2.1 of the GNU Lesser General
+ *   Public 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
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110
+ *
+ *   $Id: inotify-handle.c 23 2005-11-09 13:21:16Z ryanl $
+ */
+
+#include <string.h>
+#include <glib.h>
+#include <linux/inotify.h>
+
+#include "inotify-handle.h"
+#include "inotify-log.h"
+
+enum inh_state
+{
+  inh_state_initial,
+  inh_state_deleted,
+  inh_state_created
+};
+
+struct _INotifyHandle
+{
+  int refcount;
+  INotifyCallback callback;
+  gpointer user_data;
+  gint32 wd;
+  guint32 mask;
+  char *filename;
+  unsigned long flags;
+  enum inh_state state;
+  INotifyHandle *parent;
+};
+
+INotifyHandle *
+inotify_handle_new( const char *filename, guint32 mask, unsigned long flags )
+{
+  INotifyHandle *inh;
+
+  inh = g_new( INotifyHandle, 1 );
+  inh->wd = -1;
+  inh->mask = mask;
+  inh->filename = g_strdup( filename );
+  inh->refcount = 1;
+  inh->callback = NULL;
+  inh->user_data = NULL;
+  inh->flags = flags;
+  inh->state = inh_state_initial;
+  inh->parent = NULL;
+
+  inotify_debug( "New handle %p on %s", inh, inh->filename );
+
+  return inh;
+}
+
+void
+inotify_handle_ref( INotifyHandle *inh )
+{
+  inotify_debug( "Ref handle %p on %s", inh, inh->filename );
+
+  g_atomic_int_inc( &inh->refcount );
+}
+
+void
+inotify_handle_unref( INotifyHandle *inh )
+{
+  inotify_debug( "Deref handle %p on %s", inh, inh->filename );
+
+  if( g_atomic_int_dec_and_test( &inh->refcount ) )
+  {
+    inotify_debug( "  and destroy" );
+    g_free( inh->filename );
+    g_free( inh );
+  }
+}
+
+gint32
+inotify_handle_get_wd( INotifyHandle *inh )
+{
+  return inh->wd;
+}
+
+void
+inotify_handle_set_wd( INotifyHandle *inh, gint32 wd )
+{
+  inh->wd = wd;
+}
+
+guint32
+inotify_handle_get_mask( INotifyHandle *inh )
+{
+  return inh->mask;
+}
+
+const char *
+inotify_handle_get_filename( INotifyHandle *inh )
+{
+  return inh->filename;
+}
+
+const char *
+inotify_handle_get_basename( INotifyHandle *inh )
+{
+  const char *bn;
+
+  bn = rindex( inh->filename, '/' );
+
+  if( bn == NULL )
+    return NULL;
+
+  if( bn == inh->filename )
+    return bn;
+
+  return bn + 1;
+}
+
+void
+inotify_handle_set_parent( INotifyHandle *inh, INotifyHandle *parent )
+{
+  inh->parent = parent;
+}
+
+INotifyHandle *
+inotify_handle_get_parent( INotifyHandle *inh )
+{
+  return inh->parent;
+}
+
+void
+inotify_handle_set_callback( INotifyHandle *inh, INotifyCallback callback,
+			     gpointer user_data )
+{
+  inh->callback = callback;
+  inh->user_data = user_data;
+}
+
+static guint32
+inotify_handle_event_applicable( INotifyHandle *inh, guint32 type,
+				 const char *filename )
+{
+  enum inh_state state = inh->state;
+
+  inotify_debug( "Juding applicability of event %x on %p/%s",
+		 type, inh, filename );
+
+  if( type & IN_SYNTHETIC )
+  {
+    inotify_debug( "  event is synthetic" );
+
+    /* Synthetic events should not be delivered except as the first event. */
+    if( state != inh_state_initial )
+    {
+      inotify_debug( "	dropping synthetic event on non-initial state" );
+      return 0;
+    }
+
+    /* Synthetic create event... */
+    if( type & IN_CREATE )
+    {
+      inotify_debug( "	synthetic create event" );
+
+      inh->state = inh_state_created;
+
+      /* Only deliver if the user wants to receive synthetic create events. */
+      if( inh->flags & IN_FLAG_SYNTH_CREATE )
+      {
+	inotify_debug( "  user wants it -- delivering" );
+	return IN_CREATE | (IN_SYNTHETIC & inh->mask);
+      }
+      else
+      {
+	inotify_debug( "  user doesn't want it -- dropping" );
+	return 0;
+      }
+    }
+
+    if( type & IN_DELETE )
+    {
+      inotify_debug( "	synthetic delete event" );
+      inh->state = inh_state_deleted;
+
+      /* Only deliver if the user wants to receive synthetic delete events. */
+      if( inh->flags & IN_FLAG_SYNTH_DELETE )
+      {
+	inotify_debug( "  user wants it -- delivering" );
+	return IN_DELETE | (IN_SYNTHETIC & inh->mask);
+      }
+      else
+      {
+	inotify_debug( "  user doesn't want it -- dropping" );
+	return 0;
+      }
+    }
+
+    inotify_warn( "Invalid synthetic event" );
+    return 0;
+  }
+
+  /* Non-synthetic events. */
+  type &= inh->mask;
+
+  /* Event occured on a file in a directory -- not the object itself. */
+  if( filename != NULL )
+  {
+    if( state != inh_state_created )
+      inotify_warn( "Received directory event on non-created inh" );
+
+    inotify_debug( "  event is on file -- passing through" );
+
+    return type;
+  }
+
+  /* Else, non-synthetic event directly on the watched object. */
+  switch( type )
+  {
+    case IN_CREATE:
+    case IN_MOVED_TO:
+      inh->state = inh_state_created;
+
+      if( state == inh_state_created )
+	inotify_warn( "Create on already-existing file" );
+
+      inotify_debug( "	event is create-type.  passing through" );
+
+      return type;
+
+    case IN_DELETE:
+    case IN_DELETE_SELF:
+    case IN_MOVED_FROM:
+      inh->state = inh_state_deleted;
+
+      if( state == inh_state_deleted )
+      {
+	inotify_debug( "  dropping remove event on already-removed file" );
+	return 0;
+      }
+
+      inotify_debug( "	event is delete-type.  passing through" );
+
+      return type;
+
+    default:
+      if( state != inh_state_created )
+	inotify_warn( "Received direct event on non-created inh" );
+
+      inotify_debug( "	event is other type.  passing through" );
+
+      return type;
+  }
+}
+
+void
+inotify_handle_invoke_callback( INotifyHandle *inh, const char *filename,
+				guint32 type, guint32 cookie )
+{
+  type = inotify_handle_event_applicable( inh, type, filename );
+
+  if( type != 0 && inh->callback )
+    inh->callback( inh, inh->filename, filename, type,
+		   cookie, inh->user_data );
+}

Added: trunk/src/libinotify/inotify-handle.h
==============================================================================
--- (empty file)
+++ trunk/src/libinotify/inotify-handle.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,57 @@
+/*
+ * inotify-handle.h - a structure to represent a client-side watch
+ * Copyright  2005 Ryan Lortie <desrt desrt ca>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of version 2.1 of the GNU Lesser General
+ *   Public 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
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110
+ *
+ *   $Id: inotify-handle.h 22 2005-09-16 23:16:11Z ryanl $
+ */
+
+#ifndef _libinotify_inotify_handle_h_
+#define _libinotify_inotify_handle_h_
+
+#define IN_FLAG_NONE			0x00000000
+#define IN_FLAG_FILE_BASED		0x00000001
+#define IN_FLAG_SYNTH_CREATE		0x00000002
+#define IN_FLAG_SYNTH_DELETE		0x00000004
+
+#define IN_SYNTHETIC			0x00001000
+
+#include <glib.h>
+
+typedef struct _INotifyHandle INotifyHandle;
+typedef void (*INotifyCallback)( INotifyHandle *inh,
+				 const char *monitor_name,
+				 const char *filename,
+				 guint32 event_type,
+				 guint32 cookie,
+				 gpointer user_data );
+
+INotifyHandle *inotify_handle_new( const char *filename, guint32 mask,
+				   unsigned long flags );
+void inotify_handle_ref( INotifyHandle *inh );
+void inotify_handle_unref( INotifyHandle *inh );
+gint32 inotify_handle_get_wd( INotifyHandle *inh );
+void inotify_handle_set_wd( INotifyHandle *inh, gint32 wd );
+guint32 inotify_handle_get_mask( INotifyHandle *inh );
+const char *inotify_handle_get_filename( INotifyHandle *inh );
+const char *inotify_handle_get_basename( INotifyHandle *inh );
+void inotify_handle_set_parent( INotifyHandle *inh, INotifyHandle *parent );
+INotifyHandle *inotify_handle_get_parent( INotifyHandle *inh );
+void inotify_handle_set_callback( INotifyHandle *inh, INotifyCallback callback,
+				  gpointer user_data );
+void inotify_handle_invoke_callback( INotifyHandle *inh, const char *filename,
+				     guint32 event_type, guint32 cookie );
+
+#endif /* _libinotify_inotify_handle_h_ */

Added: trunk/src/libinotify/inotify-listhash.c
==============================================================================
--- (empty file)
+++ trunk/src/libinotify/inotify-listhash.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,133 @@
+/*
+ * inotify-listhash.c - a structure to map wd's to client-side watches
+ * Copyright  2005 Ryan Lortie <desrt desrt ca>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of version 2.1 of the GNU Lesser General
+ *   Public 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
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110
+ *
+ *   $Id: inotify-listhash.c 22 2005-09-16 23:16:11Z ryanl $
+ */
+
+#include <glib.h>
+
+#include "inotify-handle.h"
+
+#include "inotify-listhash.h"
+
+static GHashTable *inotify_wd_table;
+
+GSList *
+inotify_listhash_get( gint32 wd )
+{
+  GSList *list;
+
+  list = g_hash_table_lookup( inotify_wd_table, (gpointer) wd );
+
+  return list;
+}
+
+int
+inotify_listhash_remove( INotifyHandle *inh )
+{
+  GSList *list;
+  gint32 wd;
+
+  wd = inotify_handle_get_wd( inh );
+
+  list = g_hash_table_lookup( inotify_wd_table, (gpointer) wd );
+
+  if( list == NULL )
+    return -1;
+
+  list = g_slist_remove( list, inh );
+  inotify_handle_unref( inh );
+
+  if( list != NULL )
+    g_hash_table_replace( inotify_wd_table, (gpointer) wd, list );
+  else
+    g_hash_table_remove( inotify_wd_table, (gpointer) wd );
+
+  return g_slist_length( list );
+}
+
+void
+inotify_listhash_append( INotifyHandle *inh, gint32 wd )
+{
+  GSList *list;
+
+  inotify_handle_ref( inh );
+  inotify_handle_set_wd( inh, wd );
+
+  list = g_hash_table_lookup( inotify_wd_table, (gpointer) wd );
+  list = g_slist_append( list, inh );
+  g_hash_table_replace( inotify_wd_table, (gpointer) wd, list );
+}
+
+int
+inotify_listhash_ignore( gint32 wd )
+{
+  GSList *link, *next;
+
+  link = g_hash_table_lookup( inotify_wd_table, (gpointer) wd );
+  g_hash_table_remove( inotify_wd_table, (gpointer) wd );
+
+  if( link == NULL )
+    return -1;
+
+  for( ; link; link = next )
+  {
+    next = link->next;
+
+    inotify_handle_unref( link->data );
+    g_slist_free_1( link );
+  }
+
+  return 0;
+}
+
+int
+inotify_listhash_length( gint32 wd )
+{
+  GSList *list;
+
+  list = g_hash_table_lookup( inotify_wd_table, (gpointer) wd );
+
+  return g_slist_length( list );
+}
+
+guint32
+inotify_listhash_get_mask( gint32 wd )
+{
+  GSList *list;
+  guint32 mask;
+
+  list = g_hash_table_lookup( inotify_wd_table, (gpointer) wd );
+
+  for( mask = 0; list; list = list->next )
+    mask |= inotify_handle_get_mask( list->data );
+
+  return mask;
+}
+
+void
+inotify_listhash_initialise( void )
+{
+  inotify_wd_table = g_hash_table_new( NULL, NULL );
+}
+
+void
+inotify_listhash_destroy( void )
+{
+  g_hash_table_destroy( inotify_wd_table );
+}
+

Added: trunk/src/libinotify/inotify-listhash.h
==============================================================================
--- (empty file)
+++ trunk/src/libinotify/inotify-listhash.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,35 @@
+/*
+ * inotify-listhash.h - a structure to map wd's to client-side watches
+ * Copyright  2005 Ryan Lortie <desrt desrt ca>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of version 2.1 of the GNU Lesser General
+ *   Public 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
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110
+ *
+ *   $Id: inotify-listhash.h 17 2005-09-15 01:22:30Z ryanl $
+ */
+
+#ifndef _libinotify_inotify_listhash_h_
+#define _libinotify_inotify_listhash_h_
+
+#include "inotify-handle.h"
+
+GSList *inotify_listhash_get( gint32 wd );
+int inotify_listhash_remove( INotifyHandle *inh );
+void inotify_listhash_append( INotifyHandle *inh, gint32 wd );
+int inotify_listhash_ignore( gint32 wd );
+int inotify_listhash_length( gint32 wd );
+guint32 inotify_listhash_get_mask( gint32 wd );
+void inotify_listhash_initialise( void );
+void inotify_listhash_destroy( void );
+
+#endif /* _libinotify_inotify_lasthash_h_ */

Added: trunk/src/libinotify/inotify-log.h
==============================================================================
--- (empty file)
+++ trunk/src/libinotify/inotify-log.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,52 @@
+/*
+ * inotify-log.h - internal libinotify logging/debugging functions
+ * Copyright  2005 Ryan Lortie <desrt desrt ca>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of version 2.1 of the GNU Lesser General
+ *   Public 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
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110
+ *
+ *   $Id: inotify-log.h 17 2005-09-15 01:22:30Z ryanl $
+ */
+
+#ifndef _libinotify_inotify_log_h_
+#define _libinotify_inotify_log_h_
+
+#include <stdlib.h>
+#include <glib.h>
+
+#include "config.h"
+
+#ifdef LIBINOTIFY_DEBUG
+# define inotify_debug(x, ...) g_log("libinotify", G_LOG_LEVEL_DEBUG, \
+				     x, ## __VA_ARGS__)
+#else
+# define inotify_debug(x, ...) 0
+#endif
+
+#define inotify_warn(x, ...) g_log("libinotify", G_LOG_LEVEL_WARNING, \
+				   x, ## __VA_ARGS__)
+
+#define inotify_fatal(x, ...) g_log("libinotify", G_LOG_LEVEL_ERROR, \
+				    x, ## __VA_ARGS__)
+
+static inline void
+inotify_debug_initialise()
+{
+#ifdef LIBINOTIFY_DEBUG
+  if( getenv( "DEBUG_LIBINOTIFY" ) == NULL )
+    g_log_set_handler( "libinotify", G_LOG_LEVEL_DEBUG,
+		       (GLogFunc) strlen, NULL );
+#endif
+}
+
+#endif /* _libinotify_inotify_log_h_ */

Added: trunk/src/libinotify/inotify-monitor.c
==============================================================================
--- (empty file)
+++ trunk/src/libinotify/inotify-monitor.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,390 @@
+/*
+ * inotify-monitor.c - the primary interface for adding/removing watches
+ * Copyright  2005 Ryan Lortie <desrt desrt ca>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of version 2.1 of the GNU Lesser General
+ *   Public 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
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110
+ *
+ *   $Id: inotify-monitor.c 26 2005-12-01 19:11:01Z ryanl $
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <linux/inotify.h>
+
+#include "linux-inotify-syscalls.h"
+
+#include "inotify-handle.h"
+#include "inotify-listhash.h"
+#include "inotify-log.h"
+
+#include "inotify-monitor.h"
+
+GStaticRecMutex inotify_monitor_lock = G_STATIC_REC_MUTEX_INIT;
+static int inotify_monitor_fd = -1;
+
+static void
+process_one_event( struct inotify_event *ine )
+{
+  const char *filename;
+  GSList *list;
+
+  if( ine->len )
+    filename = ine->name;
+  else
+    filename = NULL;
+
+  inotify_debug( "Got one event" );
+
+  for( list = inotify_listhash_get( ine->wd ); list; list = list->next )
+  {
+    inotify_debug( "  dispatch to %p", list->data );
+    inotify_handle_invoke_callback( list->data, filename,
+				    ine->mask, ine->cookie );
+  }
+
+  if( ine->mask & IN_IGNORED )
+    inotify_listhash_ignore( ine->wd );
+}
+
+static gboolean
+inotify_watch_func( GIOChannel *source, GIOCondition condition, gpointer data )
+{
+  struct inotify_event ine[20];
+  int size, namesize;
+  int fd;
+
+  g_static_rec_mutex_lock( &inotify_monitor_lock );
+
+  fd = g_io_channel_unix_get_fd( source );
+
+  while( (size = read( fd, ine, sizeof ine )) >= 0 )
+  {
+    inotify_debug( "Original size %d", size );
+    size /= sizeof *ine;
+
+    inotify_debug( "Got size %d", size );
+
+    while( size > 0 )
+    {
+      /* Division, rounding up. */
+      namesize = (ine->len + sizeof *ine - 1) / sizeof *ine + 1;
+
+      if( namesize > size )
+      {
+	// XXX might be false if lots of events stack up
+	inotify_fatal( "namesize > size!" );
+      }
+
+      size -= namesize;
+
+      process_one_event( ine );
+      memmove( ine, &ine[namesize], sizeof *ine * size );
+    }
+  }
+
+  g_static_rec_mutex_unlock( &inotify_monitor_lock );
+
+  return TRUE;
+}
+
+static int
+inotify_monitor_initialise( void )
+{
+  GIOChannel *gio;
+
+  if( inotify_monitor_fd != -1 )
+    return 0;
+
+  inotify_monitor_fd = inotify_init();
+
+  if( inotify_monitor_fd < 0 )
+    return -1;
+
+  inotify_listhash_initialise();
+
+  gio = g_io_channel_unix_new( inotify_monitor_fd );
+  g_io_add_watch( gio, G_IO_IN, inotify_watch_func, NULL );
+  g_io_channel_set_flags( gio, G_IO_FLAG_NONBLOCK, NULL );
+
+  inotify_debug_initialise();
+
+  return 0;
+}
+
+static int
+inotify_monitor_add_raw( INotifyHandle *inh )
+{
+  const char *filename = inotify_handle_get_filename( inh );
+  guint32 mask = inotify_handle_get_mask( inh );
+#ifndef IN_MASK_ADD
+  guint32 needmask;
+  int wd2;
+#endif
+  int wd;
+
+#ifdef IN_MASK_ADD
+  wd = inotify_add_watch( inotify_monitor_fd, filename, mask | IN_MASK_ADD );
+#else
+  wd = inotify_add_watch( inotify_monitor_fd, filename, mask );
+
+  if( wd < 0 )
+    return -1;
+
+  needmask = mask | inotify_listhash_get_mask( wd );
+
+  if( needmask != mask )
+  {
+    /* This can only happen if we've already been watching the inode and we
+     * just requested another watch on it with fewer events.  We now have
+     * to change the watch mask to restore the events we just blew away.
+     */
+
+    /* *Very* slight risk of race condition here if 'filename' has
+     * disappeared or changed inodes since last inotify_add_watch call.
+     */
+    wd2 = inotify_add_watch( inotify_monitor_fd, filename, needmask );
+
+    /* If this happens, we're in trouble no matter how you look at it since
+     * we have no way of giving the proper mask to the inode.  Even worse,
+     * we might have just screwed up the mask on another inode.  Find out.
+     */
+    if( wd2 != wd )
+      inotify_warn( "race condition in inotify_monitor_add! (%d vs %d)",
+								 wd, wd2 );
+
+    if( wd2 < 0 )
+    {
+      /* File has since disappeared -- nothing we can do! */
+    }
+    else if( wd2 != wd )
+    {
+      /* File has changed inode.  Even worse! */
+
+      if( inotify_listhash_length( wd2 ) == 0 )
+      {
+	/* We're not supposed to be watching this inode. */
+	inotify_rm_watch( inotify_monitor_fd, wd2 );
+      }
+      else
+      {
+	/* If we did hit an inode we're already watching then we just
+	 * modified its mask.  Ow.  We could go hunting with the list of
+	 * filenames that we have in the listhash in hopes that one of them
+	 * still references the inode that we're looking for but this is
+	 * such a rare case and going hunting is likely to cause further
+	 * errors anyway...
+	 */
+      }
+    }
+
+    /* We've either fixed it or can't fix it.  Proceed... */
+  }
+#endif
+
+  if( wd < 0 )
+    return -1;
+
+  inotify_listhash_append( inh, wd );
+
+  return 0;
+}
+
+static void
+inotify_monitor_remove_raw( INotifyHandle *inh )
+{
+  if( inotify_listhash_remove( inh ) == 0 )
+  {
+    /* No watches left, so cancel the watch with the kernel.  This Will
+     * generate an IN_IGNORED event which will free the listhash stuff.
+     */
+    inotify_rm_watch( inotify_monitor_fd, inotify_handle_get_wd( inh ) );
+  }
+
+  /* We currently have no way of safely reducing the event mask on an
+   * inode that we're watching.  As such, just leave it alone.	This means
+   * that we'll receive extra events (which we'll filter out), but at least
+   * we won't potentially put ourselves in an inconsistent state.
+   */
+}
+
+static void
+inotify_internal_callback( INotifyHandle *inh, const char *monitor_name,
+			   const char *filename, guint32 event_type,
+			   guint32 cookie, gpointer user_data )
+{
+  INotifyHandle *child = user_data;
+  int result;
+
+  inotify_debug( "Got event for %s:%x while watching for %s on %s",
+		 filename, event_type, inotify_handle_get_basename( child ),
+		 monitor_name );
+
+  event_type &= ~IN_ISDIR;
+
+  if( !filename )
+  {
+    switch( event_type & ~IN_SYNTHETIC )
+    {
+      case IN_CREATE:
+      case IN_MOVED_TO:
+	result = inotify_monitor_add_raw( child );
+
+	/* If child exists... */
+	if( result == 0 )
+	  inotify_handle_invoke_callback( child, NULL, event_type, cookie );
+
+	break;
+
+      case IN_DELETE:
+      case IN_DELETE_SELF:
+      case IN_MOVE_SELF:
+      case IN_MOVED_FROM:
+	/* Parent just disappeared.  Report that we've also been deleted. */
+	inotify_handle_invoke_callback( child, NULL, event_type, cookie );
+
+	/* Then unregister our watch with the kernel. */
+	inotify_monitor_remove_raw( child );
+
+	break;
+    }
+
+    return;
+  }
+
+  if( strcmp( inotify_handle_get_basename( child ), filename ) )
+    return;
+
+  switch( event_type )
+  {
+    case IN_CREATE:
+    case IN_MOVED_TO:
+      result = inotify_monitor_add_raw( child );
+
+      inotify_handle_invoke_callback( child, NULL, event_type, cookie );
+
+      if( result != 0 )
+	inotify_handle_invoke_callback( child, NULL, IN_DELETE, cookie );
+
+      break;
+
+    case IN_DELETE:
+    case IN_MOVED_FROM:
+      /* We just disappeared.  Report that we've been deleted.	We must
+       * send the event manually since the remove_raw might cause the
+       * event not to be delivered normally.
+       */
+      inotify_handle_invoke_callback( child, NULL, event_type, cookie );
+
+      /* Then unregister our watch with the kernel. */
+      inotify_monitor_remove_raw( child );
+      break;
+  }
+}
+
+INotifyHandle *
+inotify_monitor_add( const char *filename, guint32 mask, unsigned long flags,
+		     INotifyCallback callback, gpointer user_data )
+{
+  INotifyHandle *pinh, *inh;
+  int result;
+
+  g_static_rec_mutex_lock( &inotify_monitor_lock );
+
+  if( inotify_monitor_initialise() )
+    return NULL;
+
+  inh = inotify_handle_new( filename, mask, flags );
+  inotify_handle_set_callback( inh, callback, user_data );
+
+  if( (flags & IN_FLAG_FILE_BASED) == 0 || !strcmp( filename, "/" ) )
+  {
+    inotify_debug( "%s is raw", filename );
+    result = inotify_monitor_add_raw( inh );
+
+    if( result == 0 )
+      inotify_handle_invoke_callback( inh, NULL, IN_CREATE | IN_SYNTHETIC, -1 );
+  }
+  else
+  {
+    const char *parent = g_path_get_dirname( filename );
+    unsigned long flags;
+    guint32 mask;
+
+    flags = IN_FLAG_FILE_BASED | IN_FLAG_SYNTH_CREATE;
+    mask = IN_MOVED_FROM | IN_MOVED_TO | IN_CREATE | IN_DELETE |
+	   IN_DELETE_SELF | IN_MOVE_SELF | IN_SYNTHETIC;
+
+    inotify_debug( "Adding internal callback %p for %p(%s)",
+		   inotify_internal_callback, inh, parent );
+
+    pinh = inotify_monitor_add( parent, mask, flags,
+				inotify_internal_callback, inh );
+
+    inotify_handle_set_parent( inh, pinh );
+
+    /* This will be filtered out if it shouldn't be delivered. */
+    inotify_handle_invoke_callback( inh, NULL, IN_DELETE | IN_SYNTHETIC, -1 );
+
+    result = 0;
+  }
+
+  if( result )
+  {
+    inotify_handle_unref( inh );
+    inh = NULL;
+  }
+
+  g_static_rec_mutex_unlock( &inotify_monitor_lock );
+
+  return inh;
+}
+
+void
+inotify_monitor_remove( INotifyHandle *inh )
+{
+  INotifyHandle *parent;
+
+  g_static_rec_mutex_lock( &inotify_monitor_lock );
+
+  if( inotify_monitor_initialise() )
+    goto error;
+
+  if( inh == NULL )
+    goto error;
+
+  if( (parent = inotify_handle_get_parent( inh )) != NULL )
+    inotify_monitor_remove( parent );
+
+  inotify_monitor_remove_raw( inh );
+
+  inotify_handle_unref( inh );
+
+error:
+  g_static_rec_mutex_unlock( &inotify_monitor_lock );
+}
+
+gboolean
+inotify_is_available( void )
+{
+  int result;
+
+  g_static_rec_mutex_lock( &inotify_monitor_lock );
+
+  result = inotify_monitor_initialise();
+
+  g_static_rec_mutex_unlock( &inotify_monitor_lock );
+
+  return (result == 0);
+}

Added: trunk/src/libinotify/inotify-monitor.h
==============================================================================
--- (empty file)
+++ trunk/src/libinotify/inotify-monitor.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,34 @@
+/*
+ * inotify-monitor.h - the primary interface for adding/removing watches
+ * Copyright  2005 Ryan Lortie <desrt desrt ca>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of version 2.1 of the GNU Lesser General
+ *   Public 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
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110
+ *
+ *   $Id: inotify-monitor.h 22 2005-09-16 23:16:11Z ryanl $
+ */
+
+#ifndef _libinotify_inotify_monitor_h_
+#define _libinotify_inotify_monitor_h_
+
+#include "inotify-handle.h"
+
+INotifyHandle *inotify_monitor_add( const char *filename,
+				    guint32 mask,
+				    unsigned long flags,
+				    INotifyCallback callback,
+				    gpointer user_data );
+void inotify_monitor_remove( INotifyHandle *inh );
+gboolean inotify_is_available( void );
+
+#endif /* _libinotify_inotify_monitor_h_ */

Added: trunk/src/libinotify/libinotify.h
==============================================================================
--- (empty file)
+++ trunk/src/libinotify/libinotify.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,27 @@
+/*
+ * inotify.h - public interface to libinotify
+ * Copyright  2005 Ryan Lortie <desrt desrt ca>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of version 2.1 of the GNU Lesser General
+ *   Public 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
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110
+ *
+ *   $Id: libinotify.h 12 2005-09-04 15:55:51Z ryanl $
+ */
+
+#ifndef _libinotify_libinotify_h_
+#define _libinotify_libinotify_h_
+
+#include <libinotify/inotify-handle.h>
+#include <libinotify/inotify-monitor.h>
+
+#endif /* _libinotify_libinotify_h_ */

Added: trunk/src/libinotify/linux-inotify-syscalls.h
==============================================================================
--- (empty file)
+++ trunk/src/libinotify/linux-inotify-syscalls.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,90 @@
+/*
+ * linux-inotify-syscalls.h - temporary shim for syscalls
+ * Copyright  2005 Ryan Lortie <desrt desrt ca>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of version 2.1 of the GNU Lesser General
+ *   Public 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
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110
+ *
+ *   $Id: linux-inotify-syscalls.h 12 2005-09-04 15:55:51Z ryanl $
+ */
+
+#ifndef _linux_inotify_syscalls_h_
+#define _linux_inotify_syscalls_h_
+
+#include <asm/types.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#if defined(__i386__)
+# define __NR_inotify_init	291
+# define __NR_inotify_add_watch	292
+# define __NR_inotify_rm_watch	293
+#elif defined(__x86_64__)
+# define __NR_inotify_init	253
+# define __NR_inotify_add_watch	254
+# define __NR_inotify_rm_watch	255
+#elif defined(__alpha__)
+# define __NR_inotify_init	444
+# define __NR_inotify_add_watch 445
+# define __NR_inotify_rm_watch	446
+#elif defined(__ppc__) || defined(__powerpc__) || defined(__powerpc64__)
+# define __NR_inotify_init	275
+# define __NR_inotify_add_watch 276
+# define __NR_inotify_rm_watch	277
+#elif defined(__sparc__) || defined (__sparc64__)
+# define __NR_inotify_init	151
+# define __NR_inotify_add_watch 152
+# define __NR_inotify_rm_watch	156
+#elif defined (__ia64__)
+# define __NR_inotify_init  1277
+# define __NR_inotify_add_watch 1278
+# define __NR_inotify_rm_watch	1279
+#elif defined (__s390__) || defined (__s390x__)
+# define __NR_inotify_init  284
+# define __NR_inotify_add_watch 285
+# define __NR_inotify_rm_watch	286
+#elif defined (__arm__)
+# define __NR_inotify_init  316
+# define __NR_inotify_add_watch 317
+# define __NR_inotify_rm_watch	318
+#elif defined (__SH4__)
+# define __NR_inotify_init  290
+# define __NR_inotify_add_watch 291
+# define __NR_inotify_rm_watch	292
+#elif defined (__SH5__)
+# define __NR_inotify_init  318
+# define __NR_inotify_add_watch 319
+# define __NR_inotify_rm_watch	320
+#else
+# error "Unsupported architecture"
+#endif
+
+static inline int
+inotify_init (void)
+{
+  return syscall( __NR_inotify_init );
+}
+
+static inline int
+inotify_add_watch( int fd, const char *name, guint32 mask )
+{
+  return syscall( __NR_inotify_add_watch, fd, name, mask );
+}
+
+static inline int
+inotify_rm_watch( int fd, gint32 wd )
+{
+  return syscall( __NR_inotify_rm_watch, fd, wd );
+}
+
+#endif /* _linux_inotify_syscalls_h_ */

Added: trunk/src/libstemmer/Copyright
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/Copyright	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,12 @@
+All software in and under this folder is licensed as follows:
+
+Copyright (c) 2001, Dr Martin Porter
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+    * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Added: trunk/src/libstemmer/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,51 @@
+include $(top_srcdir)/Makefile.decl
+
+INCLUDES =						\
+	-DSHAREDIR=\""$(datadir)"\"			\
+	-DG_LOG_DOMAIN=\"Tracker\"			\
+	-I$(top_srcdir)/src
+
+libstemmerdir = $(libdir)/tracker
+libstemmer_LTLIBRARIES = libstemmer.la
+
+libstemmer_la_SOURCES = 		\
+	libstemmer.c 			\
+	api.c 				\
+	utilities.c 			\
+	stem_UTF_8_danish.c 		\
+	stem_UTF_8_dutch.c 		\
+	stem_UTF_8_english.c 		\
+	stem_UTF_8_finnish.c 		\
+	stem_UTF_8_french.c 		\
+	stem_UTF_8_german.c 		\
+	stem_UTF_8_hungarian.c 		\
+	stem_UTF_8_italian.c 		\
+	stem_UTF_8_norwegian.c 		\
+	stem_UTF_8_porter.c 		\
+	stem_UTF_8_portuguese.c 	\
+	stem_UTF_8_russian.c 		\
+	stem_UTF_8_spanish.c 		\
+	stem_UTF_8_swedish.c
+
+noinst_HEADERS =			\
+	api.h				\
+	header.h			\
+	libstemmer.h			\
+	modules.h			\
+	stem_UTF_8_danish.h		\
+	stem_UTF_8_dutch.h		\
+	stem_UTF_8_english.h		\
+	stem_UTF_8_finnish.h		\
+	stem_UTF_8_french.h		\
+	stem_UTF_8_german.h		\
+	stem_UTF_8_hungarian.h		\
+	stem_UTF_8_italian.h		\
+	stem_UTF_8_norwegian.h		\
+	stem_UTF_8_porter.h		\
+	stem_UTF_8_portuguese.h		\
+	stem_UTF_8_russian.h		\
+	stem_UTF_8_spanish.h		\
+	stem_UTF_8_swedish.h
+
+libstemmer_la_LDFLAGS = -version-info 0:0:0
+

Added: trunk/src/libstemmer/api.c
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/api.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2001, Dr Martin Porter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+#include <stdlib.h> /* for calloc, free */
+#include "header.h"
+
+extern struct SN_env * SN_create_env(int S_size, int I_size, int B_size)
+{
+    struct SN_env * z = (struct SN_env *) calloc(1, sizeof(struct SN_env));
+    if (z == NULL) return NULL;
+    z->p = create_s();
+    if (z->p == NULL) goto error;
+    if (S_size)
+    {
+	int i;
+	z->S = (symbol * *) calloc(S_size, sizeof(symbol *));
+	if (z->S == NULL) goto error;
+
+	for (i = 0; i < S_size; i++)
+	{
+	    z->S[i] = create_s();
+	    if (z->S[i] == NULL) goto error;
+	}
+	z->S_size = S_size;
+    }
+
+    if (I_size)
+    {
+	z->I = (int *) calloc(I_size, sizeof(int));
+	if (z->I == NULL) goto error;
+	z->I_size = I_size;
+    }
+
+    if (B_size)
+    {
+	z->B = (symbol *) calloc(B_size, sizeof(symbol));
+	if (z->B == NULL) goto error;
+	z->B_size = B_size;
+    }
+
+    return z;
+error:
+    SN_close_env(z);
+    return NULL;
+}
+
+extern void SN_close_env(struct SN_env * z)
+{
+    if (z == NULL) return;
+    if (z->S_size)
+    {
+	int i;
+	for (i = 0; i < z->S_size; i++)
+	{
+	    lose_s(z->S[i]);
+	}
+	free(z->S);
+    }
+    if (z->I_size) free(z->I);
+    if (z->B_size) free(z->B);
+    if (z->p) lose_s(z->p);
+    free(z);
+}
+
+extern int SN_set_current(struct SN_env * z, int size, const symbol * s)
+{
+    int err = replace_s(z, 0, z->l, size, s, NULL);
+    z->c = 0;
+    return err;
+}
+

Added: trunk/src/libstemmer/api.h
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/api.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2001, Dr Martin Porter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+typedef unsigned char symbol;
+
+/* Or replace 'char' above with 'short' for 16 bit characters.
+
+   More precisely, replace 'char' with whatever type guarantees the
+   character width you need. Note however that sizeof(symbol) should divide
+   HEAD, defined in header.h as 2*sizeof(int), without remainder, otherwise
+   there is an alignment problem. In the unlikely event of a problem here,
+   consult Martin Porter.
+
+*/
+
+struct SN_env {
+    symbol * p;
+    int c; int a; int l; int lb; int bra; int ket;
+    int S_size; int I_size; int B_size;
+    symbol * * S;
+    int * I;
+    symbol * B;
+};
+
+extern struct SN_env * SN_create_env(int S_size, int I_size, int B_size);
+extern void SN_close_env(struct SN_env * z);
+
+extern int SN_set_current(struct SN_env * z, int size, const symbol * s);
+

Added: trunk/src/libstemmer/header.h
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/header.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2001, Dr Martin Porter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+#include <limits.h>
+
+#include "api.h"
+
+#define MAXINT INT_MAX
+#define MININT INT_MIN
+
+#define HEAD 2*sizeof(int)
+
+#define SIZE(p)        ((int *)(p))[-1]
+#define SET_SIZE(p, n) ((int *)(p))[-1] = n
+#define CAPACITY(p)    ((int *)(p))[-2]
+
+struct among
+{   int s_size;     /* number of chars in string */
+    symbol * s;       /* search string */
+    int substring_i;/* index to longest matching substring */
+    int result;     /* result of the lookup */
+    int (* function)(struct SN_env *);
+};
+
+extern symbol * create_s(void);
+extern void lose_s(symbol * p);
+
+extern int skip_utf8(const symbol * p, int c, int lb, int l, int n);
+
+extern int in_grouping_U(struct SN_env * z, unsigned char * s, int min, int max);
+extern int in_grouping_b_U(struct SN_env * z, unsigned char * s, int min, int max);
+extern int out_grouping_U(struct SN_env * z, unsigned char * s, int min, int max);
+extern int out_grouping_b_U(struct SN_env * z, unsigned char * s, int min, int max);
+
+extern int in_grouping(struct SN_env * z, unsigned char * s, int min, int max);
+extern int in_grouping_b(struct SN_env * z, unsigned char * s, int min, int max);
+extern int out_grouping(struct SN_env * z, unsigned char * s, int min, int max);
+extern int out_grouping_b(struct SN_env * z, unsigned char * s, int min, int max);
+
+extern int eq_s(struct SN_env * z, int s_size, symbol * s);
+extern int eq_s_b(struct SN_env * z, int s_size, symbol * s);
+extern int eq_v(struct SN_env * z, symbol * p);
+extern int eq_v_b(struct SN_env * z, symbol * p);
+
+extern int find_among(struct SN_env * z, struct among * v, int v_size);
+extern int find_among_b(struct SN_env * z, struct among * v, int v_size);
+
+extern int replace_s(struct SN_env * z, int c_bra, int c_ket, int s_size, const symbol * s, int * adjustment);
+extern int slice_from_s(struct SN_env * z, int s_size, symbol * s);
+extern int slice_from_v(struct SN_env * z, symbol * p);
+extern int slice_del(struct SN_env * z);
+
+extern int insert_s(struct SN_env * z, int bra, int ket, int s_size, symbol * s);
+extern int insert_v(struct SN_env * z, int bra, int ket, symbol * p);
+
+extern symbol * slice_to(struct SN_env * z, symbol * p);
+extern symbol * assign_to(struct SN_env * z, symbol * p);
+
+extern void debug(struct SN_env * z, int number, int line_count);
+

Added: trunk/src/libstemmer/libstemmer.c
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/libstemmer.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2001, Dr Martin Porter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "libstemmer.h"
+#include "api.h"
+#include "modules.h"
+
+struct sb_stemmer {
+    struct SN_env * (*create)(void);
+    void (*close)(struct SN_env *);
+    int (*stem)(struct SN_env *);
+
+    struct SN_env * env;
+};
+
+extern const char **
+sb_stemmer_list(void)
+{
+    return algorithm_names;
+}
+
+static stemmer_encoding sb_getenc(const char * charenc)
+{
+    struct stemmer_encoding * encoding;
+    if (charenc == NULL) return ENC_UTF_8;
+    for (encoding = encodings; encoding->name != 0; encoding++) {
+	if (strcmp(encoding->name, charenc) == 0) break;
+    }
+    if (encoding->name == NULL) return ENC_UNKNOWN;
+    return encoding->enc;
+}
+
+extern struct sb_stemmer *
+sb_stemmer_new(const char * algorithm, const char * charenc)
+{
+    stemmer_encoding enc;
+    struct stemmer_modules * module;
+    struct sb_stemmer * stemmer =
+	    (struct sb_stemmer *) malloc(sizeof(struct sb_stemmer));
+    if (stemmer == NULL) return NULL;
+    enc = sb_getenc(charenc);
+    if (enc == ENC_UNKNOWN) return NULL;
+
+    for (module = modules; module->name != 0; module++) {
+	if (strcmp(module->name, algorithm) == 0 && module->enc == enc) break;
+    }
+    if (module->name == NULL) return NULL;
+
+    stemmer->create = module->create;
+    stemmer->close = module->close;
+    stemmer->stem = module->stem;
+
+    stemmer->env = stemmer->create();
+    if (stemmer->env == NULL)
+    {
+	sb_stemmer_delete(stemmer);
+	return NULL;
+    }
+
+    return stemmer;
+}
+
+void
+sb_stemmer_delete(struct sb_stemmer * stemmer)
+{
+    if (stemmer == 0) return;
+    if (stemmer->close == 0) return;
+    stemmer->close(stemmer->env);
+    stemmer->close = 0;
+    free(stemmer);
+}
+
+const sb_symbol *
+sb_stemmer_stem(struct sb_stemmer * stemmer, const sb_symbol * word, int size)
+{
+    int ret;
+    if (SN_set_current(stemmer->env, size, (const symbol *)(word)))
+    {
+	stemmer->env->l = 0;
+	return NULL;
+    }
+    ret = stemmer->stem(stemmer->env);
+    if (ret < 0) return NULL;
+    stemmer->env->p[stemmer->env->l] = 0;
+    return (const sb_symbol *)(stemmer->env->p);
+}
+
+int
+sb_stemmer_length(struct sb_stemmer * stemmer)
+{
+    return stemmer->env->l;
+}

Added: trunk/src/libstemmer/libstemmer.h
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/libstemmer.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2001, Dr Martin Porter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _LIBSTEMMER_H_
+#define _LIBSTEMMER_H_
+
+/* Make header file work when included from C++ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct sb_stemmer;
+typedef unsigned char sb_symbol;
+
+/* FIXME - should be able to get a version number for each stemming
+ * algorithm (which will be incremented each time the output changes). */
+
+/** Returns an array of the names of the available stemming algorithms.
+ *  Note that these are the canonical names - aliases (ie, other names for
+ *  the same algorithm) will not be included in the list.
+ *  The list is terminated with a null pointer.
+ *
+ *  The list must not be modified in any way.
+ */
+const char ** sb_stemmer_list(void);
+
+/** Create a new stemmer object, using the specified algorithm, for the
+ *  specified character encoding.
+ *
+ *  All algorithms will usually be available in UTF-8, but may also be
+ *  available in other character encodings.
+ *
+ *  @param algorithm The algorithm name.  This is either the english
+ *  name of the algorithm, or the 2 or 3 letter ISO 639 codes for the
+ *  language.  Note that case is significant in this parameter - the
+ *  value should be supplied in lower case.
+ *
+ *  @param charenc The character encoding.  NULL may be passed as
+ *  this value, in which case UTF-8 encoding will be assumed. Otherwise,
+ *  the argument may be one of "UTF_8", "ISO_8859_1" (ie, Latin 1),
+ *  "CP850" (ie, MS-DOS Latin 1) or "KOI8_R" (Russian).  Note that
+ *  case is significant in this parameter.
+ *
+ *  @return NULL if the specified algorithm is not recognised, or the
+ *  algorithm is not available for the requested encoding.  Otherwise,
+ *  returns a pointer to a newly created stemmer for the requested algorithm.
+ *  The returned pointer must be deleted by calling sb_stemmer_delete().
+ *
+ *  @note NULL will also be returned if an out of memory error occurs.
+ */
+struct sb_stemmer * sb_stemmer_new(const char * algorithm, const char * charenc);
+
+/** Delete a stemmer object.
+ *
+ *  This frees all resources allocated for the stemmer.  After calling
+ *  this function, the supplied stemmer may no longer be used in any way.
+ *
+ *  It is safe to pass a null pointer to this function - this will have
+ *  no effect.
+ */
+void		    sb_stemmer_delete(struct sb_stemmer * stemmer);
+
+/** Stem a word.
+ *
+ *  The return value is owned by the stemmer - it must not be freed or
+ *  modified, and it will become invalid when the stemmer is called again,
+ *  or if the stemmer is freed.
+ *
+ *  The length of the return value can be obtained using sb_stemmer_length().
+ *
+ *  If an out-of-memory error occurs, this will return NULL.
+ */
+const sb_symbol *   sb_stemmer_stem(struct sb_stemmer * stemmer,
+				    const sb_symbol * word, int size);
+
+/** Get the length of the result of the last stemmed word.
+ *  This should not be called before sb_stemmer_stem() has been called.
+ */
+int		    sb_stemmer_length(struct sb_stemmer * stemmer);
+
+#ifdef __cplusplus
+}
+#endif
+#endif

Added: trunk/src/libstemmer/modules.h
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/modules.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2001, Dr Martin Porter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+/* libstemmer/modules.h: List of stemming modules.
+ *
+ * This file is generated by mkmodules.pl from a list of module names.
+ * Do not edit manually.
+ *
+ * Modules included by this file are: danish, dutch, english, finnish, french,
+ * german, italian, norwegian, porter, portuguese, russian, spanish, swedish
+ */
+
+#include "stem_UTF_8_danish.h"
+#include "stem_UTF_8_dutch.h"
+#include "stem_UTF_8_english.h"
+#include "stem_UTF_8_finnish.h"
+#include "stem_UTF_8_french.h"
+#include "stem_UTF_8_german.h"
+#include "stem_UTF_8_italian.h"
+#include "stem_UTF_8_norwegian.h"
+#include "stem_UTF_8_porter.h"
+#include "stem_UTF_8_portuguese.h"
+#include "stem_UTF_8_russian.h"
+#include "stem_UTF_8_spanish.h"
+#include "stem_UTF_8_swedish.h"
+
+typedef enum {
+  ENC_UNKNOWN,
+  ENC_ISO_8859_1,
+  ENC_KOI8_R,
+  ENC_UTF_8
+} stemmer_encoding;
+
+struct stemmer_encoding {
+  const char * name;
+  stemmer_encoding enc;
+};
+static struct stemmer_encoding encodings[] = {
+  {"ISO_8859_1", ENC_ISO_8859_1},
+  {"KOI8_R", ENC_KOI8_R},
+  {"UTF_8", ENC_UTF_8},
+  {0,0}
+};
+
+struct stemmer_modules {
+  const char * name;
+  stemmer_encoding enc;
+  struct SN_env * (*create)(void);
+  void (*close)(struct SN_env *);
+  int (*stem)(struct SN_env *);
+};
+static struct stemmer_modules modules[] = {
+  {"da", ENC_UTF_8, danish_UTF_8_create_env, danish_UTF_8_close_env, danish_UTF_8_stem},
+  {"dan", ENC_UTF_8, danish_UTF_8_create_env, danish_UTF_8_close_env, danish_UTF_8_stem},
+  {"danish", ENC_UTF_8, danish_UTF_8_create_env, danish_UTF_8_close_env, danish_UTF_8_stem},
+  {"de", ENC_UTF_8, german_UTF_8_create_env, german_UTF_8_close_env, german_UTF_8_stem},
+  {"deu", ENC_UTF_8, german_UTF_8_create_env, german_UTF_8_close_env, german_UTF_8_stem},
+  {"dut", ENC_UTF_8, dutch_UTF_8_create_env, dutch_UTF_8_close_env, dutch_UTF_8_stem},
+  {"dutch", ENC_UTF_8, dutch_UTF_8_create_env, dutch_UTF_8_close_env, dutch_UTF_8_stem},
+  {"en", ENC_UTF_8, english_UTF_8_create_env, english_UTF_8_close_env, english_UTF_8_stem},
+  {"eng", ENC_UTF_8, english_UTF_8_create_env, english_UTF_8_close_env, english_UTF_8_stem},
+  {"english", ENC_UTF_8, english_UTF_8_create_env, english_UTF_8_close_env, english_UTF_8_stem},
+  {"es", ENC_UTF_8, spanish_UTF_8_create_env, spanish_UTF_8_close_env, spanish_UTF_8_stem},
+  {"esl", ENC_UTF_8, spanish_UTF_8_create_env, spanish_UTF_8_close_env, spanish_UTF_8_stem},
+  {"fi", ENC_UTF_8, finnish_UTF_8_create_env, finnish_UTF_8_close_env, finnish_UTF_8_stem},
+  {"fin", ENC_UTF_8, finnish_UTF_8_create_env, finnish_UTF_8_close_env, finnish_UTF_8_stem},
+  {"finnish", ENC_UTF_8, finnish_UTF_8_create_env, finnish_UTF_8_close_env, finnish_UTF_8_stem},
+  {"fr", ENC_UTF_8, french_UTF_8_create_env, french_UTF_8_close_env, french_UTF_8_stem},
+  {"fra", ENC_UTF_8, french_UTF_8_create_env, french_UTF_8_close_env, french_UTF_8_stem},
+  {"fre", ENC_UTF_8, french_UTF_8_create_env, french_UTF_8_close_env, french_UTF_8_stem},
+  {"french", ENC_UTF_8, french_UTF_8_create_env, french_UTF_8_close_env, french_UTF_8_stem},
+  {"ger", ENC_UTF_8, german_UTF_8_create_env, german_UTF_8_close_env, german_UTF_8_stem},
+  {"german", ENC_UTF_8, german_UTF_8_create_env, german_UTF_8_close_env, german_UTF_8_stem},
+  {"it", ENC_UTF_8, italian_UTF_8_create_env, italian_UTF_8_close_env, italian_UTF_8_stem},
+  {"ita", ENC_UTF_8, italian_UTF_8_create_env, italian_UTF_8_close_env, italian_UTF_8_stem},
+  {"italian", ENC_UTF_8, italian_UTF_8_create_env, italian_UTF_8_close_env, italian_UTF_8_stem},
+  {"nl", ENC_UTF_8, dutch_UTF_8_create_env, dutch_UTF_8_close_env, dutch_UTF_8_stem},
+  {"nld", ENC_UTF_8, dutch_UTF_8_create_env, dutch_UTF_8_close_env, dutch_UTF_8_stem},
+  {"no", ENC_UTF_8, norwegian_UTF_8_create_env, norwegian_UTF_8_close_env, norwegian_UTF_8_stem},
+  {"nor", ENC_UTF_8, norwegian_UTF_8_create_env, norwegian_UTF_8_close_env, norwegian_UTF_8_stem},
+  {"norwegian", ENC_UTF_8, norwegian_UTF_8_create_env, norwegian_UTF_8_close_env, norwegian_UTF_8_stem},
+  {"por", ENC_UTF_8, portuguese_UTF_8_create_env, portuguese_UTF_8_close_env, portuguese_UTF_8_stem},
+  {"porter", ENC_UTF_8, porter_UTF_8_create_env, porter_UTF_8_close_env, porter_UTF_8_stem},
+  {"portuguese", ENC_UTF_8, portuguese_UTF_8_create_env, portuguese_UTF_8_close_env, portuguese_UTF_8_stem},
+  {"pt", ENC_UTF_8, portuguese_UTF_8_create_env, portuguese_UTF_8_close_env, portuguese_UTF_8_stem},
+  {"ru", ENC_UTF_8, russian_UTF_8_create_env, russian_UTF_8_close_env, russian_UTF_8_stem},
+  {"rus", ENC_UTF_8, russian_UTF_8_create_env, russian_UTF_8_close_env, russian_UTF_8_stem},
+  {"russian", ENC_UTF_8, russian_UTF_8_create_env, russian_UTF_8_close_env, russian_UTF_8_stem},
+  {"spa", ENC_UTF_8, spanish_UTF_8_create_env, spanish_UTF_8_close_env, spanish_UTF_8_stem},
+  {"spanish", ENC_UTF_8, spanish_UTF_8_create_env, spanish_UTF_8_close_env, spanish_UTF_8_stem},
+  {"sv", ENC_UTF_8, swedish_UTF_8_create_env, swedish_UTF_8_close_env, swedish_UTF_8_stem},
+  {"swe", ENC_UTF_8, swedish_UTF_8_create_env, swedish_UTF_8_close_env, swedish_UTF_8_stem},
+  {"swedish", ENC_UTF_8, swedish_UTF_8_create_env, swedish_UTF_8_close_env, swedish_UTF_8_stem},
+  {0,0,0,0,0}
+};
+static const char * algorithm_names[] = {
+  "danish",
+  "dutch",
+  "english",
+  "finnish",
+  "french",
+  "german",
+  "italian",
+  "norwegian",
+  "porter",
+  "portuguese",
+  "russian",
+  "spanish",
+  "swedish",
+  0
+};

Added: trunk/src/libstemmer/stem_UTF_8_danish.c
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/stem_UTF_8_danish.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,357 @@
+/*
+ * Copyright (c) 2001, Dr Martin Porter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+/* This file was generated automatically by the Snowball to ANSI C compiler */
+
+#include "header.h"
+
+extern int danish_UTF_8_stem(struct SN_env * z);
+static int r_undouble(struct SN_env * z);
+static int r_other_suffix(struct SN_env * z);
+static int r_consonant_pair(struct SN_env * z);
+static int r_main_suffix(struct SN_env * z);
+static int r_mark_regions(struct SN_env * z);
+
+extern struct SN_env * danish_UTF_8_create_env(void);
+extern void danish_UTF_8_close_env(struct SN_env * z);
+
+static symbol s_0_0[3] = { 'h', 'e', 'd' };
+static symbol s_0_1[5] = { 'e', 't', 'h', 'e', 'd' };
+static symbol s_0_2[4] = { 'e', 'r', 'e', 'd' };
+static symbol s_0_3[1] = { 'e' };
+static symbol s_0_4[5] = { 'e', 'r', 'e', 'd', 'e' };
+static symbol s_0_5[4] = { 'e', 'n', 'd', 'e' };
+static symbol s_0_6[6] = { 'e', 'r', 'e', 'n', 'd', 'e' };
+static symbol s_0_7[3] = { 'e', 'n', 'e' };
+static symbol s_0_8[4] = { 'e', 'r', 'n', 'e' };
+static symbol s_0_9[3] = { 'e', 'r', 'e' };
+static symbol s_0_10[2] = { 'e', 'n' };
+static symbol s_0_11[5] = { 'h', 'e', 'd', 'e', 'n' };
+static symbol s_0_12[4] = { 'e', 'r', 'e', 'n' };
+static symbol s_0_13[2] = { 'e', 'r' };
+static symbol s_0_14[5] = { 'h', 'e', 'd', 'e', 'r' };
+static symbol s_0_15[4] = { 'e', 'r', 'e', 'r' };
+static symbol s_0_16[1] = { 's' };
+static symbol s_0_17[4] = { 'h', 'e', 'd', 's' };
+static symbol s_0_18[2] = { 'e', 's' };
+static symbol s_0_19[5] = { 'e', 'n', 'd', 'e', 's' };
+static symbol s_0_20[7] = { 'e', 'r', 'e', 'n', 'd', 'e', 's' };
+static symbol s_0_21[4] = { 'e', 'n', 'e', 's' };
+static symbol s_0_22[5] = { 'e', 'r', 'n', 'e', 's' };
+static symbol s_0_23[4] = { 'e', 'r', 'e', 's' };
+static symbol s_0_24[3] = { 'e', 'n', 's' };
+static symbol s_0_25[6] = { 'h', 'e', 'd', 'e', 'n', 's' };
+static symbol s_0_26[5] = { 'e', 'r', 'e', 'n', 's' };
+static symbol s_0_27[3] = { 'e', 'r', 's' };
+static symbol s_0_28[3] = { 'e', 't', 's' };
+static symbol s_0_29[5] = { 'e', 'r', 'e', 't', 's' };
+static symbol s_0_30[2] = { 'e', 't' };
+static symbol s_0_31[4] = { 'e', 'r', 'e', 't' };
+
+static struct among a_0[32] =
+{
+/*  0 */ { 3, s_0_0, -1, 1, 0},
+/*  1 */ { 5, s_0_1, 0, 1, 0},
+/*  2 */ { 4, s_0_2, -1, 1, 0},
+/*  3 */ { 1, s_0_3, -1, 1, 0},
+/*  4 */ { 5, s_0_4, 3, 1, 0},
+/*  5 */ { 4, s_0_5, 3, 1, 0},
+/*  6 */ { 6, s_0_6, 5, 1, 0},
+/*  7 */ { 3, s_0_7, 3, 1, 0},
+/*  8 */ { 4, s_0_8, 3, 1, 0},
+/*  9 */ { 3, s_0_9, 3, 1, 0},
+/* 10 */ { 2, s_0_10, -1, 1, 0},
+/* 11 */ { 5, s_0_11, 10, 1, 0},
+/* 12 */ { 4, s_0_12, 10, 1, 0},
+/* 13 */ { 2, s_0_13, -1, 1, 0},
+/* 14 */ { 5, s_0_14, 13, 1, 0},
+/* 15 */ { 4, s_0_15, 13, 1, 0},
+/* 16 */ { 1, s_0_16, -1, 2, 0},
+/* 17 */ { 4, s_0_17, 16, 1, 0},
+/* 18 */ { 2, s_0_18, 16, 1, 0},
+/* 19 */ { 5, s_0_19, 18, 1, 0},
+/* 20 */ { 7, s_0_20, 19, 1, 0},
+/* 21 */ { 4, s_0_21, 18, 1, 0},
+/* 22 */ { 5, s_0_22, 18, 1, 0},
+/* 23 */ { 4, s_0_23, 18, 1, 0},
+/* 24 */ { 3, s_0_24, 16, 1, 0},
+/* 25 */ { 6, s_0_25, 24, 1, 0},
+/* 26 */ { 5, s_0_26, 24, 1, 0},
+/* 27 */ { 3, s_0_27, 16, 1, 0},
+/* 28 */ { 3, s_0_28, 16, 1, 0},
+/* 29 */ { 5, s_0_29, 28, 1, 0},
+/* 30 */ { 2, s_0_30, -1, 1, 0},
+/* 31 */ { 4, s_0_31, 30, 1, 0}
+};
+
+static symbol s_1_0[2] = { 'g', 'd' };
+static symbol s_1_1[2] = { 'd', 't' };
+static symbol s_1_2[2] = { 'g', 't' };
+static symbol s_1_3[2] = { 'k', 't' };
+
+static struct among a_1[4] =
+{
+/*  0 */ { 2, s_1_0, -1, -1, 0},
+/*  1 */ { 2, s_1_1, -1, -1, 0},
+/*  2 */ { 2, s_1_2, -1, -1, 0},
+/*  3 */ { 2, s_1_3, -1, -1, 0}
+};
+
+static symbol s_2_0[2] = { 'i', 'g' };
+static symbol s_2_1[3] = { 'l', 'i', 'g' };
+static symbol s_2_2[4] = { 'e', 'l', 'i', 'g' };
+static symbol s_2_3[3] = { 'e', 'l', 's' };
+static symbol s_2_4[5] = { 'l', 0xC3, 0xB8, 's', 't' };
+
+static struct among a_2[5] =
+{
+/*  0 */ { 2, s_2_0, -1, 1, 0},
+/*  1 */ { 3, s_2_1, 0, 1, 0},
+/*  2 */ { 4, s_2_2, 1, 1, 0},
+/*  3 */ { 3, s_2_3, -1, 1, 0},
+/*  4 */ { 5, s_2_4, -1, 2, 0}
+};
+
+static unsigned char g_v[] = { 17, 65, 16, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 128 };
+
+static unsigned char g_s_ending[] = { 239, 254, 42, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16 };
+
+static symbol s_0[] = { 's', 't' };
+static symbol s_1[] = { 'i', 'g' };
+static symbol s_2[] = { 'l', 0xC3, 0xB8, 's' };
+
+static int r_mark_regions(struct SN_env * z) {
+    z->I[0] = z->l;
+    {	int c_test = z->c; /* test, line 33 */
+	{   int c = skip_utf8(z->p, z->c, 0, z->l, + 3);
+	    if (c < 0) return 0;
+	    z->c = c; /* hop, line 33 */
+	}
+	z->I[1] = z->c; /* setmark x, line 33 */
+	z->c = c_test;
+    }
+    while(1) { /* goto, line 34 */
+	int c = z->c;
+	if (!(in_grouping_U(z, g_v, 97, 248))) goto lab0;
+	z->c = c;
+	break;
+    lab0:
+	z->c = c;
+	{   int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+	    if (c < 0) return 0;
+	    z->c = c; /* goto, line 34 */
+	}
+    }
+    while(1) { /* gopast, line 34 */
+	if (!(out_grouping_U(z, g_v, 97, 248))) goto lab1;
+	break;
+    lab1:
+	{   int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+	    if (c < 0) return 0;
+	    z->c = c; /* gopast, line 34 */
+	}
+    }
+    z->I[0] = z->c; /* setmark p1, line 34 */
+     /* try, line 35 */
+    if (!(z->I[0] < z->I[1])) goto lab2;
+    z->I[0] = z->I[1];
+lab2:
+    return 1;
+}
+
+static int r_main_suffix(struct SN_env * z) {
+    int among_var;
+    {	int m3; /* setlimit, line 41 */
+	int m = z->l - z->c; (void) m;
+	if (z->c < z->I[0]) return 0;
+	z->c = z->I[0]; /* tomark, line 41 */
+	m3 = z->lb; z->lb = z->c;
+	z->c = z->l - m;
+	z->ket = z->c; /* [, line 41 */
+	among_var = find_among_b(z, a_0, 32); /* substring, line 41 */
+	if (!(among_var)) { z->lb = m3; return 0; }
+	z->bra = z->c; /* ], line 41 */
+	z->lb = m3;
+    }
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 48 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    if (!(in_grouping_b_U(z, g_s_ending, 97, 229))) return 0;
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 50 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_consonant_pair(struct SN_env * z) {
+    {	int m_test = z->l - z->c; /* test, line 55 */
+	{   int m3; /* setlimit, line 56 */
+	    int m = z->l - z->c; (void) m;
+	    if (z->c < z->I[0]) return 0;
+	    z->c = z->I[0]; /* tomark, line 56 */
+	    m3 = z->lb; z->lb = z->c;
+	    z->c = z->l - m;
+	    z->ket = z->c; /* [, line 56 */
+	    if (!(find_among_b(z, a_1, 4))) { z->lb = m3; return 0; } /* substring, line 56 */
+	    z->bra = z->c; /* ], line 56 */
+	    z->lb = m3;
+	}
+	z->c = z->l - m_test;
+    }
+    {	int c = skip_utf8(z->p, z->c, z->lb, 0, -1);
+	if (c < 0) return 0;
+	z->c = c; /* next, line 62 */
+    }
+    z->bra = z->c; /* ], line 62 */
+    {	int ret;
+	ret = slice_del(z); /* delete, line 62 */
+	if (ret < 0) return ret;
+    }
+    return 1;
+}
+
+static int r_other_suffix(struct SN_env * z) {
+    int among_var;
+    {	int m = z->l - z->c; (void) m; /* do, line 66 */
+	z->ket = z->c; /* [, line 66 */
+	if (!(eq_s_b(z, 2, s_0))) goto lab0;
+	z->bra = z->c; /* ], line 66 */
+	if (!(eq_s_b(z, 2, s_1))) goto lab0;
+	{   int ret;
+	    ret = slice_del(z); /* delete, line 66 */
+	    if (ret < 0) return ret;
+	}
+    lab0:
+	z->c = z->l - m;
+    }
+    {	int m3; /* setlimit, line 67 */
+	int m = z->l - z->c; (void) m;
+	if (z->c < z->I[0]) return 0;
+	z->c = z->I[0]; /* tomark, line 67 */
+	m3 = z->lb; z->lb = z->c;
+	z->c = z->l - m;
+	z->ket = z->c; /* [, line 67 */
+	among_var = find_among_b(z, a_2, 5); /* substring, line 67 */
+	if (!(among_var)) { z->lb = m3; return 0; }
+	z->bra = z->c; /* ], line 67 */
+	z->lb = m3;
+    }
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 70 */
+		if (ret < 0) return ret;
+	    }
+	    {	int m = z->l - z->c; (void) m; /* do, line 70 */
+		{   int ret = r_consonant_pair(z);
+		    if (ret == 0) goto lab1; /* call consonant_pair, line 70 */
+		    if (ret < 0) return ret;
+		}
+	    lab1:
+		z->c = z->l - m;
+	    }
+	    break;
+	case 2:
+	    {	int ret;
+		ret = slice_from_s(z, 4, s_2); /* <-, line 72 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_undouble(struct SN_env * z) {
+    {	int m3; /* setlimit, line 76 */
+	int m = z->l - z->c; (void) m;
+	if (z->c < z->I[0]) return 0;
+	z->c = z->I[0]; /* tomark, line 76 */
+	m3 = z->lb; z->lb = z->c;
+	z->c = z->l - m;
+	z->ket = z->c; /* [, line 76 */
+	if (!(out_grouping_b_U(z, g_v, 97, 248))) { z->lb = m3; return 0; }
+	z->bra = z->c; /* ], line 76 */
+	z->S[0] = slice_to(z, z->S[0]); /* -> ch, line 76 */
+	if (z->S[0] == 0) return -1; /* -> ch, line 76 */
+	z->lb = m3;
+    }
+    if (!(eq_v_b(z, z->S[0]))) return 0; /* name ch, line 77 */
+    {	int ret;
+	ret = slice_del(z); /* delete, line 78 */
+	if (ret < 0) return ret;
+    }
+    return 1;
+}
+
+extern int danish_UTF_8_stem(struct SN_env * z) {
+    {	int c = z->c; /* do, line 84 */
+	{   int ret = r_mark_regions(z);
+	    if (ret == 0) goto lab0; /* call mark_regions, line 84 */
+	    if (ret < 0) return ret;
+	}
+    lab0:
+	z->c = c;
+    }
+    z->lb = z->c; z->c = z->l; /* backwards, line 85 */
+
+    {	int m = z->l - z->c; (void) m; /* do, line 86 */
+	{   int ret = r_main_suffix(z);
+	    if (ret == 0) goto lab1; /* call main_suffix, line 86 */
+	    if (ret < 0) return ret;
+	}
+    lab1:
+	z->c = z->l - m;
+    }
+    {	int m = z->l - z->c; (void) m; /* do, line 87 */
+	{   int ret = r_consonant_pair(z);
+	    if (ret == 0) goto lab2; /* call consonant_pair, line 87 */
+	    if (ret < 0) return ret;
+	}
+    lab2:
+	z->c = z->l - m;
+    }
+    {	int m = z->l - z->c; (void) m; /* do, line 88 */
+	{   int ret = r_other_suffix(z);
+	    if (ret == 0) goto lab3; /* call other_suffix, line 88 */
+	    if (ret < 0) return ret;
+	}
+    lab3:
+	z->c = z->l - m;
+    }
+    {	int m = z->l - z->c; (void) m; /* do, line 89 */
+	{   int ret = r_undouble(z);
+	    if (ret == 0) goto lab4; /* call undouble, line 89 */
+	    if (ret < 0) return ret;
+	}
+    lab4:
+	z->c = z->l - m;
+    }
+    z->c = z->lb;
+    return 1;
+}
+
+extern struct SN_env * danish_UTF_8_create_env(void) { return SN_create_env(1, 2, 0); }
+
+extern void danish_UTF_8_close_env(struct SN_env * z) { SN_close_env(z); }
+

Added: trunk/src/libstemmer/stem_UTF_8_danish.h
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/stem_UTF_8_danish.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2001, Dr Martin Porter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+/* This file was generated automatically by the Snowball to ANSI C compiler */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern struct SN_env * danish_UTF_8_create_env(void);
+extern void danish_UTF_8_close_env(struct SN_env * z);
+
+extern int danish_UTF_8_stem(struct SN_env * z);
+
+#ifdef __cplusplus
+}
+#endif
+

Added: trunk/src/libstemmer/stem_UTF_8_dutch.c
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/stem_UTF_8_dutch.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,666 @@
+/*
+ * Copyright (c) 2001, Dr Martin Porter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+/* This file was generated automatically by the Snowball to ANSI C compiler */
+
+#include "header.h"
+
+extern int dutch_UTF_8_stem(struct SN_env * z);
+static int r_standard_suffix(struct SN_env * z);
+static int r_undouble(struct SN_env * z);
+static int r_R2(struct SN_env * z);
+static int r_R1(struct SN_env * z);
+static int r_mark_regions(struct SN_env * z);
+static int r_en_ending(struct SN_env * z);
+static int r_e_ending(struct SN_env * z);
+static int r_postlude(struct SN_env * z);
+static int r_prelude(struct SN_env * z);
+
+extern struct SN_env * dutch_UTF_8_create_env(void);
+extern void dutch_UTF_8_close_env(struct SN_env * z);
+
+static symbol s_0_1[2] = { 0xC3, 0xA1 };
+static symbol s_0_2[2] = { 0xC3, 0xA4 };
+static symbol s_0_3[2] = { 0xC3, 0xA9 };
+static symbol s_0_4[2] = { 0xC3, 0xAB };
+static symbol s_0_5[2] = { 0xC3, 0xAD };
+static symbol s_0_6[2] = { 0xC3, 0xAF };
+static symbol s_0_7[2] = { 0xC3, 0xB3 };
+static symbol s_0_8[2] = { 0xC3, 0xB6 };
+static symbol s_0_9[2] = { 0xC3, 0xBA };
+static symbol s_0_10[2] = { 0xC3, 0xBC };
+
+static struct among a_0[11] =
+{
+/*  0 */ { 0, 0, -1, 6, 0},
+/*  1 */ { 2, s_0_1, 0, 1, 0},
+/*  2 */ { 2, s_0_2, 0, 1, 0},
+/*  3 */ { 2, s_0_3, 0, 2, 0},
+/*  4 */ { 2, s_0_4, 0, 2, 0},
+/*  5 */ { 2, s_0_5, 0, 3, 0},
+/*  6 */ { 2, s_0_6, 0, 3, 0},
+/*  7 */ { 2, s_0_7, 0, 4, 0},
+/*  8 */ { 2, s_0_8, 0, 4, 0},
+/*  9 */ { 2, s_0_9, 0, 5, 0},
+/* 10 */ { 2, s_0_10, 0, 5, 0}
+};
+
+static symbol s_1_1[1] = { 'I' };
+static symbol s_1_2[1] = { 'Y' };
+
+static struct among a_1[3] =
+{
+/*  0 */ { 0, 0, -1, 3, 0},
+/*  1 */ { 1, s_1_1, 0, 2, 0},
+/*  2 */ { 1, s_1_2, 0, 1, 0}
+};
+
+static symbol s_2_0[2] = { 'd', 'd' };
+static symbol s_2_1[2] = { 'k', 'k' };
+static symbol s_2_2[2] = { 't', 't' };
+
+static struct among a_2[3] =
+{
+/*  0 */ { 2, s_2_0, -1, -1, 0},
+/*  1 */ { 2, s_2_1, -1, -1, 0},
+/*  2 */ { 2, s_2_2, -1, -1, 0}
+};
+
+static symbol s_3_0[3] = { 'e', 'n', 'e' };
+static symbol s_3_1[2] = { 's', 'e' };
+static symbol s_3_2[2] = { 'e', 'n' };
+static symbol s_3_3[5] = { 'h', 'e', 'd', 'e', 'n' };
+static symbol s_3_4[1] = { 's' };
+
+static struct among a_3[5] =
+{
+/*  0 */ { 3, s_3_0, -1, 2, 0},
+/*  1 */ { 2, s_3_1, -1, 3, 0},
+/*  2 */ { 2, s_3_2, -1, 2, 0},
+/*  3 */ { 5, s_3_3, 2, 1, 0},
+/*  4 */ { 1, s_3_4, -1, 3, 0}
+};
+
+static symbol s_4_0[3] = { 'e', 'n', 'd' };
+static symbol s_4_1[2] = { 'i', 'g' };
+static symbol s_4_2[3] = { 'i', 'n', 'g' };
+static symbol s_4_3[4] = { 'l', 'i', 'j', 'k' };
+static symbol s_4_4[4] = { 'b', 'a', 'a', 'r' };
+static symbol s_4_5[3] = { 'b', 'a', 'r' };
+
+static struct among a_4[6] =
+{
+/*  0 */ { 3, s_4_0, -1, 1, 0},
+/*  1 */ { 2, s_4_1, -1, 2, 0},
+/*  2 */ { 3, s_4_2, -1, 1, 0},
+/*  3 */ { 4, s_4_3, -1, 3, 0},
+/*  4 */ { 4, s_4_4, -1, 4, 0},
+/*  5 */ { 3, s_4_5, -1, 5, 0}
+};
+
+static symbol s_5_0[2] = { 'a', 'a' };
+static symbol s_5_1[2] = { 'e', 'e' };
+static symbol s_5_2[2] = { 'o', 'o' };
+static symbol s_5_3[2] = { 'u', 'u' };
+
+static struct among a_5[4] =
+{
+/*  0 */ { 2, s_5_0, -1, -1, 0},
+/*  1 */ { 2, s_5_1, -1, -1, 0},
+/*  2 */ { 2, s_5_2, -1, -1, 0},
+/*  3 */ { 2, s_5_3, -1, -1, 0}
+};
+
+static unsigned char g_v[] = { 17, 65, 16, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128 };
+
+static unsigned char g_v_I[] = { 1, 0, 0, 17, 65, 16, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128 };
+
+static unsigned char g_v_j[] = { 17, 67, 16, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128 };
+
+static symbol s_0[] = { 'a' };
+static symbol s_1[] = { 'e' };
+static symbol s_2[] = { 'i' };
+static symbol s_3[] = { 'o' };
+static symbol s_4[] = { 'u' };
+static symbol s_5[] = { 'y' };
+static symbol s_6[] = { 'Y' };
+static symbol s_7[] = { 'i' };
+static symbol s_8[] = { 'I' };
+static symbol s_9[] = { 'y' };
+static symbol s_10[] = { 'Y' };
+static symbol s_11[] = { 'y' };
+static symbol s_12[] = { 'i' };
+static symbol s_13[] = { 'e' };
+static symbol s_14[] = { 'g', 'e', 'm' };
+static symbol s_15[] = { 'h', 'e', 'i', 'd' };
+static symbol s_16[] = { 'h', 'e', 'i', 'd' };
+static symbol s_17[] = { 'c' };
+static symbol s_18[] = { 'e', 'n' };
+static symbol s_19[] = { 'i', 'g' };
+static symbol s_20[] = { 'e' };
+static symbol s_21[] = { 'e' };
+
+static int r_prelude(struct SN_env * z) {
+    int among_var;
+    {	int c_test = z->c; /* test, line 42 */
+	while(1) { /* repeat, line 42 */
+	    int c = z->c;
+	    z->bra = z->c; /* [, line 43 */
+	    among_var = find_among(z, a_0, 11); /* substring, line 43 */
+	    if (!(among_var)) goto lab0;
+	    z->ket = z->c; /* ], line 43 */
+	    switch(among_var) {
+		case 0: goto lab0;
+		case 1:
+		    {	int ret;
+			ret = slice_from_s(z, 1, s_0); /* <-, line 45 */
+			if (ret < 0) return ret;
+		    }
+		    break;
+		case 2:
+		    {	int ret;
+			ret = slice_from_s(z, 1, s_1); /* <-, line 47 */
+			if (ret < 0) return ret;
+		    }
+		    break;
+		case 3:
+		    {	int ret;
+			ret = slice_from_s(z, 1, s_2); /* <-, line 49 */
+			if (ret < 0) return ret;
+		    }
+		    break;
+		case 4:
+		    {	int ret;
+			ret = slice_from_s(z, 1, s_3); /* <-, line 51 */
+			if (ret < 0) return ret;
+		    }
+		    break;
+		case 5:
+		    {	int ret;
+			ret = slice_from_s(z, 1, s_4); /* <-, line 53 */
+			if (ret < 0) return ret;
+		    }
+		    break;
+		case 6:
+		    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+			if (c < 0) goto lab0;
+			z->c = c; /* next, line 54 */
+		    }
+		    break;
+	    }
+	    continue;
+	lab0:
+	    z->c = c;
+	    break;
+	}
+	z->c = c_test;
+    }
+    {	int c = z->c; /* try, line 57 */
+	z->bra = z->c; /* [, line 57 */
+	if (!(eq_s(z, 1, s_5))) { z->c = c; goto lab1; }
+	z->ket = z->c; /* ], line 57 */
+	{   int ret;
+	    ret = slice_from_s(z, 1, s_6); /* <-, line 57 */
+	    if (ret < 0) return ret;
+	}
+    lab1:
+	;
+    }
+    while(1) { /* repeat, line 58 */
+	int c = z->c;
+	while(1) { /* goto, line 58 */
+	    int c = z->c;
+	    if (!(in_grouping_U(z, g_v, 97, 232))) goto lab3;
+	    z->bra = z->c; /* [, line 59 */
+	    {	int c = z->c; /* or, line 59 */
+		if (!(eq_s(z, 1, s_7))) goto lab5;
+		z->ket = z->c; /* ], line 59 */
+		if (!(in_grouping_U(z, g_v, 97, 232))) goto lab5;
+		{   int ret;
+		    ret = slice_from_s(z, 1, s_8); /* <-, line 59 */
+		    if (ret < 0) return ret;
+		}
+		goto lab4;
+	    lab5:
+		z->c = c;
+		if (!(eq_s(z, 1, s_9))) goto lab3;
+		z->ket = z->c; /* ], line 60 */
+		{   int ret;
+		    ret = slice_from_s(z, 1, s_10); /* <-, line 60 */
+		    if (ret < 0) return ret;
+		}
+	    }
+	lab4:
+	    z->c = c;
+	    break;
+	lab3:
+	    z->c = c;
+	    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		if (c < 0) goto lab2;
+		z->c = c; /* goto, line 58 */
+	    }
+	}
+	continue;
+    lab2:
+	z->c = c;
+	break;
+    }
+    return 1;
+}
+
+static int r_mark_regions(struct SN_env * z) {
+    z->I[0] = z->l;
+    z->I[1] = z->l;
+    while(1) { /* gopast, line 69 */
+	if (!(in_grouping_U(z, g_v, 97, 232))) goto lab0;
+	break;
+    lab0:
+	{   int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+	    if (c < 0) return 0;
+	    z->c = c; /* gopast, line 69 */
+	}
+    }
+    while(1) { /* gopast, line 69 */
+	if (!(out_grouping_U(z, g_v, 97, 232))) goto lab1;
+	break;
+    lab1:
+	{   int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+	    if (c < 0) return 0;
+	    z->c = c; /* gopast, line 69 */
+	}
+    }
+    z->I[0] = z->c; /* setmark p1, line 69 */
+     /* try, line 70 */
+    if (!(z->I[0] < 3)) goto lab2;
+    z->I[0] = 3;
+lab2:
+    while(1) { /* gopast, line 71 */
+	if (!(in_grouping_U(z, g_v, 97, 232))) goto lab3;
+	break;
+    lab3:
+	{   int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+	    if (c < 0) return 0;
+	    z->c = c; /* gopast, line 71 */
+	}
+    }
+    while(1) { /* gopast, line 71 */
+	if (!(out_grouping_U(z, g_v, 97, 232))) goto lab4;
+	break;
+    lab4:
+	{   int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+	    if (c < 0) return 0;
+	    z->c = c; /* gopast, line 71 */
+	}
+    }
+    z->I[1] = z->c; /* setmark p2, line 71 */
+    return 1;
+}
+
+static int r_postlude(struct SN_env * z) {
+    int among_var;
+    while(1) { /* repeat, line 75 */
+	int c = z->c;
+	z->bra = z->c; /* [, line 77 */
+	among_var = find_among(z, a_1, 3); /* substring, line 77 */
+	if (!(among_var)) goto lab0;
+	z->ket = z->c; /* ], line 77 */
+	switch(among_var) {
+	    case 0: goto lab0;
+	    case 1:
+		{   int ret;
+		    ret = slice_from_s(z, 1, s_11); /* <-, line 78 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 2:
+		{   int ret;
+		    ret = slice_from_s(z, 1, s_12); /* <-, line 79 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 3:
+		{   int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		    if (c < 0) goto lab0;
+		    z->c = c; /* next, line 80 */
+		}
+		break;
+	}
+	continue;
+    lab0:
+	z->c = c;
+	break;
+    }
+    return 1;
+}
+
+static int r_R1(struct SN_env * z) {
+    if (!(z->I[0] <= z->c)) return 0;
+    return 1;
+}
+
+static int r_R2(struct SN_env * z) {
+    if (!(z->I[1] <= z->c)) return 0;
+    return 1;
+}
+
+static int r_undouble(struct SN_env * z) {
+    {	int m_test = z->l - z->c; /* test, line 91 */
+	if (!(find_among_b(z, a_2, 3))) return 0; /* among, line 91 */
+	z->c = z->l - m_test;
+    }
+    z->ket = z->c; /* [, line 91 */
+    {	int c = skip_utf8(z->p, z->c, z->lb, 0, -1);
+	if (c < 0) return 0;
+	z->c = c; /* next, line 91 */
+    }
+    z->bra = z->c; /* ], line 91 */
+    {	int ret;
+	ret = slice_del(z); /* delete, line 91 */
+	if (ret < 0) return ret;
+    }
+    return 1;
+}
+
+static int r_e_ending(struct SN_env * z) {
+    z->B[0] = 0; /* unset e_found, line 95 */
+    z->ket = z->c; /* [, line 96 */
+    if (!(eq_s_b(z, 1, s_13))) return 0;
+    z->bra = z->c; /* ], line 96 */
+    {	int ret = r_R1(z);
+	if (ret == 0) return 0; /* call R1, line 96 */
+	if (ret < 0) return ret;
+    }
+    {	int m_test = z->l - z->c; /* test, line 96 */
+	if (!(out_grouping_b_U(z, g_v, 97, 232))) return 0;
+	z->c = z->l - m_test;
+    }
+    {	int ret;
+	ret = slice_del(z); /* delete, line 96 */
+	if (ret < 0) return ret;
+    }
+    z->B[0] = 1; /* set e_found, line 97 */
+    {	int ret = r_undouble(z);
+	if (ret == 0) return 0; /* call undouble, line 98 */
+	if (ret < 0) return ret;
+    }
+    return 1;
+}
+
+static int r_en_ending(struct SN_env * z) {
+    {	int ret = r_R1(z);
+	if (ret == 0) return 0; /* call R1, line 102 */
+	if (ret < 0) return ret;
+    }
+    {	int m = z->l - z->c; (void) m; /* and, line 102 */
+	if (!(out_grouping_b_U(z, g_v, 97, 232))) return 0;
+	z->c = z->l - m;
+	{   int m = z->l - z->c; (void) m; /* not, line 102 */
+	    if (!(eq_s_b(z, 3, s_14))) goto lab0;
+	    return 0;
+	lab0:
+	    z->c = z->l - m;
+	}
+    }
+    {	int ret;
+	ret = slice_del(z); /* delete, line 102 */
+	if (ret < 0) return ret;
+    }
+    {	int ret = r_undouble(z);
+	if (ret == 0) return 0; /* call undouble, line 103 */
+	if (ret < 0) return ret;
+    }
+    return 1;
+}
+
+static int r_standard_suffix(struct SN_env * z) {
+    int among_var;
+    {	int m = z->l - z->c; (void) m; /* do, line 107 */
+	z->ket = z->c; /* [, line 108 */
+	among_var = find_among_b(z, a_3, 5); /* substring, line 108 */
+	if (!(among_var)) goto lab0;
+	z->bra = z->c; /* ], line 108 */
+	switch(among_var) {
+	    case 0: goto lab0;
+	    case 1:
+		{   int ret = r_R1(z);
+		    if (ret == 0) goto lab0; /* call R1, line 110 */
+		    if (ret < 0) return ret;
+		}
+		{   int ret;
+		    ret = slice_from_s(z, 4, s_15); /* <-, line 110 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 2:
+		{   int ret = r_en_ending(z);
+		    if (ret == 0) goto lab0; /* call en_ending, line 113 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 3:
+		{   int ret = r_R1(z);
+		    if (ret == 0) goto lab0; /* call R1, line 116 */
+		    if (ret < 0) return ret;
+		}
+		if (!(out_grouping_b_U(z, g_v_j, 97, 232))) goto lab0;
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 116 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	}
+    lab0:
+	z->c = z->l - m;
+    }
+    {	int m = z->l - z->c; (void) m; /* do, line 120 */
+	{   int ret = r_e_ending(z);
+	    if (ret == 0) goto lab1; /* call e_ending, line 120 */
+	    if (ret < 0) return ret;
+	}
+    lab1:
+	z->c = z->l - m;
+    }
+    {	int m = z->l - z->c; (void) m; /* do, line 122 */
+	z->ket = z->c; /* [, line 122 */
+	if (!(eq_s_b(z, 4, s_16))) goto lab2;
+	z->bra = z->c; /* ], line 122 */
+	{   int ret = r_R2(z);
+	    if (ret == 0) goto lab2; /* call R2, line 122 */
+	    if (ret < 0) return ret;
+	}
+	{   int m = z->l - z->c; (void) m; /* not, line 122 */
+	    if (!(eq_s_b(z, 1, s_17))) goto lab3;
+	    goto lab2;
+	lab3:
+	    z->c = z->l - m;
+	}
+	{   int ret;
+	    ret = slice_del(z); /* delete, line 122 */
+	    if (ret < 0) return ret;
+	}
+	z->ket = z->c; /* [, line 123 */
+	if (!(eq_s_b(z, 2, s_18))) goto lab2;
+	z->bra = z->c; /* ], line 123 */
+	{   int ret = r_en_ending(z);
+	    if (ret == 0) goto lab2; /* call en_ending, line 123 */
+	    if (ret < 0) return ret;
+	}
+    lab2:
+	z->c = z->l - m;
+    }
+    {	int m = z->l - z->c; (void) m; /* do, line 126 */
+	z->ket = z->c; /* [, line 127 */
+	among_var = find_among_b(z, a_4, 6); /* substring, line 127 */
+	if (!(among_var)) goto lab4;
+	z->bra = z->c; /* ], line 127 */
+	switch(among_var) {
+	    case 0: goto lab4;
+	    case 1:
+		{   int ret = r_R2(z);
+		    if (ret == 0) goto lab4; /* call R2, line 129 */
+		    if (ret < 0) return ret;
+		}
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 129 */
+		    if (ret < 0) return ret;
+		}
+		{   int m = z->l - z->c; (void) m; /* or, line 130 */
+		    z->ket = z->c; /* [, line 130 */
+		    if (!(eq_s_b(z, 2, s_19))) goto lab6;
+		    z->bra = z->c; /* ], line 130 */
+		    {	int ret = r_R2(z);
+			if (ret == 0) goto lab6; /* call R2, line 130 */
+			if (ret < 0) return ret;
+		    }
+		    {	int m = z->l - z->c; (void) m; /* not, line 130 */
+			if (!(eq_s_b(z, 1, s_20))) goto lab7;
+			goto lab6;
+		    lab7:
+			z->c = z->l - m;
+		    }
+		    {	int ret;
+			ret = slice_del(z); /* delete, line 130 */
+			if (ret < 0) return ret;
+		    }
+		    goto lab5;
+		lab6:
+		    z->c = z->l - m;
+		    {	int ret = r_undouble(z);
+			if (ret == 0) goto lab4; /* call undouble, line 130 */
+			if (ret < 0) return ret;
+		    }
+		}
+	    lab5:
+		break;
+	    case 2:
+		{   int ret = r_R2(z);
+		    if (ret == 0) goto lab4; /* call R2, line 133 */
+		    if (ret < 0) return ret;
+		}
+		{   int m = z->l - z->c; (void) m; /* not, line 133 */
+		    if (!(eq_s_b(z, 1, s_21))) goto lab8;
+		    goto lab4;
+		lab8:
+		    z->c = z->l - m;
+		}
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 133 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 3:
+		{   int ret = r_R2(z);
+		    if (ret == 0) goto lab4; /* call R2, line 136 */
+		    if (ret < 0) return ret;
+		}
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 136 */
+		    if (ret < 0) return ret;
+		}
+		{   int ret = r_e_ending(z);
+		    if (ret == 0) goto lab4; /* call e_ending, line 136 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 4:
+		{   int ret = r_R2(z);
+		    if (ret == 0) goto lab4; /* call R2, line 139 */
+		    if (ret < 0) return ret;
+		}
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 139 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 5:
+		{   int ret = r_R2(z);
+		    if (ret == 0) goto lab4; /* call R2, line 142 */
+		    if (ret < 0) return ret;
+		}
+		if (!(z->B[0])) goto lab4; /* Boolean test e_found, line 142 */
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 142 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	}
+    lab4:
+	z->c = z->l - m;
+    }
+    {	int m = z->l - z->c; (void) m; /* do, line 146 */
+	if (!(out_grouping_b_U(z, g_v_I, 73, 232))) goto lab9;
+	{   int m_test = z->l - z->c; /* test, line 148 */
+	    if (!(find_among_b(z, a_5, 4))) goto lab9; /* among, line 149 */
+	    if (!(out_grouping_b_U(z, g_v, 97, 232))) goto lab9;
+	    z->c = z->l - m_test;
+	}
+	z->ket = z->c; /* [, line 152 */
+	{   int c = skip_utf8(z->p, z->c, z->lb, 0, -1);
+	    if (c < 0) goto lab9;
+	    z->c = c; /* next, line 152 */
+	}
+	z->bra = z->c; /* ], line 152 */
+	{   int ret;
+	    ret = slice_del(z); /* delete, line 152 */
+	    if (ret < 0) return ret;
+	}
+    lab9:
+	z->c = z->l - m;
+    }
+    return 1;
+}
+
+extern int dutch_UTF_8_stem(struct SN_env * z) {
+    {	int c = z->c; /* do, line 159 */
+	{   int ret = r_prelude(z);
+	    if (ret == 0) goto lab0; /* call prelude, line 159 */
+	    if (ret < 0) return ret;
+	}
+    lab0:
+	z->c = c;
+    }
+    {	int c = z->c; /* do, line 160 */
+	{   int ret = r_mark_regions(z);
+	    if (ret == 0) goto lab1; /* call mark_regions, line 160 */
+	    if (ret < 0) return ret;
+	}
+    lab1:
+	z->c = c;
+    }
+    z->lb = z->c; z->c = z->l; /* backwards, line 161 */
+
+    {	int m = z->l - z->c; (void) m; /* do, line 162 */
+	{   int ret = r_standard_suffix(z);
+	    if (ret == 0) goto lab2; /* call standard_suffix, line 162 */
+	    if (ret < 0) return ret;
+	}
+    lab2:
+	z->c = z->l - m;
+    }
+    z->c = z->lb;
+    {	int c = z->c; /* do, line 163 */
+	{   int ret = r_postlude(z);
+	    if (ret == 0) goto lab3; /* call postlude, line 163 */
+	    if (ret < 0) return ret;
+	}
+    lab3:
+	z->c = c;
+    }
+    return 1;
+}
+
+extern struct SN_env * dutch_UTF_8_create_env(void) { return SN_create_env(0, 2, 1); }
+
+extern void dutch_UTF_8_close_env(struct SN_env * z) { SN_close_env(z); }
+

Added: trunk/src/libstemmer/stem_UTF_8_dutch.h
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/stem_UTF_8_dutch.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2001, Dr Martin Porter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+/* This file was generated automatically by the Snowball to ANSI C compiler */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern struct SN_env * dutch_UTF_8_create_env(void);
+extern void dutch_UTF_8_close_env(struct SN_env * z);
+
+extern int dutch_UTF_8_stem(struct SN_env * z);
+
+#ifdef __cplusplus
+}
+#endif
+

Added: trunk/src/libstemmer/stem_UTF_8_english.c
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/stem_UTF_8_english.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,1189 @@
+/*
+ * Copyright (c) 2001, Dr Martin Porter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+/* This file was generated automatically by the Snowball to ANSI C compiler */
+
+#include "header.h"
+
+extern int english_UTF_8_stem(struct SN_env * z);
+static int r_exception2(struct SN_env * z);
+static int r_exception1(struct SN_env * z);
+static int r_Step_5(struct SN_env * z);
+static int r_Step_4(struct SN_env * z);
+static int r_Step_3(struct SN_env * z);
+static int r_Step_2(struct SN_env * z);
+static int r_Step_1c(struct SN_env * z);
+static int r_Step_1b(struct SN_env * z);
+static int r_Step_1a(struct SN_env * z);
+static int r_R2(struct SN_env * z);
+static int r_R1(struct SN_env * z);
+static int r_shortv(struct SN_env * z);
+static int r_mark_regions(struct SN_env * z);
+static int r_postlude(struct SN_env * z);
+static int r_prelude(struct SN_env * z);
+
+extern struct SN_env * english_UTF_8_create_env(void);
+extern void english_UTF_8_close_env(struct SN_env * z);
+
+static symbol s_0_0[6] = { 'c', 'o', 'm', 'm', 'u', 'n' };
+static symbol s_0_1[5] = { 'g', 'e', 'n', 'e', 'r' };
+
+static struct among a_0[2] =
+{
+/*  0 */ { 6, s_0_0, -1, -1, 0},
+/*  1 */ { 5, s_0_1, -1, -1, 0}
+};
+
+static symbol s_1_0[1] = { '\'' };
+static symbol s_1_1[3] = { '\'', 's', '\'' };
+static symbol s_1_2[2] = { '\'', 's' };
+
+static struct among a_1[3] =
+{
+/*  0 */ { 1, s_1_0, -1, 1, 0},
+/*  1 */ { 3, s_1_1, 0, 1, 0},
+/*  2 */ { 2, s_1_2, -1, 1, 0}
+};
+
+static symbol s_2_0[3] = { 'i', 'e', 'd' };
+static symbol s_2_1[1] = { 's' };
+static symbol s_2_2[3] = { 'i', 'e', 's' };
+static symbol s_2_3[4] = { 's', 's', 'e', 's' };
+static symbol s_2_4[2] = { 's', 's' };
+static symbol s_2_5[2] = { 'u', 's' };
+
+static struct among a_2[6] =
+{
+/*  0 */ { 3, s_2_0, -1, 2, 0},
+/*  1 */ { 1, s_2_1, -1, 3, 0},
+/*  2 */ { 3, s_2_2, 1, 2, 0},
+/*  3 */ { 4, s_2_3, 1, 1, 0},
+/*  4 */ { 2, s_2_4, 1, -1, 0},
+/*  5 */ { 2, s_2_5, 1, -1, 0}
+};
+
+static symbol s_3_1[2] = { 'b', 'b' };
+static symbol s_3_2[2] = { 'd', 'd' };
+static symbol s_3_3[2] = { 'f', 'f' };
+static symbol s_3_4[2] = { 'g', 'g' };
+static symbol s_3_5[2] = { 'b', 'l' };
+static symbol s_3_6[2] = { 'm', 'm' };
+static symbol s_3_7[2] = { 'n', 'n' };
+static symbol s_3_8[2] = { 'p', 'p' };
+static symbol s_3_9[2] = { 'r', 'r' };
+static symbol s_3_10[2] = { 'a', 't' };
+static symbol s_3_11[2] = { 't', 't' };
+static symbol s_3_12[2] = { 'i', 'z' };
+
+static struct among a_3[13] =
+{
+/*  0 */ { 0, 0, -1, 3, 0},
+/*  1 */ { 2, s_3_1, 0, 2, 0},
+/*  2 */ { 2, s_3_2, 0, 2, 0},
+/*  3 */ { 2, s_3_3, 0, 2, 0},
+/*  4 */ { 2, s_3_4, 0, 2, 0},
+/*  5 */ { 2, s_3_5, 0, 1, 0},
+/*  6 */ { 2, s_3_6, 0, 2, 0},
+/*  7 */ { 2, s_3_7, 0, 2, 0},
+/*  8 */ { 2, s_3_8, 0, 2, 0},
+/*  9 */ { 2, s_3_9, 0, 2, 0},
+/* 10 */ { 2, s_3_10, 0, 1, 0},
+/* 11 */ { 2, s_3_11, 0, 2, 0},
+/* 12 */ { 2, s_3_12, 0, 1, 0}
+};
+
+static symbol s_4_0[2] = { 'e', 'd' };
+static symbol s_4_1[3] = { 'e', 'e', 'd' };
+static symbol s_4_2[3] = { 'i', 'n', 'g' };
+static symbol s_4_3[4] = { 'e', 'd', 'l', 'y' };
+static symbol s_4_4[5] = { 'e', 'e', 'd', 'l', 'y' };
+static symbol s_4_5[5] = { 'i', 'n', 'g', 'l', 'y' };
+
+static struct among a_4[6] =
+{
+/*  0 */ { 2, s_4_0, -1, 2, 0},
+/*  1 */ { 3, s_4_1, 0, 1, 0},
+/*  2 */ { 3, s_4_2, -1, 2, 0},
+/*  3 */ { 4, s_4_3, -1, 2, 0},
+/*  4 */ { 5, s_4_4, 3, 1, 0},
+/*  5 */ { 5, s_4_5, -1, 2, 0}
+};
+
+static symbol s_5_0[4] = { 'a', 'n', 'c', 'i' };
+static symbol s_5_1[4] = { 'e', 'n', 'c', 'i' };
+static symbol s_5_2[3] = { 'o', 'g', 'i' };
+static symbol s_5_3[2] = { 'l', 'i' };
+static symbol s_5_4[3] = { 'b', 'l', 'i' };
+static symbol s_5_5[4] = { 'a', 'b', 'l', 'i' };
+static symbol s_5_6[4] = { 'a', 'l', 'l', 'i' };
+static symbol s_5_7[5] = { 'f', 'u', 'l', 'l', 'i' };
+static symbol s_5_8[6] = { 'l', 'e', 's', 's', 'l', 'i' };
+static symbol s_5_9[5] = { 'o', 'u', 's', 'l', 'i' };
+static symbol s_5_10[5] = { 'e', 'n', 't', 'l', 'i' };
+static symbol s_5_11[5] = { 'a', 'l', 'i', 't', 'i' };
+static symbol s_5_12[6] = { 'b', 'i', 'l', 'i', 't', 'i' };
+static symbol s_5_13[5] = { 'i', 'v', 'i', 't', 'i' };
+static symbol s_5_14[6] = { 't', 'i', 'o', 'n', 'a', 'l' };
+static symbol s_5_15[7] = { 'a', 't', 'i', 'o', 'n', 'a', 'l' };
+static symbol s_5_16[5] = { 'a', 'l', 'i', 's', 'm' };
+static symbol s_5_17[5] = { 'a', 't', 'i', 'o', 'n' };
+static symbol s_5_18[7] = { 'i', 'z', 'a', 't', 'i', 'o', 'n' };
+static symbol s_5_19[4] = { 'i', 'z', 'e', 'r' };
+static symbol s_5_20[4] = { 'a', 't', 'o', 'r' };
+static symbol s_5_21[7] = { 'i', 'v', 'e', 'n', 'e', 's', 's' };
+static symbol s_5_22[7] = { 'f', 'u', 'l', 'n', 'e', 's', 's' };
+static symbol s_5_23[7] = { 'o', 'u', 's', 'n', 'e', 's', 's' };
+
+static struct among a_5[24] =
+{
+/*  0 */ { 4, s_5_0, -1, 3, 0},
+/*  1 */ { 4, s_5_1, -1, 2, 0},
+/*  2 */ { 3, s_5_2, -1, 13, 0},
+/*  3 */ { 2, s_5_3, -1, 16, 0},
+/*  4 */ { 3, s_5_4, 3, 12, 0},
+/*  5 */ { 4, s_5_5, 4, 4, 0},
+/*  6 */ { 4, s_5_6, 3, 8, 0},
+/*  7 */ { 5, s_5_7, 3, 14, 0},
+/*  8 */ { 6, s_5_8, 3, 15, 0},
+/*  9 */ { 5, s_5_9, 3, 10, 0},
+/* 10 */ { 5, s_5_10, 3, 5, 0},
+/* 11 */ { 5, s_5_11, -1, 8, 0},
+/* 12 */ { 6, s_5_12, -1, 12, 0},
+/* 13 */ { 5, s_5_13, -1, 11, 0},
+/* 14 */ { 6, s_5_14, -1, 1, 0},
+/* 15 */ { 7, s_5_15, 14, 7, 0},
+/* 16 */ { 5, s_5_16, -1, 8, 0},
+/* 17 */ { 5, s_5_17, -1, 7, 0},
+/* 18 */ { 7, s_5_18, 17, 6, 0},
+/* 19 */ { 4, s_5_19, -1, 6, 0},
+/* 20 */ { 4, s_5_20, -1, 7, 0},
+/* 21 */ { 7, s_5_21, -1, 11, 0},
+/* 22 */ { 7, s_5_22, -1, 9, 0},
+/* 23 */ { 7, s_5_23, -1, 10, 0}
+};
+
+static symbol s_6_0[5] = { 'i', 'c', 'a', 't', 'e' };
+static symbol s_6_1[5] = { 'a', 't', 'i', 'v', 'e' };
+static symbol s_6_2[5] = { 'a', 'l', 'i', 'z', 'e' };
+static symbol s_6_3[5] = { 'i', 'c', 'i', 't', 'i' };
+static symbol s_6_4[4] = { 'i', 'c', 'a', 'l' };
+static symbol s_6_5[6] = { 't', 'i', 'o', 'n', 'a', 'l' };
+static symbol s_6_6[7] = { 'a', 't', 'i', 'o', 'n', 'a', 'l' };
+static symbol s_6_7[3] = { 'f', 'u', 'l' };
+static symbol s_6_8[4] = { 'n', 'e', 's', 's' };
+
+static struct among a_6[9] =
+{
+/*  0 */ { 5, s_6_0, -1, 4, 0},
+/*  1 */ { 5, s_6_1, -1, 6, 0},
+/*  2 */ { 5, s_6_2, -1, 3, 0},
+/*  3 */ { 5, s_6_3, -1, 4, 0},
+/*  4 */ { 4, s_6_4, -1, 4, 0},
+/*  5 */ { 6, s_6_5, -1, 1, 0},
+/*  6 */ { 7, s_6_6, 5, 2, 0},
+/*  7 */ { 3, s_6_7, -1, 5, 0},
+/*  8 */ { 4, s_6_8, -1, 5, 0}
+};
+
+static symbol s_7_0[2] = { 'i', 'c' };
+static symbol s_7_1[4] = { 'a', 'n', 'c', 'e' };
+static symbol s_7_2[4] = { 'e', 'n', 'c', 'e' };
+static symbol s_7_3[4] = { 'a', 'b', 'l', 'e' };
+static symbol s_7_4[4] = { 'i', 'b', 'l', 'e' };
+static symbol s_7_5[3] = { 'a', 't', 'e' };
+static symbol s_7_6[3] = { 'i', 'v', 'e' };
+static symbol s_7_7[3] = { 'i', 'z', 'e' };
+static symbol s_7_8[3] = { 'i', 't', 'i' };
+static symbol s_7_9[2] = { 'a', 'l' };
+static symbol s_7_10[3] = { 'i', 's', 'm' };
+static symbol s_7_11[3] = { 'i', 'o', 'n' };
+static symbol s_7_12[2] = { 'e', 'r' };
+static symbol s_7_13[3] = { 'o', 'u', 's' };
+static symbol s_7_14[3] = { 'a', 'n', 't' };
+static symbol s_7_15[3] = { 'e', 'n', 't' };
+static symbol s_7_16[4] = { 'm', 'e', 'n', 't' };
+static symbol s_7_17[5] = { 'e', 'm', 'e', 'n', 't' };
+
+static struct among a_7[18] =
+{
+/*  0 */ { 2, s_7_0, -1, 1, 0},
+/*  1 */ { 4, s_7_1, -1, 1, 0},
+/*  2 */ { 4, s_7_2, -1, 1, 0},
+/*  3 */ { 4, s_7_3, -1, 1, 0},
+/*  4 */ { 4, s_7_4, -1, 1, 0},
+/*  5 */ { 3, s_7_5, -1, 1, 0},
+/*  6 */ { 3, s_7_6, -1, 1, 0},
+/*  7 */ { 3, s_7_7, -1, 1, 0},
+/*  8 */ { 3, s_7_8, -1, 1, 0},
+/*  9 */ { 2, s_7_9, -1, 1, 0},
+/* 10 */ { 3, s_7_10, -1, 1, 0},
+/* 11 */ { 3, s_7_11, -1, 2, 0},
+/* 12 */ { 2, s_7_12, -1, 1, 0},
+/* 13 */ { 3, s_7_13, -1, 1, 0},
+/* 14 */ { 3, s_7_14, -1, 1, 0},
+/* 15 */ { 3, s_7_15, -1, 1, 0},
+/* 16 */ { 4, s_7_16, 15, 1, 0},
+/* 17 */ { 5, s_7_17, 16, 1, 0}
+};
+
+static symbol s_8_0[1] = { 'e' };
+static symbol s_8_1[1] = { 'l' };
+
+static struct among a_8[2] =
+{
+/*  0 */ { 1, s_8_0, -1, 1, 0},
+/*  1 */ { 1, s_8_1, -1, 2, 0}
+};
+
+static symbol s_9_0[7] = { 's', 'u', 'c', 'c', 'e', 'e', 'd' };
+static symbol s_9_1[7] = { 'p', 'r', 'o', 'c', 'e', 'e', 'd' };
+static symbol s_9_2[6] = { 'e', 'x', 'c', 'e', 'e', 'd' };
+static symbol s_9_3[7] = { 'c', 'a', 'n', 'n', 'i', 'n', 'g' };
+static symbol s_9_4[6] = { 'i', 'n', 'n', 'i', 'n', 'g' };
+static symbol s_9_5[7] = { 'e', 'a', 'r', 'r', 'i', 'n', 'g' };
+static symbol s_9_6[7] = { 'h', 'e', 'r', 'r', 'i', 'n', 'g' };
+static symbol s_9_7[6] = { 'o', 'u', 't', 'i', 'n', 'g' };
+
+static struct among a_9[8] =
+{
+/*  0 */ { 7, s_9_0, -1, -1, 0},
+/*  1 */ { 7, s_9_1, -1, -1, 0},
+/*  2 */ { 6, s_9_2, -1, -1, 0},
+/*  3 */ { 7, s_9_3, -1, -1, 0},
+/*  4 */ { 6, s_9_4, -1, -1, 0},
+/*  5 */ { 7, s_9_5, -1, -1, 0},
+/*  6 */ { 7, s_9_6, -1, -1, 0},
+/*  7 */ { 6, s_9_7, -1, -1, 0}
+};
+
+static symbol s_10_0[5] = { 'a', 'n', 'd', 'e', 's' };
+static symbol s_10_1[5] = { 'a', 't', 'l', 'a', 's' };
+static symbol s_10_2[4] = { 'b', 'i', 'a', 's' };
+static symbol s_10_3[6] = { 'c', 'o', 's', 'm', 'o', 's' };
+static symbol s_10_4[5] = { 'd', 'y', 'i', 'n', 'g' };
+static symbol s_10_5[5] = { 'e', 'a', 'r', 'l', 'y' };
+static symbol s_10_6[6] = { 'g', 'e', 'n', 't', 'l', 'y' };
+static symbol s_10_7[4] = { 'h', 'o', 'w', 'e' };
+static symbol s_10_8[4] = { 'i', 'd', 'l', 'y' };
+static symbol s_10_9[5] = { 'l', 'y', 'i', 'n', 'g' };
+static symbol s_10_10[4] = { 'n', 'e', 'w', 's' };
+static symbol s_10_11[4] = { 'o', 'n', 'l', 'y' };
+static symbol s_10_12[6] = { 's', 'i', 'n', 'g', 'l', 'y' };
+static symbol s_10_13[5] = { 's', 'k', 'i', 'e', 's' };
+static symbol s_10_14[4] = { 's', 'k', 'i', 's' };
+static symbol s_10_15[3] = { 's', 'k', 'y' };
+static symbol s_10_16[5] = { 't', 'y', 'i', 'n', 'g' };
+static symbol s_10_17[4] = { 'u', 'g', 'l', 'y' };
+
+static struct among a_10[18] =
+{
+/*  0 */ { 5, s_10_0, -1, -1, 0},
+/*  1 */ { 5, s_10_1, -1, -1, 0},
+/*  2 */ { 4, s_10_2, -1, -1, 0},
+/*  3 */ { 6, s_10_3, -1, -1, 0},
+/*  4 */ { 5, s_10_4, -1, 3, 0},
+/*  5 */ { 5, s_10_5, -1, 9, 0},
+/*  6 */ { 6, s_10_6, -1, 7, 0},
+/*  7 */ { 4, s_10_7, -1, -1, 0},
+/*  8 */ { 4, s_10_8, -1, 6, 0},
+/*  9 */ { 5, s_10_9, -1, 4, 0},
+/* 10 */ { 4, s_10_10, -1, -1, 0},
+/* 11 */ { 4, s_10_11, -1, 10, 0},
+/* 12 */ { 6, s_10_12, -1, 11, 0},
+/* 13 */ { 5, s_10_13, -1, 2, 0},
+/* 14 */ { 4, s_10_14, -1, 1, 0},
+/* 15 */ { 3, s_10_15, -1, -1, 0},
+/* 16 */ { 5, s_10_16, -1, 5, 0},
+/* 17 */ { 4, s_10_17, -1, 8, 0}
+};
+
+static unsigned char g_v[] = { 17, 65, 16, 1 };
+
+static unsigned char g_v_WXY[] = { 1, 17, 65, 208, 1 };
+
+static unsigned char g_valid_LI[] = { 55, 141, 2 };
+
+static symbol s_0[] = { '\'' };
+static symbol s_1[] = { 'y' };
+static symbol s_2[] = { 'Y' };
+static symbol s_3[] = { 'y' };
+static symbol s_4[] = { 'Y' };
+static symbol s_5[] = { 's', 's' };
+static symbol s_6[] = { 'i' };
+static symbol s_7[] = { 'i', 'e' };
+static symbol s_8[] = { 'e', 'e' };
+static symbol s_9[] = { 'e' };
+static symbol s_10[] = { 'e' };
+static symbol s_11[] = { 'y' };
+static symbol s_12[] = { 'Y' };
+static symbol s_13[] = { 'i' };
+static symbol s_14[] = { 't', 'i', 'o', 'n' };
+static symbol s_15[] = { 'e', 'n', 'c', 'e' };
+static symbol s_16[] = { 'a', 'n', 'c', 'e' };
+static symbol s_17[] = { 'a', 'b', 'l', 'e' };
+static symbol s_18[] = { 'e', 'n', 't' };
+static symbol s_19[] = { 'i', 'z', 'e' };
+static symbol s_20[] = { 'a', 't', 'e' };
+static symbol s_21[] = { 'a', 'l' };
+static symbol s_22[] = { 'f', 'u', 'l' };
+static symbol s_23[] = { 'o', 'u', 's' };
+static symbol s_24[] = { 'i', 'v', 'e' };
+static symbol s_25[] = { 'b', 'l', 'e' };
+static symbol s_26[] = { 'l' };
+static symbol s_27[] = { 'o', 'g' };
+static symbol s_28[] = { 'f', 'u', 'l' };
+static symbol s_29[] = { 'l', 'e', 's', 's' };
+static symbol s_30[] = { 't', 'i', 'o', 'n' };
+static symbol s_31[] = { 'a', 't', 'e' };
+static symbol s_32[] = { 'a', 'l' };
+static symbol s_33[] = { 'i', 'c' };
+static symbol s_34[] = { 's' };
+static symbol s_35[] = { 't' };
+static symbol s_36[] = { 'l' };
+static symbol s_37[] = { 's', 'k', 'i' };
+static symbol s_38[] = { 's', 'k', 'y' };
+static symbol s_39[] = { 'd', 'i', 'e' };
+static symbol s_40[] = { 'l', 'i', 'e' };
+static symbol s_41[] = { 't', 'i', 'e' };
+static symbol s_42[] = { 'i', 'd', 'l' };
+static symbol s_43[] = { 'g', 'e', 'n', 't', 'l' };
+static symbol s_44[] = { 'u', 'g', 'l', 'i' };
+static symbol s_45[] = { 'e', 'a', 'r', 'l', 'i' };
+static symbol s_46[] = { 'o', 'n', 'l', 'i' };
+static symbol s_47[] = { 's', 'i', 'n', 'g', 'l' };
+static symbol s_48[] = { 'Y' };
+static symbol s_49[] = { 'y' };
+
+static int r_prelude(struct SN_env * z) {
+    z->B[0] = 0; /* unset Y_found, line 26 */
+    {	int c = z->c; /* do, line 27 */
+	z->bra = z->c; /* [, line 27 */
+	if (!(eq_s(z, 1, s_0))) goto lab0;
+	z->ket = z->c; /* ], line 27 */
+	{   int ret;
+	    ret = slice_del(z); /* delete, line 27 */
+	    if (ret < 0) return ret;
+	}
+    lab0:
+	z->c = c;
+    }
+    {	int c = z->c; /* do, line 28 */
+	z->bra = z->c; /* [, line 28 */
+	if (!(eq_s(z, 1, s_1))) goto lab1;
+	z->ket = z->c; /* ], line 28 */
+	{   int ret;
+	    ret = slice_from_s(z, 1, s_2); /* <-, line 28 */
+	    if (ret < 0) return ret;
+	}
+	z->B[0] = 1; /* set Y_found, line 28 */
+    lab1:
+	z->c = c;
+    }
+    {	int c = z->c; /* do, line 29 */
+	while(1) { /* repeat, line 29 */
+	    int c = z->c;
+	    while(1) { /* goto, line 29 */
+		int c = z->c;
+		if (!(in_grouping_U(z, g_v, 97, 121))) goto lab4;
+		z->bra = z->c; /* [, line 29 */
+		if (!(eq_s(z, 1, s_3))) goto lab4;
+		z->ket = z->c; /* ], line 29 */
+		z->c = c;
+		break;
+	    lab4:
+		z->c = c;
+		{   int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		    if (c < 0) goto lab3;
+		    z->c = c; /* goto, line 29 */
+		}
+	    }
+	    {	int ret;
+		ret = slice_from_s(z, 1, s_4); /* <-, line 29 */
+		if (ret < 0) return ret;
+	    }
+	    z->B[0] = 1; /* set Y_found, line 29 */
+	    continue;
+	lab3:
+	    z->c = c;
+	    break;
+	}
+	z->c = c;
+    }
+    return 1;
+}
+
+static int r_mark_regions(struct SN_env * z) {
+    z->I[0] = z->l;
+    z->I[1] = z->l;
+    {	int c = z->c; /* do, line 35 */
+	{   int c = z->c; /* or, line 40 */
+	    if (!(find_among(z, a_0, 2))) goto lab2; /* among, line 36 */
+	    goto lab1;
+	lab2:
+	    z->c = c;
+	    while(1) { /* gopast, line 40 */
+		if (!(in_grouping_U(z, g_v, 97, 121))) goto lab3;
+		break;
+	    lab3:
+		{   int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		    if (c < 0) goto lab0;
+		    z->c = c; /* gopast, line 40 */
+		}
+	    }
+	    while(1) { /* gopast, line 40 */
+		if (!(out_grouping_U(z, g_v, 97, 121))) goto lab4;
+		break;
+	    lab4:
+		{   int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		    if (c < 0) goto lab0;
+		    z->c = c; /* gopast, line 40 */
+		}
+	    }
+	}
+    lab1:
+	z->I[0] = z->c; /* setmark p1, line 41 */
+	while(1) { /* gopast, line 42 */
+	    if (!(in_grouping_U(z, g_v, 97, 121))) goto lab5;
+	    break;
+	lab5:
+	    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		if (c < 0) goto lab0;
+		z->c = c; /* gopast, line 42 */
+	    }
+	}
+	while(1) { /* gopast, line 42 */
+	    if (!(out_grouping_U(z, g_v, 97, 121))) goto lab6;
+	    break;
+	lab6:
+	    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		if (c < 0) goto lab0;
+		z->c = c; /* gopast, line 42 */
+	    }
+	}
+	z->I[1] = z->c; /* setmark p2, line 42 */
+    lab0:
+	z->c = c;
+    }
+    return 1;
+}
+
+static int r_shortv(struct SN_env * z) {
+    {	int m = z->l - z->c; (void) m; /* or, line 50 */
+	if (!(out_grouping_b_U(z, g_v_WXY, 89, 121))) goto lab1;
+	if (!(in_grouping_b_U(z, g_v, 97, 121))) goto lab1;
+	if (!(out_grouping_b_U(z, g_v, 97, 121))) goto lab1;
+	goto lab0;
+    lab1:
+	z->c = z->l - m;
+	if (!(out_grouping_b_U(z, g_v, 97, 121))) return 0;
+	if (!(in_grouping_b_U(z, g_v, 97, 121))) return 0;
+	if (z->c > z->lb) return 0; /* atlimit, line 51 */
+    }
+lab0:
+    return 1;
+}
+
+static int r_R1(struct SN_env * z) {
+    if (!(z->I[0] <= z->c)) return 0;
+    return 1;
+}
+
+static int r_R2(struct SN_env * z) {
+    if (!(z->I[1] <= z->c)) return 0;
+    return 1;
+}
+
+static int r_Step_1a(struct SN_env * z) {
+    int among_var;
+    {	int m = z->l - z->c; (void) m; /* try, line 58 */
+	z->ket = z->c; /* [, line 59 */
+	among_var = find_among_b(z, a_1, 3); /* substring, line 59 */
+	if (!(among_var)) { z->c = z->l - m; goto lab0; }
+	z->bra = z->c; /* ], line 59 */
+	switch(among_var) {
+	    case 0: { z->c = z->l - m; goto lab0; }
+	    case 1:
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 61 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	}
+    lab0:
+	;
+    }
+    z->ket = z->c; /* [, line 64 */
+    among_var = find_among_b(z, a_2, 6); /* substring, line 64 */
+    if (!(among_var)) return 0;
+    z->bra = z->c; /* ], line 64 */
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret;
+		ret = slice_from_s(z, 2, s_5); /* <-, line 65 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    {	int m = z->l - z->c; (void) m; /* or, line 67 */
+		{   int c = skip_utf8(z->p, z->c, z->lb, z->l, - 2);
+		    if (c < 0) goto lab2;
+		    z->c = c; /* hop, line 67 */
+		}
+		{   int ret;
+		    ret = slice_from_s(z, 1, s_6); /* <-, line 67 */
+		    if (ret < 0) return ret;
+		}
+		goto lab1;
+	    lab2:
+		z->c = z->l - m;
+		{   int ret;
+		    ret = slice_from_s(z, 2, s_7); /* <-, line 67 */
+		    if (ret < 0) return ret;
+		}
+	    }
+	lab1:
+	    break;
+	case 3:
+	    {	int c = skip_utf8(z->p, z->c, z->lb, 0, -1);
+		if (c < 0) return 0;
+		z->c = c; /* next, line 68 */
+	    }
+	    while(1) { /* gopast, line 68 */
+		if (!(in_grouping_b_U(z, g_v, 97, 121))) goto lab3;
+		break;
+	    lab3:
+		{   int c = skip_utf8(z->p, z->c, z->lb, 0, -1);
+		    if (c < 0) return 0;
+		    z->c = c; /* gopast, line 68 */
+		}
+	    }
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 68 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_Step_1b(struct SN_env * z) {
+    int among_var;
+    z->ket = z->c; /* [, line 74 */
+    among_var = find_among_b(z, a_4, 6); /* substring, line 74 */
+    if (!(among_var)) return 0;
+    z->bra = z->c; /* ], line 74 */
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret = r_R1(z);
+		if (ret == 0) return 0; /* call R1, line 76 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_from_s(z, 2, s_8); /* <-, line 76 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    {	int m_test = z->l - z->c; /* test, line 79 */
+		while(1) { /* gopast, line 79 */
+		    if (!(in_grouping_b_U(z, g_v, 97, 121))) goto lab0;
+		    break;
+		lab0:
+		    {	int c = skip_utf8(z->p, z->c, z->lb, 0, -1);
+			if (c < 0) return 0;
+			z->c = c; /* gopast, line 79 */
+		    }
+		}
+		z->c = z->l - m_test;
+	    }
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 79 */
+		if (ret < 0) return ret;
+	    }
+	    {	int m_test = z->l - z->c; /* test, line 80 */
+		among_var = find_among_b(z, a_3, 13); /* substring, line 80 */
+		if (!(among_var)) return 0;
+		z->c = z->l - m_test;
+	    }
+	    switch(among_var) {
+		case 0: return 0;
+		case 1:
+		    {	int ret;
+			{   int c = z->c;
+			    ret = insert_s(z, z->c, z->c, 1, s_9); /* <+, line 82 */
+			    z->c = c;
+			}
+			if (ret < 0) return ret;
+		    }
+		    break;
+		case 2:
+		    z->ket = z->c; /* [, line 85 */
+		    {	int c = skip_utf8(z->p, z->c, z->lb, 0, -1);
+			if (c < 0) return 0;
+			z->c = c; /* next, line 85 */
+		    }
+		    z->bra = z->c; /* ], line 85 */
+		    {	int ret;
+			ret = slice_del(z); /* delete, line 85 */
+			if (ret < 0) return ret;
+		    }
+		    break;
+		case 3:
+		    if (z->c != z->I[0]) return 0; /* atmark, line 86 */
+		    {	int m_test = z->l - z->c; /* test, line 86 */
+			{   int ret = r_shortv(z);
+			    if (ret == 0) return 0; /* call shortv, line 86 */
+			    if (ret < 0) return ret;
+			}
+			z->c = z->l - m_test;
+		    }
+		    {	int ret;
+			{   int c = z->c;
+			    ret = insert_s(z, z->c, z->c, 1, s_10); /* <+, line 86 */
+			    z->c = c;
+			}
+			if (ret < 0) return ret;
+		    }
+		    break;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_Step_1c(struct SN_env * z) {
+    z->ket = z->c; /* [, line 93 */
+    {	int m = z->l - z->c; (void) m; /* or, line 93 */
+	if (!(eq_s_b(z, 1, s_11))) goto lab1;
+	goto lab0;
+    lab1:
+	z->c = z->l - m;
+	if (!(eq_s_b(z, 1, s_12))) return 0;
+    }
+lab0:
+    z->bra = z->c; /* ], line 93 */
+    if (!(out_grouping_b_U(z, g_v, 97, 121))) return 0;
+    {	int m = z->l - z->c; (void) m; /* not, line 94 */
+	if (z->c > z->lb) goto lab2; /* atlimit, line 94 */
+	return 0;
+    lab2:
+	z->c = z->l - m;
+    }
+    {	int ret;
+	ret = slice_from_s(z, 1, s_13); /* <-, line 95 */
+	if (ret < 0) return ret;
+    }
+    return 1;
+}
+
+static int r_Step_2(struct SN_env * z) {
+    int among_var;
+    z->ket = z->c; /* [, line 99 */
+    among_var = find_among_b(z, a_5, 24); /* substring, line 99 */
+    if (!(among_var)) return 0;
+    z->bra = z->c; /* ], line 99 */
+    {	int ret = r_R1(z);
+	if (ret == 0) return 0; /* call R1, line 99 */
+	if (ret < 0) return ret;
+    }
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret;
+		ret = slice_from_s(z, 4, s_14); /* <-, line 100 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    {	int ret;
+		ret = slice_from_s(z, 4, s_15); /* <-, line 101 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 3:
+	    {	int ret;
+		ret = slice_from_s(z, 4, s_16); /* <-, line 102 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 4:
+	    {	int ret;
+		ret = slice_from_s(z, 4, s_17); /* <-, line 103 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 5:
+	    {	int ret;
+		ret = slice_from_s(z, 3, s_18); /* <-, line 104 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 6:
+	    {	int ret;
+		ret = slice_from_s(z, 3, s_19); /* <-, line 106 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 7:
+	    {	int ret;
+		ret = slice_from_s(z, 3, s_20); /* <-, line 108 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 8:
+	    {	int ret;
+		ret = slice_from_s(z, 2, s_21); /* <-, line 110 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 9:
+	    {	int ret;
+		ret = slice_from_s(z, 3, s_22); /* <-, line 111 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 10:
+	    {	int ret;
+		ret = slice_from_s(z, 3, s_23); /* <-, line 113 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 11:
+	    {	int ret;
+		ret = slice_from_s(z, 3, s_24); /* <-, line 115 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 12:
+	    {	int ret;
+		ret = slice_from_s(z, 3, s_25); /* <-, line 117 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 13:
+	    if (!(eq_s_b(z, 1, s_26))) return 0;
+	    {	int ret;
+		ret = slice_from_s(z, 2, s_27); /* <-, line 118 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 14:
+	    {	int ret;
+		ret = slice_from_s(z, 3, s_28); /* <-, line 119 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 15:
+	    {	int ret;
+		ret = slice_from_s(z, 4, s_29); /* <-, line 120 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 16:
+	    if (!(in_grouping_b_U(z, g_valid_LI, 99, 116))) return 0;
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 121 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_Step_3(struct SN_env * z) {
+    int among_var;
+    z->ket = z->c; /* [, line 126 */
+    among_var = find_among_b(z, a_6, 9); /* substring, line 126 */
+    if (!(among_var)) return 0;
+    z->bra = z->c; /* ], line 126 */
+    {	int ret = r_R1(z);
+	if (ret == 0) return 0; /* call R1, line 126 */
+	if (ret < 0) return ret;
+    }
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret;
+		ret = slice_from_s(z, 4, s_30); /* <-, line 127 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    {	int ret;
+		ret = slice_from_s(z, 3, s_31); /* <-, line 128 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 3:
+	    {	int ret;
+		ret = slice_from_s(z, 2, s_32); /* <-, line 129 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 4:
+	    {	int ret;
+		ret = slice_from_s(z, 2, s_33); /* <-, line 131 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 5:
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 133 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 6:
+	    {	int ret = r_R2(z);
+		if (ret == 0) return 0; /* call R2, line 135 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 135 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_Step_4(struct SN_env * z) {
+    int among_var;
+    z->ket = z->c; /* [, line 140 */
+    among_var = find_among_b(z, a_7, 18); /* substring, line 140 */
+    if (!(among_var)) return 0;
+    z->bra = z->c; /* ], line 140 */
+    {	int ret = r_R2(z);
+	if (ret == 0) return 0; /* call R2, line 140 */
+	if (ret < 0) return ret;
+    }
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 143 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    {	int m = z->l - z->c; (void) m; /* or, line 144 */
+		if (!(eq_s_b(z, 1, s_34))) goto lab1;
+		goto lab0;
+	    lab1:
+		z->c = z->l - m;
+		if (!(eq_s_b(z, 1, s_35))) return 0;
+	    }
+	lab0:
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 144 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_Step_5(struct SN_env * z) {
+    int among_var;
+    z->ket = z->c; /* [, line 149 */
+    among_var = find_among_b(z, a_8, 2); /* substring, line 149 */
+    if (!(among_var)) return 0;
+    z->bra = z->c; /* ], line 149 */
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int m = z->l - z->c; (void) m; /* or, line 150 */
+		{   int ret = r_R2(z);
+		    if (ret == 0) goto lab1; /* call R2, line 150 */
+		    if (ret < 0) return ret;
+		}
+		goto lab0;
+	    lab1:
+		z->c = z->l - m;
+		{   int ret = r_R1(z);
+		    if (ret == 0) return 0; /* call R1, line 150 */
+		    if (ret < 0) return ret;
+		}
+		{   int m = z->l - z->c; (void) m; /* not, line 150 */
+		    {	int ret = r_shortv(z);
+			if (ret == 0) goto lab2; /* call shortv, line 150 */
+			if (ret < 0) return ret;
+		    }
+		    return 0;
+		lab2:
+		    z->c = z->l - m;
+		}
+	    }
+	lab0:
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 150 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    {	int ret = r_R2(z);
+		if (ret == 0) return 0; /* call R2, line 151 */
+		if (ret < 0) return ret;
+	    }
+	    if (!(eq_s_b(z, 1, s_36))) return 0;
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 151 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_exception2(struct SN_env * z) {
+    z->ket = z->c; /* [, line 157 */
+    if (!(find_among_b(z, a_9, 8))) return 0; /* substring, line 157 */
+    z->bra = z->c; /* ], line 157 */
+    if (z->c > z->lb) return 0; /* atlimit, line 157 */
+    return 1;
+}
+
+static int r_exception1(struct SN_env * z) {
+    int among_var;
+    z->bra = z->c; /* [, line 169 */
+    among_var = find_among(z, a_10, 18); /* substring, line 169 */
+    if (!(among_var)) return 0;
+    z->ket = z->c; /* ], line 169 */
+    if (z->c < z->l) return 0; /* atlimit, line 169 */
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret;
+		ret = slice_from_s(z, 3, s_37); /* <-, line 173 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    {	int ret;
+		ret = slice_from_s(z, 3, s_38); /* <-, line 174 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 3:
+	    {	int ret;
+		ret = slice_from_s(z, 3, s_39); /* <-, line 175 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 4:
+	    {	int ret;
+		ret = slice_from_s(z, 3, s_40); /* <-, line 176 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 5:
+	    {	int ret;
+		ret = slice_from_s(z, 3, s_41); /* <-, line 177 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 6:
+	    {	int ret;
+		ret = slice_from_s(z, 3, s_42); /* <-, line 181 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 7:
+	    {	int ret;
+		ret = slice_from_s(z, 5, s_43); /* <-, line 182 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 8:
+	    {	int ret;
+		ret = slice_from_s(z, 4, s_44); /* <-, line 183 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 9:
+	    {	int ret;
+		ret = slice_from_s(z, 5, s_45); /* <-, line 184 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 10:
+	    {	int ret;
+		ret = slice_from_s(z, 4, s_46); /* <-, line 185 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 11:
+	    {	int ret;
+		ret = slice_from_s(z, 5, s_47); /* <-, line 186 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_postlude(struct SN_env * z) {
+    if (!(z->B[0])) return 0; /* Boolean test Y_found, line 202 */
+    while(1) { /* repeat, line 202 */
+	int c = z->c;
+	while(1) { /* goto, line 202 */
+	    int c = z->c;
+	    z->bra = z->c; /* [, line 202 */
+	    if (!(eq_s(z, 1, s_48))) goto lab1;
+	    z->ket = z->c; /* ], line 202 */
+	    z->c = c;
+	    break;
+	lab1:
+	    z->c = c;
+	    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		if (c < 0) goto lab0;
+		z->c = c; /* goto, line 202 */
+	    }
+	}
+	{   int ret;
+	    ret = slice_from_s(z, 1, s_49); /* <-, line 202 */
+	    if (ret < 0) return ret;
+	}
+	continue;
+    lab0:
+	z->c = c;
+	break;
+    }
+    return 1;
+}
+
+extern int english_UTF_8_stem(struct SN_env * z) {
+    {	int c = z->c; /* or, line 206 */
+	{   int ret = r_exception1(z);
+	    if (ret == 0) goto lab1; /* call exception1, line 206 */
+	    if (ret < 0) return ret;
+	}
+	goto lab0;
+    lab1:
+	z->c = c;
+	{   int c = z->c; /* not, line 207 */
+	    {	int c = skip_utf8(z->p, z->c, 0, z->l, + 3);
+		if (c < 0) goto lab3;
+		z->c = c; /* hop, line 207 */
+	    }
+	    goto lab2;
+	lab3:
+	    z->c = c;
+	}
+	goto lab0;
+    lab2:
+	z->c = c;
+	{   int c = z->c; /* do, line 208 */
+	    {	int ret = r_prelude(z);
+		if (ret == 0) goto lab4; /* call prelude, line 208 */
+		if (ret < 0) return ret;
+	    }
+	lab4:
+	    z->c = c;
+	}
+	{   int c = z->c; /* do, line 209 */
+	    {	int ret = r_mark_regions(z);
+		if (ret == 0) goto lab5; /* call mark_regions, line 209 */
+		if (ret < 0) return ret;
+	    }
+	lab5:
+	    z->c = c;
+	}
+	z->lb = z->c; z->c = z->l; /* backwards, line 210 */
+
+	{   int m = z->l - z->c; (void) m; /* do, line 212 */
+	    {	int ret = r_Step_1a(z);
+		if (ret == 0) goto lab6; /* call Step_1a, line 212 */
+		if (ret < 0) return ret;
+	    }
+	lab6:
+	    z->c = z->l - m;
+	}
+	{   int m = z->l - z->c; (void) m; /* or, line 214 */
+	    {	int ret = r_exception2(z);
+		if (ret == 0) goto lab8; /* call exception2, line 214 */
+		if (ret < 0) return ret;
+	    }
+	    goto lab7;
+	lab8:
+	    z->c = z->l - m;
+	    {	int m = z->l - z->c; (void) m; /* do, line 216 */
+		{   int ret = r_Step_1b(z);
+		    if (ret == 0) goto lab9; /* call Step_1b, line 216 */
+		    if (ret < 0) return ret;
+		}
+	    lab9:
+		z->c = z->l - m;
+	    }
+	    {	int m = z->l - z->c; (void) m; /* do, line 217 */
+		{   int ret = r_Step_1c(z);
+		    if (ret == 0) goto lab10; /* call Step_1c, line 217 */
+		    if (ret < 0) return ret;
+		}
+	    lab10:
+		z->c = z->l - m;
+	    }
+	    {	int m = z->l - z->c; (void) m; /* do, line 219 */
+		{   int ret = r_Step_2(z);
+		    if (ret == 0) goto lab11; /* call Step_2, line 219 */
+		    if (ret < 0) return ret;
+		}
+	    lab11:
+		z->c = z->l - m;
+	    }
+	    {	int m = z->l - z->c; (void) m; /* do, line 220 */
+		{   int ret = r_Step_3(z);
+		    if (ret == 0) goto lab12; /* call Step_3, line 220 */
+		    if (ret < 0) return ret;
+		}
+	    lab12:
+		z->c = z->l - m;
+	    }
+	    {	int m = z->l - z->c; (void) m; /* do, line 221 */
+		{   int ret = r_Step_4(z);
+		    if (ret == 0) goto lab13; /* call Step_4, line 221 */
+		    if (ret < 0) return ret;
+		}
+	    lab13:
+		z->c = z->l - m;
+	    }
+	    {	int m = z->l - z->c; (void) m; /* do, line 223 */
+		{   int ret = r_Step_5(z);
+		    if (ret == 0) goto lab14; /* call Step_5, line 223 */
+		    if (ret < 0) return ret;
+		}
+	    lab14:
+		z->c = z->l - m;
+	    }
+	}
+    lab7:
+	z->c = z->lb;
+	{   int c = z->c; /* do, line 226 */
+	    {	int ret = r_postlude(z);
+		if (ret == 0) goto lab15; /* call postlude, line 226 */
+		if (ret < 0) return ret;
+	    }
+	lab15:
+	    z->c = c;
+	}
+    }
+lab0:
+    return 1;
+}
+
+extern struct SN_env * english_UTF_8_create_env(void) { return SN_create_env(0, 2, 1); }
+
+extern void english_UTF_8_close_env(struct SN_env * z) { SN_close_env(z); }
+

Added: trunk/src/libstemmer/stem_UTF_8_english.h
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/stem_UTF_8_english.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2001, Dr Martin Porter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+/* This file was generated automatically by the Snowball to ANSI C compiler */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern struct SN_env * english_UTF_8_create_env(void);
+extern void english_UTF_8_close_env(struct SN_env * z);
+
+extern int english_UTF_8_stem(struct SN_env * z);
+
+#ifdef __cplusplus
+}
+#endif
+

Added: trunk/src/libstemmer/stem_UTF_8_finnish.c
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/stem_UTF_8_finnish.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,821 @@
+/*
+ * Copyright (c) 2001, Dr Martin Porter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+/* This file was generated automatically by the Snowball to ANSI C compiler */
+
+#include "header.h"
+
+extern int finnish_UTF_8_stem(struct SN_env * z);
+static int r_tidy(struct SN_env * z);
+static int r_other_endings(struct SN_env * z);
+static int r_t_plural(struct SN_env * z);
+static int r_i_plural(struct SN_env * z);
+static int r_case_ending(struct SN_env * z);
+static int r_VI(struct SN_env * z);
+static int r_LONG(struct SN_env * z);
+static int r_possessive(struct SN_env * z);
+static int r_particle_etc(struct SN_env * z);
+static int r_R2(struct SN_env * z);
+static int r_mark_regions(struct SN_env * z);
+
+extern struct SN_env * finnish_UTF_8_create_env(void);
+extern void finnish_UTF_8_close_env(struct SN_env * z);
+
+static symbol s_0_0[2] = { 'p', 'a' };
+static symbol s_0_1[3] = { 's', 't', 'i' };
+static symbol s_0_2[4] = { 'k', 'a', 'a', 'n' };
+static symbol s_0_3[3] = { 'h', 'a', 'n' };
+static symbol s_0_4[3] = { 'k', 'i', 'n' };
+static symbol s_0_5[4] = { 'h', 0xC3, 0xA4, 'n' };
+static symbol s_0_6[6] = { 'k', 0xC3, 0xA4, 0xC3, 0xA4, 'n' };
+static symbol s_0_7[2] = { 'k', 'o' };
+static symbol s_0_8[3] = { 'p', 0xC3, 0xA4 };
+static symbol s_0_9[3] = { 'k', 0xC3, 0xB6 };
+
+static struct among a_0[10] =
+{
+/*  0 */ { 2, s_0_0, -1, 1, 0},
+/*  1 */ { 3, s_0_1, -1, 2, 0},
+/*  2 */ { 4, s_0_2, -1, 1, 0},
+/*  3 */ { 3, s_0_3, -1, 1, 0},
+/*  4 */ { 3, s_0_4, -1, 1, 0},
+/*  5 */ { 4, s_0_5, -1, 1, 0},
+/*  6 */ { 6, s_0_6, -1, 1, 0},
+/*  7 */ { 2, s_0_7, -1, 1, 0},
+/*  8 */ { 3, s_0_8, -1, 1, 0},
+/*  9 */ { 3, s_0_9, -1, 1, 0}
+};
+
+static symbol s_1_0[3] = { 'l', 'l', 'a' };
+static symbol s_1_1[2] = { 'n', 'a' };
+static symbol s_1_2[3] = { 's', 's', 'a' };
+static symbol s_1_3[2] = { 't', 'a' };
+static symbol s_1_4[3] = { 'l', 't', 'a' };
+static symbol s_1_5[3] = { 's', 't', 'a' };
+
+static struct among a_1[6] =
+{
+/*  0 */ { 3, s_1_0, -1, -1, 0},
+/*  1 */ { 2, s_1_1, -1, -1, 0},
+/*  2 */ { 3, s_1_2, -1, -1, 0},
+/*  3 */ { 2, s_1_3, -1, -1, 0},
+/*  4 */ { 3, s_1_4, 3, -1, 0},
+/*  5 */ { 3, s_1_5, 3, -1, 0}
+};
+
+static symbol s_2_0[4] = { 'l', 'l', 0xC3, 0xA4 };
+static symbol s_2_1[3] = { 'n', 0xC3, 0xA4 };
+static symbol s_2_2[4] = { 's', 's', 0xC3, 0xA4 };
+static symbol s_2_3[3] = { 't', 0xC3, 0xA4 };
+static symbol s_2_4[4] = { 'l', 't', 0xC3, 0xA4 };
+static symbol s_2_5[4] = { 's', 't', 0xC3, 0xA4 };
+
+static struct among a_2[6] =
+{
+/*  0 */ { 4, s_2_0, -1, -1, 0},
+/*  1 */ { 3, s_2_1, -1, -1, 0},
+/*  2 */ { 4, s_2_2, -1, -1, 0},
+/*  3 */ { 3, s_2_3, -1, -1, 0},
+/*  4 */ { 4, s_2_4, 3, -1, 0},
+/*  5 */ { 4, s_2_5, 3, -1, 0}
+};
+
+static symbol s_3_0[3] = { 'l', 'l', 'e' };
+static symbol s_3_1[3] = { 'i', 'n', 'e' };
+
+static struct among a_3[2] =
+{
+/*  0 */ { 3, s_3_0, -1, -1, 0},
+/*  1 */ { 3, s_3_1, -1, -1, 0}
+};
+
+static symbol s_4_0[3] = { 'n', 's', 'a' };
+static symbol s_4_1[3] = { 'm', 'm', 'e' };
+static symbol s_4_2[3] = { 'n', 'n', 'e' };
+static symbol s_4_3[2] = { 'n', 'i' };
+static symbol s_4_4[2] = { 's', 'i' };
+static symbol s_4_5[2] = { 'a', 'n' };
+static symbol s_4_6[2] = { 'e', 'n' };
+static symbol s_4_7[3] = { 0xC3, 0xA4, 'n' };
+static symbol s_4_8[4] = { 'n', 's', 0xC3, 0xA4 };
+
+static struct among a_4[9] =
+{
+/*  0 */ { 3, s_4_0, -1, 3, 0},
+/*  1 */ { 3, s_4_1, -1, 3, 0},
+/*  2 */ { 3, s_4_2, -1, 3, 0},
+/*  3 */ { 2, s_4_3, -1, 2, 0},
+/*  4 */ { 2, s_4_4, -1, 1, 0},
+/*  5 */ { 2, s_4_5, -1, 4, 0},
+/*  6 */ { 2, s_4_6, -1, 6, 0},
+/*  7 */ { 3, s_4_7, -1, 5, 0},
+/*  8 */ { 4, s_4_8, -1, 3, 0}
+};
+
+static symbol s_5_0[2] = { 'a', 'a' };
+static symbol s_5_1[2] = { 'e', 'e' };
+static symbol s_5_2[2] = { 'i', 'i' };
+static symbol s_5_3[2] = { 'o', 'o' };
+static symbol s_5_4[2] = { 'u', 'u' };
+static symbol s_5_5[4] = { 0xC3, 0xA4, 0xC3, 0xA4 };
+static symbol s_5_6[4] = { 0xC3, 0xB6, 0xC3, 0xB6 };
+
+static struct among a_5[7] =
+{
+/*  0 */ { 2, s_5_0, -1, -1, 0},
+/*  1 */ { 2, s_5_1, -1, -1, 0},
+/*  2 */ { 2, s_5_2, -1, -1, 0},
+/*  3 */ { 2, s_5_3, -1, -1, 0},
+/*  4 */ { 2, s_5_4, -1, -1, 0},
+/*  5 */ { 4, s_5_5, -1, -1, 0},
+/*  6 */ { 4, s_5_6, -1, -1, 0}
+};
+
+static symbol s_6_0[1] = { 'a' };
+static symbol s_6_1[3] = { 'l', 'l', 'a' };
+static symbol s_6_2[2] = { 'n', 'a' };
+static symbol s_6_3[3] = { 's', 's', 'a' };
+static symbol s_6_4[2] = { 't', 'a' };
+static symbol s_6_5[3] = { 'l', 't', 'a' };
+static symbol s_6_6[3] = { 's', 't', 'a' };
+static symbol s_6_7[3] = { 't', 't', 'a' };
+static symbol s_6_8[3] = { 'l', 'l', 'e' };
+static symbol s_6_9[3] = { 'i', 'n', 'e' };
+static symbol s_6_10[3] = { 'k', 's', 'i' };
+static symbol s_6_11[1] = { 'n' };
+static symbol s_6_12[3] = { 'h', 'a', 'n' };
+static symbol s_6_13[3] = { 'd', 'e', 'n' };
+static symbol s_6_14[4] = { 's', 'e', 'e', 'n' };
+static symbol s_6_15[3] = { 'h', 'e', 'n' };
+static symbol s_6_16[4] = { 't', 't', 'e', 'n' };
+static symbol s_6_17[3] = { 'h', 'i', 'n' };
+static symbol s_6_18[4] = { 's', 'i', 'i', 'n' };
+static symbol s_6_19[3] = { 'h', 'o', 'n' };
+static symbol s_6_20[4] = { 'h', 0xC3, 0xA4, 'n' };
+static symbol s_6_21[4] = { 'h', 0xC3, 0xB6, 'n' };
+static symbol s_6_22[2] = { 0xC3, 0xA4 };
+static symbol s_6_23[4] = { 'l', 'l', 0xC3, 0xA4 };
+static symbol s_6_24[3] = { 'n', 0xC3, 0xA4 };
+static symbol s_6_25[4] = { 's', 's', 0xC3, 0xA4 };
+static symbol s_6_26[3] = { 't', 0xC3, 0xA4 };
+static symbol s_6_27[4] = { 'l', 't', 0xC3, 0xA4 };
+static symbol s_6_28[4] = { 's', 't', 0xC3, 0xA4 };
+static symbol s_6_29[4] = { 't', 't', 0xC3, 0xA4 };
+
+static struct among a_6[30] =
+{
+/*  0 */ { 1, s_6_0, -1, 8, 0},
+/*  1 */ { 3, s_6_1, 0, -1, 0},
+/*  2 */ { 2, s_6_2, 0, -1, 0},
+/*  3 */ { 3, s_6_3, 0, -1, 0},
+/*  4 */ { 2, s_6_4, 0, -1, 0},
+/*  5 */ { 3, s_6_5, 4, -1, 0},
+/*  6 */ { 3, s_6_6, 4, -1, 0},
+/*  7 */ { 3, s_6_7, 4, 9, 0},
+/*  8 */ { 3, s_6_8, -1, -1, 0},
+/*  9 */ { 3, s_6_9, -1, -1, 0},
+/* 10 */ { 3, s_6_10, -1, -1, 0},
+/* 11 */ { 1, s_6_11, -1, 7, 0},
+/* 12 */ { 3, s_6_12, 11, 1, 0},
+/* 13 */ { 3, s_6_13, 11, -1, r_VI},
+/* 14 */ { 4, s_6_14, 11, -1, r_LONG},
+/* 15 */ { 3, s_6_15, 11, 2, 0},
+/* 16 */ { 4, s_6_16, 11, -1, r_VI},
+/* 17 */ { 3, s_6_17, 11, 3, 0},
+/* 18 */ { 4, s_6_18, 11, -1, r_VI},
+/* 19 */ { 3, s_6_19, 11, 4, 0},
+/* 20 */ { 4, s_6_20, 11, 5, 0},
+/* 21 */ { 4, s_6_21, 11, 6, 0},
+/* 22 */ { 2, s_6_22, -1, 8, 0},
+/* 23 */ { 4, s_6_23, 22, -1, 0},
+/* 24 */ { 3, s_6_24, 22, -1, 0},
+/* 25 */ { 4, s_6_25, 22, -1, 0},
+/* 26 */ { 3, s_6_26, 22, -1, 0},
+/* 27 */ { 4, s_6_27, 26, -1, 0},
+/* 28 */ { 4, s_6_28, 26, -1, 0},
+/* 29 */ { 4, s_6_29, 26, 9, 0}
+};
+
+static symbol s_7_0[3] = { 'e', 'j', 'a' };
+static symbol s_7_1[3] = { 'm', 'm', 'a' };
+static symbol s_7_2[4] = { 'i', 'm', 'm', 'a' };
+static symbol s_7_3[3] = { 'm', 'p', 'a' };
+static symbol s_7_4[4] = { 'i', 'm', 'p', 'a' };
+static symbol s_7_5[3] = { 'm', 'm', 'i' };
+static symbol s_7_6[4] = { 'i', 'm', 'm', 'i' };
+static symbol s_7_7[3] = { 'm', 'p', 'i' };
+static symbol s_7_8[4] = { 'i', 'm', 'p', 'i' };
+static symbol s_7_9[4] = { 'e', 'j', 0xC3, 0xA4 };
+static symbol s_7_10[4] = { 'm', 'm', 0xC3, 0xA4 };
+static symbol s_7_11[5] = { 'i', 'm', 'm', 0xC3, 0xA4 };
+static symbol s_7_12[4] = { 'm', 'p', 0xC3, 0xA4 };
+static symbol s_7_13[5] = { 'i', 'm', 'p', 0xC3, 0xA4 };
+
+static struct among a_7[14] =
+{
+/*  0 */ { 3, s_7_0, -1, -1, 0},
+/*  1 */ { 3, s_7_1, -1, 1, 0},
+/*  2 */ { 4, s_7_2, 1, -1, 0},
+/*  3 */ { 3, s_7_3, -1, 1, 0},
+/*  4 */ { 4, s_7_4, 3, -1, 0},
+/*  5 */ { 3, s_7_5, -1, 1, 0},
+/*  6 */ { 4, s_7_6, 5, -1, 0},
+/*  7 */ { 3, s_7_7, -1, 1, 0},
+/*  8 */ { 4, s_7_8, 7, -1, 0},
+/*  9 */ { 4, s_7_9, -1, -1, 0},
+/* 10 */ { 4, s_7_10, -1, 1, 0},
+/* 11 */ { 5, s_7_11, 10, -1, 0},
+/* 12 */ { 4, s_7_12, -1, 1, 0},
+/* 13 */ { 5, s_7_13, 12, -1, 0}
+};
+
+static symbol s_8_0[1] = { 'i' };
+static symbol s_8_1[1] = { 'j' };
+
+static struct among a_8[2] =
+{
+/*  0 */ { 1, s_8_0, -1, -1, 0},
+/*  1 */ { 1, s_8_1, -1, -1, 0}
+};
+
+static symbol s_9_0[3] = { 'm', 'm', 'a' };
+static symbol s_9_1[4] = { 'i', 'm', 'm', 'a' };
+
+static struct among a_9[2] =
+{
+/*  0 */ { 3, s_9_0, -1, 1, 0},
+/*  1 */ { 4, s_9_1, 0, -1, 0}
+};
+
+static unsigned char g_AEI[] = { 17, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8 };
+
+static unsigned char g_V1[] = { 17, 65, 16, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 32 };
+
+static unsigned char g_V2[] = { 17, 65, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 32 };
+
+static unsigned char g_particle_end[] = { 17, 97, 24, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 32 };
+
+static symbol s_0[] = { 'k' };
+static symbol s_1[] = { 'k', 's', 'e' };
+static symbol s_2[] = { 'k', 's', 'i' };
+static symbol s_3[] = { 'i' };
+static symbol s_4[] = { 'a' };
+static symbol s_5[] = { 'e' };
+static symbol s_6[] = { 'i' };
+static symbol s_7[] = { 'o' };
+static symbol s_8[] = { 0xC3, 0xA4 };
+static symbol s_9[] = { 0xC3, 0xB6 };
+static symbol s_10[] = { 'i', 'e' };
+static symbol s_11[] = { 'e' };
+static symbol s_12[] = { 'p', 'o' };
+static symbol s_13[] = { 't' };
+static symbol s_14[] = { 'p', 'o' };
+static symbol s_15[] = { 'j' };
+static symbol s_16[] = { 'o' };
+static symbol s_17[] = { 'u' };
+static symbol s_18[] = { 'o' };
+static symbol s_19[] = { 'j' };
+
+static int r_mark_regions(struct SN_env * z) {
+    z->I[0] = z->l;
+    z->I[1] = z->l;
+    while(1) { /* goto, line 46 */
+	int c = z->c;
+	if (!(in_grouping_U(z, g_V1, 97, 246))) goto lab0;
+	z->c = c;
+	break;
+    lab0:
+	z->c = c;
+	{   int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+	    if (c < 0) return 0;
+	    z->c = c; /* goto, line 46 */
+	}
+    }
+    while(1) { /* gopast, line 46 */
+	if (!(out_grouping_U(z, g_V1, 97, 246))) goto lab1;
+	break;
+    lab1:
+	{   int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+	    if (c < 0) return 0;
+	    z->c = c; /* gopast, line 46 */
+	}
+    }
+    z->I[0] = z->c; /* setmark p1, line 46 */
+    while(1) { /* goto, line 47 */
+	int c = z->c;
+	if (!(in_grouping_U(z, g_V1, 97, 246))) goto lab2;
+	z->c = c;
+	break;
+    lab2:
+	z->c = c;
+	{   int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+	    if (c < 0) return 0;
+	    z->c = c; /* goto, line 47 */
+	}
+    }
+    while(1) { /* gopast, line 47 */
+	if (!(out_grouping_U(z, g_V1, 97, 246))) goto lab3;
+	break;
+    lab3:
+	{   int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+	    if (c < 0) return 0;
+	    z->c = c; /* gopast, line 47 */
+	}
+    }
+    z->I[1] = z->c; /* setmark p2, line 47 */
+    return 1;
+}
+
+static int r_R2(struct SN_env * z) {
+    if (!(z->I[1] <= z->c)) return 0;
+    return 1;
+}
+
+static int r_particle_etc(struct SN_env * z) {
+    int among_var;
+    {	int m3; /* setlimit, line 55 */
+	int m = z->l - z->c; (void) m;
+	if (z->c < z->I[0]) return 0;
+	z->c = z->I[0]; /* tomark, line 55 */
+	m3 = z->lb; z->lb = z->c;
+	z->c = z->l - m;
+	z->ket = z->c; /* [, line 55 */
+	among_var = find_among_b(z, a_0, 10); /* substring, line 55 */
+	if (!(among_var)) { z->lb = m3; return 0; }
+	z->bra = z->c; /* ], line 55 */
+	z->lb = m3;
+    }
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    if (!(in_grouping_b_U(z, g_particle_end, 97, 246))) return 0;
+	    break;
+	case 2:
+	    {	int ret = r_R2(z);
+		if (ret == 0) return 0; /* call R2, line 64 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    {	int ret;
+	ret = slice_del(z); /* delete, line 66 */
+	if (ret < 0) return ret;
+    }
+    return 1;
+}
+
+static int r_possessive(struct SN_env * z) {
+    int among_var;
+    {	int m3; /* setlimit, line 69 */
+	int m = z->l - z->c; (void) m;
+	if (z->c < z->I[0]) return 0;
+	z->c = z->I[0]; /* tomark, line 69 */
+	m3 = z->lb; z->lb = z->c;
+	z->c = z->l - m;
+	z->ket = z->c; /* [, line 69 */
+	among_var = find_among_b(z, a_4, 9); /* substring, line 69 */
+	if (!(among_var)) { z->lb = m3; return 0; }
+	z->bra = z->c; /* ], line 69 */
+	z->lb = m3;
+    }
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int m = z->l - z->c; (void) m; /* not, line 72 */
+		if (!(eq_s_b(z, 1, s_0))) goto lab0;
+		return 0;
+	    lab0:
+		z->c = z->l - m;
+	    }
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 72 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 74 */
+		if (ret < 0) return ret;
+	    }
+	    z->ket = z->c; /* [, line 74 */
+	    if (!(eq_s_b(z, 3, s_1))) return 0;
+	    z->bra = z->c; /* ], line 74 */
+	    {	int ret;
+		ret = slice_from_s(z, 3, s_2); /* <-, line 74 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 3:
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 78 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 4:
+	    if (!(find_among_b(z, a_1, 6))) return 0; /* among, line 81 */
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 81 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 5:
+	    if (!(find_among_b(z, a_2, 6))) return 0; /* among, line 83 */
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 84 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 6:
+	    if (!(find_among_b(z, a_3, 2))) return 0; /* among, line 86 */
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 86 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_LONG(struct SN_env * z) {
+    if (!(find_among_b(z, a_5, 7))) return 0; /* among, line 91 */
+    return 1;
+}
+
+static int r_VI(struct SN_env * z) {
+    if (!(eq_s_b(z, 1, s_3))) return 0;
+    if (!(in_grouping_b_U(z, g_V2, 97, 246))) return 0;
+    return 1;
+}
+
+static int r_case_ending(struct SN_env * z) {
+    int among_var;
+    {	int m3; /* setlimit, line 96 */
+	int m = z->l - z->c; (void) m;
+	if (z->c < z->I[0]) return 0;
+	z->c = z->I[0]; /* tomark, line 96 */
+	m3 = z->lb; z->lb = z->c;
+	z->c = z->l - m;
+	z->ket = z->c; /* [, line 96 */
+	among_var = find_among_b(z, a_6, 30); /* substring, line 96 */
+	if (!(among_var)) { z->lb = m3; return 0; }
+	z->bra = z->c; /* ], line 96 */
+	z->lb = m3;
+    }
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    if (!(eq_s_b(z, 1, s_4))) return 0;
+	    break;
+	case 2:
+	    if (!(eq_s_b(z, 1, s_5))) return 0;
+	    break;
+	case 3:
+	    if (!(eq_s_b(z, 1, s_6))) return 0;
+	    break;
+	case 4:
+	    if (!(eq_s_b(z, 1, s_7))) return 0;
+	    break;
+	case 5:
+	    if (!(eq_s_b(z, 2, s_8))) return 0;
+	    break;
+	case 6:
+	    if (!(eq_s_b(z, 2, s_9))) return 0;
+	    break;
+	case 7:
+	    {	int m = z->l - z->c; (void) m; /* try, line 111 */
+		{   int m = z->l - z->c; (void) m; /* and, line 113 */
+		    {	int m = z->l - z->c; (void) m; /* or, line 112 */
+			{   int ret = r_LONG(z);
+			    if (ret == 0) goto lab2; /* call LONG, line 111 */
+			    if (ret < 0) return ret;
+			}
+			goto lab1;
+		    lab2:
+			z->c = z->l - m;
+			if (!(eq_s_b(z, 2, s_10))) { z->c = z->l - m; goto lab0; }
+		    }
+		lab1:
+		    z->c = z->l - m;
+		    {	int c = skip_utf8(z->p, z->c, z->lb, 0, -1);
+			if (c < 0) { z->c = z->l - m; goto lab0; }
+			z->c = c; /* next, line 113 */
+		    }
+		}
+		z->bra = z->c; /* ], line 113 */
+	    lab0:
+		;
+	    }
+	    break;
+	case 8:
+	    if (!(in_grouping_b_U(z, g_V1, 97, 246))) return 0;
+	    if (!(out_grouping_b_U(z, g_V1, 97, 246))) return 0;
+	    break;
+	case 9:
+	    if (!(eq_s_b(z, 1, s_11))) return 0;
+	    break;
+    }
+    {	int ret;
+	ret = slice_del(z); /* delete, line 138 */
+	if (ret < 0) return ret;
+    }
+    z->B[0] = 1; /* set ending_removed, line 139 */
+    return 1;
+}
+
+static int r_other_endings(struct SN_env * z) {
+    int among_var;
+    {	int m3; /* setlimit, line 142 */
+	int m = z->l - z->c; (void) m;
+	if (z->c < z->I[1]) return 0;
+	z->c = z->I[1]; /* tomark, line 142 */
+	m3 = z->lb; z->lb = z->c;
+	z->c = z->l - m;
+	z->ket = z->c; /* [, line 142 */
+	among_var = find_among_b(z, a_7, 14); /* substring, line 142 */
+	if (!(among_var)) { z->lb = m3; return 0; }
+	z->bra = z->c; /* ], line 142 */
+	z->lb = m3;
+    }
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int m = z->l - z->c; (void) m; /* not, line 146 */
+		if (!(eq_s_b(z, 2, s_12))) goto lab0;
+		return 0;
+	    lab0:
+		z->c = z->l - m;
+	    }
+	    break;
+    }
+    {	int ret;
+	ret = slice_del(z); /* delete, line 151 */
+	if (ret < 0) return ret;
+    }
+    return 1;
+}
+
+static int r_i_plural(struct SN_env * z) {
+    {	int m3; /* setlimit, line 154 */
+	int m = z->l - z->c; (void) m;
+	if (z->c < z->I[0]) return 0;
+	z->c = z->I[0]; /* tomark, line 154 */
+	m3 = z->lb; z->lb = z->c;
+	z->c = z->l - m;
+	z->ket = z->c; /* [, line 154 */
+	if (!(find_among_b(z, a_8, 2))) { z->lb = m3; return 0; } /* substring, line 154 */
+	z->bra = z->c; /* ], line 154 */
+	z->lb = m3;
+    }
+    {	int ret;
+	ret = slice_del(z); /* delete, line 158 */
+	if (ret < 0) return ret;
+    }
+    return 1;
+}
+
+static int r_t_plural(struct SN_env * z) {
+    int among_var;
+    {	int m3; /* setlimit, line 161 */
+	int m = z->l - z->c; (void) m;
+	if (z->c < z->I[0]) return 0;
+	z->c = z->I[0]; /* tomark, line 161 */
+	m3 = z->lb; z->lb = z->c;
+	z->c = z->l - m;
+	z->ket = z->c; /* [, line 162 */
+	if (!(eq_s_b(z, 1, s_13))) { z->lb = m3; return 0; }
+	z->bra = z->c; /* ], line 162 */
+	{   int m_test = z->l - z->c; /* test, line 162 */
+	    if (!(in_grouping_b_U(z, g_V1, 97, 246))) { z->lb = m3; return 0; }
+	    z->c = z->l - m_test;
+	}
+	{   int ret;
+	    ret = slice_del(z); /* delete, line 163 */
+	    if (ret < 0) return ret;
+	}
+	z->lb = m3;
+    }
+    {	int m3; /* setlimit, line 165 */
+	int m = z->l - z->c; (void) m;
+	if (z->c < z->I[1]) return 0;
+	z->c = z->I[1]; /* tomark, line 165 */
+	m3 = z->lb; z->lb = z->c;
+	z->c = z->l - m;
+	z->ket = z->c; /* [, line 165 */
+	among_var = find_among_b(z, a_9, 2); /* substring, line 165 */
+	if (!(among_var)) { z->lb = m3; return 0; }
+	z->bra = z->c; /* ], line 165 */
+	z->lb = m3;
+    }
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int m = z->l - z->c; (void) m; /* not, line 167 */
+		if (!(eq_s_b(z, 2, s_14))) goto lab0;
+		return 0;
+	    lab0:
+		z->c = z->l - m;
+	    }
+	    break;
+    }
+    {	int ret;
+	ret = slice_del(z); /* delete, line 170 */
+	if (ret < 0) return ret;
+    }
+    return 1;
+}
+
+static int r_tidy(struct SN_env * z) {
+    {	int m3; /* setlimit, line 173 */
+	int m = z->l - z->c; (void) m;
+	if (z->c < z->I[0]) return 0;
+	z->c = z->I[0]; /* tomark, line 173 */
+	m3 = z->lb; z->lb = z->c;
+	z->c = z->l - m;
+	{   int m = z->l - z->c; (void) m; /* do, line 174 */
+	    {	int m = z->l - z->c; (void) m; /* and, line 174 */
+		{   int ret = r_LONG(z);
+		    if (ret == 0) goto lab0; /* call LONG, line 174 */
+		    if (ret < 0) return ret;
+		}
+		z->c = z->l - m;
+		z->ket = z->c; /* [, line 174 */
+		{   int c = skip_utf8(z->p, z->c, z->lb, 0, -1);
+		    if (c < 0) goto lab0;
+		    z->c = c; /* next, line 174 */
+		}
+		z->bra = z->c; /* ], line 174 */
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 174 */
+		    if (ret < 0) return ret;
+		}
+	    }
+	lab0:
+	    z->c = z->l - m;
+	}
+	{   int m = z->l - z->c; (void) m; /* do, line 175 */
+	    z->ket = z->c; /* [, line 175 */
+	    if (!(in_grouping_b_U(z, g_AEI, 97, 228))) goto lab1;
+	    z->bra = z->c; /* ], line 175 */
+	    if (!(out_grouping_b_U(z, g_V1, 97, 246))) goto lab1;
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 175 */
+		if (ret < 0) return ret;
+	    }
+	lab1:
+	    z->c = z->l - m;
+	}
+	{   int m = z->l - z->c; (void) m; /* do, line 176 */
+	    z->ket = z->c; /* [, line 176 */
+	    if (!(eq_s_b(z, 1, s_15))) goto lab2;
+	    z->bra = z->c; /* ], line 176 */
+	    {	int m = z->l - z->c; (void) m; /* or, line 176 */
+		if (!(eq_s_b(z, 1, s_16))) goto lab4;
+		goto lab3;
+	    lab4:
+		z->c = z->l - m;
+		if (!(eq_s_b(z, 1, s_17))) goto lab2;
+	    }
+	lab3:
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 176 */
+		if (ret < 0) return ret;
+	    }
+	lab2:
+	    z->c = z->l - m;
+	}
+	{   int m = z->l - z->c; (void) m; /* do, line 177 */
+	    z->ket = z->c; /* [, line 177 */
+	    if (!(eq_s_b(z, 1, s_18))) goto lab5;
+	    z->bra = z->c; /* ], line 177 */
+	    if (!(eq_s_b(z, 1, s_19))) goto lab5;
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 177 */
+		if (ret < 0) return ret;
+	    }
+	lab5:
+	    z->c = z->l - m;
+	}
+	z->lb = m3;
+    }
+    while(1) { /* goto, line 179 */
+	int m = z->l - z->c; (void) m;
+	if (!(out_grouping_b_U(z, g_V1, 97, 246))) goto lab6;
+	z->c = z->l - m;
+	break;
+    lab6:
+	z->c = z->l - m;
+	{   int c = skip_utf8(z->p, z->c, z->lb, 0, -1);
+	    if (c < 0) return 0;
+	    z->c = c; /* goto, line 179 */
+	}
+    }
+    z->ket = z->c; /* [, line 179 */
+    {	int c = skip_utf8(z->p, z->c, z->lb, 0, -1);
+	if (c < 0) return 0;
+	z->c = c; /* next, line 179 */
+    }
+    z->bra = z->c; /* ], line 179 */
+    z->S[0] = slice_to(z, z->S[0]); /* -> x, line 179 */
+    if (z->S[0] == 0) return -1; /* -> x, line 179 */
+    if (!(eq_v_b(z, z->S[0]))) return 0; /* name x, line 179 */
+    {	int ret;
+	ret = slice_del(z); /* delete, line 179 */
+	if (ret < 0) return ret;
+    }
+    return 1;
+}
+
+extern int finnish_UTF_8_stem(struct SN_env * z) {
+    {	int c = z->c; /* do, line 185 */
+	{   int ret = r_mark_regions(z);
+	    if (ret == 0) goto lab0; /* call mark_regions, line 185 */
+	    if (ret < 0) return ret;
+	}
+    lab0:
+	z->c = c;
+    }
+    z->B[0] = 0; /* unset ending_removed, line 186 */
+    z->lb = z->c; z->c = z->l; /* backwards, line 187 */
+
+    {	int m = z->l - z->c; (void) m; /* do, line 188 */
+	{   int ret = r_particle_etc(z);
+	    if (ret == 0) goto lab1; /* call particle_etc, line 188 */
+	    if (ret < 0) return ret;
+	}
+    lab1:
+	z->c = z->l - m;
+    }
+    {	int m = z->l - z->c; (void) m; /* do, line 189 */
+	{   int ret = r_possessive(z);
+	    if (ret == 0) goto lab2; /* call possessive, line 189 */
+	    if (ret < 0) return ret;
+	}
+    lab2:
+	z->c = z->l - m;
+    }
+    {	int m = z->l - z->c; (void) m; /* do, line 190 */
+	{   int ret = r_case_ending(z);
+	    if (ret == 0) goto lab3; /* call case_ending, line 190 */
+	    if (ret < 0) return ret;
+	}
+    lab3:
+	z->c = z->l - m;
+    }
+    {	int m = z->l - z->c; (void) m; /* do, line 191 */
+	{   int ret = r_other_endings(z);
+	    if (ret == 0) goto lab4; /* call other_endings, line 191 */
+	    if (ret < 0) return ret;
+	}
+    lab4:
+	z->c = z->l - m;
+    }
+    {	int m = z->l - z->c; (void) m; /* or, line 192 */
+	if (!(z->B[0])) goto lab6; /* Boolean test ending_removed, line 192 */
+	{   int m = z->l - z->c; (void) m; /* do, line 192 */
+	    {	int ret = r_i_plural(z);
+		if (ret == 0) goto lab7; /* call i_plural, line 192 */
+		if (ret < 0) return ret;
+	    }
+	lab7:
+	    z->c = z->l - m;
+	}
+	goto lab5;
+    lab6:
+	z->c = z->l - m;
+	{   int m = z->l - z->c; (void) m; /* do, line 192 */
+	    {	int ret = r_t_plural(z);
+		if (ret == 0) goto lab8; /* call t_plural, line 192 */
+		if (ret < 0) return ret;
+	    }
+	lab8:
+	    z->c = z->l - m;
+	}
+    }
+lab5:
+    {	int m = z->l - z->c; (void) m; /* do, line 193 */
+	{   int ret = r_tidy(z);
+	    if (ret == 0) goto lab9; /* call tidy, line 193 */
+	    if (ret < 0) return ret;
+	}
+    lab9:
+	z->c = z->l - m;
+    }
+    z->c = z->lb;
+    return 1;
+}
+
+extern struct SN_env * finnish_UTF_8_create_env(void) { return SN_create_env(1, 2, 1); }
+
+extern void finnish_UTF_8_close_env(struct SN_env * z) { SN_close_env(z); }
+

Added: trunk/src/libstemmer/stem_UTF_8_finnish.h
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/stem_UTF_8_finnish.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2001, Dr Martin Porter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+/* This file was generated automatically by the Snowball to ANSI C compiler */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern struct SN_env * finnish_UTF_8_create_env(void);
+extern void finnish_UTF_8_close_env(struct SN_env * z);
+
+extern int finnish_UTF_8_stem(struct SN_env * z);
+
+#ifdef __cplusplus
+}
+#endif
+

Added: trunk/src/libstemmer/stem_UTF_8_french.c
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/stem_UTF_8_french.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,1309 @@
+/*
+ * Copyright (c) 2001, Dr Martin Porter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+/* This file was generated automatically by the Snowball to ANSI C compiler */
+
+#include "header.h"
+
+extern int french_UTF_8_stem(struct SN_env * z);
+static int r_un_accent(struct SN_env * z);
+static int r_un_double(struct SN_env * z);
+static int r_residual_suffix(struct SN_env * z);
+static int r_verb_suffix(struct SN_env * z);
+static int r_i_verb_suffix(struct SN_env * z);
+static int r_standard_suffix(struct SN_env * z);
+static int r_R2(struct SN_env * z);
+static int r_R1(struct SN_env * z);
+static int r_RV(struct SN_env * z);
+static int r_mark_regions(struct SN_env * z);
+static int r_postlude(struct SN_env * z);
+static int r_prelude(struct SN_env * z);
+
+extern struct SN_env * french_UTF_8_create_env(void);
+extern void french_UTF_8_close_env(struct SN_env * z);
+
+static symbol s_0_1[1] = { 'I' };
+static symbol s_0_2[1] = { 'U' };
+static symbol s_0_3[1] = { 'Y' };
+
+static struct among a_0[4] =
+{
+/*  0 */ { 0, 0, -1, 4, 0},
+/*  1 */ { 1, s_0_1, 0, 1, 0},
+/*  2 */ { 1, s_0_2, 0, 2, 0},
+/*  3 */ { 1, s_0_3, 0, 3, 0}
+};
+
+static symbol s_1_0[3] = { 'i', 'q', 'U' };
+static symbol s_1_1[3] = { 'a', 'b', 'l' };
+static symbol s_1_2[4] = { 'I', 0xC3, 0xA8, 'r' };
+static symbol s_1_3[4] = { 'i', 0xC3, 0xA8, 'r' };
+static symbol s_1_4[3] = { 'e', 'u', 's' };
+static symbol s_1_5[2] = { 'i', 'v' };
+
+static struct among a_1[6] =
+{
+/*  0 */ { 3, s_1_0, -1, 3, 0},
+/*  1 */ { 3, s_1_1, -1, 3, 0},
+/*  2 */ { 4, s_1_2, -1, 4, 0},
+/*  3 */ { 4, s_1_3, -1, 4, 0},
+/*  4 */ { 3, s_1_4, -1, 2, 0},
+/*  5 */ { 2, s_1_5, -1, 1, 0}
+};
+
+static symbol s_2_0[2] = { 'i', 'c' };
+static symbol s_2_1[4] = { 'a', 'b', 'i', 'l' };
+static symbol s_2_2[2] = { 'i', 'v' };
+
+static struct among a_2[3] =
+{
+/*  0 */ { 2, s_2_0, -1, 2, 0},
+/*  1 */ { 4, s_2_1, -1, 1, 0},
+/*  2 */ { 2, s_2_2, -1, 3, 0}
+};
+
+static symbol s_3_0[4] = { 'i', 'q', 'U', 'e' };
+static symbol s_3_1[6] = { 'a', 't', 'r', 'i', 'c', 'e' };
+static symbol s_3_2[4] = { 'a', 'n', 'c', 'e' };
+static symbol s_3_3[4] = { 'e', 'n', 'c', 'e' };
+static symbol s_3_4[5] = { 'l', 'o', 'g', 'i', 'e' };
+static symbol s_3_5[4] = { 'a', 'b', 'l', 'e' };
+static symbol s_3_6[4] = { 'i', 's', 'm', 'e' };
+static symbol s_3_7[4] = { 'e', 'u', 's', 'e' };
+static symbol s_3_8[4] = { 'i', 's', 't', 'e' };
+static symbol s_3_9[3] = { 'i', 'v', 'e' };
+static symbol s_3_10[2] = { 'i', 'f' };
+static symbol s_3_11[5] = { 'u', 's', 'i', 'o', 'n' };
+static symbol s_3_12[5] = { 'a', 't', 'i', 'o', 'n' };
+static symbol s_3_13[5] = { 'u', 't', 'i', 'o', 'n' };
+static symbol s_3_14[5] = { 'a', 't', 'e', 'u', 'r' };
+static symbol s_3_15[5] = { 'i', 'q', 'U', 'e', 's' };
+static symbol s_3_16[7] = { 'a', 't', 'r', 'i', 'c', 'e', 's' };
+static symbol s_3_17[5] = { 'a', 'n', 'c', 'e', 's' };
+static symbol s_3_18[5] = { 'e', 'n', 'c', 'e', 's' };
+static symbol s_3_19[6] = { 'l', 'o', 'g', 'i', 'e', 's' };
+static symbol s_3_20[5] = { 'a', 'b', 'l', 'e', 's' };
+static symbol s_3_21[5] = { 'i', 's', 'm', 'e', 's' };
+static symbol s_3_22[5] = { 'e', 'u', 's', 'e', 's' };
+static symbol s_3_23[5] = { 'i', 's', 't', 'e', 's' };
+static symbol s_3_24[4] = { 'i', 'v', 'e', 's' };
+static symbol s_3_25[3] = { 'i', 'f', 's' };
+static symbol s_3_26[6] = { 'u', 's', 'i', 'o', 'n', 's' };
+static symbol s_3_27[6] = { 'a', 't', 'i', 'o', 'n', 's' };
+static symbol s_3_28[6] = { 'u', 't', 'i', 'o', 'n', 's' };
+static symbol s_3_29[6] = { 'a', 't', 'e', 'u', 'r', 's' };
+static symbol s_3_30[5] = { 'm', 'e', 'n', 't', 's' };
+static symbol s_3_31[6] = { 'e', 'm', 'e', 'n', 't', 's' };
+static symbol s_3_32[9] = { 'i', 's', 's', 'e', 'm', 'e', 'n', 't', 's' };
+static symbol s_3_33[5] = { 'i', 't', 0xC3, 0xA9, 's' };
+static symbol s_3_34[4] = { 'm', 'e', 'n', 't' };
+static symbol s_3_35[5] = { 'e', 'm', 'e', 'n', 't' };
+static symbol s_3_36[8] = { 'i', 's', 's', 'e', 'm', 'e', 'n', 't' };
+static symbol s_3_37[6] = { 'a', 'm', 'm', 'e', 'n', 't' };
+static symbol s_3_38[6] = { 'e', 'm', 'm', 'e', 'n', 't' };
+static symbol s_3_39[3] = { 'a', 'u', 'x' };
+static symbol s_3_40[4] = { 'e', 'a', 'u', 'x' };
+static symbol s_3_41[3] = { 'e', 'u', 'x' };
+static symbol s_3_42[4] = { 'i', 't', 0xC3, 0xA9 };
+
+static struct among a_3[43] =
+{
+/*  0 */ { 4, s_3_0, -1, 1, 0},
+/*  1 */ { 6, s_3_1, -1, 2, 0},
+/*  2 */ { 4, s_3_2, -1, 1, 0},
+/*  3 */ { 4, s_3_3, -1, 5, 0},
+/*  4 */ { 5, s_3_4, -1, 3, 0},
+/*  5 */ { 4, s_3_5, -1, 1, 0},
+/*  6 */ { 4, s_3_6, -1, 1, 0},
+/*  7 */ { 4, s_3_7, -1, 11, 0},
+/*  8 */ { 4, s_3_8, -1, 1, 0},
+/*  9 */ { 3, s_3_9, -1, 8, 0},
+/* 10 */ { 2, s_3_10, -1, 8, 0},
+/* 11 */ { 5, s_3_11, -1, 4, 0},
+/* 12 */ { 5, s_3_12, -1, 2, 0},
+/* 13 */ { 5, s_3_13, -1, 4, 0},
+/* 14 */ { 5, s_3_14, -1, 2, 0},
+/* 15 */ { 5, s_3_15, -1, 1, 0},
+/* 16 */ { 7, s_3_16, -1, 2, 0},
+/* 17 */ { 5, s_3_17, -1, 1, 0},
+/* 18 */ { 5, s_3_18, -1, 5, 0},
+/* 19 */ { 6, s_3_19, -1, 3, 0},
+/* 20 */ { 5, s_3_20, -1, 1, 0},
+/* 21 */ { 5, s_3_21, -1, 1, 0},
+/* 22 */ { 5, s_3_22, -1, 11, 0},
+/* 23 */ { 5, s_3_23, -1, 1, 0},
+/* 24 */ { 4, s_3_24, -1, 8, 0},
+/* 25 */ { 3, s_3_25, -1, 8, 0},
+/* 26 */ { 6, s_3_26, -1, 4, 0},
+/* 27 */ { 6, s_3_27, -1, 2, 0},
+/* 28 */ { 6, s_3_28, -1, 4, 0},
+/* 29 */ { 6, s_3_29, -1, 2, 0},
+/* 30 */ { 5, s_3_30, -1, 15, 0},
+/* 31 */ { 6, s_3_31, 30, 6, 0},
+/* 32 */ { 9, s_3_32, 31, 12, 0},
+/* 33 */ { 5, s_3_33, -1, 7, 0},
+/* 34 */ { 4, s_3_34, -1, 15, 0},
+/* 35 */ { 5, s_3_35, 34, 6, 0},
+/* 36 */ { 8, s_3_36, 35, 12, 0},
+/* 37 */ { 6, s_3_37, 34, 13, 0},
+/* 38 */ { 6, s_3_38, 34, 14, 0},
+/* 39 */ { 3, s_3_39, -1, 10, 0},
+/* 40 */ { 4, s_3_40, 39, 9, 0},
+/* 41 */ { 3, s_3_41, -1, 1, 0},
+/* 42 */ { 4, s_3_42, -1, 7, 0}
+};
+
+static symbol s_4_0[3] = { 'i', 'r', 'a' };
+static symbol s_4_1[2] = { 'i', 'e' };
+static symbol s_4_2[4] = { 'i', 's', 's', 'e' };
+static symbol s_4_3[7] = { 'i', 's', 's', 'a', 'n', 't', 'e' };
+static symbol s_4_4[1] = { 'i' };
+static symbol s_4_5[4] = { 'i', 'r', 'a', 'i' };
+static symbol s_4_6[2] = { 'i', 'r' };
+static symbol s_4_7[4] = { 'i', 'r', 'a', 's' };
+static symbol s_4_8[3] = { 'i', 'e', 's' };
+static symbol s_4_9[5] = { 0xC3, 0xAE, 'm', 'e', 's' };
+static symbol s_4_10[5] = { 'i', 's', 's', 'e', 's' };
+static symbol s_4_11[8] = { 'i', 's', 's', 'a', 'n', 't', 'e', 's' };
+static symbol s_4_12[5] = { 0xC3, 0xAE, 't', 'e', 's' };
+static symbol s_4_13[2] = { 'i', 's' };
+static symbol s_4_14[5] = { 'i', 'r', 'a', 'i', 's' };
+static symbol s_4_15[6] = { 'i', 's', 's', 'a', 'i', 's' };
+static symbol s_4_16[6] = { 'i', 'r', 'i', 'o', 'n', 's' };
+static symbol s_4_17[7] = { 'i', 's', 's', 'i', 'o', 'n', 's' };
+static symbol s_4_18[5] = { 'i', 'r', 'o', 'n', 's' };
+static symbol s_4_19[6] = { 'i', 's', 's', 'o', 'n', 's' };
+static symbol s_4_20[7] = { 'i', 's', 's', 'a', 'n', 't', 's' };
+static symbol s_4_21[2] = { 'i', 't' };
+static symbol s_4_22[5] = { 'i', 'r', 'a', 'i', 't' };
+static symbol s_4_23[6] = { 'i', 's', 's', 'a', 'i', 't' };
+static symbol s_4_24[6] = { 'i', 's', 's', 'a', 'n', 't' };
+static symbol s_4_25[7] = { 'i', 'r', 'a', 'I', 'e', 'n', 't' };
+static symbol s_4_26[8] = { 'i', 's', 's', 'a', 'I', 'e', 'n', 't' };
+static symbol s_4_27[5] = { 'i', 'r', 'e', 'n', 't' };
+static symbol s_4_28[6] = { 'i', 's', 's', 'e', 'n', 't' };
+static symbol s_4_29[5] = { 'i', 'r', 'o', 'n', 't' };
+static symbol s_4_30[3] = { 0xC3, 0xAE, 't' };
+static symbol s_4_31[5] = { 'i', 'r', 'i', 'e', 'z' };
+static symbol s_4_32[6] = { 'i', 's', 's', 'i', 'e', 'z' };
+static symbol s_4_33[4] = { 'i', 'r', 'e', 'z' };
+static symbol s_4_34[5] = { 'i', 's', 's', 'e', 'z' };
+
+static struct among a_4[35] =
+{
+/*  0 */ { 3, s_4_0, -1, 1, 0},
+/*  1 */ { 2, s_4_1, -1, 1, 0},
+/*  2 */ { 4, s_4_2, -1, 1, 0},
+/*  3 */ { 7, s_4_3, -1, 1, 0},
+/*  4 */ { 1, s_4_4, -1, 1, 0},
+/*  5 */ { 4, s_4_5, 4, 1, 0},
+/*  6 */ { 2, s_4_6, -1, 1, 0},
+/*  7 */ { 4, s_4_7, -1, 1, 0},
+/*  8 */ { 3, s_4_8, -1, 1, 0},
+/*  9 */ { 5, s_4_9, -1, 1, 0},
+/* 10 */ { 5, s_4_10, -1, 1, 0},
+/* 11 */ { 8, s_4_11, -1, 1, 0},
+/* 12 */ { 5, s_4_12, -1, 1, 0},
+/* 13 */ { 2, s_4_13, -1, 1, 0},
+/* 14 */ { 5, s_4_14, 13, 1, 0},
+/* 15 */ { 6, s_4_15, 13, 1, 0},
+/* 16 */ { 6, s_4_16, -1, 1, 0},
+/* 17 */ { 7, s_4_17, -1, 1, 0},
+/* 18 */ { 5, s_4_18, -1, 1, 0},
+/* 19 */ { 6, s_4_19, -1, 1, 0},
+/* 20 */ { 7, s_4_20, -1, 1, 0},
+/* 21 */ { 2, s_4_21, -1, 1, 0},
+/* 22 */ { 5, s_4_22, 21, 1, 0},
+/* 23 */ { 6, s_4_23, 21, 1, 0},
+/* 24 */ { 6, s_4_24, -1, 1, 0},
+/* 25 */ { 7, s_4_25, -1, 1, 0},
+/* 26 */ { 8, s_4_26, -1, 1, 0},
+/* 27 */ { 5, s_4_27, -1, 1, 0},
+/* 28 */ { 6, s_4_28, -1, 1, 0},
+/* 29 */ { 5, s_4_29, -1, 1, 0},
+/* 30 */ { 3, s_4_30, -1, 1, 0},
+/* 31 */ { 5, s_4_31, -1, 1, 0},
+/* 32 */ { 6, s_4_32, -1, 1, 0},
+/* 33 */ { 4, s_4_33, -1, 1, 0},
+/* 34 */ { 5, s_4_34, -1, 1, 0}
+};
+
+static symbol s_5_0[1] = { 'a' };
+static symbol s_5_1[3] = { 'e', 'r', 'a' };
+static symbol s_5_2[4] = { 'a', 's', 's', 'e' };
+static symbol s_5_3[4] = { 'a', 'n', 't', 'e' };
+static symbol s_5_4[3] = { 0xC3, 0xA9, 'e' };
+static symbol s_5_5[2] = { 'a', 'i' };
+static symbol s_5_6[4] = { 'e', 'r', 'a', 'i' };
+static symbol s_5_7[2] = { 'e', 'r' };
+static symbol s_5_8[2] = { 'a', 's' };
+static symbol s_5_9[4] = { 'e', 'r', 'a', 's' };
+static symbol s_5_10[5] = { 0xC3, 0xA2, 'm', 'e', 's' };
+static symbol s_5_11[5] = { 'a', 's', 's', 'e', 's' };
+static symbol s_5_12[5] = { 'a', 'n', 't', 'e', 's' };
+static symbol s_5_13[5] = { 0xC3, 0xA2, 't', 'e', 's' };
+static symbol s_5_14[4] = { 0xC3, 0xA9, 'e', 's' };
+static symbol s_5_15[3] = { 'a', 'i', 's' };
+static symbol s_5_16[5] = { 'e', 'r', 'a', 'i', 's' };
+static symbol s_5_17[4] = { 'i', 'o', 'n', 's' };
+static symbol s_5_18[6] = { 'e', 'r', 'i', 'o', 'n', 's' };
+static symbol s_5_19[7] = { 'a', 's', 's', 'i', 'o', 'n', 's' };
+static symbol s_5_20[5] = { 'e', 'r', 'o', 'n', 's' };
+static symbol s_5_21[4] = { 'a', 'n', 't', 's' };
+static symbol s_5_22[3] = { 0xC3, 0xA9, 's' };
+static symbol s_5_23[3] = { 'a', 'i', 't' };
+static symbol s_5_24[5] = { 'e', 'r', 'a', 'i', 't' };
+static symbol s_5_25[3] = { 'a', 'n', 't' };
+static symbol s_5_26[5] = { 'a', 'I', 'e', 'n', 't' };
+static symbol s_5_27[7] = { 'e', 'r', 'a', 'I', 'e', 'n', 't' };
+static symbol s_5_28[6] = { 0xC3, 0xA8, 'r', 'e', 'n', 't' };
+static symbol s_5_29[6] = { 'a', 's', 's', 'e', 'n', 't' };
+static symbol s_5_30[5] = { 'e', 'r', 'o', 'n', 't' };
+static symbol s_5_31[3] = { 0xC3, 0xA2, 't' };
+static symbol s_5_32[2] = { 'e', 'z' };
+static symbol s_5_33[3] = { 'i', 'e', 'z' };
+static symbol s_5_34[5] = { 'e', 'r', 'i', 'e', 'z' };
+static symbol s_5_35[6] = { 'a', 's', 's', 'i', 'e', 'z' };
+static symbol s_5_36[4] = { 'e', 'r', 'e', 'z' };
+static symbol s_5_37[2] = { 0xC3, 0xA9 };
+
+static struct among a_5[38] =
+{
+/*  0 */ { 1, s_5_0, -1, 3, 0},
+/*  1 */ { 3, s_5_1, 0, 2, 0},
+/*  2 */ { 4, s_5_2, -1, 3, 0},
+/*  3 */ { 4, s_5_3, -1, 3, 0},
+/*  4 */ { 3, s_5_4, -1, 2, 0},
+/*  5 */ { 2, s_5_5, -1, 3, 0},
+/*  6 */ { 4, s_5_6, 5, 2, 0},
+/*  7 */ { 2, s_5_7, -1, 2, 0},
+/*  8 */ { 2, s_5_8, -1, 3, 0},
+/*  9 */ { 4, s_5_9, 8, 2, 0},
+/* 10 */ { 5, s_5_10, -1, 3, 0},
+/* 11 */ { 5, s_5_11, -1, 3, 0},
+/* 12 */ { 5, s_5_12, -1, 3, 0},
+/* 13 */ { 5, s_5_13, -1, 3, 0},
+/* 14 */ { 4, s_5_14, -1, 2, 0},
+/* 15 */ { 3, s_5_15, -1, 3, 0},
+/* 16 */ { 5, s_5_16, 15, 2, 0},
+/* 17 */ { 4, s_5_17, -1, 1, 0},
+/* 18 */ { 6, s_5_18, 17, 2, 0},
+/* 19 */ { 7, s_5_19, 17, 3, 0},
+/* 20 */ { 5, s_5_20, -1, 2, 0},
+/* 21 */ { 4, s_5_21, -1, 3, 0},
+/* 22 */ { 3, s_5_22, -1, 2, 0},
+/* 23 */ { 3, s_5_23, -1, 3, 0},
+/* 24 */ { 5, s_5_24, 23, 2, 0},
+/* 25 */ { 3, s_5_25, -1, 3, 0},
+/* 26 */ { 5, s_5_26, -1, 3, 0},
+/* 27 */ { 7, s_5_27, 26, 2, 0},
+/* 28 */ { 6, s_5_28, -1, 2, 0},
+/* 29 */ { 6, s_5_29, -1, 3, 0},
+/* 30 */ { 5, s_5_30, -1, 2, 0},
+/* 31 */ { 3, s_5_31, -1, 3, 0},
+/* 32 */ { 2, s_5_32, -1, 2, 0},
+/* 33 */ { 3, s_5_33, 32, 2, 0},
+/* 34 */ { 5, s_5_34, 33, 2, 0},
+/* 35 */ { 6, s_5_35, 33, 3, 0},
+/* 36 */ { 4, s_5_36, 32, 2, 0},
+/* 37 */ { 2, s_5_37, -1, 2, 0}
+};
+
+static symbol s_6_0[1] = { 'e' };
+static symbol s_6_1[5] = { 'I', 0xC3, 0xA8, 'r', 'e' };
+static symbol s_6_2[5] = { 'i', 0xC3, 0xA8, 'r', 'e' };
+static symbol s_6_3[3] = { 'i', 'o', 'n' };
+static symbol s_6_4[3] = { 'I', 'e', 'r' };
+static symbol s_6_5[3] = { 'i', 'e', 'r' };
+static symbol s_6_6[2] = { 0xC3, 0xAB };
+
+static struct among a_6[7] =
+{
+/*  0 */ { 1, s_6_0, -1, 3, 0},
+/*  1 */ { 5, s_6_1, 0, 2, 0},
+/*  2 */ { 5, s_6_2, 0, 2, 0},
+/*  3 */ { 3, s_6_3, -1, 1, 0},
+/*  4 */ { 3, s_6_4, -1, 2, 0},
+/*  5 */ { 3, s_6_5, -1, 2, 0},
+/*  6 */ { 2, s_6_6, -1, 4, 0}
+};
+
+static symbol s_7_0[3] = { 'e', 'l', 'l' };
+static symbol s_7_1[4] = { 'e', 'i', 'l', 'l' };
+static symbol s_7_2[3] = { 'e', 'n', 'n' };
+static symbol s_7_3[3] = { 'o', 'n', 'n' };
+static symbol s_7_4[3] = { 'e', 't', 't' };
+
+static struct among a_7[5] =
+{
+/*  0 */ { 3, s_7_0, -1, -1, 0},
+/*  1 */ { 4, s_7_1, -1, -1, 0},
+/*  2 */ { 3, s_7_2, -1, -1, 0},
+/*  3 */ { 3, s_7_3, -1, -1, 0},
+/*  4 */ { 3, s_7_4, -1, -1, 0}
+};
+
+static unsigned char g_v[] = { 17, 65, 16, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 130, 103, 8, 5 };
+
+static unsigned char g_keep_with_s[] = { 1, 65, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128 };
+
+static symbol s_0[] = { 'u' };
+static symbol s_1[] = { 'U' };
+static symbol s_2[] = { 'i' };
+static symbol s_3[] = { 'I' };
+static symbol s_4[] = { 'y' };
+static symbol s_5[] = { 'Y' };
+static symbol s_6[] = { 'y' };
+static symbol s_7[] = { 'Y' };
+static symbol s_8[] = { 'q' };
+static symbol s_9[] = { 'u' };
+static symbol s_10[] = { 'U' };
+static symbol s_11[] = { 'i' };
+static symbol s_12[] = { 'u' };
+static symbol s_13[] = { 'y' };
+static symbol s_14[] = { 'i', 'c' };
+static symbol s_15[] = { 'i', 'q', 'U' };
+static symbol s_16[] = { 'l', 'o', 'g' };
+static symbol s_17[] = { 'u' };
+static symbol s_18[] = { 'e', 'n', 't' };
+static symbol s_19[] = { 'a', 't' };
+static symbol s_20[] = { 'e', 'u', 'x' };
+static symbol s_21[] = { 'i' };
+static symbol s_22[] = { 'a', 'b', 'l' };
+static symbol s_23[] = { 'i', 'q', 'U' };
+static symbol s_24[] = { 'a', 't' };
+static symbol s_25[] = { 'i', 'c' };
+static symbol s_26[] = { 'i', 'q', 'U' };
+static symbol s_27[] = { 'e', 'a', 'u' };
+static symbol s_28[] = { 'a', 'l' };
+static symbol s_29[] = { 'e', 'u', 'x' };
+static symbol s_30[] = { 'a', 'n', 't' };
+static symbol s_31[] = { 'e', 'n', 't' };
+static symbol s_32[] = { 'e' };
+static symbol s_33[] = { 's' };
+static symbol s_34[] = { 's' };
+static symbol s_35[] = { 't' };
+static symbol s_36[] = { 'i' };
+static symbol s_37[] = { 'g', 'u' };
+static symbol s_38[] = { 0xC3, 0xA9 };
+static symbol s_39[] = { 0xC3, 0xA8 };
+static symbol s_40[] = { 'e' };
+static symbol s_41[] = { 'Y' };
+static symbol s_42[] = { 'i' };
+static symbol s_43[] = { 0xC3, 0xA7 };
+static symbol s_44[] = { 'c' };
+
+static int r_prelude(struct SN_env * z) {
+    while(1) { /* repeat, line 38 */
+	int c = z->c;
+	while(1) { /* goto, line 38 */
+	    int c = z->c;
+	    {	int c = z->c; /* or, line 44 */
+		if (!(in_grouping_U(z, g_v, 97, 251))) goto lab3;
+		z->bra = z->c; /* [, line 40 */
+		{   int c = z->c; /* or, line 40 */
+		    if (!(eq_s(z, 1, s_0))) goto lab5;
+		    z->ket = z->c; /* ], line 40 */
+		    if (!(in_grouping_U(z, g_v, 97, 251))) goto lab5;
+		    {	int ret;
+			ret = slice_from_s(z, 1, s_1); /* <-, line 40 */
+			if (ret < 0) return ret;
+		    }
+		    goto lab4;
+		lab5:
+		    z->c = c;
+		    if (!(eq_s(z, 1, s_2))) goto lab6;
+		    z->ket = z->c; /* ], line 41 */
+		    if (!(in_grouping_U(z, g_v, 97, 251))) goto lab6;
+		    {	int ret;
+			ret = slice_from_s(z, 1, s_3); /* <-, line 41 */
+			if (ret < 0) return ret;
+		    }
+		    goto lab4;
+		lab6:
+		    z->c = c;
+		    if (!(eq_s(z, 1, s_4))) goto lab3;
+		    z->ket = z->c; /* ], line 42 */
+		    {	int ret;
+			ret = slice_from_s(z, 1, s_5); /* <-, line 42 */
+			if (ret < 0) return ret;
+		    }
+		}
+	    lab4:
+		goto lab2;
+	    lab3:
+		z->c = c;
+		z->bra = z->c; /* [, line 45 */
+		if (!(eq_s(z, 1, s_6))) goto lab7;
+		z->ket = z->c; /* ], line 45 */
+		if (!(in_grouping_U(z, g_v, 97, 251))) goto lab7;
+		{   int ret;
+		    ret = slice_from_s(z, 1, s_7); /* <-, line 45 */
+		    if (ret < 0) return ret;
+		}
+		goto lab2;
+	    lab7:
+		z->c = c;
+		if (!(eq_s(z, 1, s_8))) goto lab1;
+		z->bra = z->c; /* [, line 47 */
+		if (!(eq_s(z, 1, s_9))) goto lab1;
+		z->ket = z->c; /* ], line 47 */
+		{   int ret;
+		    ret = slice_from_s(z, 1, s_10); /* <-, line 47 */
+		    if (ret < 0) return ret;
+		}
+	    }
+	lab2:
+	    z->c = c;
+	    break;
+	lab1:
+	    z->c = c;
+	    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		if (c < 0) goto lab0;
+		z->c = c; /* goto, line 38 */
+	    }
+	}
+	continue;
+    lab0:
+	z->c = c;
+	break;
+    }
+    return 1;
+}
+
+static int r_mark_regions(struct SN_env * z) {
+    z->I[0] = z->l;
+    z->I[1] = z->l;
+    z->I[2] = z->l;
+    {	int c = z->c; /* do, line 56 */
+	{   int c = z->c; /* or, line 57 */
+	    if (!(in_grouping_U(z, g_v, 97, 251))) goto lab2;
+	    if (!(in_grouping_U(z, g_v, 97, 251))) goto lab2;
+	    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		if (c < 0) goto lab2;
+		z->c = c; /* next, line 57 */
+	    }
+	    goto lab1;
+	lab2:
+	    z->c = c;
+	    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		if (c < 0) goto lab0;
+		z->c = c; /* next, line 57 */
+	    }
+	    while(1) { /* gopast, line 57 */
+		if (!(in_grouping_U(z, g_v, 97, 251))) goto lab3;
+		break;
+	    lab3:
+		{   int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		    if (c < 0) goto lab0;
+		    z->c = c; /* gopast, line 57 */
+		}
+	    }
+	}
+    lab1:
+	z->I[0] = z->c; /* setmark pV, line 58 */
+    lab0:
+	z->c = c;
+    }
+    {	int c = z->c; /* do, line 60 */
+	while(1) { /* gopast, line 61 */
+	    if (!(in_grouping_U(z, g_v, 97, 251))) goto lab5;
+	    break;
+	lab5:
+	    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		if (c < 0) goto lab4;
+		z->c = c; /* gopast, line 61 */
+	    }
+	}
+	while(1) { /* gopast, line 61 */
+	    if (!(out_grouping_U(z, g_v, 97, 251))) goto lab6;
+	    break;
+	lab6:
+	    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		if (c < 0) goto lab4;
+		z->c = c; /* gopast, line 61 */
+	    }
+	}
+	z->I[1] = z->c; /* setmark p1, line 61 */
+	while(1) { /* gopast, line 62 */
+	    if (!(in_grouping_U(z, g_v, 97, 251))) goto lab7;
+	    break;
+	lab7:
+	    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		if (c < 0) goto lab4;
+		z->c = c; /* gopast, line 62 */
+	    }
+	}
+	while(1) { /* gopast, line 62 */
+	    if (!(out_grouping_U(z, g_v, 97, 251))) goto lab8;
+	    break;
+	lab8:
+	    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		if (c < 0) goto lab4;
+		z->c = c; /* gopast, line 62 */
+	    }
+	}
+	z->I[2] = z->c; /* setmark p2, line 62 */
+    lab4:
+	z->c = c;
+    }
+    return 1;
+}
+
+static int r_postlude(struct SN_env * z) {
+    int among_var;
+    while(1) { /* repeat, line 66 */
+	int c = z->c;
+	z->bra = z->c; /* [, line 68 */
+	among_var = find_among(z, a_0, 4); /* substring, line 68 */
+	if (!(among_var)) goto lab0;
+	z->ket = z->c; /* ], line 68 */
+	switch(among_var) {
+	    case 0: goto lab0;
+	    case 1:
+		{   int ret;
+		    ret = slice_from_s(z, 1, s_11); /* <-, line 69 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 2:
+		{   int ret;
+		    ret = slice_from_s(z, 1, s_12); /* <-, line 70 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 3:
+		{   int ret;
+		    ret = slice_from_s(z, 1, s_13); /* <-, line 71 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 4:
+		{   int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		    if (c < 0) goto lab0;
+		    z->c = c; /* next, line 72 */
+		}
+		break;
+	}
+	continue;
+    lab0:
+	z->c = c;
+	break;
+    }
+    return 1;
+}
+
+static int r_RV(struct SN_env * z) {
+    if (!(z->I[0] <= z->c)) return 0;
+    return 1;
+}
+
+static int r_R1(struct SN_env * z) {
+    if (!(z->I[1] <= z->c)) return 0;
+    return 1;
+}
+
+static int r_R2(struct SN_env * z) {
+    if (!(z->I[2] <= z->c)) return 0;
+    return 1;
+}
+
+static int r_standard_suffix(struct SN_env * z) {
+    int among_var;
+    z->ket = z->c; /* [, line 83 */
+    among_var = find_among_b(z, a_3, 43); /* substring, line 83 */
+    if (!(among_var)) return 0;
+    z->bra = z->c; /* ], line 83 */
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret = r_R2(z);
+		if (ret == 0) return 0; /* call R2, line 87 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 87 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    {	int ret = r_R2(z);
+		if (ret == 0) return 0; /* call R2, line 90 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 90 */
+		if (ret < 0) return ret;
+	    }
+	    {	int m = z->l - z->c; (void) m; /* try, line 91 */
+		z->ket = z->c; /* [, line 91 */
+		if (!(eq_s_b(z, 2, s_14))) { z->c = z->l - m; goto lab0; }
+		z->bra = z->c; /* ], line 91 */
+		{   int m = z->l - z->c; (void) m; /* or, line 91 */
+		    {	int ret = r_R2(z);
+			if (ret == 0) goto lab2; /* call R2, line 91 */
+			if (ret < 0) return ret;
+		    }
+		    {	int ret;
+			ret = slice_del(z); /* delete, line 91 */
+			if (ret < 0) return ret;
+		    }
+		    goto lab1;
+		lab2:
+		    z->c = z->l - m;
+		    {	int ret;
+			ret = slice_from_s(z, 3, s_15); /* <-, line 91 */
+			if (ret < 0) return ret;
+		    }
+		}
+	    lab1:
+	    lab0:
+		;
+	    }
+	    break;
+	case 3:
+	    {	int ret = r_R2(z);
+		if (ret == 0) return 0; /* call R2, line 95 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_from_s(z, 3, s_16); /* <-, line 95 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 4:
+	    {	int ret = r_R2(z);
+		if (ret == 0) return 0; /* call R2, line 98 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_from_s(z, 1, s_17); /* <-, line 98 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 5:
+	    {	int ret = r_R2(z);
+		if (ret == 0) return 0; /* call R2, line 101 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_from_s(z, 3, s_18); /* <-, line 101 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 6:
+	    {	int ret = r_RV(z);
+		if (ret == 0) return 0; /* call RV, line 105 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 105 */
+		if (ret < 0) return ret;
+	    }
+	    {	int m = z->l - z->c; (void) m; /* try, line 106 */
+		z->ket = z->c; /* [, line 107 */
+		among_var = find_among_b(z, a_1, 6); /* substring, line 107 */
+		if (!(among_var)) { z->c = z->l - m; goto lab3; }
+		z->bra = z->c; /* ], line 107 */
+		switch(among_var) {
+		    case 0: { z->c = z->l - m; goto lab3; }
+		    case 1:
+			{   int ret = r_R2(z);
+			    if (ret == 0) { z->c = z->l - m; goto lab3; } /* call R2, line 108 */
+			    if (ret < 0) return ret;
+			}
+			{   int ret;
+			    ret = slice_del(z); /* delete, line 108 */
+			    if (ret < 0) return ret;
+			}
+			z->ket = z->c; /* [, line 108 */
+			if (!(eq_s_b(z, 2, s_19))) { z->c = z->l - m; goto lab3; }
+			z->bra = z->c; /* ], line 108 */
+			{   int ret = r_R2(z);
+			    if (ret == 0) { z->c = z->l - m; goto lab3; } /* call R2, line 108 */
+			    if (ret < 0) return ret;
+			}
+			{   int ret;
+			    ret = slice_del(z); /* delete, line 108 */
+			    if (ret < 0) return ret;
+			}
+			break;
+		    case 2:
+			{   int m = z->l - z->c; (void) m; /* or, line 109 */
+			    {	int ret = r_R2(z);
+				if (ret == 0) goto lab5; /* call R2, line 109 */
+				if (ret < 0) return ret;
+			    }
+			    {	int ret;
+				ret = slice_del(z); /* delete, line 109 */
+				if (ret < 0) return ret;
+			    }
+			    goto lab4;
+			lab5:
+			    z->c = z->l - m;
+			    {	int ret = r_R1(z);
+				if (ret == 0) { z->c = z->l - m; goto lab3; } /* call R1, line 109 */
+				if (ret < 0) return ret;
+			    }
+			    {	int ret;
+				ret = slice_from_s(z, 3, s_20); /* <-, line 109 */
+				if (ret < 0) return ret;
+			    }
+			}
+		    lab4:
+			break;
+		    case 3:
+			{   int ret = r_R2(z);
+			    if (ret == 0) { z->c = z->l - m; goto lab3; } /* call R2, line 111 */
+			    if (ret < 0) return ret;
+			}
+			{   int ret;
+			    ret = slice_del(z); /* delete, line 111 */
+			    if (ret < 0) return ret;
+			}
+			break;
+		    case 4:
+			{   int ret = r_RV(z);
+			    if (ret == 0) { z->c = z->l - m; goto lab3; } /* call RV, line 113 */
+			    if (ret < 0) return ret;
+			}
+			{   int ret;
+			    ret = slice_from_s(z, 1, s_21); /* <-, line 113 */
+			    if (ret < 0) return ret;
+			}
+			break;
+		}
+	    lab3:
+		;
+	    }
+	    break;
+	case 7:
+	    {	int ret = r_R2(z);
+		if (ret == 0) return 0; /* call R2, line 120 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 120 */
+		if (ret < 0) return ret;
+	    }
+	    {	int m = z->l - z->c; (void) m; /* try, line 121 */
+		z->ket = z->c; /* [, line 122 */
+		among_var = find_among_b(z, a_2, 3); /* substring, line 122 */
+		if (!(among_var)) { z->c = z->l - m; goto lab6; }
+		z->bra = z->c; /* ], line 122 */
+		switch(among_var) {
+		    case 0: { z->c = z->l - m; goto lab6; }
+		    case 1:
+			{   int m = z->l - z->c; (void) m; /* or, line 123 */
+			    {	int ret = r_R2(z);
+				if (ret == 0) goto lab8; /* call R2, line 123 */
+				if (ret < 0) return ret;
+			    }
+			    {	int ret;
+				ret = slice_del(z); /* delete, line 123 */
+				if (ret < 0) return ret;
+			    }
+			    goto lab7;
+			lab8:
+			    z->c = z->l - m;
+			    {	int ret;
+				ret = slice_from_s(z, 3, s_22); /* <-, line 123 */
+				if (ret < 0) return ret;
+			    }
+			}
+		    lab7:
+			break;
+		    case 2:
+			{   int m = z->l - z->c; (void) m; /* or, line 124 */
+			    {	int ret = r_R2(z);
+				if (ret == 0) goto lab10; /* call R2, line 124 */
+				if (ret < 0) return ret;
+			    }
+			    {	int ret;
+				ret = slice_del(z); /* delete, line 124 */
+				if (ret < 0) return ret;
+			    }
+			    goto lab9;
+			lab10:
+			    z->c = z->l - m;
+			    {	int ret;
+				ret = slice_from_s(z, 3, s_23); /* <-, line 124 */
+				if (ret < 0) return ret;
+			    }
+			}
+		    lab9:
+			break;
+		    case 3:
+			{   int ret = r_R2(z);
+			    if (ret == 0) { z->c = z->l - m; goto lab6; } /* call R2, line 125 */
+			    if (ret < 0) return ret;
+			}
+			{   int ret;
+			    ret = slice_del(z); /* delete, line 125 */
+			    if (ret < 0) return ret;
+			}
+			break;
+		}
+	    lab6:
+		;
+	    }
+	    break;
+	case 8:
+	    {	int ret = r_R2(z);
+		if (ret == 0) return 0; /* call R2, line 132 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 132 */
+		if (ret < 0) return ret;
+	    }
+	    {	int m = z->l - z->c; (void) m; /* try, line 133 */
+		z->ket = z->c; /* [, line 133 */
+		if (!(eq_s_b(z, 2, s_24))) { z->c = z->l - m; goto lab11; }
+		z->bra = z->c; /* ], line 133 */
+		{   int ret = r_R2(z);
+		    if (ret == 0) { z->c = z->l - m; goto lab11; } /* call R2, line 133 */
+		    if (ret < 0) return ret;
+		}
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 133 */
+		    if (ret < 0) return ret;
+		}
+		z->ket = z->c; /* [, line 133 */
+		if (!(eq_s_b(z, 2, s_25))) { z->c = z->l - m; goto lab11; }
+		z->bra = z->c; /* ], line 133 */
+		{   int m = z->l - z->c; (void) m; /* or, line 133 */
+		    {	int ret = r_R2(z);
+			if (ret == 0) goto lab13; /* call R2, line 133 */
+			if (ret < 0) return ret;
+		    }
+		    {	int ret;
+			ret = slice_del(z); /* delete, line 133 */
+			if (ret < 0) return ret;
+		    }
+		    goto lab12;
+		lab13:
+		    z->c = z->l - m;
+		    {	int ret;
+			ret = slice_from_s(z, 3, s_26); /* <-, line 133 */
+			if (ret < 0) return ret;
+		    }
+		}
+	    lab12:
+	    lab11:
+		;
+	    }
+	    break;
+	case 9:
+	    {	int ret;
+		ret = slice_from_s(z, 3, s_27); /* <-, line 135 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 10:
+	    {	int ret = r_R1(z);
+		if (ret == 0) return 0; /* call R1, line 136 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_from_s(z, 2, s_28); /* <-, line 136 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 11:
+	    {	int m = z->l - z->c; (void) m; /* or, line 138 */
+		{   int ret = r_R2(z);
+		    if (ret == 0) goto lab15; /* call R2, line 138 */
+		    if (ret < 0) return ret;
+		}
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 138 */
+		    if (ret < 0) return ret;
+		}
+		goto lab14;
+	    lab15:
+		z->c = z->l - m;
+		{   int ret = r_R1(z);
+		    if (ret == 0) return 0; /* call R1, line 138 */
+		    if (ret < 0) return ret;
+		}
+		{   int ret;
+		    ret = slice_from_s(z, 3, s_29); /* <-, line 138 */
+		    if (ret < 0) return ret;
+		}
+	    }
+	lab14:
+	    break;
+	case 12:
+	    {	int ret = r_R1(z);
+		if (ret == 0) return 0; /* call R1, line 141 */
+		if (ret < 0) return ret;
+	    }
+	    if (!(out_grouping_b_U(z, g_v, 97, 251))) return 0;
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 141 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 13:
+	    {	int ret = r_RV(z);
+		if (ret == 0) return 0; /* call RV, line 146 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_from_s(z, 3, s_30); /* <-, line 146 */
+		if (ret < 0) return ret;
+	    }
+	    return 0; /* fail, line 146 */
+	    break;
+	case 14:
+	    {	int ret = r_RV(z);
+		if (ret == 0) return 0; /* call RV, line 147 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_from_s(z, 3, s_31); /* <-, line 147 */
+		if (ret < 0) return ret;
+	    }
+	    return 0; /* fail, line 147 */
+	    break;
+	case 15:
+	    {	int m_test = z->l - z->c; /* test, line 149 */
+		if (!(in_grouping_b_U(z, g_v, 97, 251))) return 0;
+		{   int ret = r_RV(z);
+		    if (ret == 0) return 0; /* call RV, line 149 */
+		    if (ret < 0) return ret;
+		}
+		z->c = z->l - m_test;
+	    }
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 149 */
+		if (ret < 0) return ret;
+	    }
+	    return 0; /* fail, line 149 */
+	    break;
+    }
+    return 1;
+}
+
+static int r_i_verb_suffix(struct SN_env * z) {
+    int among_var;
+    {	int m3; /* setlimit, line 154 */
+	int m = z->l - z->c; (void) m;
+	if (z->c < z->I[0]) return 0;
+	z->c = z->I[0]; /* tomark, line 154 */
+	m3 = z->lb; z->lb = z->c;
+	z->c = z->l - m;
+	z->ket = z->c; /* [, line 155 */
+	among_var = find_among_b(z, a_4, 35); /* substring, line 155 */
+	if (!(among_var)) { z->lb = m3; return 0; }
+	z->bra = z->c; /* ], line 155 */
+	switch(among_var) {
+	    case 0: { z->lb = m3; return 0; }
+	    case 1:
+		if (!(out_grouping_b_U(z, g_v, 97, 251))) { z->lb = m3; return 0; }
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 161 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	}
+	z->lb = m3;
+    }
+    return 1;
+}
+
+static int r_verb_suffix(struct SN_env * z) {
+    int among_var;
+    {	int m3; /* setlimit, line 165 */
+	int m = z->l - z->c; (void) m;
+	if (z->c < z->I[0]) return 0;
+	z->c = z->I[0]; /* tomark, line 165 */
+	m3 = z->lb; z->lb = z->c;
+	z->c = z->l - m;
+	z->ket = z->c; /* [, line 166 */
+	among_var = find_among_b(z, a_5, 38); /* substring, line 166 */
+	if (!(among_var)) { z->lb = m3; return 0; }
+	z->bra = z->c; /* ], line 166 */
+	switch(among_var) {
+	    case 0: { z->lb = m3; return 0; }
+	    case 1:
+		{   int ret = r_R2(z);
+		    if (ret == 0) { z->lb = m3; return 0; } /* call R2, line 168 */
+		    if (ret < 0) return ret;
+		}
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 168 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 2:
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 176 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 3:
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 181 */
+		    if (ret < 0) return ret;
+		}
+		{   int m = z->l - z->c; (void) m; /* try, line 182 */
+		    z->ket = z->c; /* [, line 182 */
+		    if (!(eq_s_b(z, 1, s_32))) { z->c = z->l - m; goto lab0; }
+		    z->bra = z->c; /* ], line 182 */
+		    {	int ret;
+			ret = slice_del(z); /* delete, line 182 */
+			if (ret < 0) return ret;
+		    }
+		lab0:
+		    ;
+		}
+		break;
+	}
+	z->lb = m3;
+    }
+    return 1;
+}
+
+static int r_residual_suffix(struct SN_env * z) {
+    int among_var;
+    {	int m = z->l - z->c; (void) m; /* try, line 190 */
+	z->ket = z->c; /* [, line 190 */
+	if (!(eq_s_b(z, 1, s_33))) { z->c = z->l - m; goto lab0; }
+	z->bra = z->c; /* ], line 190 */
+	{   int m_test = z->l - z->c; /* test, line 190 */
+	    if (!(out_grouping_b_U(z, g_keep_with_s, 97, 232))) { z->c = z->l - m; goto lab0; }
+	    z->c = z->l - m_test;
+	}
+	{   int ret;
+	    ret = slice_del(z); /* delete, line 190 */
+	    if (ret < 0) return ret;
+	}
+    lab0:
+	;
+    }
+    {	int m3; /* setlimit, line 191 */
+	int m = z->l - z->c; (void) m;
+	if (z->c < z->I[0]) return 0;
+	z->c = z->I[0]; /* tomark, line 191 */
+	m3 = z->lb; z->lb = z->c;
+	z->c = z->l - m;
+	z->ket = z->c; /* [, line 192 */
+	among_var = find_among_b(z, a_6, 7); /* substring, line 192 */
+	if (!(among_var)) { z->lb = m3; return 0; }
+	z->bra = z->c; /* ], line 192 */
+	switch(among_var) {
+	    case 0: { z->lb = m3; return 0; }
+	    case 1:
+		{   int ret = r_R2(z);
+		    if (ret == 0) { z->lb = m3; return 0; } /* call R2, line 193 */
+		    if (ret < 0) return ret;
+		}
+		{   int m = z->l - z->c; (void) m; /* or, line 193 */
+		    if (!(eq_s_b(z, 1, s_34))) goto lab2;
+		    goto lab1;
+		lab2:
+		    z->c = z->l - m;
+		    if (!(eq_s_b(z, 1, s_35))) { z->lb = m3; return 0; }
+		}
+	    lab1:
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 193 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 2:
+		{   int ret;
+		    ret = slice_from_s(z, 1, s_36); /* <-, line 195 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 3:
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 196 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 4:
+		if (!(eq_s_b(z, 2, s_37))) { z->lb = m3; return 0; }
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 197 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	}
+	z->lb = m3;
+    }
+    return 1;
+}
+
+static int r_un_double(struct SN_env * z) {
+    {	int m_test = z->l - z->c; /* test, line 203 */
+	if (!(find_among_b(z, a_7, 5))) return 0; /* among, line 203 */
+	z->c = z->l - m_test;
+    }
+    z->ket = z->c; /* [, line 203 */
+    {	int c = skip_utf8(z->p, z->c, z->lb, 0, -1);
+	if (c < 0) return 0;
+	z->c = c; /* next, line 203 */
+    }
+    z->bra = z->c; /* ], line 203 */
+    {	int ret;
+	ret = slice_del(z); /* delete, line 203 */
+	if (ret < 0) return ret;
+    }
+    return 1;
+}
+
+static int r_un_accent(struct SN_env * z) {
+    {	int i = 1;
+	while(1) { /* atleast, line 207 */
+	    if (!(out_grouping_b_U(z, g_v, 97, 251))) goto lab0;
+	    i--;
+	    continue;
+	lab0:
+	    break;
+	}
+	if (i > 0) return 0;
+    }
+    z->ket = z->c; /* [, line 208 */
+    {	int m = z->l - z->c; (void) m; /* or, line 208 */
+	if (!(eq_s_b(z, 2, s_38))) goto lab2;
+	goto lab1;
+    lab2:
+	z->c = z->l - m;
+	if (!(eq_s_b(z, 2, s_39))) return 0;
+    }
+lab1:
+    z->bra = z->c; /* ], line 208 */
+    {	int ret;
+	ret = slice_from_s(z, 1, s_40); /* <-, line 208 */
+	if (ret < 0) return ret;
+    }
+    return 1;
+}
+
+extern int french_UTF_8_stem(struct SN_env * z) {
+    {	int c = z->c; /* do, line 214 */
+	{   int ret = r_prelude(z);
+	    if (ret == 0) goto lab0; /* call prelude, line 214 */
+	    if (ret < 0) return ret;
+	}
+    lab0:
+	z->c = c;
+    }
+    {	int c = z->c; /* do, line 215 */
+	{   int ret = r_mark_regions(z);
+	    if (ret == 0) goto lab1; /* call mark_regions, line 215 */
+	    if (ret < 0) return ret;
+	}
+    lab1:
+	z->c = c;
+    }
+    z->lb = z->c; z->c = z->l; /* backwards, line 216 */
+
+    {	int m = z->l - z->c; (void) m; /* do, line 218 */
+	{   int m = z->l - z->c; (void) m; /* or, line 228 */
+	    {	int m = z->l - z->c; (void) m; /* and, line 224 */
+		{   int m = z->l - z->c; (void) m; /* or, line 220 */
+		    {	int ret = r_standard_suffix(z);
+			if (ret == 0) goto lab6; /* call standard_suffix, line 220 */
+			if (ret < 0) return ret;
+		    }
+		    goto lab5;
+		lab6:
+		    z->c = z->l - m;
+		    {	int ret = r_i_verb_suffix(z);
+			if (ret == 0) goto lab7; /* call i_verb_suffix, line 221 */
+			if (ret < 0) return ret;
+		    }
+		    goto lab5;
+		lab7:
+		    z->c = z->l - m;
+		    {	int ret = r_verb_suffix(z);
+			if (ret == 0) goto lab4; /* call verb_suffix, line 222 */
+			if (ret < 0) return ret;
+		    }
+		}
+	    lab5:
+		z->c = z->l - m;
+		{   int m = z->l - z->c; (void) m; /* try, line 225 */
+		    z->ket = z->c; /* [, line 225 */
+		    {	int m = z->l - z->c; (void) m; /* or, line 225 */
+			if (!(eq_s_b(z, 1, s_41))) goto lab10;
+			z->bra = z->c; /* ], line 225 */
+			{   int ret;
+			    ret = slice_from_s(z, 1, s_42); /* <-, line 225 */
+			    if (ret < 0) return ret;
+			}
+			goto lab9;
+		    lab10:
+			z->c = z->l - m;
+			if (!(eq_s_b(z, 2, s_43))) { z->c = z->l - m; goto lab8; }
+			z->bra = z->c; /* ], line 226 */
+			{   int ret;
+			    ret = slice_from_s(z, 1, s_44); /* <-, line 226 */
+			    if (ret < 0) return ret;
+			}
+		    }
+		lab9:
+		lab8:
+		    ;
+		}
+	    }
+	    goto lab3;
+	lab4:
+	    z->c = z->l - m;
+	    {	int ret = r_residual_suffix(z);
+		if (ret == 0) goto lab2; /* call residual_suffix, line 229 */
+		if (ret < 0) return ret;
+	    }
+	}
+    lab3:
+    lab2:
+	z->c = z->l - m;
+    }
+    {	int m = z->l - z->c; (void) m; /* do, line 234 */
+	{   int ret = r_un_double(z);
+	    if (ret == 0) goto lab11; /* call un_double, line 234 */
+	    if (ret < 0) return ret;
+	}
+    lab11:
+	z->c = z->l - m;
+    }
+    {	int m = z->l - z->c; (void) m; /* do, line 235 */
+	{   int ret = r_un_accent(z);
+	    if (ret == 0) goto lab12; /* call un_accent, line 235 */
+	    if (ret < 0) return ret;
+	}
+    lab12:
+	z->c = z->l - m;
+    }
+    z->c = z->lb;
+    {	int c = z->c; /* do, line 237 */
+	{   int ret = r_postlude(z);
+	    if (ret == 0) goto lab13; /* call postlude, line 237 */
+	    if (ret < 0) return ret;
+	}
+    lab13:
+	z->c = c;
+    }
+    return 1;
+}
+
+extern struct SN_env * french_UTF_8_create_env(void) { return SN_create_env(0, 3, 0); }
+
+extern void french_UTF_8_close_env(struct SN_env * z) { SN_close_env(z); }
+

Added: trunk/src/libstemmer/stem_UTF_8_french.h
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/stem_UTF_8_french.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2001, Dr Martin Porter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+/* This file was generated automatically by the Snowball to ANSI C compiler */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern struct SN_env * french_UTF_8_create_env(void);
+extern void french_UTF_8_close_env(struct SN_env * z);
+
+extern int french_UTF_8_stem(struct SN_env * z);
+
+#ifdef __cplusplus
+}
+#endif
+

Added: trunk/src/libstemmer/stem_UTF_8_german.c
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/stem_UTF_8_german.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,539 @@
+/*
+ * Copyright (c) 2001, Dr Martin Porter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+/* This file was generated automatically by the Snowball to ANSI C compiler */
+
+#include "header.h"
+
+extern int german_UTF_8_stem(struct SN_env * z);
+static int r_standard_suffix(struct SN_env * z);
+static int r_R2(struct SN_env * z);
+static int r_R1(struct SN_env * z);
+static int r_mark_regions(struct SN_env * z);
+static int r_postlude(struct SN_env * z);
+static int r_prelude(struct SN_env * z);
+
+extern struct SN_env * german_UTF_8_create_env(void);
+extern void german_UTF_8_close_env(struct SN_env * z);
+
+static symbol s_0_1[1] = { 'U' };
+static symbol s_0_2[1] = { 'Y' };
+static symbol s_0_3[2] = { 0xC3, 0xA4 };
+static symbol s_0_4[2] = { 0xC3, 0xB6 };
+static symbol s_0_5[2] = { 0xC3, 0xBC };
+
+static struct among a_0[6] =
+{
+/*  0 */ { 0, 0, -1, 6, 0},
+/*  1 */ { 1, s_0_1, 0, 2, 0},
+/*  2 */ { 1, s_0_2, 0, 1, 0},
+/*  3 */ { 2, s_0_3, 0, 3, 0},
+/*  4 */ { 2, s_0_4, 0, 4, 0},
+/*  5 */ { 2, s_0_5, 0, 5, 0}
+};
+
+static symbol s_1_0[1] = { 'e' };
+static symbol s_1_1[2] = { 'e', 'm' };
+static symbol s_1_2[2] = { 'e', 'n' };
+static symbol s_1_3[3] = { 'e', 'r', 'n' };
+static symbol s_1_4[2] = { 'e', 'r' };
+static symbol s_1_5[1] = { 's' };
+static symbol s_1_6[2] = { 'e', 's' };
+
+static struct among a_1[7] =
+{
+/*  0 */ { 1, s_1_0, -1, 1, 0},
+/*  1 */ { 2, s_1_1, -1, 1, 0},
+/*  2 */ { 2, s_1_2, -1, 1, 0},
+/*  3 */ { 3, s_1_3, -1, 1, 0},
+/*  4 */ { 2, s_1_4, -1, 1, 0},
+/*  5 */ { 1, s_1_5, -1, 2, 0},
+/*  6 */ { 2, s_1_6, 5, 1, 0}
+};
+
+static symbol s_2_0[2] = { 'e', 'n' };
+static symbol s_2_1[2] = { 'e', 'r' };
+static symbol s_2_2[2] = { 's', 't' };
+static symbol s_2_3[3] = { 'e', 's', 't' };
+
+static struct among a_2[4] =
+{
+/*  0 */ { 2, s_2_0, -1, 1, 0},
+/*  1 */ { 2, s_2_1, -1, 1, 0},
+/*  2 */ { 2, s_2_2, -1, 2, 0},
+/*  3 */ { 3, s_2_3, 2, 1, 0}
+};
+
+static symbol s_3_0[2] = { 'i', 'g' };
+static symbol s_3_1[4] = { 'l', 'i', 'c', 'h' };
+
+static struct among a_3[2] =
+{
+/*  0 */ { 2, s_3_0, -1, 1, 0},
+/*  1 */ { 4, s_3_1, -1, 1, 0}
+};
+
+static symbol s_4_0[3] = { 'e', 'n', 'd' };
+static symbol s_4_1[2] = { 'i', 'g' };
+static symbol s_4_2[3] = { 'u', 'n', 'g' };
+static symbol s_4_3[4] = { 'l', 'i', 'c', 'h' };
+static symbol s_4_4[4] = { 'i', 's', 'c', 'h' };
+static symbol s_4_5[2] = { 'i', 'k' };
+static symbol s_4_6[4] = { 'h', 'e', 'i', 't' };
+static symbol s_4_7[4] = { 'k', 'e', 'i', 't' };
+
+static struct among a_4[8] =
+{
+/*  0 */ { 3, s_4_0, -1, 1, 0},
+/*  1 */ { 2, s_4_1, -1, 2, 0},
+/*  2 */ { 3, s_4_2, -1, 1, 0},
+/*  3 */ { 4, s_4_3, -1, 3, 0},
+/*  4 */ { 4, s_4_4, -1, 2, 0},
+/*  5 */ { 2, s_4_5, -1, 2, 0},
+/*  6 */ { 4, s_4_6, -1, 3, 0},
+/*  7 */ { 4, s_4_7, -1, 4, 0}
+};
+
+static unsigned char g_v[] = { 17, 65, 16, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 32, 8 };
+
+static unsigned char g_s_ending[] = { 117, 30, 5 };
+
+static unsigned char g_st_ending[] = { 117, 30, 4 };
+
+static symbol s_0[] = { 0xC3, 0x9F };
+static symbol s_1[] = { 's', 's' };
+static symbol s_2[] = { 'u' };
+static symbol s_3[] = { 'U' };
+static symbol s_4[] = { 'y' };
+static symbol s_5[] = { 'Y' };
+static symbol s_6[] = { 'y' };
+static symbol s_7[] = { 'u' };
+static symbol s_8[] = { 'a' };
+static symbol s_9[] = { 'o' };
+static symbol s_10[] = { 'u' };
+static symbol s_11[] = { 'i', 'g' };
+static symbol s_12[] = { 'e' };
+static symbol s_13[] = { 'e' };
+static symbol s_14[] = { 'e', 'r' };
+static symbol s_15[] = { 'e', 'n' };
+
+static int r_prelude(struct SN_env * z) {
+    {	int c_test = z->c; /* test, line 30 */
+	while(1) { /* repeat, line 30 */
+	    int c = z->c;
+	    {	int c = z->c; /* or, line 33 */
+		z->bra = z->c; /* [, line 32 */
+		if (!(eq_s(z, 2, s_0))) goto lab2;
+		z->ket = z->c; /* ], line 32 */
+		{   int ret;
+		    ret = slice_from_s(z, 2, s_1); /* <-, line 32 */
+		    if (ret < 0) return ret;
+		}
+		goto lab1;
+	    lab2:
+		z->c = c;
+		{   int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		    if (c < 0) goto lab0;
+		    z->c = c; /* next, line 33 */
+		}
+	    }
+	lab1:
+	    continue;
+	lab0:
+	    z->c = c;
+	    break;
+	}
+	z->c = c_test;
+    }
+    while(1) { /* repeat, line 36 */
+	int c = z->c;
+	while(1) { /* goto, line 36 */
+	    int c = z->c;
+	    if (!(in_grouping_U(z, g_v, 97, 252))) goto lab4;
+	    z->bra = z->c; /* [, line 37 */
+	    {	int c = z->c; /* or, line 37 */
+		if (!(eq_s(z, 1, s_2))) goto lab6;
+		z->ket = z->c; /* ], line 37 */
+		if (!(in_grouping_U(z, g_v, 97, 252))) goto lab6;
+		{   int ret;
+		    ret = slice_from_s(z, 1, s_3); /* <-, line 37 */
+		    if (ret < 0) return ret;
+		}
+		goto lab5;
+	    lab6:
+		z->c = c;
+		if (!(eq_s(z, 1, s_4))) goto lab4;
+		z->ket = z->c; /* ], line 38 */
+		if (!(in_grouping_U(z, g_v, 97, 252))) goto lab4;
+		{   int ret;
+		    ret = slice_from_s(z, 1, s_5); /* <-, line 38 */
+		    if (ret < 0) return ret;
+		}
+	    }
+	lab5:
+	    z->c = c;
+	    break;
+	lab4:
+	    z->c = c;
+	    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		if (c < 0) goto lab3;
+		z->c = c; /* goto, line 36 */
+	    }
+	}
+	continue;
+    lab3:
+	z->c = c;
+	break;
+    }
+    return 1;
+}
+
+static int r_mark_regions(struct SN_env * z) {
+    z->I[0] = z->l;
+    z->I[1] = z->l;
+    {	int c_test = z->c; /* test, line 47 */
+	{   int c = skip_utf8(z->p, z->c, 0, z->l, + 3);
+	    if (c < 0) return 0;
+	    z->c = c; /* hop, line 47 */
+	}
+	z->I[2] = z->c; /* setmark x, line 47 */
+	z->c = c_test;
+    }
+    while(1) { /* gopast, line 49 */
+	if (!(in_grouping_U(z, g_v, 97, 252))) goto lab0;
+	break;
+    lab0:
+	{   int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+	    if (c < 0) return 0;
+	    z->c = c; /* gopast, line 49 */
+	}
+    }
+    while(1) { /* gopast, line 49 */
+	if (!(out_grouping_U(z, g_v, 97, 252))) goto lab1;
+	break;
+    lab1:
+	{   int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+	    if (c < 0) return 0;
+	    z->c = c; /* gopast, line 49 */
+	}
+    }
+    z->I[0] = z->c; /* setmark p1, line 49 */
+     /* try, line 50 */
+    if (!(z->I[0] < z->I[2])) goto lab2;
+    z->I[0] = z->I[2];
+lab2:
+    while(1) { /* gopast, line 51 */
+	if (!(in_grouping_U(z, g_v, 97, 252))) goto lab3;
+	break;
+    lab3:
+	{   int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+	    if (c < 0) return 0;
+	    z->c = c; /* gopast, line 51 */
+	}
+    }
+    while(1) { /* gopast, line 51 */
+	if (!(out_grouping_U(z, g_v, 97, 252))) goto lab4;
+	break;
+    lab4:
+	{   int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+	    if (c < 0) return 0;
+	    z->c = c; /* gopast, line 51 */
+	}
+    }
+    z->I[1] = z->c; /* setmark p2, line 51 */
+    return 1;
+}
+
+static int r_postlude(struct SN_env * z) {
+    int among_var;
+    while(1) { /* repeat, line 55 */
+	int c = z->c;
+	z->bra = z->c; /* [, line 57 */
+	among_var = find_among(z, a_0, 6); /* substring, line 57 */
+	if (!(among_var)) goto lab0;
+	z->ket = z->c; /* ], line 57 */
+	switch(among_var) {
+	    case 0: goto lab0;
+	    case 1:
+		{   int ret;
+		    ret = slice_from_s(z, 1, s_6); /* <-, line 58 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 2:
+		{   int ret;
+		    ret = slice_from_s(z, 1, s_7); /* <-, line 59 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 3:
+		{   int ret;
+		    ret = slice_from_s(z, 1, s_8); /* <-, line 60 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 4:
+		{   int ret;
+		    ret = slice_from_s(z, 1, s_9); /* <-, line 61 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 5:
+		{   int ret;
+		    ret = slice_from_s(z, 1, s_10); /* <-, line 62 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 6:
+		{   int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		    if (c < 0) goto lab0;
+		    z->c = c; /* next, line 63 */
+		}
+		break;
+	}
+	continue;
+    lab0:
+	z->c = c;
+	break;
+    }
+    return 1;
+}
+
+static int r_R1(struct SN_env * z) {
+    if (!(z->I[0] <= z->c)) return 0;
+    return 1;
+}
+
+static int r_R2(struct SN_env * z) {
+    if (!(z->I[1] <= z->c)) return 0;
+    return 1;
+}
+
+static int r_standard_suffix(struct SN_env * z) {
+    int among_var;
+    {	int m = z->l - z->c; (void) m; /* do, line 74 */
+	z->ket = z->c; /* [, line 75 */
+	among_var = find_among_b(z, a_1, 7); /* substring, line 75 */
+	if (!(among_var)) goto lab0;
+	z->bra = z->c; /* ], line 75 */
+	{   int ret = r_R1(z);
+	    if (ret == 0) goto lab0; /* call R1, line 75 */
+	    if (ret < 0) return ret;
+	}
+	switch(among_var) {
+	    case 0: goto lab0;
+	    case 1:
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 77 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 2:
+		if (!(in_grouping_b_U(z, g_s_ending, 98, 116))) goto lab0;
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 80 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	}
+    lab0:
+	z->c = z->l - m;
+    }
+    {	int m = z->l - z->c; (void) m; /* do, line 84 */
+	z->ket = z->c; /* [, line 85 */
+	among_var = find_among_b(z, a_2, 4); /* substring, line 85 */
+	if (!(among_var)) goto lab1;
+	z->bra = z->c; /* ], line 85 */
+	{   int ret = r_R1(z);
+	    if (ret == 0) goto lab1; /* call R1, line 85 */
+	    if (ret < 0) return ret;
+	}
+	switch(among_var) {
+	    case 0: goto lab1;
+	    case 1:
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 87 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 2:
+		if (!(in_grouping_b_U(z, g_st_ending, 98, 116))) goto lab1;
+		{   int c = skip_utf8(z->p, z->c, z->lb, z->l, - 3);
+		    if (c < 0) goto lab1;
+		    z->c = c; /* hop, line 90 */
+		}
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 90 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	}
+    lab1:
+	z->c = z->l - m;
+    }
+    {	int m = z->l - z->c; (void) m; /* do, line 94 */
+	z->ket = z->c; /* [, line 95 */
+	among_var = find_among_b(z, a_4, 8); /* substring, line 95 */
+	if (!(among_var)) goto lab2;
+	z->bra = z->c; /* ], line 95 */
+	{   int ret = r_R2(z);
+	    if (ret == 0) goto lab2; /* call R2, line 95 */
+	    if (ret < 0) return ret;
+	}
+	switch(among_var) {
+	    case 0: goto lab2;
+	    case 1:
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 97 */
+		    if (ret < 0) return ret;
+		}
+		{   int m = z->l - z->c; (void) m; /* try, line 98 */
+		    z->ket = z->c; /* [, line 98 */
+		    if (!(eq_s_b(z, 2, s_11))) { z->c = z->l - m; goto lab3; }
+		    z->bra = z->c; /* ], line 98 */
+		    {	int m = z->l - z->c; (void) m; /* not, line 98 */
+			if (!(eq_s_b(z, 1, s_12))) goto lab4;
+			{ z->c = z->l - m; goto lab3; }
+		    lab4:
+			z->c = z->l - m;
+		    }
+		    {	int ret = r_R2(z);
+			if (ret == 0) { z->c = z->l - m; goto lab3; } /* call R2, line 98 */
+			if (ret < 0) return ret;
+		    }
+		    {	int ret;
+			ret = slice_del(z); /* delete, line 98 */
+			if (ret < 0) return ret;
+		    }
+		lab3:
+		    ;
+		}
+		break;
+	    case 2:
+		{   int m = z->l - z->c; (void) m; /* not, line 101 */
+		    if (!(eq_s_b(z, 1, s_13))) goto lab5;
+		    goto lab2;
+		lab5:
+		    z->c = z->l - m;
+		}
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 101 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 3:
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 104 */
+		    if (ret < 0) return ret;
+		}
+		{   int m = z->l - z->c; (void) m; /* try, line 105 */
+		    z->ket = z->c; /* [, line 106 */
+		    {	int m = z->l - z->c; (void) m; /* or, line 106 */
+			if (!(eq_s_b(z, 2, s_14))) goto lab8;
+			goto lab7;
+		    lab8:
+			z->c = z->l - m;
+			if (!(eq_s_b(z, 2, s_15))) { z->c = z->l - m; goto lab6; }
+		    }
+		lab7:
+		    z->bra = z->c; /* ], line 106 */
+		    {	int ret = r_R1(z);
+			if (ret == 0) { z->c = z->l - m; goto lab6; } /* call R1, line 106 */
+			if (ret < 0) return ret;
+		    }
+		    {	int ret;
+			ret = slice_del(z); /* delete, line 106 */
+			if (ret < 0) return ret;
+		    }
+		lab6:
+		    ;
+		}
+		break;
+	    case 4:
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 110 */
+		    if (ret < 0) return ret;
+		}
+		{   int m = z->l - z->c; (void) m; /* try, line 111 */
+		    z->ket = z->c; /* [, line 112 */
+		    among_var = find_among_b(z, a_3, 2); /* substring, line 112 */
+		    if (!(among_var)) { z->c = z->l - m; goto lab9; }
+		    z->bra = z->c; /* ], line 112 */
+		    {	int ret = r_R2(z);
+			if (ret == 0) { z->c = z->l - m; goto lab9; } /* call R2, line 112 */
+			if (ret < 0) return ret;
+		    }
+		    switch(among_var) {
+			case 0: { z->c = z->l - m; goto lab9; }
+			case 1:
+			    {	int ret;
+				ret = slice_del(z); /* delete, line 114 */
+				if (ret < 0) return ret;
+			    }
+			    break;
+		    }
+		lab9:
+		    ;
+		}
+		break;
+	}
+    lab2:
+	z->c = z->l - m;
+    }
+    return 1;
+}
+
+extern int german_UTF_8_stem(struct SN_env * z) {
+    {	int c = z->c; /* do, line 125 */
+	{   int ret = r_prelude(z);
+	    if (ret == 0) goto lab0; /* call prelude, line 125 */
+	    if (ret < 0) return ret;
+	}
+    lab0:
+	z->c = c;
+    }
+    {	int c = z->c; /* do, line 126 */
+	{   int ret = r_mark_regions(z);
+	    if (ret == 0) goto lab1; /* call mark_regions, line 126 */
+	    if (ret < 0) return ret;
+	}
+    lab1:
+	z->c = c;
+    }
+    z->lb = z->c; z->c = z->l; /* backwards, line 127 */
+
+    {	int m = z->l - z->c; (void) m; /* do, line 128 */
+	{   int ret = r_standard_suffix(z);
+	    if (ret == 0) goto lab2; /* call standard_suffix, line 128 */
+	    if (ret < 0) return ret;
+	}
+    lab2:
+	z->c = z->l - m;
+    }
+    z->c = z->lb;
+    {	int c = z->c; /* do, line 129 */
+	{   int ret = r_postlude(z);
+	    if (ret == 0) goto lab3; /* call postlude, line 129 */
+	    if (ret < 0) return ret;
+	}
+    lab3:
+	z->c = c;
+    }
+    return 1;
+}
+
+extern struct SN_env * german_UTF_8_create_env(void) { return SN_create_env(0, 3, 0); }
+
+extern void german_UTF_8_close_env(struct SN_env * z) { SN_close_env(z); }
+

Added: trunk/src/libstemmer/stem_UTF_8_german.h
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/stem_UTF_8_german.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2001, Dr Martin Porter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+/* This file was generated automatically by the Snowball to ANSI C compiler */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern struct SN_env * german_UTF_8_create_env(void);
+extern void german_UTF_8_close_env(struct SN_env * z);
+
+extern int german_UTF_8_stem(struct SN_env * z);
+
+#ifdef __cplusplus
+}
+#endif
+

Added: trunk/src/libstemmer/stem_UTF_8_hungarian.c
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/stem_UTF_8_hungarian.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,1239 @@
+/*
+ * Copyright (c) 2001, Dr Martin Porter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+/* This file was generated automatically by the Snowball to ANSI C compiler */
+
+#include "header.h"
+
+extern int hungarian_UTF_8_stem(struct SN_env * z);
+
+static int r_double(struct SN_env * z);
+static int r_undouble(struct SN_env * z);
+static int r_factive(struct SN_env * z);
+static int r_instrum(struct SN_env * z);
+static int r_plur_owner(struct SN_env * z);
+static int r_sing_owner(struct SN_env * z);
+static int r_owned(struct SN_env * z);
+static int r_plural(struct SN_env * z);
+static int r_case_other(struct SN_env * z);
+static int r_case_special(struct SN_env * z);
+static int r_case(struct SN_env * z);
+static int r_v_ending(struct SN_env * z);
+static int r_R1(struct SN_env * z);
+static int r_mark_regions(struct SN_env * z);
+
+extern struct SN_env * hungarian_UTF_8_create_env(void);
+extern void hungarian_UTF_8_close_env(struct SN_env * z);
+
+static symbol s_0_0[2] = { 'c', 's' };
+static symbol s_0_1[3] = { 'd', 'z', 's' };
+static symbol s_0_2[2] = { 'g', 'y' };
+static symbol s_0_3[2] = { 'l', 'y' };
+static symbol s_0_4[2] = { 'n', 'y' };
+static symbol s_0_5[2] = { 's', 'z' };
+static symbol s_0_6[2] = { 't', 'y' };
+static symbol s_0_7[2] = { 'z', 's' };
+
+static struct among a_0[8] =
+{
+/*  0 */ { 2, s_0_0, -1, -1, 0},
+/*  1 */ { 3, s_0_1, -1, -1, 0},
+/*  2 */ { 2, s_0_2, -1, -1, 0},
+/*  3 */ { 2, s_0_3, -1, -1, 0},
+/*  4 */ { 2, s_0_4, -1, -1, 0},
+/*  5 */ { 2, s_0_5, -1, -1, 0},
+/*  6 */ { 2, s_0_6, -1, -1, 0},
+/*  7 */ { 2, s_0_7, -1, -1, 0}
+};
+
+static symbol s_1_0[2] = { 0xC3, 0xA1 };
+static symbol s_1_1[2] = { 0xC3, 0xA9 };
+
+static struct among a_1[2] =
+{
+/*  0 */ { 2, s_1_0, -1, 1, 0},
+/*  1 */ { 2, s_1_1, -1, 2, 0}
+};
+
+static symbol s_2_0[2] = { 'b', 'b' };
+static symbol s_2_1[2] = { 'c', 'c' };
+static symbol s_2_2[2] = { 'd', 'd' };
+static symbol s_2_3[2] = { 'f', 'f' };
+static symbol s_2_4[2] = { 'g', 'g' };
+static symbol s_2_5[2] = { 'j', 'j' };
+static symbol s_2_6[2] = { 'k', 'k' };
+static symbol s_2_7[2] = { 'l', 'l' };
+static symbol s_2_8[2] = { 'm', 'm' };
+static symbol s_2_9[2] = { 'n', 'n' };
+static symbol s_2_10[2] = { 'p', 'p' };
+static symbol s_2_11[2] = { 'r', 'r' };
+static symbol s_2_12[3] = { 'c', 'c', 's' };
+static symbol s_2_13[2] = { 's', 's' };
+static symbol s_2_14[3] = { 'z', 'z', 's' };
+static symbol s_2_15[2] = { 't', 't' };
+static symbol s_2_16[2] = { 'v', 'v' };
+static symbol s_2_17[3] = { 'g', 'g', 'y' };
+static symbol s_2_18[3] = { 'l', 'l', 'y' };
+static symbol s_2_19[3] = { 'n', 'n', 'y' };
+static symbol s_2_20[3] = { 't', 't', 'y' };
+static symbol s_2_21[3] = { 's', 's', 'z' };
+static symbol s_2_22[2] = { 'z', 'z' };
+
+static struct among a_2[23] =
+{
+/*  0 */ { 2, s_2_0, -1, -1, 0},
+/*  1 */ { 2, s_2_1, -1, -1, 0},
+/*  2 */ { 2, s_2_2, -1, -1, 0},
+/*  3 */ { 2, s_2_3, -1, -1, 0},
+/*  4 */ { 2, s_2_4, -1, -1, 0},
+/*  5 */ { 2, s_2_5, -1, -1, 0},
+/*  6 */ { 2, s_2_6, -1, -1, 0},
+/*  7 */ { 2, s_2_7, -1, -1, 0},
+/*  8 */ { 2, s_2_8, -1, -1, 0},
+/*  9 */ { 2, s_2_9, -1, -1, 0},
+/* 10 */ { 2, s_2_10, -1, -1, 0},
+/* 11 */ { 2, s_2_11, -1, -1, 0},
+/* 12 */ { 3, s_2_12, -1, -1, 0},
+/* 13 */ { 2, s_2_13, -1, -1, 0},
+/* 14 */ { 3, s_2_14, -1, -1, 0},
+/* 15 */ { 2, s_2_15, -1, -1, 0},
+/* 16 */ { 2, s_2_16, -1, -1, 0},
+/* 17 */ { 3, s_2_17, -1, -1, 0},
+/* 18 */ { 3, s_2_18, -1, -1, 0},
+/* 19 */ { 3, s_2_19, -1, -1, 0},
+/* 20 */ { 3, s_2_20, -1, -1, 0},
+/* 21 */ { 3, s_2_21, -1, -1, 0},
+/* 22 */ { 2, s_2_22, -1, -1, 0}
+};
+
+static symbol s_3_0[2] = { 'a', 'l' };
+static symbol s_3_1[2] = { 'e', 'l' };
+
+static struct among a_3[2] =
+{
+/*  0 */ { 2, s_3_0, -1, 1, 0},
+/*  1 */ { 2, s_3_1, -1, 2, 0}
+};
+
+static symbol s_4_0[2] = { 'b', 'a' };
+static symbol s_4_1[2] = { 'r', 'a' };
+static symbol s_4_2[2] = { 'b', 'e' };
+static symbol s_4_3[2] = { 'r', 'e' };
+static symbol s_4_4[2] = { 'i', 'g' };
+static symbol s_4_5[3] = { 'n', 'a', 'k' };
+static symbol s_4_6[3] = { 'n', 'e', 'k' };
+static symbol s_4_7[3] = { 'v', 'a', 'l' };
+static symbol s_4_8[3] = { 'v', 'e', 'l' };
+static symbol s_4_9[2] = { 'u', 'l' };
+static symbol s_4_10[4] = { 'b', 0xC5, 0x91, 'l' };
+static symbol s_4_11[4] = { 'r', 0xC5, 0x91, 'l' };
+static symbol s_4_12[4] = { 't', 0xC5, 0x91, 'l' };
+static symbol s_4_13[4] = { 'n', 0xC3, 0xA1, 'l' };
+static symbol s_4_14[4] = { 'n', 0xC3, 0xA9, 'l' };
+static symbol s_4_15[4] = { 'b', 0xC3, 0xB3, 'l' };
+static symbol s_4_16[4] = { 'r', 0xC3, 0xB3, 'l' };
+static symbol s_4_17[4] = { 't', 0xC3, 0xB3, 'l' };
+static symbol s_4_18[3] = { 0xC3, 0xBC, 'l' };
+static symbol s_4_19[1] = { 'n' };
+static symbol s_4_20[2] = { 'a', 'n' };
+static symbol s_4_21[3] = { 'b', 'a', 'n' };
+static symbol s_4_22[2] = { 'e', 'n' };
+static symbol s_4_23[3] = { 'b', 'e', 'n' };
+static symbol s_4_24[7] = { 'k', 0xC3, 0xA9, 'p', 'p', 'e', 'n' };
+static symbol s_4_25[2] = { 'o', 'n' };
+static symbol s_4_26[3] = { 0xC3, 0xB6, 'n' };
+static symbol s_4_27[5] = { 'k', 0xC3, 0xA9, 'p', 'p' };
+static symbol s_4_28[3] = { 'k', 'o', 'r' };
+static symbol s_4_29[1] = { 't' };
+static symbol s_4_30[2] = { 'a', 't' };
+static symbol s_4_31[2] = { 'e', 't' };
+static symbol s_4_32[5] = { 'k', 0xC3, 0xA9, 'n', 't' };
+static symbol s_4_33[7] = { 'a', 'n', 'k', 0xC3, 0xA9, 'n', 't' };
+static symbol s_4_34[7] = { 'e', 'n', 'k', 0xC3, 0xA9, 'n', 't' };
+static symbol s_4_35[7] = { 'o', 'n', 'k', 0xC3, 0xA9, 'n', 't' };
+static symbol s_4_36[2] = { 'o', 't' };
+static symbol s_4_37[4] = { 0xC3, 0xA9, 'r', 't' };
+static symbol s_4_38[3] = { 0xC3, 0xB6, 't' };
+static symbol s_4_39[3] = { 'h', 'e', 'z' };
+static symbol s_4_40[3] = { 'h', 'o', 'z' };
+static symbol s_4_41[4] = { 'h', 0xC3, 0xB6, 'z' };
+static symbol s_4_42[3] = { 'v', 0xC3, 0xA1 };
+static symbol s_4_43[3] = { 'v', 0xC3, 0xA9 };
+
+static struct among a_4[44] =
+{
+/*  0 */ { 2, s_4_0, -1, -1, 0},
+/*  1 */ { 2, s_4_1, -1, -1, 0},
+/*  2 */ { 2, s_4_2, -1, -1, 0},
+/*  3 */ { 2, s_4_3, -1, -1, 0},
+/*  4 */ { 2, s_4_4, -1, -1, 0},
+/*  5 */ { 3, s_4_5, -1, -1, 0},
+/*  6 */ { 3, s_4_6, -1, -1, 0},
+/*  7 */ { 3, s_4_7, -1, -1, 0},
+/*  8 */ { 3, s_4_8, -1, -1, 0},
+/*  9 */ { 2, s_4_9, -1, -1, 0},
+/* 10 */ { 4, s_4_10, -1, -1, 0},
+/* 11 */ { 4, s_4_11, -1, -1, 0},
+/* 12 */ { 4, s_4_12, -1, -1, 0},
+/* 13 */ { 4, s_4_13, -1, -1, 0},
+/* 14 */ { 4, s_4_14, -1, -1, 0},
+/* 15 */ { 4, s_4_15, -1, -1, 0},
+/* 16 */ { 4, s_4_16, -1, -1, 0},
+/* 17 */ { 4, s_4_17, -1, -1, 0},
+/* 18 */ { 3, s_4_18, -1, -1, 0},
+/* 19 */ { 1, s_4_19, -1, -1, 0},
+/* 20 */ { 2, s_4_20, 19, -1, 0},
+/* 21 */ { 3, s_4_21, 20, -1, 0},
+/* 22 */ { 2, s_4_22, 19, -1, 0},
+/* 23 */ { 3, s_4_23, 22, -1, 0},
+/* 24 */ { 7, s_4_24, 22, -1, 0},
+/* 25 */ { 2, s_4_25, 19, -1, 0},
+/* 26 */ { 3, s_4_26, 19, -1, 0},
+/* 27 */ { 5, s_4_27, -1, -1, 0},
+/* 28 */ { 3, s_4_28, -1, -1, 0},
+/* 29 */ { 1, s_4_29, -1, -1, 0},
+/* 30 */ { 2, s_4_30, 29, -1, 0},
+/* 31 */ { 2, s_4_31, 29, -1, 0},
+/* 32 */ { 5, s_4_32, 29, -1, 0},
+/* 33 */ { 7, s_4_33, 32, -1, 0},
+/* 34 */ { 7, s_4_34, 32, -1, 0},
+/* 35 */ { 7, s_4_35, 32, -1, 0},
+/* 36 */ { 2, s_4_36, 29, -1, 0},
+/* 37 */ { 4, s_4_37, 29, -1, 0},
+/* 38 */ { 3, s_4_38, 29, -1, 0},
+/* 39 */ { 3, s_4_39, -1, -1, 0},
+/* 40 */ { 3, s_4_40, -1, -1, 0},
+/* 41 */ { 4, s_4_41, -1, -1, 0},
+/* 42 */ { 3, s_4_42, -1, -1, 0},
+/* 43 */ { 3, s_4_43, -1, -1, 0}
+};
+
+static symbol s_5_0[3] = { 0xC3, 0xA1, 'n' };
+static symbol s_5_1[3] = { 0xC3, 0xA9, 'n' };
+static symbol s_5_2[8] = { 0xC3, 0xA1, 'n', 'k', 0xC3, 0xA9, 'n', 't' };
+
+static struct among a_5[3] =
+{
+/*  0 */ { 3, s_5_0, -1, 2, 0},
+/*  1 */ { 3, s_5_1, -1, 1, 0},
+/*  2 */ { 8, s_5_2, -1, 3, 0}
+};
+
+static symbol s_6_0[4] = { 's', 't', 'u', 'l' };
+static symbol s_6_1[5] = { 'a', 's', 't', 'u', 'l' };
+static symbol s_6_2[6] = { 0xC3, 0xA1, 's', 't', 'u', 'l' };
+static symbol s_6_3[5] = { 's', 't', 0xC3, 0xBC, 'l' };
+static symbol s_6_4[6] = { 'e', 's', 't', 0xC3, 0xBC, 'l' };
+static symbol s_6_5[7] = { 0xC3, 0xA9, 's', 't', 0xC3, 0xBC, 'l' };
+
+static struct among a_6[6] =
+{
+/*  0 */ { 4, s_6_0, -1, 2, 0},
+/*  1 */ { 5, s_6_1, 0, 1, 0},
+/*  2 */ { 6, s_6_2, 0, 3, 0},
+/*  3 */ { 5, s_6_3, -1, 2, 0},
+/*  4 */ { 6, s_6_4, 3, 1, 0},
+/*  5 */ { 7, s_6_5, 3, 4, 0}
+};
+
+static symbol s_7_0[2] = { 0xC3, 0xA1 };
+static symbol s_7_1[2] = { 0xC3, 0xA9 };
+
+static struct among a_7[2] =
+{
+/*  0 */ { 2, s_7_0, -1, 1, 0},
+/*  1 */ { 2, s_7_1, -1, 2, 0}
+};
+
+static symbol s_8_0[1] = { 'k' };
+static symbol s_8_1[2] = { 'a', 'k' };
+static symbol s_8_2[2] = { 'e', 'k' };
+static symbol s_8_3[2] = { 'o', 'k' };
+static symbol s_8_4[3] = { 0xC3, 0xA1, 'k' };
+static symbol s_8_5[3] = { 0xC3, 0xA9, 'k' };
+static symbol s_8_6[3] = { 0xC3, 0xB6, 'k' };
+
+static struct among a_8[7] =
+{
+/*  0 */ { 1, s_8_0, -1, 7, 0},
+/*  1 */ { 2, s_8_1, 0, 4, 0},
+/*  2 */ { 2, s_8_2, 0, 6, 0},
+/*  3 */ { 2, s_8_3, 0, 5, 0},
+/*  4 */ { 3, s_8_4, 0, 1, 0},
+/*  5 */ { 3, s_8_5, 0, 2, 0},
+/*  6 */ { 3, s_8_6, 0, 3, 0}
+};
+
+static symbol s_9_0[3] = { 0xC3, 0xA9, 'i' };
+static symbol s_9_1[5] = { 0xC3, 0xA1, 0xC3, 0xA9, 'i' };
+static symbol s_9_2[5] = { 0xC3, 0xA9, 0xC3, 0xA9, 'i' };
+static symbol s_9_3[2] = { 0xC3, 0xA9 };
+static symbol s_9_4[3] = { 'k', 0xC3, 0xA9 };
+static symbol s_9_5[4] = { 'a', 'k', 0xC3, 0xA9 };
+static symbol s_9_6[4] = { 'e', 'k', 0xC3, 0xA9 };
+static symbol s_9_7[4] = { 'o', 'k', 0xC3, 0xA9 };
+static symbol s_9_8[5] = { 0xC3, 0xA1, 'k', 0xC3, 0xA9 };
+static symbol s_9_9[5] = { 0xC3, 0xA9, 'k', 0xC3, 0xA9 };
+static symbol s_9_10[5] = { 0xC3, 0xB6, 'k', 0xC3, 0xA9 };
+static symbol s_9_11[4] = { 0xC3, 0xA9, 0xC3, 0xA9 };
+
+static struct among a_9[12] =
+{
+/*  0 */ { 3, s_9_0, -1, 7, 0},
+/*  1 */ { 5, s_9_1, 0, 6, 0},
+/*  2 */ { 5, s_9_2, 0, 5, 0},
+/*  3 */ { 2, s_9_3, -1, 9, 0},
+/*  4 */ { 3, s_9_4, 3, 4, 0},
+/*  5 */ { 4, s_9_5, 4, 1, 0},
+/*  6 */ { 4, s_9_6, 4, 1, 0},
+/*  7 */ { 4, s_9_7, 4, 1, 0},
+/*  8 */ { 5, s_9_8, 4, 3, 0},
+/*  9 */ { 5, s_9_9, 4, 2, 0},
+/* 10 */ { 5, s_9_10, 4, 1, 0},
+/* 11 */ { 4, s_9_11, 3, 8, 0}
+};
+
+static symbol s_10_0[1] = { 'a' };
+static symbol s_10_1[2] = { 'j', 'a' };
+static symbol s_10_2[1] = { 'd' };
+static symbol s_10_3[2] = { 'a', 'd' };
+static symbol s_10_4[2] = { 'e', 'd' };
+static symbol s_10_5[2] = { 'o', 'd' };
+static symbol s_10_6[3] = { 0xC3, 0xA1, 'd' };
+static symbol s_10_7[3] = { 0xC3, 0xA9, 'd' };
+static symbol s_10_8[3] = { 0xC3, 0xB6, 'd' };
+static symbol s_10_9[1] = { 'e' };
+static symbol s_10_10[2] = { 'j', 'e' };
+static symbol s_10_11[2] = { 'n', 'k' };
+static symbol s_10_12[3] = { 'u', 'n', 'k' };
+static symbol s_10_13[4] = { 0xC3, 0xA1, 'n', 'k' };
+static symbol s_10_14[4] = { 0xC3, 0xA9, 'n', 'k' };
+static symbol s_10_15[4] = { 0xC3, 0xBC, 'n', 'k' };
+static symbol s_10_16[2] = { 'u', 'k' };
+static symbol s_10_17[3] = { 'j', 'u', 'k' };
+static symbol s_10_18[5] = { 0xC3, 0xA1, 'j', 'u', 'k' };
+static symbol s_10_19[3] = { 0xC3, 0xBC, 'k' };
+static symbol s_10_20[4] = { 'j', 0xC3, 0xBC, 'k' };
+static symbol s_10_21[6] = { 0xC3, 0xA9, 'j', 0xC3, 0xBC, 'k' };
+static symbol s_10_22[1] = { 'm' };
+static symbol s_10_23[2] = { 'a', 'm' };
+static symbol s_10_24[2] = { 'e', 'm' };
+static symbol s_10_25[2] = { 'o', 'm' };
+static symbol s_10_26[3] = { 0xC3, 0xA1, 'm' };
+static symbol s_10_27[3] = { 0xC3, 0xA9, 'm' };
+static symbol s_10_28[1] = { 'o' };
+static symbol s_10_29[2] = { 0xC3, 0xA1 };
+static symbol s_10_30[2] = { 0xC3, 0xA9 };
+
+static struct among a_10[31] =
+{
+/*  0 */ { 1, s_10_0, -1, 18, 0},
+/*  1 */ { 2, s_10_1, 0, 17, 0},
+/*  2 */ { 1, s_10_2, -1, 16, 0},
+/*  3 */ { 2, s_10_3, 2, 13, 0},
+/*  4 */ { 2, s_10_4, 2, 13, 0},
+/*  5 */ { 2, s_10_5, 2, 13, 0},
+/*  6 */ { 3, s_10_6, 2, 14, 0},
+/*  7 */ { 3, s_10_7, 2, 15, 0},
+/*  8 */ { 3, s_10_8, 2, 13, 0},
+/*  9 */ { 1, s_10_9, -1, 18, 0},
+/* 10 */ { 2, s_10_10, 9, 17, 0},
+/* 11 */ { 2, s_10_11, -1, 4, 0},
+/* 12 */ { 3, s_10_12, 11, 1, 0},
+/* 13 */ { 4, s_10_13, 11, 2, 0},
+/* 14 */ { 4, s_10_14, 11, 3, 0},
+/* 15 */ { 4, s_10_15, 11, 1, 0},
+/* 16 */ { 2, s_10_16, -1, 8, 0},
+/* 17 */ { 3, s_10_17, 16, 7, 0},
+/* 18 */ { 5, s_10_18, 17, 5, 0},
+/* 19 */ { 3, s_10_19, -1, 8, 0},
+/* 20 */ { 4, s_10_20, 19, 7, 0},
+/* 21 */ { 6, s_10_21, 20, 6, 0},
+/* 22 */ { 1, s_10_22, -1, 12, 0},
+/* 23 */ { 2, s_10_23, 22, 9, 0},
+/* 24 */ { 2, s_10_24, 22, 9, 0},
+/* 25 */ { 2, s_10_25, 22, 9, 0},
+/* 26 */ { 3, s_10_26, 22, 10, 0},
+/* 27 */ { 3, s_10_27, 22, 11, 0},
+/* 28 */ { 1, s_10_28, -1, 18, 0},
+/* 29 */ { 2, s_10_29, -1, 19, 0},
+/* 30 */ { 2, s_10_30, -1, 20, 0}
+};
+
+static symbol s_11_0[2] = { 'i', 'd' };
+static symbol s_11_1[3] = { 'a', 'i', 'd' };
+static symbol s_11_2[4] = { 'j', 'a', 'i', 'd' };
+static symbol s_11_3[3] = { 'e', 'i', 'd' };
+static symbol s_11_4[4] = { 'j', 'e', 'i', 'd' };
+static symbol s_11_5[4] = { 0xC3, 0xA1, 'i', 'd' };
+static symbol s_11_6[4] = { 0xC3, 0xA9, 'i', 'd' };
+static symbol s_11_7[1] = { 'i' };
+static symbol s_11_8[2] = { 'a', 'i' };
+static symbol s_11_9[3] = { 'j', 'a', 'i' };
+static symbol s_11_10[2] = { 'e', 'i' };
+static symbol s_11_11[3] = { 'j', 'e', 'i' };
+static symbol s_11_12[3] = { 0xC3, 0xA1, 'i' };
+static symbol s_11_13[3] = { 0xC3, 0xA9, 'i' };
+static symbol s_11_14[4] = { 'i', 't', 'e', 'k' };
+static symbol s_11_15[5] = { 'e', 'i', 't', 'e', 'k' };
+static symbol s_11_16[6] = { 'j', 'e', 'i', 't', 'e', 'k' };
+static symbol s_11_17[6] = { 0xC3, 0xA9, 'i', 't', 'e', 'k' };
+static symbol s_11_18[2] = { 'i', 'k' };
+static symbol s_11_19[3] = { 'a', 'i', 'k' };
+static symbol s_11_20[4] = { 'j', 'a', 'i', 'k' };
+static symbol s_11_21[3] = { 'e', 'i', 'k' };
+static symbol s_11_22[4] = { 'j', 'e', 'i', 'k' };
+static symbol s_11_23[4] = { 0xC3, 0xA1, 'i', 'k' };
+static symbol s_11_24[4] = { 0xC3, 0xA9, 'i', 'k' };
+static symbol s_11_25[3] = { 'i', 'n', 'k' };
+static symbol s_11_26[4] = { 'a', 'i', 'n', 'k' };
+static symbol s_11_27[5] = { 'j', 'a', 'i', 'n', 'k' };
+static symbol s_11_28[4] = { 'e', 'i', 'n', 'k' };
+static symbol s_11_29[5] = { 'j', 'e', 'i', 'n', 'k' };
+static symbol s_11_30[5] = { 0xC3, 0xA1, 'i', 'n', 'k' };
+static symbol s_11_31[5] = { 0xC3, 0xA9, 'i', 'n', 'k' };
+static symbol s_11_32[5] = { 'a', 'i', 't', 'o', 'k' };
+static symbol s_11_33[6] = { 'j', 'a', 'i', 't', 'o', 'k' };
+static symbol s_11_34[6] = { 0xC3, 0xA1, 'i', 't', 'o', 'k' };
+static symbol s_11_35[2] = { 'i', 'm' };
+static symbol s_11_36[3] = { 'a', 'i', 'm' };
+static symbol s_11_37[4] = { 'j', 'a', 'i', 'm' };
+static symbol s_11_38[3] = { 'e', 'i', 'm' };
+static symbol s_11_39[4] = { 'j', 'e', 'i', 'm' };
+static symbol s_11_40[4] = { 0xC3, 0xA1, 'i', 'm' };
+static symbol s_11_41[4] = { 0xC3, 0xA9, 'i', 'm' };
+
+static struct among a_11[42] =
+{
+/*  0 */ { 2, s_11_0, -1, 10, 0},
+/*  1 */ { 3, s_11_1, 0, 9, 0},
+/*  2 */ { 4, s_11_2, 1, 6, 0},
+/*  3 */ { 3, s_11_3, 0, 9, 0},
+/*  4 */ { 4, s_11_4, 3, 6, 0},
+/*  5 */ { 4, s_11_5, 0, 7, 0},
+/*  6 */ { 4, s_11_6, 0, 8, 0},
+/*  7 */ { 1, s_11_7, -1, 15, 0},
+/*  8 */ { 2, s_11_8, 7, 14, 0},
+/*  9 */ { 3, s_11_9, 8, 11, 0},
+/* 10 */ { 2, s_11_10, 7, 14, 0},
+/* 11 */ { 3, s_11_11, 10, 11, 0},
+/* 12 */ { 3, s_11_12, 7, 12, 0},
+/* 13 */ { 3, s_11_13, 7, 13, 0},
+/* 14 */ { 4, s_11_14, -1, 24, 0},
+/* 15 */ { 5, s_11_15, 14, 21, 0},
+/* 16 */ { 6, s_11_16, 15, 20, 0},
+/* 17 */ { 6, s_11_17, 14, 23, 0},
+/* 18 */ { 2, s_11_18, -1, 29, 0},
+/* 19 */ { 3, s_11_19, 18, 26, 0},
+/* 20 */ { 4, s_11_20, 19, 25, 0},
+/* 21 */ { 3, s_11_21, 18, 26, 0},
+/* 22 */ { 4, s_11_22, 21, 25, 0},
+/* 23 */ { 4, s_11_23, 18, 27, 0},
+/* 24 */ { 4, s_11_24, 18, 28, 0},
+/* 25 */ { 3, s_11_25, -1, 20, 0},
+/* 26 */ { 4, s_11_26, 25, 17, 0},
+/* 27 */ { 5, s_11_27, 26, 16, 0},
+/* 28 */ { 4, s_11_28, 25, 17, 0},
+/* 29 */ { 5, s_11_29, 28, 16, 0},
+/* 30 */ { 5, s_11_30, 25, 18, 0},
+/* 31 */ { 5, s_11_31, 25, 19, 0},
+/* 32 */ { 5, s_11_32, -1, 21, 0},
+/* 33 */ { 6, s_11_33, 32, 20, 0},
+/* 34 */ { 6, s_11_34, -1, 22, 0},
+/* 35 */ { 2, s_11_35, -1, 5, 0},
+/* 36 */ { 3, s_11_36, 35, 4, 0},
+/* 37 */ { 4, s_11_37, 36, 1, 0},
+/* 38 */ { 3, s_11_38, 35, 4, 0},
+/* 39 */ { 4, s_11_39, 38, 1, 0},
+/* 40 */ { 4, s_11_40, 35, 2, 0},
+/* 41 */ { 4, s_11_41, 35, 3, 0}
+};
+
+static unsigned char g_v[] = { 17, 65, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 17, 36, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1 };
+
+static symbol s_0[] = { 'a' };
+static symbol s_1[] = { 'e' };
+static symbol s_2[] = { 'e' };
+static symbol s_3[] = { 'a' };
+static symbol s_4[] = { 'a' };
+static symbol s_5[] = { 'a' };
+static symbol s_6[] = { 'e' };
+static symbol s_7[] = { 'a' };
+static symbol s_8[] = { 'e' };
+static symbol s_9[] = { 'e' };
+static symbol s_10[] = { 'a' };
+static symbol s_11[] = { 'e' };
+static symbol s_12[] = { 'a' };
+static symbol s_13[] = { 'e' };
+static symbol s_14[] = { 'a' };
+static symbol s_15[] = { 'e' };
+static symbol s_16[] = { 'a' };
+static symbol s_17[] = { 'e' };
+static symbol s_18[] = { 'a' };
+static symbol s_19[] = { 'e' };
+static symbol s_20[] = { 'a' };
+static symbol s_21[] = { 'e' };
+static symbol s_22[] = { 'a' };
+static symbol s_23[] = { 'e' };
+static symbol s_24[] = { 'a' };
+static symbol s_25[] = { 'e' };
+static symbol s_26[] = { 'a' };
+static symbol s_27[] = { 'e' };
+static symbol s_28[] = { 'a' };
+static symbol s_29[] = { 'e' };
+static symbol s_30[] = { 'a' };
+static symbol s_31[] = { 'e' };
+static symbol s_32[] = { 'a' };
+static symbol s_33[] = { 'e' };
+static symbol s_34[] = { 'a' };
+static symbol s_35[] = { 'e' };
+
+static int r_mark_regions(struct SN_env * z) {
+    z->I[0] = z->l;
+    {	int c1 = z->c; /* or, line 51 */
+	//if (in_grouping_U(z, g_v, 97, 369, 0)) goto lab1;
+	if (in_grouping_U(z, g_v, 97, 369)) goto lab1;
+	//if (in_grouping_U(z, g_v, 97, 369, 1) < 0) goto lab1; /* goto */ /* non v, line 48 */
+	if (in_grouping_U(z, g_v, 97, 369) < 0) goto lab1; /* goto */ /* non v, line 48 */
+	{   int c2 = z->c; /* or, line 49 */
+	    if (z->c + 1 >= z->l || z->p[z->c + 1] >> 5 != 3 || !((101187584 >> (z->p[z->c + 1] & 0x1f)) & 1)) goto lab3;
+	    if (!(find_among(z, a_0, 8))) goto lab3; /* among, line 49 */
+	    goto lab2;
+	lab3:
+	    z->c = c2;
+	    {	int ret = skip_utf8(z->p, z->c, 0, z->l, 1);
+		if (ret < 0) goto lab1;
+		z->c = ret; /* next, line 49 */
+	    }
+	}
+    lab2:
+	z->I[0] = z->c; /* setmark p1, line 50 */
+	goto lab0;
+    lab1:
+	z->c = c1;
+	//if (out_grouping_U(z, g_v, 97, 369, 0)) return 0;
+	if (out_grouping_U(z, g_v, 97, 369)) return 0;
+	{    /* gopast */ /* grouping v, line 53 */
+	    //int ret = out_grouping_U(z, g_v, 97, 369, 1);
+	    int ret = out_grouping_U(z, g_v, 97, 369);
+	    if (ret < 0) return 0;
+	    z->c += ret;
+	}
+	z->I[0] = z->c; /* setmark p1, line 53 */
+    }
+lab0:
+    return 1;
+}
+
+static int r_R1(struct SN_env * z) {
+    if (!(z->I[0] <= z->c)) return 0;
+    return 1;
+}
+
+static int r_v_ending(struct SN_env * z) {
+    int among_var;
+    z->ket = z->c; /* [, line 61 */
+    if (z->c - 1 <= z->lb || (z->p[z->c - 1] != 161 && z->p[z->c - 1] != 169)) return 0;
+    among_var = find_among_b(z, a_1, 2); /* substring, line 61 */
+    if (!(among_var)) return 0;
+    z->bra = z->c; /* ], line 61 */
+    {	int ret = r_R1(z);
+	if (ret == 0) return 0; /* call R1, line 61 */
+	if (ret < 0) return ret;
+    }
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret = slice_from_s(z, 1, s_0); /* <-, line 62 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    {	int ret = slice_from_s(z, 1, s_1); /* <-, line 63 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_double(struct SN_env * z) {
+    {	int m_test = z->l - z->c; /* test, line 68 */
+	if (z->c - 1 <= z->lb || z->p[z->c - 1] >> 5 != 3 || !((106790108 >> (z->p[z->c - 1] & 0x1f)) & 1)) return 0;
+	if (!(find_among_b(z, a_2, 23))) return 0; /* among, line 68 */
+	z->c = z->l - m_test;
+    }
+    return 1;
+}
+
+static int r_undouble(struct SN_env * z) {
+    {	int ret = skip_utf8(z->p, z->c, z->lb, 0, -1);
+	if (ret < 0) return 0;
+	z->c = ret; /* next, line 73 */
+    }
+    z->ket = z->c; /* [, line 73 */
+    {	int ret = skip_utf8(z->p, z->c, z->lb, z->l, - 1);
+	if (ret < 0) return 0;
+	z->c = ret; /* hop, line 73 */
+    }
+    z->bra = z->c; /* ], line 73 */
+    {	int ret = slice_del(z); /* delete, line 73 */
+	if (ret < 0) return ret;
+    }
+    return 1;
+}
+
+static int r_instrum(struct SN_env * z) {
+    int among_var;
+    z->ket = z->c; /* [, line 77 */
+    if (z->c - 1 <= z->lb || z->p[z->c - 1] != 108) return 0;
+    among_var = find_among_b(z, a_3, 2); /* substring, line 77 */
+    if (!(among_var)) return 0;
+    z->bra = z->c; /* ], line 77 */
+    {	int ret = r_R1(z);
+	if (ret == 0) return 0; /* call R1, line 77 */
+	if (ret < 0) return ret;
+    }
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret = r_double(z);
+		if (ret == 0) return 0; /* call double, line 78 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    {	int ret = r_double(z);
+		if (ret == 0) return 0; /* call double, line 79 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    {	int ret = slice_del(z); /* delete, line 81 */
+	if (ret < 0) return ret;
+    }
+    {	int ret = r_undouble(z);
+	if (ret == 0) return 0; /* call undouble, line 82 */
+	if (ret < 0) return ret;
+    }
+    return 1;
+}
+
+static int r_case(struct SN_env * z) {
+    z->ket = z->c; /* [, line 87 */
+    if (!(find_among_b(z, a_4, 44))) return 0; /* substring, line 87 */
+    z->bra = z->c; /* ], line 87 */
+    {	int ret = r_R1(z);
+	if (ret == 0) return 0; /* call R1, line 87 */
+	if (ret < 0) return ret;
+    }
+    {	int ret = slice_del(z); /* delete, line 111 */
+	if (ret < 0) return ret;
+    }
+    {	int ret = r_v_ending(z);
+	if (ret == 0) return 0; /* call v_ending, line 112 */
+	if (ret < 0) return ret;
+    }
+    return 1;
+}
+
+static int r_case_special(struct SN_env * z) {
+    int among_var;
+    z->ket = z->c; /* [, line 116 */
+    if (z->c - 2 <= z->lb || (z->p[z->c - 1] != 110 && z->p[z->c - 1] != 116)) return 0;
+    among_var = find_among_b(z, a_5, 3); /* substring, line 116 */
+    if (!(among_var)) return 0;
+    z->bra = z->c; /* ], line 116 */
+    {	int ret = r_R1(z);
+	if (ret == 0) return 0; /* call R1, line 116 */
+	if (ret < 0) return ret;
+    }
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret = slice_from_s(z, 1, s_2); /* <-, line 117 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    {	int ret = slice_from_s(z, 1, s_3); /* <-, line 118 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 3:
+	    {	int ret = slice_from_s(z, 1, s_4); /* <-, line 119 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_case_other(struct SN_env * z) {
+    int among_var;
+    z->ket = z->c; /* [, line 124 */
+    if (z->c - 3 <= z->lb || z->p[z->c - 1] != 108) return 0;
+    among_var = find_among_b(z, a_6, 6); /* substring, line 124 */
+    if (!(among_var)) return 0;
+    z->bra = z->c; /* ], line 124 */
+    {	int ret = r_R1(z);
+	if (ret == 0) return 0; /* call R1, line 124 */
+	if (ret < 0) return ret;
+    }
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret = slice_del(z); /* delete, line 125 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    {	int ret = slice_del(z); /* delete, line 126 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 3:
+	    {	int ret = slice_from_s(z, 1, s_5); /* <-, line 127 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 4:
+	    {	int ret = slice_from_s(z, 1, s_6); /* <-, line 128 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_factive(struct SN_env * z) {
+    int among_var;
+    z->ket = z->c; /* [, line 133 */
+    if (z->c - 1 <= z->lb || (z->p[z->c - 1] != 161 && z->p[z->c - 1] != 169)) return 0;
+    among_var = find_among_b(z, a_7, 2); /* substring, line 133 */
+    if (!(among_var)) return 0;
+    z->bra = z->c; /* ], line 133 */
+    {	int ret = r_R1(z);
+	if (ret == 0) return 0; /* call R1, line 133 */
+	if (ret < 0) return ret;
+    }
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret = r_double(z);
+		if (ret == 0) return 0; /* call double, line 134 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    {	int ret = r_double(z);
+		if (ret == 0) return 0; /* call double, line 135 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    {	int ret = slice_del(z); /* delete, line 137 */
+	if (ret < 0) return ret;
+    }
+    {	int ret = r_undouble(z);
+	if (ret == 0) return 0; /* call undouble, line 138 */
+	if (ret < 0) return ret;
+    }
+    return 1;
+}
+
+static int r_plural(struct SN_env * z) {
+    int among_var;
+    z->ket = z->c; /* [, line 142 */
+    if (z->c <= z->lb || z->p[z->c - 1] != 107) return 0;
+    among_var = find_among_b(z, a_8, 7); /* substring, line 142 */
+    if (!(among_var)) return 0;
+    z->bra = z->c; /* ], line 142 */
+    {	int ret = r_R1(z);
+	if (ret == 0) return 0; /* call R1, line 142 */
+	if (ret < 0) return ret;
+    }
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret = slice_from_s(z, 1, s_7); /* <-, line 143 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    {	int ret = slice_from_s(z, 1, s_8); /* <-, line 144 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 3:
+	    {	int ret = slice_del(z); /* delete, line 145 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 4:
+	    {	int ret = slice_del(z); /* delete, line 146 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 5:
+	    {	int ret = slice_del(z); /* delete, line 147 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 6:
+	    {	int ret = slice_del(z); /* delete, line 148 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 7:
+	    {	int ret = slice_del(z); /* delete, line 149 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_owned(struct SN_env * z) {
+    int among_var;
+    z->ket = z->c; /* [, line 154 */
+    if (z->c - 1 <= z->lb || (z->p[z->c - 1] != 105 && z->p[z->c - 1] != 169)) return 0;
+    among_var = find_among_b(z, a_9, 12); /* substring, line 154 */
+    if (!(among_var)) return 0;
+    z->bra = z->c; /* ], line 154 */
+    {	int ret = r_R1(z);
+	if (ret == 0) return 0; /* call R1, line 154 */
+	if (ret < 0) return ret;
+    }
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret = slice_del(z); /* delete, line 155 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    {	int ret = slice_from_s(z, 1, s_9); /* <-, line 156 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 3:
+	    {	int ret = slice_from_s(z, 1, s_10); /* <-, line 157 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 4:
+	    {	int ret = slice_del(z); /* delete, line 158 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 5:
+	    {	int ret = slice_from_s(z, 1, s_11); /* <-, line 159 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 6:
+	    {	int ret = slice_from_s(z, 1, s_12); /* <-, line 160 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 7:
+	    {	int ret = slice_del(z); /* delete, line 161 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 8:
+	    {	int ret = slice_from_s(z, 1, s_13); /* <-, line 162 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 9:
+	    {	int ret = slice_del(z); /* delete, line 163 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_sing_owner(struct SN_env * z) {
+    int among_var;
+    z->ket = z->c; /* [, line 168 */
+    among_var = find_among_b(z, a_10, 31); /* substring, line 168 */
+    if (!(among_var)) return 0;
+    z->bra = z->c; /* ], line 168 */
+    {	int ret = r_R1(z);
+	if (ret == 0) return 0; /* call R1, line 168 */
+	if (ret < 0) return ret;
+    }
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret = slice_del(z); /* delete, line 169 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    {	int ret = slice_from_s(z, 1, s_14); /* <-, line 170 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 3:
+	    {	int ret = slice_from_s(z, 1, s_15); /* <-, line 171 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 4:
+	    {	int ret = slice_del(z); /* delete, line 172 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 5:
+	    {	int ret = slice_from_s(z, 1, s_16); /* <-, line 173 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 6:
+	    {	int ret = slice_from_s(z, 1, s_17); /* <-, line 174 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 7:
+	    {	int ret = slice_del(z); /* delete, line 175 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 8:
+	    {	int ret = slice_del(z); /* delete, line 176 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 9:
+	    {	int ret = slice_del(z); /* delete, line 177 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 10:
+	    {	int ret = slice_from_s(z, 1, s_18); /* <-, line 178 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 11:
+	    {	int ret = slice_from_s(z, 1, s_19); /* <-, line 179 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 12:
+	    {	int ret = slice_del(z); /* delete, line 180 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 13:
+	    {	int ret = slice_del(z); /* delete, line 181 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 14:
+	    {	int ret = slice_from_s(z, 1, s_20); /* <-, line 182 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 15:
+	    {	int ret = slice_from_s(z, 1, s_21); /* <-, line 183 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 16:
+	    {	int ret = slice_del(z); /* delete, line 184 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 17:
+	    {	int ret = slice_del(z); /* delete, line 185 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 18:
+	    {	int ret = slice_del(z); /* delete, line 186 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 19:
+	    {	int ret = slice_from_s(z, 1, s_22); /* <-, line 187 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 20:
+	    {	int ret = slice_from_s(z, 1, s_23); /* <-, line 188 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_plur_owner(struct SN_env * z) {
+    int among_var;
+    z->ket = z->c; /* [, line 193 */
+    if (z->c <= z->lb || z->p[z->c - 1] >> 5 != 3 || !((10768 >> (z->p[z->c - 1] & 0x1f)) & 1)) return 0;
+    among_var = find_among_b(z, a_11, 42); /* substring, line 193 */
+    if (!(among_var)) return 0;
+    z->bra = z->c; /* ], line 193 */
+    {	int ret = r_R1(z);
+	if (ret == 0) return 0; /* call R1, line 193 */
+	if (ret < 0) return ret;
+    }
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret = slice_del(z); /* delete, line 194 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    {	int ret = slice_from_s(z, 1, s_24); /* <-, line 195 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 3:
+	    {	int ret = slice_from_s(z, 1, s_25); /* <-, line 196 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 4:
+	    {	int ret = slice_del(z); /* delete, line 197 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 5:
+	    {	int ret = slice_del(z); /* delete, line 198 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 6:
+	    {	int ret = slice_del(z); /* delete, line 199 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 7:
+	    {	int ret = slice_from_s(z, 1, s_26); /* <-, line 200 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 8:
+	    {	int ret = slice_from_s(z, 1, s_27); /* <-, line 201 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 9:
+	    {	int ret = slice_del(z); /* delete, line 202 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 10:
+	    {	int ret = slice_del(z); /* delete, line 203 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 11:
+	    {	int ret = slice_del(z); /* delete, line 204 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 12:
+	    {	int ret = slice_from_s(z, 1, s_28); /* <-, line 205 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 13:
+	    {	int ret = slice_from_s(z, 1, s_29); /* <-, line 206 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 14:
+	    {	int ret = slice_del(z); /* delete, line 207 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 15:
+	    {	int ret = slice_del(z); /* delete, line 208 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 16:
+	    {	int ret = slice_del(z); /* delete, line 209 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 17:
+	    {	int ret = slice_del(z); /* delete, line 210 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 18:
+	    {	int ret = slice_from_s(z, 1, s_30); /* <-, line 211 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 19:
+	    {	int ret = slice_from_s(z, 1, s_31); /* <-, line 212 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 20:
+	    {	int ret = slice_del(z); /* delete, line 214 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 21:
+	    {	int ret = slice_del(z); /* delete, line 215 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 22:
+	    {	int ret = slice_from_s(z, 1, s_32); /* <-, line 216 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 23:
+	    {	int ret = slice_from_s(z, 1, s_33); /* <-, line 217 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 24:
+	    {	int ret = slice_del(z); /* delete, line 218 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 25:
+	    {	int ret = slice_del(z); /* delete, line 219 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 26:
+	    {	int ret = slice_del(z); /* delete, line 220 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 27:
+	    {	int ret = slice_from_s(z, 1, s_34); /* <-, line 221 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 28:
+	    {	int ret = slice_from_s(z, 1, s_35); /* <-, line 222 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 29:
+	    {	int ret = slice_del(z); /* delete, line 223 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+extern int hungarian_UTF_8_stem(struct SN_env * z) {
+    {	int c1 = z->c; /* do, line 229 */
+	{   int ret = r_mark_regions(z);
+	    if (ret == 0) goto lab0; /* call mark_regions, line 229 */
+	    if (ret < 0) return ret;
+	}
+    lab0:
+	z->c = c1;
+    }
+    z->lb = z->c; z->c = z->l; /* backwards, line 230 */
+
+    {	int m2 = z->l - z->c; (void)m2; /* do, line 231 */
+	{   int ret = r_instrum(z);
+	    if (ret == 0) goto lab1; /* call instrum, line 231 */
+	    if (ret < 0) return ret;
+	}
+    lab1:
+	z->c = z->l - m2;
+    }
+    {	int m3 = z->l - z->c; (void)m3; /* do, line 232 */
+	{   int ret = r_case(z);
+	    if (ret == 0) goto lab2; /* call case, line 232 */
+	    if (ret < 0) return ret;
+	}
+    lab2:
+	z->c = z->l - m3;
+    }
+    {	int m4 = z->l - z->c; (void)m4; /* do, line 233 */
+	{   int ret = r_case_special(z);
+	    if (ret == 0) goto lab3; /* call case_special, line 233 */
+	    if (ret < 0) return ret;
+	}
+    lab3:
+	z->c = z->l - m4;
+    }
+    {	int m5 = z->l - z->c; (void)m5; /* do, line 234 */
+	{   int ret = r_case_other(z);
+	    if (ret == 0) goto lab4; /* call case_other, line 234 */
+	    if (ret < 0) return ret;
+	}
+    lab4:
+	z->c = z->l - m5;
+    }
+    {	int m6 = z->l - z->c; (void)m6; /* do, line 235 */
+	{   int ret = r_factive(z);
+	    if (ret == 0) goto lab5; /* call factive, line 235 */
+	    if (ret < 0) return ret;
+	}
+    lab5:
+	z->c = z->l - m6;
+    }
+    {	int m7 = z->l - z->c; (void)m7; /* do, line 236 */
+	{   int ret = r_owned(z);
+	    if (ret == 0) goto lab6; /* call owned, line 236 */
+	    if (ret < 0) return ret;
+	}
+    lab6:
+	z->c = z->l - m7;
+    }
+    {	int m8 = z->l - z->c; (void)m8; /* do, line 237 */
+	{   int ret = r_sing_owner(z);
+	    if (ret == 0) goto lab7; /* call sing_owner, line 237 */
+	    if (ret < 0) return ret;
+	}
+    lab7:
+	z->c = z->l - m8;
+    }
+    {	int m9 = z->l - z->c; (void)m9; /* do, line 238 */
+	{   int ret = r_plur_owner(z);
+	    if (ret == 0) goto lab8; /* call plur_owner, line 238 */
+	    if (ret < 0) return ret;
+	}
+    lab8:
+	z->c = z->l - m9;
+    }
+    {	int m10 = z->l - z->c; (void)m10; /* do, line 239 */
+	{   int ret = r_plural(z);
+	    if (ret == 0) goto lab9; /* call plural, line 239 */
+	    if (ret < 0) return ret;
+	}
+    lab9:
+	z->c = z->l - m10;
+    }
+    z->c = z->lb;
+    return 1;
+}
+
+extern struct SN_env * hungarian_UTF_8_create_env(void) { return SN_create_env(0, 1, 0); }
+
+//extern void hungarian_UTF_8_close_env(struct SN_env * z) { SN_close_env(z, 0); }
+extern void hungarian_UTF_8_close_env(struct SN_env * z) { SN_close_env(z); }
+

Added: trunk/src/libstemmer/stem_UTF_8_hungarian.h
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/stem_UTF_8_hungarian.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,16 @@
+
+/* This file was generated automatically by the Snowball to ANSI C compiler */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern struct SN_env * hungarian_UTF_8_create_env(void);
+extern void hungarian_UTF_8_close_env(struct SN_env * z);
+
+extern int hungarian_UTF_8_stem(struct SN_env * z);
+
+#ifdef __cplusplus
+}
+#endif
+

Added: trunk/src/libstemmer/stem_UTF_8_italian.c
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/stem_UTF_8_italian.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,1126 @@
+/*
+ * Copyright (c) 2001, Dr Martin Porter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+/* This file was generated automatically by the Snowball to ANSI C compiler */
+
+#include "header.h"
+
+extern int italian_UTF_8_stem(struct SN_env * z);
+static int r_vowel_suffix(struct SN_env * z);
+static int r_verb_suffix(struct SN_env * z);
+static int r_standard_suffix(struct SN_env * z);
+static int r_attached_pronoun(struct SN_env * z);
+static int r_R2(struct SN_env * z);
+static int r_R1(struct SN_env * z);
+static int r_RV(struct SN_env * z);
+static int r_mark_regions(struct SN_env * z);
+static int r_postlude(struct SN_env * z);
+static int r_prelude(struct SN_env * z);
+
+extern struct SN_env * italian_UTF_8_create_env(void);
+extern void italian_UTF_8_close_env(struct SN_env * z);
+
+static symbol s_0_1[2] = { 'q', 'u' };
+static symbol s_0_2[2] = { 0xC3, 0xA1 };
+static symbol s_0_3[2] = { 0xC3, 0xA9 };
+static symbol s_0_4[2] = { 0xC3, 0xAD };
+static symbol s_0_5[2] = { 0xC3, 0xB3 };
+static symbol s_0_6[2] = { 0xC3, 0xBA };
+
+static struct among a_0[7] =
+{
+/*  0 */ { 0, 0, -1, 7, 0},
+/*  1 */ { 2, s_0_1, 0, 6, 0},
+/*  2 */ { 2, s_0_2, 0, 1, 0},
+/*  3 */ { 2, s_0_3, 0, 2, 0},
+/*  4 */ { 2, s_0_4, 0, 3, 0},
+/*  5 */ { 2, s_0_5, 0, 4, 0},
+/*  6 */ { 2, s_0_6, 0, 5, 0}
+};
+
+static symbol s_1_1[1] = { 'I' };
+static symbol s_1_2[1] = { 'U' };
+
+static struct among a_1[3] =
+{
+/*  0 */ { 0, 0, -1, 3, 0},
+/*  1 */ { 1, s_1_1, 0, 1, 0},
+/*  2 */ { 1, s_1_2, 0, 2, 0}
+};
+
+static symbol s_2_0[2] = { 'l', 'a' };
+static symbol s_2_1[4] = { 'c', 'e', 'l', 'a' };
+static symbol s_2_2[6] = { 'g', 'l', 'i', 'e', 'l', 'a' };
+static symbol s_2_3[4] = { 'm', 'e', 'l', 'a' };
+static symbol s_2_4[4] = { 't', 'e', 'l', 'a' };
+static symbol s_2_5[4] = { 'v', 'e', 'l', 'a' };
+static symbol s_2_6[2] = { 'l', 'e' };
+static symbol s_2_7[4] = { 'c', 'e', 'l', 'e' };
+static symbol s_2_8[6] = { 'g', 'l', 'i', 'e', 'l', 'e' };
+static symbol s_2_9[4] = { 'm', 'e', 'l', 'e' };
+static symbol s_2_10[4] = { 't', 'e', 'l', 'e' };
+static symbol s_2_11[4] = { 'v', 'e', 'l', 'e' };
+static symbol s_2_12[2] = { 'n', 'e' };
+static symbol s_2_13[4] = { 'c', 'e', 'n', 'e' };
+static symbol s_2_14[6] = { 'g', 'l', 'i', 'e', 'n', 'e' };
+static symbol s_2_15[4] = { 'm', 'e', 'n', 'e' };
+static symbol s_2_16[4] = { 's', 'e', 'n', 'e' };
+static symbol s_2_17[4] = { 't', 'e', 'n', 'e' };
+static symbol s_2_18[4] = { 'v', 'e', 'n', 'e' };
+static symbol s_2_19[2] = { 'c', 'i' };
+static symbol s_2_20[2] = { 'l', 'i' };
+static symbol s_2_21[4] = { 'c', 'e', 'l', 'i' };
+static symbol s_2_22[6] = { 'g', 'l', 'i', 'e', 'l', 'i' };
+static symbol s_2_23[4] = { 'm', 'e', 'l', 'i' };
+static symbol s_2_24[4] = { 't', 'e', 'l', 'i' };
+static symbol s_2_25[4] = { 'v', 'e', 'l', 'i' };
+static symbol s_2_26[3] = { 'g', 'l', 'i' };
+static symbol s_2_27[2] = { 'm', 'i' };
+static symbol s_2_28[2] = { 's', 'i' };
+static symbol s_2_29[2] = { 't', 'i' };
+static symbol s_2_30[2] = { 'v', 'i' };
+static symbol s_2_31[2] = { 'l', 'o' };
+static symbol s_2_32[4] = { 'c', 'e', 'l', 'o' };
+static symbol s_2_33[6] = { 'g', 'l', 'i', 'e', 'l', 'o' };
+static symbol s_2_34[4] = { 'm', 'e', 'l', 'o' };
+static symbol s_2_35[4] = { 't', 'e', 'l', 'o' };
+static symbol s_2_36[4] = { 'v', 'e', 'l', 'o' };
+
+static struct among a_2[37] =
+{
+/*  0 */ { 2, s_2_0, -1, -1, 0},
+/*  1 */ { 4, s_2_1, 0, -1, 0},
+/*  2 */ { 6, s_2_2, 0, -1, 0},
+/*  3 */ { 4, s_2_3, 0, -1, 0},
+/*  4 */ { 4, s_2_4, 0, -1, 0},
+/*  5 */ { 4, s_2_5, 0, -1, 0},
+/*  6 */ { 2, s_2_6, -1, -1, 0},
+/*  7 */ { 4, s_2_7, 6, -1, 0},
+/*  8 */ { 6, s_2_8, 6, -1, 0},
+/*  9 */ { 4, s_2_9, 6, -1, 0},
+/* 10 */ { 4, s_2_10, 6, -1, 0},
+/* 11 */ { 4, s_2_11, 6, -1, 0},
+/* 12 */ { 2, s_2_12, -1, -1, 0},
+/* 13 */ { 4, s_2_13, 12, -1, 0},
+/* 14 */ { 6, s_2_14, 12, -1, 0},
+/* 15 */ { 4, s_2_15, 12, -1, 0},
+/* 16 */ { 4, s_2_16, 12, -1, 0},
+/* 17 */ { 4, s_2_17, 12, -1, 0},
+/* 18 */ { 4, s_2_18, 12, -1, 0},
+/* 19 */ { 2, s_2_19, -1, -1, 0},
+/* 20 */ { 2, s_2_20, -1, -1, 0},
+/* 21 */ { 4, s_2_21, 20, -1, 0},
+/* 22 */ { 6, s_2_22, 20, -1, 0},
+/* 23 */ { 4, s_2_23, 20, -1, 0},
+/* 24 */ { 4, s_2_24, 20, -1, 0},
+/* 25 */ { 4, s_2_25, 20, -1, 0},
+/* 26 */ { 3, s_2_26, 20, -1, 0},
+/* 27 */ { 2, s_2_27, -1, -1, 0},
+/* 28 */ { 2, s_2_28, -1, -1, 0},
+/* 29 */ { 2, s_2_29, -1, -1, 0},
+/* 30 */ { 2, s_2_30, -1, -1, 0},
+/* 31 */ { 2, s_2_31, -1, -1, 0},
+/* 32 */ { 4, s_2_32, 31, -1, 0},
+/* 33 */ { 6, s_2_33, 31, -1, 0},
+/* 34 */ { 4, s_2_34, 31, -1, 0},
+/* 35 */ { 4, s_2_35, 31, -1, 0},
+/* 36 */ { 4, s_2_36, 31, -1, 0}
+};
+
+static symbol s_3_0[4] = { 'a', 'n', 'd', 'o' };
+static symbol s_3_1[4] = { 'e', 'n', 'd', 'o' };
+static symbol s_3_2[2] = { 'a', 'r' };
+static symbol s_3_3[2] = { 'e', 'r' };
+static symbol s_3_4[2] = { 'i', 'r' };
+
+static struct among a_3[5] =
+{
+/*  0 */ { 4, s_3_0, -1, 1, 0},
+/*  1 */ { 4, s_3_1, -1, 1, 0},
+/*  2 */ { 2, s_3_2, -1, 2, 0},
+/*  3 */ { 2, s_3_3, -1, 2, 0},
+/*  4 */ { 2, s_3_4, -1, 2, 0}
+};
+
+static symbol s_4_0[2] = { 'i', 'c' };
+static symbol s_4_1[4] = { 'a', 'b', 'i', 'l' };
+static symbol s_4_2[2] = { 'o', 's' };
+static symbol s_4_3[2] = { 'i', 'v' };
+
+static struct among a_4[4] =
+{
+/*  0 */ { 2, s_4_0, -1, -1, 0},
+/*  1 */ { 4, s_4_1, -1, -1, 0},
+/*  2 */ { 2, s_4_2, -1, -1, 0},
+/*  3 */ { 2, s_4_3, -1, 1, 0}
+};
+
+static symbol s_5_0[2] = { 'i', 'c' };
+static symbol s_5_1[4] = { 'a', 'b', 'i', 'l' };
+static symbol s_5_2[2] = { 'i', 'v' };
+
+static struct among a_5[3] =
+{
+/*  0 */ { 2, s_5_0, -1, 1, 0},
+/*  1 */ { 4, s_5_1, -1, 1, 0},
+/*  2 */ { 2, s_5_2, -1, 1, 0}
+};
+
+static symbol s_6_0[3] = { 'i', 'c', 'a' };
+static symbol s_6_1[5] = { 'l', 'o', 'g', 'i', 'a' };
+static symbol s_6_2[3] = { 'o', 's', 'a' };
+static symbol s_6_3[4] = { 'i', 's', 't', 'a' };
+static symbol s_6_4[3] = { 'i', 'v', 'a' };
+static symbol s_6_5[4] = { 'a', 'n', 'z', 'a' };
+static symbol s_6_6[4] = { 'e', 'n', 'z', 'a' };
+static symbol s_6_7[3] = { 'i', 'c', 'e' };
+static symbol s_6_8[6] = { 'a', 't', 'r', 'i', 'c', 'e' };
+static symbol s_6_9[4] = { 'i', 'c', 'h', 'e' };
+static symbol s_6_10[5] = { 'l', 'o', 'g', 'i', 'e' };
+static symbol s_6_11[5] = { 'a', 'b', 'i', 'l', 'e' };
+static symbol s_6_12[5] = { 'i', 'b', 'i', 'l', 'e' };
+static symbol s_6_13[6] = { 'u', 's', 'i', 'o', 'n', 'e' };
+static symbol s_6_14[6] = { 'a', 'z', 'i', 'o', 'n', 'e' };
+static symbol s_6_15[6] = { 'u', 'z', 'i', 'o', 'n', 'e' };
+static symbol s_6_16[5] = { 'a', 't', 'o', 'r', 'e' };
+static symbol s_6_17[3] = { 'o', 's', 'e' };
+static symbol s_6_18[4] = { 'a', 'n', 't', 'e' };
+static symbol s_6_19[5] = { 'm', 'e', 'n', 't', 'e' };
+static symbol s_6_20[6] = { 'a', 'm', 'e', 'n', 't', 'e' };
+static symbol s_6_21[4] = { 'i', 's', 't', 'e' };
+static symbol s_6_22[3] = { 'i', 'v', 'e' };
+static symbol s_6_23[4] = { 'a', 'n', 'z', 'e' };
+static symbol s_6_24[4] = { 'e', 'n', 'z', 'e' };
+static symbol s_6_25[3] = { 'i', 'c', 'i' };
+static symbol s_6_26[6] = { 'a', 't', 'r', 'i', 'c', 'i' };
+static symbol s_6_27[4] = { 'i', 'c', 'h', 'i' };
+static symbol s_6_28[5] = { 'a', 'b', 'i', 'l', 'i' };
+static symbol s_6_29[5] = { 'i', 'b', 'i', 'l', 'i' };
+static symbol s_6_30[4] = { 'i', 's', 'm', 'i' };
+static symbol s_6_31[6] = { 'u', 's', 'i', 'o', 'n', 'i' };
+static symbol s_6_32[6] = { 'a', 'z', 'i', 'o', 'n', 'i' };
+static symbol s_6_33[6] = { 'u', 'z', 'i', 'o', 'n', 'i' };
+static symbol s_6_34[5] = { 'a', 't', 'o', 'r', 'i' };
+static symbol s_6_35[3] = { 'o', 's', 'i' };
+static symbol s_6_36[4] = { 'a', 'n', 't', 'i' };
+static symbol s_6_37[6] = { 'a', 'm', 'e', 'n', 't', 'i' };
+static symbol s_6_38[6] = { 'i', 'm', 'e', 'n', 't', 'i' };
+static symbol s_6_39[4] = { 'i', 's', 't', 'i' };
+static symbol s_6_40[3] = { 'i', 'v', 'i' };
+static symbol s_6_41[3] = { 'i', 'c', 'o' };
+static symbol s_6_42[4] = { 'i', 's', 'm', 'o' };
+static symbol s_6_43[3] = { 'o', 's', 'o' };
+static symbol s_6_44[6] = { 'a', 'm', 'e', 'n', 't', 'o' };
+static symbol s_6_45[6] = { 'i', 'm', 'e', 'n', 't', 'o' };
+static symbol s_6_46[3] = { 'i', 'v', 'o' };
+static symbol s_6_47[4] = { 'i', 't', 0xC3, 0xA0 };
+static symbol s_6_48[5] = { 'i', 's', 't', 0xC3, 0xA0 };
+static symbol s_6_49[5] = { 'i', 's', 't', 0xC3, 0xA8 };
+static symbol s_6_50[5] = { 'i', 's', 't', 0xC3, 0xAC };
+
+static struct among a_6[51] =
+{
+/*  0 */ { 3, s_6_0, -1, 1, 0},
+/*  1 */ { 5, s_6_1, -1, 3, 0},
+/*  2 */ { 3, s_6_2, -1, 1, 0},
+/*  3 */ { 4, s_6_3, -1, 1, 0},
+/*  4 */ { 3, s_6_4, -1, 9, 0},
+/*  5 */ { 4, s_6_5, -1, 1, 0},
+/*  6 */ { 4, s_6_6, -1, 5, 0},
+/*  7 */ { 3, s_6_7, -1, 1, 0},
+/*  8 */ { 6, s_6_8, 7, 1, 0},
+/*  9 */ { 4, s_6_9, -1, 1, 0},
+/* 10 */ { 5, s_6_10, -1, 3, 0},
+/* 11 */ { 5, s_6_11, -1, 1, 0},
+/* 12 */ { 5, s_6_12, -1, 1, 0},
+/* 13 */ { 6, s_6_13, -1, 4, 0},
+/* 14 */ { 6, s_6_14, -1, 2, 0},
+/* 15 */ { 6, s_6_15, -1, 4, 0},
+/* 16 */ { 5, s_6_16, -1, 2, 0},
+/* 17 */ { 3, s_6_17, -1, 1, 0},
+/* 18 */ { 4, s_6_18, -1, 1, 0},
+/* 19 */ { 5, s_6_19, -1, 1, 0},
+/* 20 */ { 6, s_6_20, 19, 7, 0},
+/* 21 */ { 4, s_6_21, -1, 1, 0},
+/* 22 */ { 3, s_6_22, -1, 9, 0},
+/* 23 */ { 4, s_6_23, -1, 1, 0},
+/* 24 */ { 4, s_6_24, -1, 5, 0},
+/* 25 */ { 3, s_6_25, -1, 1, 0},
+/* 26 */ { 6, s_6_26, 25, 1, 0},
+/* 27 */ { 4, s_6_27, -1, 1, 0},
+/* 28 */ { 5, s_6_28, -1, 1, 0},
+/* 29 */ { 5, s_6_29, -1, 1, 0},
+/* 30 */ { 4, s_6_30, -1, 1, 0},
+/* 31 */ { 6, s_6_31, -1, 4, 0},
+/* 32 */ { 6, s_6_32, -1, 2, 0},
+/* 33 */ { 6, s_6_33, -1, 4, 0},
+/* 34 */ { 5, s_6_34, -1, 2, 0},
+/* 35 */ { 3, s_6_35, -1, 1, 0},
+/* 36 */ { 4, s_6_36, -1, 1, 0},
+/* 37 */ { 6, s_6_37, -1, 6, 0},
+/* 38 */ { 6, s_6_38, -1, 6, 0},
+/* 39 */ { 4, s_6_39, -1, 1, 0},
+/* 40 */ { 3, s_6_40, -1, 9, 0},
+/* 41 */ { 3, s_6_41, -1, 1, 0},
+/* 42 */ { 4, s_6_42, -1, 1, 0},
+/* 43 */ { 3, s_6_43, -1, 1, 0},
+/* 44 */ { 6, s_6_44, -1, 6, 0},
+/* 45 */ { 6, s_6_45, -1, 6, 0},
+/* 46 */ { 3, s_6_46, -1, 9, 0},
+/* 47 */ { 4, s_6_47, -1, 8, 0},
+/* 48 */ { 5, s_6_48, -1, 1, 0},
+/* 49 */ { 5, s_6_49, -1, 1, 0},
+/* 50 */ { 5, s_6_50, -1, 1, 0}
+};
+
+static symbol s_7_0[4] = { 'i', 's', 'c', 'a' };
+static symbol s_7_1[4] = { 'e', 'n', 'd', 'a' };
+static symbol s_7_2[3] = { 'a', 't', 'a' };
+static symbol s_7_3[3] = { 'i', 't', 'a' };
+static symbol s_7_4[3] = { 'u', 't', 'a' };
+static symbol s_7_5[3] = { 'a', 'v', 'a' };
+static symbol s_7_6[3] = { 'e', 'v', 'a' };
+static symbol s_7_7[3] = { 'i', 'v', 'a' };
+static symbol s_7_8[6] = { 'e', 'r', 'e', 'b', 'b', 'e' };
+static symbol s_7_9[6] = { 'i', 'r', 'e', 'b', 'b', 'e' };
+static symbol s_7_10[4] = { 'i', 's', 'c', 'e' };
+static symbol s_7_11[4] = { 'e', 'n', 'd', 'e' };
+static symbol s_7_12[3] = { 'a', 'r', 'e' };
+static symbol s_7_13[3] = { 'e', 'r', 'e' };
+static symbol s_7_14[3] = { 'i', 'r', 'e' };
+static symbol s_7_15[4] = { 'a', 's', 's', 'e' };
+static symbol s_7_16[3] = { 'a', 't', 'e' };
+static symbol s_7_17[5] = { 'a', 'v', 'a', 't', 'e' };
+static symbol s_7_18[5] = { 'e', 'v', 'a', 't', 'e' };
+static symbol s_7_19[5] = { 'i', 'v', 'a', 't', 'e' };
+static symbol s_7_20[3] = { 'e', 't', 'e' };
+static symbol s_7_21[5] = { 'e', 'r', 'e', 't', 'e' };
+static symbol s_7_22[5] = { 'i', 'r', 'e', 't', 'e' };
+static symbol s_7_23[3] = { 'i', 't', 'e' };
+static symbol s_7_24[6] = { 'e', 'r', 'e', 's', 't', 'e' };
+static symbol s_7_25[6] = { 'i', 'r', 'e', 's', 't', 'e' };
+static symbol s_7_26[3] = { 'u', 't', 'e' };
+static symbol s_7_27[4] = { 'e', 'r', 'a', 'i' };
+static symbol s_7_28[4] = { 'i', 'r', 'a', 'i' };
+static symbol s_7_29[4] = { 'i', 's', 'c', 'i' };
+static symbol s_7_30[4] = { 'e', 'n', 'd', 'i' };
+static symbol s_7_31[4] = { 'e', 'r', 'e', 'i' };
+static symbol s_7_32[4] = { 'i', 'r', 'e', 'i' };
+static symbol s_7_33[4] = { 'a', 's', 's', 'i' };
+static symbol s_7_34[3] = { 'a', 't', 'i' };
+static symbol s_7_35[3] = { 'i', 't', 'i' };
+static symbol s_7_36[6] = { 'e', 'r', 'e', 's', 't', 'i' };
+static symbol s_7_37[6] = { 'i', 'r', 'e', 's', 't', 'i' };
+static symbol s_7_38[3] = { 'u', 't', 'i' };
+static symbol s_7_39[3] = { 'a', 'v', 'i' };
+static symbol s_7_40[3] = { 'e', 'v', 'i' };
+static symbol s_7_41[3] = { 'i', 'v', 'i' };
+static symbol s_7_42[4] = { 'i', 's', 'c', 'o' };
+static symbol s_7_43[4] = { 'a', 'n', 'd', 'o' };
+static symbol s_7_44[4] = { 'e', 'n', 'd', 'o' };
+static symbol s_7_45[4] = { 'Y', 'a', 'm', 'o' };
+static symbol s_7_46[4] = { 'i', 'a', 'm', 'o' };
+static symbol s_7_47[5] = { 'a', 'v', 'a', 'm', 'o' };
+static symbol s_7_48[5] = { 'e', 'v', 'a', 'm', 'o' };
+static symbol s_7_49[5] = { 'i', 'v', 'a', 'm', 'o' };
+static symbol s_7_50[5] = { 'e', 'r', 'e', 'm', 'o' };
+static symbol s_7_51[5] = { 'i', 'r', 'e', 'm', 'o' };
+static symbol s_7_52[6] = { 'a', 's', 's', 'i', 'm', 'o' };
+static symbol s_7_53[4] = { 'a', 'm', 'm', 'o' };
+static symbol s_7_54[4] = { 'e', 'm', 'm', 'o' };
+static symbol s_7_55[6] = { 'e', 'r', 'e', 'm', 'm', 'o' };
+static symbol s_7_56[6] = { 'i', 'r', 'e', 'm', 'm', 'o' };
+static symbol s_7_57[4] = { 'i', 'm', 'm', 'o' };
+static symbol s_7_58[3] = { 'a', 'n', 'o' };
+static symbol s_7_59[6] = { 'i', 's', 'c', 'a', 'n', 'o' };
+static symbol s_7_60[5] = { 'a', 'v', 'a', 'n', 'o' };
+static symbol s_7_61[5] = { 'e', 'v', 'a', 'n', 'o' };
+static symbol s_7_62[5] = { 'i', 'v', 'a', 'n', 'o' };
+static symbol s_7_63[6] = { 'e', 'r', 'a', 'n', 'n', 'o' };
+static symbol s_7_64[6] = { 'i', 'r', 'a', 'n', 'n', 'o' };
+static symbol s_7_65[3] = { 'o', 'n', 'o' };
+static symbol s_7_66[6] = { 'i', 's', 'c', 'o', 'n', 'o' };
+static symbol s_7_67[5] = { 'a', 'r', 'o', 'n', 'o' };
+static symbol s_7_68[5] = { 'e', 'r', 'o', 'n', 'o' };
+static symbol s_7_69[5] = { 'i', 'r', 'o', 'n', 'o' };
+static symbol s_7_70[8] = { 'e', 'r', 'e', 'b', 'b', 'e', 'r', 'o' };
+static symbol s_7_71[8] = { 'i', 'r', 'e', 'b', 'b', 'e', 'r', 'o' };
+static symbol s_7_72[6] = { 'a', 's', 's', 'e', 'r', 'o' };
+static symbol s_7_73[6] = { 'e', 's', 's', 'e', 'r', 'o' };
+static symbol s_7_74[6] = { 'i', 's', 's', 'e', 'r', 'o' };
+static symbol s_7_75[3] = { 'a', 't', 'o' };
+static symbol s_7_76[3] = { 'i', 't', 'o' };
+static symbol s_7_77[3] = { 'u', 't', 'o' };
+static symbol s_7_78[3] = { 'a', 'v', 'o' };
+static symbol s_7_79[3] = { 'e', 'v', 'o' };
+static symbol s_7_80[3] = { 'i', 'v', 'o' };
+static symbol s_7_81[2] = { 'a', 'r' };
+static symbol s_7_82[2] = { 'i', 'r' };
+static symbol s_7_83[4] = { 'e', 'r', 0xC3, 0xA0 };
+static symbol s_7_84[4] = { 'i', 'r', 0xC3, 0xA0 };
+static symbol s_7_85[4] = { 'e', 'r', 0xC3, 0xB2 };
+static symbol s_7_86[4] = { 'i', 'r', 0xC3, 0xB2 };
+
+static struct among a_7[87] =
+{
+/*  0 */ { 4, s_7_0, -1, 1, 0},
+/*  1 */ { 4, s_7_1, -1, 1, 0},
+/*  2 */ { 3, s_7_2, -1, 1, 0},
+/*  3 */ { 3, s_7_3, -1, 1, 0},
+/*  4 */ { 3, s_7_4, -1, 1, 0},
+/*  5 */ { 3, s_7_5, -1, 1, 0},
+/*  6 */ { 3, s_7_6, -1, 1, 0},
+/*  7 */ { 3, s_7_7, -1, 1, 0},
+/*  8 */ { 6, s_7_8, -1, 1, 0},
+/*  9 */ { 6, s_7_9, -1, 1, 0},
+/* 10 */ { 4, s_7_10, -1, 1, 0},
+/* 11 */ { 4, s_7_11, -1, 1, 0},
+/* 12 */ { 3, s_7_12, -1, 1, 0},
+/* 13 */ { 3, s_7_13, -1, 1, 0},
+/* 14 */ { 3, s_7_14, -1, 1, 0},
+/* 15 */ { 4, s_7_15, -1, 1, 0},
+/* 16 */ { 3, s_7_16, -1, 1, 0},
+/* 17 */ { 5, s_7_17, 16, 1, 0},
+/* 18 */ { 5, s_7_18, 16, 1, 0},
+/* 19 */ { 5, s_7_19, 16, 1, 0},
+/* 20 */ { 3, s_7_20, -1, 1, 0},
+/* 21 */ { 5, s_7_21, 20, 1, 0},
+/* 22 */ { 5, s_7_22, 20, 1, 0},
+/* 23 */ { 3, s_7_23, -1, 1, 0},
+/* 24 */ { 6, s_7_24, -1, 1, 0},
+/* 25 */ { 6, s_7_25, -1, 1, 0},
+/* 26 */ { 3, s_7_26, -1, 1, 0},
+/* 27 */ { 4, s_7_27, -1, 1, 0},
+/* 28 */ { 4, s_7_28, -1, 1, 0},
+/* 29 */ { 4, s_7_29, -1, 1, 0},
+/* 30 */ { 4, s_7_30, -1, 1, 0},
+/* 31 */ { 4, s_7_31, -1, 1, 0},
+/* 32 */ { 4, s_7_32, -1, 1, 0},
+/* 33 */ { 4, s_7_33, -1, 1, 0},
+/* 34 */ { 3, s_7_34, -1, 1, 0},
+/* 35 */ { 3, s_7_35, -1, 1, 0},
+/* 36 */ { 6, s_7_36, -1, 1, 0},
+/* 37 */ { 6, s_7_37, -1, 1, 0},
+/* 38 */ { 3, s_7_38, -1, 1, 0},
+/* 39 */ { 3, s_7_39, -1, 1, 0},
+/* 40 */ { 3, s_7_40, -1, 1, 0},
+/* 41 */ { 3, s_7_41, -1, 1, 0},
+/* 42 */ { 4, s_7_42, -1, 1, 0},
+/* 43 */ { 4, s_7_43, -1, 1, 0},
+/* 44 */ { 4, s_7_44, -1, 1, 0},
+/* 45 */ { 4, s_7_45, -1, 1, 0},
+/* 46 */ { 4, s_7_46, -1, 1, 0},
+/* 47 */ { 5, s_7_47, -1, 1, 0},
+/* 48 */ { 5, s_7_48, -1, 1, 0},
+/* 49 */ { 5, s_7_49, -1, 1, 0},
+/* 50 */ { 5, s_7_50, -1, 1, 0},
+/* 51 */ { 5, s_7_51, -1, 1, 0},
+/* 52 */ { 6, s_7_52, -1, 1, 0},
+/* 53 */ { 4, s_7_53, -1, 1, 0},
+/* 54 */ { 4, s_7_54, -1, 1, 0},
+/* 55 */ { 6, s_7_55, 54, 1, 0},
+/* 56 */ { 6, s_7_56, 54, 1, 0},
+/* 57 */ { 4, s_7_57, -1, 1, 0},
+/* 58 */ { 3, s_7_58, -1, 1, 0},
+/* 59 */ { 6, s_7_59, 58, 1, 0},
+/* 60 */ { 5, s_7_60, 58, 1, 0},
+/* 61 */ { 5, s_7_61, 58, 1, 0},
+/* 62 */ { 5, s_7_62, 58, 1, 0},
+/* 63 */ { 6, s_7_63, -1, 1, 0},
+/* 64 */ { 6, s_7_64, -1, 1, 0},
+/* 65 */ { 3, s_7_65, -1, 1, 0},
+/* 66 */ { 6, s_7_66, 65, 1, 0},
+/* 67 */ { 5, s_7_67, 65, 1, 0},
+/* 68 */ { 5, s_7_68, 65, 1, 0},
+/* 69 */ { 5, s_7_69, 65, 1, 0},
+/* 70 */ { 8, s_7_70, -1, 1, 0},
+/* 71 */ { 8, s_7_71, -1, 1, 0},
+/* 72 */ { 6, s_7_72, -1, 1, 0},
+/* 73 */ { 6, s_7_73, -1, 1, 0},
+/* 74 */ { 6, s_7_74, -1, 1, 0},
+/* 75 */ { 3, s_7_75, -1, 1, 0},
+/* 76 */ { 3, s_7_76, -1, 1, 0},
+/* 77 */ { 3, s_7_77, -1, 1, 0},
+/* 78 */ { 3, s_7_78, -1, 1, 0},
+/* 79 */ { 3, s_7_79, -1, 1, 0},
+/* 80 */ { 3, s_7_80, -1, 1, 0},
+/* 81 */ { 2, s_7_81, -1, 1, 0},
+/* 82 */ { 2, s_7_82, -1, 1, 0},
+/* 83 */ { 4, s_7_83, -1, 1, 0},
+/* 84 */ { 4, s_7_84, -1, 1, 0},
+/* 85 */ { 4, s_7_85, -1, 1, 0},
+/* 86 */ { 4, s_7_86, -1, 1, 0}
+};
+
+static unsigned char g_v[] = { 17, 65, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 8, 2, 1 };
+
+static unsigned char g_AEIO[] = { 17, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 8, 2 };
+
+static unsigned char g_CG[] = { 17 };
+
+static symbol s_0[] = { 0xC3, 0xA0 };
+static symbol s_1[] = { 0xC3, 0xA8 };
+static symbol s_2[] = { 0xC3, 0xAC };
+static symbol s_3[] = { 0xC3, 0xB2 };
+static symbol s_4[] = { 0xC3, 0xB9 };
+static symbol s_5[] = { 'q', 'U' };
+static symbol s_6[] = { 'u' };
+static symbol s_7[] = { 'U' };
+static symbol s_8[] = { 'i' };
+static symbol s_9[] = { 'I' };
+static symbol s_10[] = { 'i' };
+static symbol s_11[] = { 'u' };
+static symbol s_12[] = { 'e' };
+static symbol s_13[] = { 'i', 'c' };
+static symbol s_14[] = { 'l', 'o', 'g' };
+static symbol s_15[] = { 'u' };
+static symbol s_16[] = { 'e', 'n', 't', 'e' };
+static symbol s_17[] = { 'a', 't' };
+static symbol s_18[] = { 'a', 't' };
+static symbol s_19[] = { 'i', 'c' };
+static symbol s_20[] = { 'i' };
+static symbol s_21[] = { 'h' };
+
+static int r_prelude(struct SN_env * z) {
+    int among_var;
+    {	int c_test = z->c; /* test, line 35 */
+	while(1) { /* repeat, line 35 */
+	    int c = z->c;
+	    z->bra = z->c; /* [, line 36 */
+	    among_var = find_among(z, a_0, 7); /* substring, line 36 */
+	    if (!(among_var)) goto lab0;
+	    z->ket = z->c; /* ], line 36 */
+	    switch(among_var) {
+		case 0: goto lab0;
+		case 1:
+		    {	int ret;
+			ret = slice_from_s(z, 2, s_0); /* <-, line 37 */
+			if (ret < 0) return ret;
+		    }
+		    break;
+		case 2:
+		    {	int ret;
+			ret = slice_from_s(z, 2, s_1); /* <-, line 38 */
+			if (ret < 0) return ret;
+		    }
+		    break;
+		case 3:
+		    {	int ret;
+			ret = slice_from_s(z, 2, s_2); /* <-, line 39 */
+			if (ret < 0) return ret;
+		    }
+		    break;
+		case 4:
+		    {	int ret;
+			ret = slice_from_s(z, 2, s_3); /* <-, line 40 */
+			if (ret < 0) return ret;
+		    }
+		    break;
+		case 5:
+		    {	int ret;
+			ret = slice_from_s(z, 2, s_4); /* <-, line 41 */
+			if (ret < 0) return ret;
+		    }
+		    break;
+		case 6:
+		    {	int ret;
+			ret = slice_from_s(z, 2, s_5); /* <-, line 42 */
+			if (ret < 0) return ret;
+		    }
+		    break;
+		case 7:
+		    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+			if (c < 0) goto lab0;
+			z->c = c; /* next, line 43 */
+		    }
+		    break;
+	    }
+	    continue;
+	lab0:
+	    z->c = c;
+	    break;
+	}
+	z->c = c_test;
+    }
+    while(1) { /* repeat, line 46 */
+	int c = z->c;
+	while(1) { /* goto, line 46 */
+	    int c = z->c;
+	    if (!(in_grouping_U(z, g_v, 97, 249))) goto lab2;
+	    z->bra = z->c; /* [, line 47 */
+	    {	int c = z->c; /* or, line 47 */
+		if (!(eq_s(z, 1, s_6))) goto lab4;
+		z->ket = z->c; /* ], line 47 */
+		if (!(in_grouping_U(z, g_v, 97, 249))) goto lab4;
+		{   int ret;
+		    ret = slice_from_s(z, 1, s_7); /* <-, line 47 */
+		    if (ret < 0) return ret;
+		}
+		goto lab3;
+	    lab4:
+		z->c = c;
+		if (!(eq_s(z, 1, s_8))) goto lab2;
+		z->ket = z->c; /* ], line 48 */
+		if (!(in_grouping_U(z, g_v, 97, 249))) goto lab2;
+		{   int ret;
+		    ret = slice_from_s(z, 1, s_9); /* <-, line 48 */
+		    if (ret < 0) return ret;
+		}
+	    }
+	lab3:
+	    z->c = c;
+	    break;
+	lab2:
+	    z->c = c;
+	    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		if (c < 0) goto lab1;
+		z->c = c; /* goto, line 46 */
+	    }
+	}
+	continue;
+    lab1:
+	z->c = c;
+	break;
+    }
+    return 1;
+}
+
+static int r_mark_regions(struct SN_env * z) {
+    z->I[0] = z->l;
+    z->I[1] = z->l;
+    z->I[2] = z->l;
+    {	int c = z->c; /* do, line 58 */
+	{   int c = z->c; /* or, line 60 */
+	    if (!(in_grouping_U(z, g_v, 97, 249))) goto lab2;
+	    {	int c = z->c; /* or, line 59 */
+		if (!(out_grouping_U(z, g_v, 97, 249))) goto lab4;
+		while(1) { /* gopast, line 59 */
+		    if (!(in_grouping_U(z, g_v, 97, 249))) goto lab5;
+		    break;
+		lab5:
+		    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+			if (c < 0) goto lab4;
+			z->c = c; /* gopast, line 59 */
+		    }
+		}
+		goto lab3;
+	    lab4:
+		z->c = c;
+		if (!(in_grouping_U(z, g_v, 97, 249))) goto lab2;
+		while(1) { /* gopast, line 59 */
+		    if (!(out_grouping_U(z, g_v, 97, 249))) goto lab6;
+		    break;
+		lab6:
+		    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+			if (c < 0) goto lab2;
+			z->c = c; /* gopast, line 59 */
+		    }
+		}
+	    }
+	lab3:
+	    goto lab1;
+	lab2:
+	    z->c = c;
+	    if (!(out_grouping_U(z, g_v, 97, 249))) goto lab0;
+	    {	int c = z->c; /* or, line 61 */
+		if (!(out_grouping_U(z, g_v, 97, 249))) goto lab8;
+		while(1) { /* gopast, line 61 */
+		    if (!(in_grouping_U(z, g_v, 97, 249))) goto lab9;
+		    break;
+		lab9:
+		    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+			if (c < 0) goto lab8;
+			z->c = c; /* gopast, line 61 */
+		    }
+		}
+		goto lab7;
+	    lab8:
+		z->c = c;
+		if (!(in_grouping_U(z, g_v, 97, 249))) goto lab0;
+		{   int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		    if (c < 0) goto lab0;
+		    z->c = c; /* next, line 61 */
+		}
+	    }
+	lab7:
+	    ;
+	}
+    lab1:
+	z->I[0] = z->c; /* setmark pV, line 62 */
+    lab0:
+	z->c = c;
+    }
+    {	int c = z->c; /* do, line 64 */
+	while(1) { /* gopast, line 65 */
+	    if (!(in_grouping_U(z, g_v, 97, 249))) goto lab11;
+	    break;
+	lab11:
+	    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		if (c < 0) goto lab10;
+		z->c = c; /* gopast, line 65 */
+	    }
+	}
+	while(1) { /* gopast, line 65 */
+	    if (!(out_grouping_U(z, g_v, 97, 249))) goto lab12;
+	    break;
+	lab12:
+	    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		if (c < 0) goto lab10;
+		z->c = c; /* gopast, line 65 */
+	    }
+	}
+	z->I[1] = z->c; /* setmark p1, line 65 */
+	while(1) { /* gopast, line 66 */
+	    if (!(in_grouping_U(z, g_v, 97, 249))) goto lab13;
+	    break;
+	lab13:
+	    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		if (c < 0) goto lab10;
+		z->c = c; /* gopast, line 66 */
+	    }
+	}
+	while(1) { /* gopast, line 66 */
+	    if (!(out_grouping_U(z, g_v, 97, 249))) goto lab14;
+	    break;
+	lab14:
+	    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		if (c < 0) goto lab10;
+		z->c = c; /* gopast, line 66 */
+	    }
+	}
+	z->I[2] = z->c; /* setmark p2, line 66 */
+    lab10:
+	z->c = c;
+    }
+    return 1;
+}
+
+static int r_postlude(struct SN_env * z) {
+    int among_var;
+    while(1) { /* repeat, line 70 */
+	int c = z->c;
+	z->bra = z->c; /* [, line 72 */
+	among_var = find_among(z, a_1, 3); /* substring, line 72 */
+	if (!(among_var)) goto lab0;
+	z->ket = z->c; /* ], line 72 */
+	switch(among_var) {
+	    case 0: goto lab0;
+	    case 1:
+		{   int ret;
+		    ret = slice_from_s(z, 1, s_10); /* <-, line 73 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 2:
+		{   int ret;
+		    ret = slice_from_s(z, 1, s_11); /* <-, line 74 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 3:
+		{   int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		    if (c < 0) goto lab0;
+		    z->c = c; /* next, line 75 */
+		}
+		break;
+	}
+	continue;
+    lab0:
+	z->c = c;
+	break;
+    }
+    return 1;
+}
+
+static int r_RV(struct SN_env * z) {
+    if (!(z->I[0] <= z->c)) return 0;
+    return 1;
+}
+
+static int r_R1(struct SN_env * z) {
+    if (!(z->I[1] <= z->c)) return 0;
+    return 1;
+}
+
+static int r_R2(struct SN_env * z) {
+    if (!(z->I[2] <= z->c)) return 0;
+    return 1;
+}
+
+static int r_attached_pronoun(struct SN_env * z) {
+    int among_var;
+    z->ket = z->c; /* [, line 87 */
+    if (!(find_among_b(z, a_2, 37))) return 0; /* substring, line 87 */
+    z->bra = z->c; /* ], line 87 */
+    among_var = find_among_b(z, a_3, 5); /* among, line 97 */
+    if (!(among_var)) return 0;
+    {	int ret = r_RV(z);
+	if (ret == 0) return 0; /* call RV, line 97 */
+	if (ret < 0) return ret;
+    }
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 98 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    {	int ret;
+		ret = slice_from_s(z, 1, s_12); /* <-, line 99 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_standard_suffix(struct SN_env * z) {
+    int among_var;
+    z->ket = z->c; /* [, line 104 */
+    among_var = find_among_b(z, a_6, 51); /* substring, line 104 */
+    if (!(among_var)) return 0;
+    z->bra = z->c; /* ], line 104 */
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret = r_R2(z);
+		if (ret == 0) return 0; /* call R2, line 111 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 111 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    {	int ret = r_R2(z);
+		if (ret == 0) return 0; /* call R2, line 113 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 113 */
+		if (ret < 0) return ret;
+	    }
+	    {	int m = z->l - z->c; (void) m; /* try, line 114 */
+		z->ket = z->c; /* [, line 114 */
+		if (!(eq_s_b(z, 2, s_13))) { z->c = z->l - m; goto lab0; }
+		z->bra = z->c; /* ], line 114 */
+		{   int ret = r_R2(z);
+		    if (ret == 0) { z->c = z->l - m; goto lab0; } /* call R2, line 114 */
+		    if (ret < 0) return ret;
+		}
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 114 */
+		    if (ret < 0) return ret;
+		}
+	    lab0:
+		;
+	    }
+	    break;
+	case 3:
+	    {	int ret = r_R2(z);
+		if (ret == 0) return 0; /* call R2, line 117 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_from_s(z, 3, s_14); /* <-, line 117 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 4:
+	    {	int ret = r_R2(z);
+		if (ret == 0) return 0; /* call R2, line 119 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_from_s(z, 1, s_15); /* <-, line 119 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 5:
+	    {	int ret = r_R2(z);
+		if (ret == 0) return 0; /* call R2, line 121 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_from_s(z, 4, s_16); /* <-, line 121 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 6:
+	    {	int ret = r_RV(z);
+		if (ret == 0) return 0; /* call RV, line 123 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 123 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 7:
+	    {	int ret = r_R1(z);
+		if (ret == 0) return 0; /* call R1, line 125 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 125 */
+		if (ret < 0) return ret;
+	    }
+	    {	int m = z->l - z->c; (void) m; /* try, line 126 */
+		z->ket = z->c; /* [, line 127 */
+		among_var = find_among_b(z, a_4, 4); /* substring, line 127 */
+		if (!(among_var)) { z->c = z->l - m; goto lab1; }
+		z->bra = z->c; /* ], line 127 */
+		{   int ret = r_R2(z);
+		    if (ret == 0) { z->c = z->l - m; goto lab1; } /* call R2, line 127 */
+		    if (ret < 0) return ret;
+		}
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 127 */
+		    if (ret < 0) return ret;
+		}
+		switch(among_var) {
+		    case 0: { z->c = z->l - m; goto lab1; }
+		    case 1:
+			z->ket = z->c; /* [, line 128 */
+			if (!(eq_s_b(z, 2, s_17))) { z->c = z->l - m; goto lab1; }
+			z->bra = z->c; /* ], line 128 */
+			{   int ret = r_R2(z);
+			    if (ret == 0) { z->c = z->l - m; goto lab1; } /* call R2, line 128 */
+			    if (ret < 0) return ret;
+			}
+			{   int ret;
+			    ret = slice_del(z); /* delete, line 128 */
+			    if (ret < 0) return ret;
+			}
+			break;
+		}
+	    lab1:
+		;
+	    }
+	    break;
+	case 8:
+	    {	int ret = r_R2(z);
+		if (ret == 0) return 0; /* call R2, line 134 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 134 */
+		if (ret < 0) return ret;
+	    }
+	    {	int m = z->l - z->c; (void) m; /* try, line 135 */
+		z->ket = z->c; /* [, line 136 */
+		among_var = find_among_b(z, a_5, 3); /* substring, line 136 */
+		if (!(among_var)) { z->c = z->l - m; goto lab2; }
+		z->bra = z->c; /* ], line 136 */
+		switch(among_var) {
+		    case 0: { z->c = z->l - m; goto lab2; }
+		    case 1:
+			{   int ret = r_R2(z);
+			    if (ret == 0) { z->c = z->l - m; goto lab2; } /* call R2, line 137 */
+			    if (ret < 0) return ret;
+			}
+			{   int ret;
+			    ret = slice_del(z); /* delete, line 137 */
+			    if (ret < 0) return ret;
+			}
+			break;
+		}
+	    lab2:
+		;
+	    }
+	    break;
+	case 9:
+	    {	int ret = r_R2(z);
+		if (ret == 0) return 0; /* call R2, line 142 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 142 */
+		if (ret < 0) return ret;
+	    }
+	    {	int m = z->l - z->c; (void) m; /* try, line 143 */
+		z->ket = z->c; /* [, line 143 */
+		if (!(eq_s_b(z, 2, s_18))) { z->c = z->l - m; goto lab3; }
+		z->bra = z->c; /* ], line 143 */
+		{   int ret = r_R2(z);
+		    if (ret == 0) { z->c = z->l - m; goto lab3; } /* call R2, line 143 */
+		    if (ret < 0) return ret;
+		}
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 143 */
+		    if (ret < 0) return ret;
+		}
+		z->ket = z->c; /* [, line 143 */
+		if (!(eq_s_b(z, 2, s_19))) { z->c = z->l - m; goto lab3; }
+		z->bra = z->c; /* ], line 143 */
+		{   int ret = r_R2(z);
+		    if (ret == 0) { z->c = z->l - m; goto lab3; } /* call R2, line 143 */
+		    if (ret < 0) return ret;
+		}
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 143 */
+		    if (ret < 0) return ret;
+		}
+	    lab3:
+		;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_verb_suffix(struct SN_env * z) {
+    int among_var;
+    {	int m3; /* setlimit, line 148 */
+	int m = z->l - z->c; (void) m;
+	if (z->c < z->I[0]) return 0;
+	z->c = z->I[0]; /* tomark, line 148 */
+	m3 = z->lb; z->lb = z->c;
+	z->c = z->l - m;
+	z->ket = z->c; /* [, line 149 */
+	among_var = find_among_b(z, a_7, 87); /* substring, line 149 */
+	if (!(among_var)) { z->lb = m3; return 0; }
+	z->bra = z->c; /* ], line 149 */
+	switch(among_var) {
+	    case 0: { z->lb = m3; return 0; }
+	    case 1:
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 163 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	}
+	z->lb = m3;
+    }
+    return 1;
+}
+
+static int r_vowel_suffix(struct SN_env * z) {
+    {	int m = z->l - z->c; (void) m; /* try, line 171 */
+	z->ket = z->c; /* [, line 172 */
+	if (!(in_grouping_b_U(z, g_AEIO, 97, 242))) { z->c = z->l - m; goto lab0; }
+	z->bra = z->c; /* ], line 172 */
+	{   int ret = r_RV(z);
+	    if (ret == 0) { z->c = z->l - m; goto lab0; } /* call RV, line 172 */
+	    if (ret < 0) return ret;
+	}
+	{   int ret;
+	    ret = slice_del(z); /* delete, line 172 */
+	    if (ret < 0) return ret;
+	}
+	z->ket = z->c; /* [, line 173 */
+	if (!(eq_s_b(z, 1, s_20))) { z->c = z->l - m; goto lab0; }
+	z->bra = z->c; /* ], line 173 */
+	{   int ret = r_RV(z);
+	    if (ret == 0) { z->c = z->l - m; goto lab0; } /* call RV, line 173 */
+	    if (ret < 0) return ret;
+	}
+	{   int ret;
+	    ret = slice_del(z); /* delete, line 173 */
+	    if (ret < 0) return ret;
+	}
+    lab0:
+	;
+    }
+    {	int m = z->l - z->c; (void) m; /* try, line 175 */
+	z->ket = z->c; /* [, line 176 */
+	if (!(eq_s_b(z, 1, s_21))) { z->c = z->l - m; goto lab1; }
+	z->bra = z->c; /* ], line 176 */
+	if (!(in_grouping_b_U(z, g_CG, 99, 103))) { z->c = z->l - m; goto lab1; }
+	{   int ret = r_RV(z);
+	    if (ret == 0) { z->c = z->l - m; goto lab1; } /* call RV, line 176 */
+	    if (ret < 0) return ret;
+	}
+	{   int ret;
+	    ret = slice_del(z); /* delete, line 176 */
+	    if (ret < 0) return ret;
+	}
+    lab1:
+	;
+    }
+    return 1;
+}
+
+extern int italian_UTF_8_stem(struct SN_env * z) {
+    {	int c = z->c; /* do, line 182 */
+	{   int ret = r_prelude(z);
+	    if (ret == 0) goto lab0; /* call prelude, line 182 */
+	    if (ret < 0) return ret;
+	}
+    lab0:
+	z->c = c;
+    }
+    {	int c = z->c; /* do, line 183 */
+	{   int ret = r_mark_regions(z);
+	    if (ret == 0) goto lab1; /* call mark_regions, line 183 */
+	    if (ret < 0) return ret;
+	}
+    lab1:
+	z->c = c;
+    }
+    z->lb = z->c; z->c = z->l; /* backwards, line 184 */
+
+    {	int m = z->l - z->c; (void) m; /* do, line 185 */
+	{   int ret = r_attached_pronoun(z);
+	    if (ret == 0) goto lab2; /* call attached_pronoun, line 185 */
+	    if (ret < 0) return ret;
+	}
+    lab2:
+	z->c = z->l - m;
+    }
+    {	int m = z->l - z->c; (void) m; /* do, line 186 */
+	{   int m = z->l - z->c; (void) m; /* or, line 186 */
+	    {	int ret = r_standard_suffix(z);
+		if (ret == 0) goto lab5; /* call standard_suffix, line 186 */
+		if (ret < 0) return ret;
+	    }
+	    goto lab4;
+	lab5:
+	    z->c = z->l - m;
+	    {	int ret = r_verb_suffix(z);
+		if (ret == 0) goto lab3; /* call verb_suffix, line 186 */
+		if (ret < 0) return ret;
+	    }
+	}
+    lab4:
+    lab3:
+	z->c = z->l - m;
+    }
+    {	int m = z->l - z->c; (void) m; /* do, line 187 */
+	{   int ret = r_vowel_suffix(z);
+	    if (ret == 0) goto lab6; /* call vowel_suffix, line 187 */
+	    if (ret < 0) return ret;
+	}
+    lab6:
+	z->c = z->l - m;
+    }
+    z->c = z->lb;
+    {	int c = z->c; /* do, line 189 */
+	{   int ret = r_postlude(z);
+	    if (ret == 0) goto lab7; /* call postlude, line 189 */
+	    if (ret < 0) return ret;
+	}
+    lab7:
+	z->c = c;
+    }
+    return 1;
+}
+
+extern struct SN_env * italian_UTF_8_create_env(void) { return SN_create_env(0, 3, 0); }
+
+extern void italian_UTF_8_close_env(struct SN_env * z) { SN_close_env(z); }
+

Added: trunk/src/libstemmer/stem_UTF_8_italian.h
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/stem_UTF_8_italian.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2001, Dr Martin Porter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+/* This file was generated automatically by the Snowball to ANSI C compiler */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern struct SN_env * italian_UTF_8_create_env(void);
+extern void italian_UTF_8_close_env(struct SN_env * z);
+
+extern int italian_UTF_8_stem(struct SN_env * z);
+
+#ifdef __cplusplus
+}
+#endif
+

Added: trunk/src/libstemmer/stem_UTF_8_norwegian.c
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/stem_UTF_8_norwegian.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,315 @@
+/*
+ * Copyright (c) 2001, Dr Martin Porter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+/* This file was generated automatically by the Snowball to ANSI C compiler */
+
+#include "header.h"
+
+extern int norwegian_UTF_8_stem(struct SN_env * z);
+static int r_other_suffix(struct SN_env * z);
+static int r_consonant_pair(struct SN_env * z);
+static int r_main_suffix(struct SN_env * z);
+static int r_mark_regions(struct SN_env * z);
+
+extern struct SN_env * norwegian_UTF_8_create_env(void);
+extern void norwegian_UTF_8_close_env(struct SN_env * z);
+
+static symbol s_0_0[1] = { 'a' };
+static symbol s_0_1[1] = { 'e' };
+static symbol s_0_2[3] = { 'e', 'd', 'e' };
+static symbol s_0_3[4] = { 'a', 'n', 'd', 'e' };
+static symbol s_0_4[4] = { 'e', 'n', 'd', 'e' };
+static symbol s_0_5[3] = { 'a', 'n', 'e' };
+static symbol s_0_6[3] = { 'e', 'n', 'e' };
+static symbol s_0_7[6] = { 'h', 'e', 't', 'e', 'n', 'e' };
+static symbol s_0_8[4] = { 'e', 'r', 't', 'e' };
+static symbol s_0_9[2] = { 'e', 'n' };
+static symbol s_0_10[5] = { 'h', 'e', 't', 'e', 'n' };
+static symbol s_0_11[2] = { 'a', 'r' };
+static symbol s_0_12[2] = { 'e', 'r' };
+static symbol s_0_13[5] = { 'h', 'e', 't', 'e', 'r' };
+static symbol s_0_14[1] = { 's' };
+static symbol s_0_15[2] = { 'a', 's' };
+static symbol s_0_16[2] = { 'e', 's' };
+static symbol s_0_17[4] = { 'e', 'd', 'e', 's' };
+static symbol s_0_18[5] = { 'e', 'n', 'd', 'e', 's' };
+static symbol s_0_19[4] = { 'e', 'n', 'e', 's' };
+static symbol s_0_20[7] = { 'h', 'e', 't', 'e', 'n', 'e', 's' };
+static symbol s_0_21[3] = { 'e', 'n', 's' };
+static symbol s_0_22[6] = { 'h', 'e', 't', 'e', 'n', 's' };
+static symbol s_0_23[3] = { 'e', 'r', 's' };
+static symbol s_0_24[3] = { 'e', 't', 's' };
+static symbol s_0_25[2] = { 'e', 't' };
+static symbol s_0_26[3] = { 'h', 'e', 't' };
+static symbol s_0_27[3] = { 'e', 'r', 't' };
+static symbol s_0_28[3] = { 'a', 's', 't' };
+
+static struct among a_0[29] =
+{
+/*  0 */ { 1, s_0_0, -1, 1, 0},
+/*  1 */ { 1, s_0_1, -1, 1, 0},
+/*  2 */ { 3, s_0_2, 1, 1, 0},
+/*  3 */ { 4, s_0_3, 1, 1, 0},
+/*  4 */ { 4, s_0_4, 1, 1, 0},
+/*  5 */ { 3, s_0_5, 1, 1, 0},
+/*  6 */ { 3, s_0_6, 1, 1, 0},
+/*  7 */ { 6, s_0_7, 6, 1, 0},
+/*  8 */ { 4, s_0_8, 1, 3, 0},
+/*  9 */ { 2, s_0_9, -1, 1, 0},
+/* 10 */ { 5, s_0_10, 9, 1, 0},
+/* 11 */ { 2, s_0_11, -1, 1, 0},
+/* 12 */ { 2, s_0_12, -1, 1, 0},
+/* 13 */ { 5, s_0_13, 12, 1, 0},
+/* 14 */ { 1, s_0_14, -1, 2, 0},
+/* 15 */ { 2, s_0_15, 14, 1, 0},
+/* 16 */ { 2, s_0_16, 14, 1, 0},
+/* 17 */ { 4, s_0_17, 16, 1, 0},
+/* 18 */ { 5, s_0_18, 16, 1, 0},
+/* 19 */ { 4, s_0_19, 16, 1, 0},
+/* 20 */ { 7, s_0_20, 19, 1, 0},
+/* 21 */ { 3, s_0_21, 14, 1, 0},
+/* 22 */ { 6, s_0_22, 21, 1, 0},
+/* 23 */ { 3, s_0_23, 14, 1, 0},
+/* 24 */ { 3, s_0_24, 14, 1, 0},
+/* 25 */ { 2, s_0_25, -1, 1, 0},
+/* 26 */ { 3, s_0_26, 25, 1, 0},
+/* 27 */ { 3, s_0_27, -1, 3, 0},
+/* 28 */ { 3, s_0_28, -1, 1, 0}
+};
+
+static symbol s_1_0[2] = { 'd', 't' };
+static symbol s_1_1[2] = { 'v', 't' };
+
+static struct among a_1[2] =
+{
+/*  0 */ { 2, s_1_0, -1, -1, 0},
+/*  1 */ { 2, s_1_1, -1, -1, 0}
+};
+
+static symbol s_2_0[3] = { 'l', 'e', 'g' };
+static symbol s_2_1[4] = { 'e', 'l', 'e', 'g' };
+static symbol s_2_2[2] = { 'i', 'g' };
+static symbol s_2_3[3] = { 'e', 'i', 'g' };
+static symbol s_2_4[3] = { 'l', 'i', 'g' };
+static symbol s_2_5[4] = { 'e', 'l', 'i', 'g' };
+static symbol s_2_6[3] = { 'e', 'l', 's' };
+static symbol s_2_7[3] = { 'l', 'o', 'v' };
+static symbol s_2_8[4] = { 'e', 'l', 'o', 'v' };
+static symbol s_2_9[4] = { 's', 'l', 'o', 'v' };
+static symbol s_2_10[7] = { 'h', 'e', 't', 's', 'l', 'o', 'v' };
+
+static struct among a_2[11] =
+{
+/*  0 */ { 3, s_2_0, -1, 1, 0},
+/*  1 */ { 4, s_2_1, 0, 1, 0},
+/*  2 */ { 2, s_2_2, -1, 1, 0},
+/*  3 */ { 3, s_2_3, 2, 1, 0},
+/*  4 */ { 3, s_2_4, 2, 1, 0},
+/*  5 */ { 4, s_2_5, 4, 1, 0},
+/*  6 */ { 3, s_2_6, -1, 1, 0},
+/*  7 */ { 3, s_2_7, -1, 1, 0},
+/*  8 */ { 4, s_2_8, 7, 1, 0},
+/*  9 */ { 4, s_2_9, 7, 1, 0},
+/* 10 */ { 7, s_2_10, 9, 1, 0}
+};
+
+static unsigned char g_v[] = { 17, 65, 16, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 128 };
+
+static unsigned char g_s_ending[] = { 119, 125, 149, 1 };
+
+static symbol s_0[] = { 'k' };
+static symbol s_1[] = { 'e', 'r' };
+
+static int r_mark_regions(struct SN_env * z) {
+    z->I[0] = z->l;
+    {	int c_test = z->c; /* test, line 30 */
+	{   int c = skip_utf8(z->p, z->c, 0, z->l, + 3);
+	    if (c < 0) return 0;
+	    z->c = c; /* hop, line 30 */
+	}
+	z->I[1] = z->c; /* setmark x, line 30 */
+	z->c = c_test;
+    }
+    while(1) { /* goto, line 31 */
+	int c = z->c;
+	if (!(in_grouping_U(z, g_v, 97, 248))) goto lab0;
+	z->c = c;
+	break;
+    lab0:
+	z->c = c;
+	{   int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+	    if (c < 0) return 0;
+	    z->c = c; /* goto, line 31 */
+	}
+    }
+    while(1) { /* gopast, line 31 */
+	if (!(out_grouping_U(z, g_v, 97, 248))) goto lab1;
+	break;
+    lab1:
+	{   int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+	    if (c < 0) return 0;
+	    z->c = c; /* gopast, line 31 */
+	}
+    }
+    z->I[0] = z->c; /* setmark p1, line 31 */
+     /* try, line 32 */
+    if (!(z->I[0] < z->I[1])) goto lab2;
+    z->I[0] = z->I[1];
+lab2:
+    return 1;
+}
+
+static int r_main_suffix(struct SN_env * z) {
+    int among_var;
+    {	int m3; /* setlimit, line 38 */
+	int m = z->l - z->c; (void) m;
+	if (z->c < z->I[0]) return 0;
+	z->c = z->I[0]; /* tomark, line 38 */
+	m3 = z->lb; z->lb = z->c;
+	z->c = z->l - m;
+	z->ket = z->c; /* [, line 38 */
+	among_var = find_among_b(z, a_0, 29); /* substring, line 38 */
+	if (!(among_var)) { z->lb = m3; return 0; }
+	z->bra = z->c; /* ], line 38 */
+	z->lb = m3;
+    }
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 44 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    {	int m = z->l - z->c; (void) m; /* or, line 46 */
+		if (!(in_grouping_b_U(z, g_s_ending, 98, 122))) goto lab1;
+		goto lab0;
+	    lab1:
+		z->c = z->l - m;
+		if (!(eq_s_b(z, 1, s_0))) return 0;
+		if (!(out_grouping_b_U(z, g_v, 97, 248))) return 0;
+	    }
+	lab0:
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 46 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 3:
+	    {	int ret;
+		ret = slice_from_s(z, 2, s_1); /* <-, line 48 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_consonant_pair(struct SN_env * z) {
+    {	int m_test = z->l - z->c; /* test, line 53 */
+	{   int m3; /* setlimit, line 54 */
+	    int m = z->l - z->c; (void) m;
+	    if (z->c < z->I[0]) return 0;
+	    z->c = z->I[0]; /* tomark, line 54 */
+	    m3 = z->lb; z->lb = z->c;
+	    z->c = z->l - m;
+	    z->ket = z->c; /* [, line 54 */
+	    if (!(find_among_b(z, a_1, 2))) { z->lb = m3; return 0; } /* substring, line 54 */
+	    z->bra = z->c; /* ], line 54 */
+	    z->lb = m3;
+	}
+	z->c = z->l - m_test;
+    }
+    {	int c = skip_utf8(z->p, z->c, z->lb, 0, -1);
+	if (c < 0) return 0;
+	z->c = c; /* next, line 59 */
+    }
+    z->bra = z->c; /* ], line 59 */
+    {	int ret;
+	ret = slice_del(z); /* delete, line 59 */
+	if (ret < 0) return ret;
+    }
+    return 1;
+}
+
+static int r_other_suffix(struct SN_env * z) {
+    int among_var;
+    {	int m3; /* setlimit, line 63 */
+	int m = z->l - z->c; (void) m;
+	if (z->c < z->I[0]) return 0;
+	z->c = z->I[0]; /* tomark, line 63 */
+	m3 = z->lb; z->lb = z->c;
+	z->c = z->l - m;
+	z->ket = z->c; /* [, line 63 */
+	among_var = find_among_b(z, a_2, 11); /* substring, line 63 */
+	if (!(among_var)) { z->lb = m3; return 0; }
+	z->bra = z->c; /* ], line 63 */
+	z->lb = m3;
+    }
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 67 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+extern int norwegian_UTF_8_stem(struct SN_env * z) {
+    {	int c = z->c; /* do, line 74 */
+	{   int ret = r_mark_regions(z);
+	    if (ret == 0) goto lab0; /* call mark_regions, line 74 */
+	    if (ret < 0) return ret;
+	}
+    lab0:
+	z->c = c;
+    }
+    z->lb = z->c; z->c = z->l; /* backwards, line 75 */
+
+    {	int m = z->l - z->c; (void) m; /* do, line 76 */
+	{   int ret = r_main_suffix(z);
+	    if (ret == 0) goto lab1; /* call main_suffix, line 76 */
+	    if (ret < 0) return ret;
+	}
+    lab1:
+	z->c = z->l - m;
+    }
+    {	int m = z->l - z->c; (void) m; /* do, line 77 */
+	{   int ret = r_consonant_pair(z);
+	    if (ret == 0) goto lab2; /* call consonant_pair, line 77 */
+	    if (ret < 0) return ret;
+	}
+    lab2:
+	z->c = z->l - m;
+    }
+    {	int m = z->l - z->c; (void) m; /* do, line 78 */
+	{   int ret = r_other_suffix(z);
+	    if (ret == 0) goto lab3; /* call other_suffix, line 78 */
+	    if (ret < 0) return ret;
+	}
+    lab3:
+	z->c = z->l - m;
+    }
+    z->c = z->lb;
+    return 1;
+}
+
+extern struct SN_env * norwegian_UTF_8_create_env(void) { return SN_create_env(0, 2, 0); }
+
+extern void norwegian_UTF_8_close_env(struct SN_env * z) { SN_close_env(z); }
+

Added: trunk/src/libstemmer/stem_UTF_8_norwegian.h
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/stem_UTF_8_norwegian.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2001, Dr Martin Porter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+/* This file was generated automatically by the Snowball to ANSI C compiler */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern struct SN_env * norwegian_UTF_8_create_env(void);
+extern void norwegian_UTF_8_close_env(struct SN_env * z);
+
+extern int norwegian_UTF_8_stem(struct SN_env * z);
+
+#ifdef __cplusplus
+}
+#endif
+

Added: trunk/src/libstemmer/stem_UTF_8_porter.c
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/stem_UTF_8_porter.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,807 @@
+/*
+ * Copyright (c) 2001, Dr Martin Porter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+/* This file was generated automatically by the Snowball to ANSI C compiler */
+
+#include "header.h"
+
+extern int porter_UTF_8_stem(struct SN_env * z);
+static int r_Step_5b(struct SN_env * z);
+static int r_Step_5a(struct SN_env * z);
+static int r_Step_4(struct SN_env * z);
+static int r_Step_3(struct SN_env * z);
+static int r_Step_2(struct SN_env * z);
+static int r_Step_1c(struct SN_env * z);
+static int r_Step_1b(struct SN_env * z);
+static int r_Step_1a(struct SN_env * z);
+static int r_R2(struct SN_env * z);
+static int r_R1(struct SN_env * z);
+static int r_shortv(struct SN_env * z);
+
+extern struct SN_env * porter_UTF_8_create_env(void);
+extern void porter_UTF_8_close_env(struct SN_env * z);
+
+static symbol s_0_0[1] = { 's' };
+static symbol s_0_1[3] = { 'i', 'e', 's' };
+static symbol s_0_2[4] = { 's', 's', 'e', 's' };
+static symbol s_0_3[2] = { 's', 's' };
+
+static struct among a_0[4] =
+{
+/*  0 */ { 1, s_0_0, -1, 3, 0},
+/*  1 */ { 3, s_0_1, 0, 2, 0},
+/*  2 */ { 4, s_0_2, 0, 1, 0},
+/*  3 */ { 2, s_0_3, 0, -1, 0}
+};
+
+static symbol s_1_1[2] = { 'b', 'b' };
+static symbol s_1_2[2] = { 'd', 'd' };
+static symbol s_1_3[2] = { 'f', 'f' };
+static symbol s_1_4[2] = { 'g', 'g' };
+static symbol s_1_5[2] = { 'b', 'l' };
+static symbol s_1_6[2] = { 'm', 'm' };
+static symbol s_1_7[2] = { 'n', 'n' };
+static symbol s_1_8[2] = { 'p', 'p' };
+static symbol s_1_9[2] = { 'r', 'r' };
+static symbol s_1_10[2] = { 'a', 't' };
+static symbol s_1_11[2] = { 't', 't' };
+static symbol s_1_12[2] = { 'i', 'z' };
+
+static struct among a_1[13] =
+{
+/*  0 */ { 0, 0, -1, 3, 0},
+/*  1 */ { 2, s_1_1, 0, 2, 0},
+/*  2 */ { 2, s_1_2, 0, 2, 0},
+/*  3 */ { 2, s_1_3, 0, 2, 0},
+/*  4 */ { 2, s_1_4, 0, 2, 0},
+/*  5 */ { 2, s_1_5, 0, 1, 0},
+/*  6 */ { 2, s_1_6, 0, 2, 0},
+/*  7 */ { 2, s_1_7, 0, 2, 0},
+/*  8 */ { 2, s_1_8, 0, 2, 0},
+/*  9 */ { 2, s_1_9, 0, 2, 0},
+/* 10 */ { 2, s_1_10, 0, 1, 0},
+/* 11 */ { 2, s_1_11, 0, 2, 0},
+/* 12 */ { 2, s_1_12, 0, 1, 0}
+};
+
+static symbol s_2_0[2] = { 'e', 'd' };
+static symbol s_2_1[3] = { 'e', 'e', 'd' };
+static symbol s_2_2[3] = { 'i', 'n', 'g' };
+
+static struct among a_2[3] =
+{
+/*  0 */ { 2, s_2_0, -1, 2, 0},
+/*  1 */ { 3, s_2_1, 0, 1, 0},
+/*  2 */ { 3, s_2_2, -1, 2, 0}
+};
+
+static symbol s_3_0[4] = { 'a', 'n', 'c', 'i' };
+static symbol s_3_1[4] = { 'e', 'n', 'c', 'i' };
+static symbol s_3_2[4] = { 'a', 'b', 'l', 'i' };
+static symbol s_3_3[3] = { 'e', 'l', 'i' };
+static symbol s_3_4[4] = { 'a', 'l', 'l', 'i' };
+static symbol s_3_5[5] = { 'o', 'u', 's', 'l', 'i' };
+static symbol s_3_6[5] = { 'e', 'n', 't', 'l', 'i' };
+static symbol s_3_7[5] = { 'a', 'l', 'i', 't', 'i' };
+static symbol s_3_8[6] = { 'b', 'i', 'l', 'i', 't', 'i' };
+static symbol s_3_9[5] = { 'i', 'v', 'i', 't', 'i' };
+static symbol s_3_10[6] = { 't', 'i', 'o', 'n', 'a', 'l' };
+static symbol s_3_11[7] = { 'a', 't', 'i', 'o', 'n', 'a', 'l' };
+static symbol s_3_12[5] = { 'a', 'l', 'i', 's', 'm' };
+static symbol s_3_13[5] = { 'a', 't', 'i', 'o', 'n' };
+static symbol s_3_14[7] = { 'i', 'z', 'a', 't', 'i', 'o', 'n' };
+static symbol s_3_15[4] = { 'i', 'z', 'e', 'r' };
+static symbol s_3_16[4] = { 'a', 't', 'o', 'r' };
+static symbol s_3_17[7] = { 'i', 'v', 'e', 'n', 'e', 's', 's' };
+static symbol s_3_18[7] = { 'f', 'u', 'l', 'n', 'e', 's', 's' };
+static symbol s_3_19[7] = { 'o', 'u', 's', 'n', 'e', 's', 's' };
+
+static struct among a_3[20] =
+{
+/*  0 */ { 4, s_3_0, -1, 3, 0},
+/*  1 */ { 4, s_3_1, -1, 2, 0},
+/*  2 */ { 4, s_3_2, -1, 4, 0},
+/*  3 */ { 3, s_3_3, -1, 6, 0},
+/*  4 */ { 4, s_3_4, -1, 9, 0},
+/*  5 */ { 5, s_3_5, -1, 12, 0},
+/*  6 */ { 5, s_3_6, -1, 5, 0},
+/*  7 */ { 5, s_3_7, -1, 10, 0},
+/*  8 */ { 6, s_3_8, -1, 14, 0},
+/*  9 */ { 5, s_3_9, -1, 13, 0},
+/* 10 */ { 6, s_3_10, -1, 1, 0},
+/* 11 */ { 7, s_3_11, 10, 8, 0},
+/* 12 */ { 5, s_3_12, -1, 10, 0},
+/* 13 */ { 5, s_3_13, -1, 8, 0},
+/* 14 */ { 7, s_3_14, 13, 7, 0},
+/* 15 */ { 4, s_3_15, -1, 7, 0},
+/* 16 */ { 4, s_3_16, -1, 8, 0},
+/* 17 */ { 7, s_3_17, -1, 13, 0},
+/* 18 */ { 7, s_3_18, -1, 11, 0},
+/* 19 */ { 7, s_3_19, -1, 12, 0}
+};
+
+static symbol s_4_0[5] = { 'i', 'c', 'a', 't', 'e' };
+static symbol s_4_1[5] = { 'a', 't', 'i', 'v', 'e' };
+static symbol s_4_2[5] = { 'a', 'l', 'i', 'z', 'e' };
+static symbol s_4_3[5] = { 'i', 'c', 'i', 't', 'i' };
+static symbol s_4_4[4] = { 'i', 'c', 'a', 'l' };
+static symbol s_4_5[3] = { 'f', 'u', 'l' };
+static symbol s_4_6[4] = { 'n', 'e', 's', 's' };
+
+static struct among a_4[7] =
+{
+/*  0 */ { 5, s_4_0, -1, 2, 0},
+/*  1 */ { 5, s_4_1, -1, 3, 0},
+/*  2 */ { 5, s_4_2, -1, 1, 0},
+/*  3 */ { 5, s_4_3, -1, 2, 0},
+/*  4 */ { 4, s_4_4, -1, 2, 0},
+/*  5 */ { 3, s_4_5, -1, 3, 0},
+/*  6 */ { 4, s_4_6, -1, 3, 0}
+};
+
+static symbol s_5_0[2] = { 'i', 'c' };
+static symbol s_5_1[4] = { 'a', 'n', 'c', 'e' };
+static symbol s_5_2[4] = { 'e', 'n', 'c', 'e' };
+static symbol s_5_3[4] = { 'a', 'b', 'l', 'e' };
+static symbol s_5_4[4] = { 'i', 'b', 'l', 'e' };
+static symbol s_5_5[3] = { 'a', 't', 'e' };
+static symbol s_5_6[3] = { 'i', 'v', 'e' };
+static symbol s_5_7[3] = { 'i', 'z', 'e' };
+static symbol s_5_8[3] = { 'i', 't', 'i' };
+static symbol s_5_9[2] = { 'a', 'l' };
+static symbol s_5_10[3] = { 'i', 's', 'm' };
+static symbol s_5_11[3] = { 'i', 'o', 'n' };
+static symbol s_5_12[2] = { 'e', 'r' };
+static symbol s_5_13[3] = { 'o', 'u', 's' };
+static symbol s_5_14[3] = { 'a', 'n', 't' };
+static symbol s_5_15[3] = { 'e', 'n', 't' };
+static symbol s_5_16[4] = { 'm', 'e', 'n', 't' };
+static symbol s_5_17[5] = { 'e', 'm', 'e', 'n', 't' };
+static symbol s_5_18[2] = { 'o', 'u' };
+
+static struct among a_5[19] =
+{
+/*  0 */ { 2, s_5_0, -1, 1, 0},
+/*  1 */ { 4, s_5_1, -1, 1, 0},
+/*  2 */ { 4, s_5_2, -1, 1, 0},
+/*  3 */ { 4, s_5_3, -1, 1, 0},
+/*  4 */ { 4, s_5_4, -1, 1, 0},
+/*  5 */ { 3, s_5_5, -1, 1, 0},
+/*  6 */ { 3, s_5_6, -1, 1, 0},
+/*  7 */ { 3, s_5_7, -1, 1, 0},
+/*  8 */ { 3, s_5_8, -1, 1, 0},
+/*  9 */ { 2, s_5_9, -1, 1, 0},
+/* 10 */ { 3, s_5_10, -1, 1, 0},
+/* 11 */ { 3, s_5_11, -1, 2, 0},
+/* 12 */ { 2, s_5_12, -1, 1, 0},
+/* 13 */ { 3, s_5_13, -1, 1, 0},
+/* 14 */ { 3, s_5_14, -1, 1, 0},
+/* 15 */ { 3, s_5_15, -1, 1, 0},
+/* 16 */ { 4, s_5_16, 15, 1, 0},
+/* 17 */ { 5, s_5_17, 16, 1, 0},
+/* 18 */ { 2, s_5_18, -1, 1, 0}
+};
+
+static unsigned char g_v[] = { 17, 65, 16, 1 };
+
+static unsigned char g_v_WXY[] = { 1, 17, 65, 208, 1 };
+
+static symbol s_0[] = { 's', 's' };
+static symbol s_1[] = { 'i' };
+static symbol s_2[] = { 'e', 'e' };
+static symbol s_3[] = { 'e' };
+static symbol s_4[] = { 'e' };
+static symbol s_5[] = { 'y' };
+static symbol s_6[] = { 'Y' };
+static symbol s_7[] = { 'i' };
+static symbol s_8[] = { 't', 'i', 'o', 'n' };
+static symbol s_9[] = { 'e', 'n', 'c', 'e' };
+static symbol s_10[] = { 'a', 'n', 'c', 'e' };
+static symbol s_11[] = { 'a', 'b', 'l', 'e' };
+static symbol s_12[] = { 'e', 'n', 't' };
+static symbol s_13[] = { 'e' };
+static symbol s_14[] = { 'i', 'z', 'e' };
+static symbol s_15[] = { 'a', 't', 'e' };
+static symbol s_16[] = { 'a', 'l' };
+static symbol s_17[] = { 'a', 'l' };
+static symbol s_18[] = { 'f', 'u', 'l' };
+static symbol s_19[] = { 'o', 'u', 's' };
+static symbol s_20[] = { 'i', 'v', 'e' };
+static symbol s_21[] = { 'b', 'l', 'e' };
+static symbol s_22[] = { 'a', 'l' };
+static symbol s_23[] = { 'i', 'c' };
+static symbol s_24[] = { 's' };
+static symbol s_25[] = { 't' };
+static symbol s_26[] = { 'e' };
+static symbol s_27[] = { 'l' };
+static symbol s_28[] = { 'l' };
+static symbol s_29[] = { 'y' };
+static symbol s_30[] = { 'Y' };
+static symbol s_31[] = { 'y' };
+static symbol s_32[] = { 'Y' };
+static symbol s_33[] = { 'Y' };
+static symbol s_34[] = { 'y' };
+
+static int r_shortv(struct SN_env * z) {
+    if (!(out_grouping_b_U(z, g_v_WXY, 89, 121))) return 0;
+    if (!(in_grouping_b_U(z, g_v, 97, 121))) return 0;
+    if (!(out_grouping_b_U(z, g_v, 97, 121))) return 0;
+    return 1;
+}
+
+static int r_R1(struct SN_env * z) {
+    if (!(z->I[0] <= z->c)) return 0;
+    return 1;
+}
+
+static int r_R2(struct SN_env * z) {
+    if (!(z->I[1] <= z->c)) return 0;
+    return 1;
+}
+
+static int r_Step_1a(struct SN_env * z) {
+    int among_var;
+    z->ket = z->c; /* [, line 25 */
+    among_var = find_among_b(z, a_0, 4); /* substring, line 25 */
+    if (!(among_var)) return 0;
+    z->bra = z->c; /* ], line 25 */
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret;
+		ret = slice_from_s(z, 2, s_0); /* <-, line 26 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    {	int ret;
+		ret = slice_from_s(z, 1, s_1); /* <-, line 27 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 3:
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 29 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_Step_1b(struct SN_env * z) {
+    int among_var;
+    z->ket = z->c; /* [, line 34 */
+    among_var = find_among_b(z, a_2, 3); /* substring, line 34 */
+    if (!(among_var)) return 0;
+    z->bra = z->c; /* ], line 34 */
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret = r_R1(z);
+		if (ret == 0) return 0; /* call R1, line 35 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_from_s(z, 2, s_2); /* <-, line 35 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    {	int m_test = z->l - z->c; /* test, line 38 */
+		while(1) { /* gopast, line 38 */
+		    if (!(in_grouping_b_U(z, g_v, 97, 121))) goto lab0;
+		    break;
+		lab0:
+		    {	int c = skip_utf8(z->p, z->c, z->lb, 0, -1);
+			if (c < 0) return 0;
+			z->c = c; /* gopast, line 38 */
+		    }
+		}
+		z->c = z->l - m_test;
+	    }
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 38 */
+		if (ret < 0) return ret;
+	    }
+	    {	int m_test = z->l - z->c; /* test, line 39 */
+		among_var = find_among_b(z, a_1, 13); /* substring, line 39 */
+		if (!(among_var)) return 0;
+		z->c = z->l - m_test;
+	    }
+	    switch(among_var) {
+		case 0: return 0;
+		case 1:
+		    {	int ret;
+			{   int c = z->c;
+			    ret = insert_s(z, z->c, z->c, 1, s_3); /* <+, line 41 */
+			    z->c = c;
+			}
+			if (ret < 0) return ret;
+		    }
+		    break;
+		case 2:
+		    z->ket = z->c; /* [, line 44 */
+		    {	int c = skip_utf8(z->p, z->c, z->lb, 0, -1);
+			if (c < 0) return 0;
+			z->c = c; /* next, line 44 */
+		    }
+		    z->bra = z->c; /* ], line 44 */
+		    {	int ret;
+			ret = slice_del(z); /* delete, line 44 */
+			if (ret < 0) return ret;
+		    }
+		    break;
+		case 3:
+		    if (z->c != z->I[0]) return 0; /* atmark, line 45 */
+		    {	int m_test = z->l - z->c; /* test, line 45 */
+			{   int ret = r_shortv(z);
+			    if (ret == 0) return 0; /* call shortv, line 45 */
+			    if (ret < 0) return ret;
+			}
+			z->c = z->l - m_test;
+		    }
+		    {	int ret;
+			{   int c = z->c;
+			    ret = insert_s(z, z->c, z->c, 1, s_4); /* <+, line 45 */
+			    z->c = c;
+			}
+			if (ret < 0) return ret;
+		    }
+		    break;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_Step_1c(struct SN_env * z) {
+    z->ket = z->c; /* [, line 52 */
+    {	int m = z->l - z->c; (void) m; /* or, line 52 */
+	if (!(eq_s_b(z, 1, s_5))) goto lab1;
+	goto lab0;
+    lab1:
+	z->c = z->l - m;
+	if (!(eq_s_b(z, 1, s_6))) return 0;
+    }
+lab0:
+    z->bra = z->c; /* ], line 52 */
+    while(1) { /* gopast, line 53 */
+	if (!(in_grouping_b_U(z, g_v, 97, 121))) goto lab2;
+	break;
+    lab2:
+	{   int c = skip_utf8(z->p, z->c, z->lb, 0, -1);
+	    if (c < 0) return 0;
+	    z->c = c; /* gopast, line 53 */
+	}
+    }
+    {	int ret;
+	ret = slice_from_s(z, 1, s_7); /* <-, line 54 */
+	if (ret < 0) return ret;
+    }
+    return 1;
+}
+
+static int r_Step_2(struct SN_env * z) {
+    int among_var;
+    z->ket = z->c; /* [, line 58 */
+    among_var = find_among_b(z, a_3, 20); /* substring, line 58 */
+    if (!(among_var)) return 0;
+    z->bra = z->c; /* ], line 58 */
+    {	int ret = r_R1(z);
+	if (ret == 0) return 0; /* call R1, line 58 */
+	if (ret < 0) return ret;
+    }
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret;
+		ret = slice_from_s(z, 4, s_8); /* <-, line 59 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    {	int ret;
+		ret = slice_from_s(z, 4, s_9); /* <-, line 60 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 3:
+	    {	int ret;
+		ret = slice_from_s(z, 4, s_10); /* <-, line 61 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 4:
+	    {	int ret;
+		ret = slice_from_s(z, 4, s_11); /* <-, line 62 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 5:
+	    {	int ret;
+		ret = slice_from_s(z, 3, s_12); /* <-, line 63 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 6:
+	    {	int ret;
+		ret = slice_from_s(z, 1, s_13); /* <-, line 64 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 7:
+	    {	int ret;
+		ret = slice_from_s(z, 3, s_14); /* <-, line 66 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 8:
+	    {	int ret;
+		ret = slice_from_s(z, 3, s_15); /* <-, line 68 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 9:
+	    {	int ret;
+		ret = slice_from_s(z, 2, s_16); /* <-, line 69 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 10:
+	    {	int ret;
+		ret = slice_from_s(z, 2, s_17); /* <-, line 71 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 11:
+	    {	int ret;
+		ret = slice_from_s(z, 3, s_18); /* <-, line 72 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 12:
+	    {	int ret;
+		ret = slice_from_s(z, 3, s_19); /* <-, line 74 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 13:
+	    {	int ret;
+		ret = slice_from_s(z, 3, s_20); /* <-, line 76 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 14:
+	    {	int ret;
+		ret = slice_from_s(z, 3, s_21); /* <-, line 77 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_Step_3(struct SN_env * z) {
+    int among_var;
+    z->ket = z->c; /* [, line 82 */
+    among_var = find_among_b(z, a_4, 7); /* substring, line 82 */
+    if (!(among_var)) return 0;
+    z->bra = z->c; /* ], line 82 */
+    {	int ret = r_R1(z);
+	if (ret == 0) return 0; /* call R1, line 82 */
+	if (ret < 0) return ret;
+    }
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret;
+		ret = slice_from_s(z, 2, s_22); /* <-, line 83 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    {	int ret;
+		ret = slice_from_s(z, 2, s_23); /* <-, line 85 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 3:
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 87 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_Step_4(struct SN_env * z) {
+    int among_var;
+    z->ket = z->c; /* [, line 92 */
+    among_var = find_among_b(z, a_5, 19); /* substring, line 92 */
+    if (!(among_var)) return 0;
+    z->bra = z->c; /* ], line 92 */
+    {	int ret = r_R2(z);
+	if (ret == 0) return 0; /* call R2, line 92 */
+	if (ret < 0) return ret;
+    }
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 95 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    {	int m = z->l - z->c; (void) m; /* or, line 96 */
+		if (!(eq_s_b(z, 1, s_24))) goto lab1;
+		goto lab0;
+	    lab1:
+		z->c = z->l - m;
+		if (!(eq_s_b(z, 1, s_25))) return 0;
+	    }
+	lab0:
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 96 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_Step_5a(struct SN_env * z) {
+    z->ket = z->c; /* [, line 101 */
+    if (!(eq_s_b(z, 1, s_26))) return 0;
+    z->bra = z->c; /* ], line 101 */
+    {	int m = z->l - z->c; (void) m; /* or, line 102 */
+	{   int ret = r_R2(z);
+	    if (ret == 0) goto lab1; /* call R2, line 102 */
+	    if (ret < 0) return ret;
+	}
+	goto lab0;
+    lab1:
+	z->c = z->l - m;
+	{   int ret = r_R1(z);
+	    if (ret == 0) return 0; /* call R1, line 102 */
+	    if (ret < 0) return ret;
+	}
+	{   int m = z->l - z->c; (void) m; /* not, line 102 */
+	    {	int ret = r_shortv(z);
+		if (ret == 0) goto lab2; /* call shortv, line 102 */
+		if (ret < 0) return ret;
+	    }
+	    return 0;
+	lab2:
+	    z->c = z->l - m;
+	}
+    }
+lab0:
+    {	int ret;
+	ret = slice_del(z); /* delete, line 103 */
+	if (ret < 0) return ret;
+    }
+    return 1;
+}
+
+static int r_Step_5b(struct SN_env * z) {
+    z->ket = z->c; /* [, line 107 */
+    if (!(eq_s_b(z, 1, s_27))) return 0;
+    z->bra = z->c; /* ], line 107 */
+    {	int ret = r_R2(z);
+	if (ret == 0) return 0; /* call R2, line 108 */
+	if (ret < 0) return ret;
+    }
+    if (!(eq_s_b(z, 1, s_28))) return 0;
+    {	int ret;
+	ret = slice_del(z); /* delete, line 109 */
+	if (ret < 0) return ret;
+    }
+    return 1;
+}
+
+extern int porter_UTF_8_stem(struct SN_env * z) {
+    z->B[0] = 0; /* unset Y_found, line 115 */
+    {	int c = z->c; /* do, line 116 */
+	z->bra = z->c; /* [, line 116 */
+	if (!(eq_s(z, 1, s_29))) goto lab0;
+	z->ket = z->c; /* ], line 116 */
+	{   int ret;
+	    ret = slice_from_s(z, 1, s_30); /* <-, line 116 */
+	    if (ret < 0) return ret;
+	}
+	z->B[0] = 1; /* set Y_found, line 116 */
+    lab0:
+	z->c = c;
+    }
+    {	int c = z->c; /* do, line 117 */
+	while(1) { /* repeat, line 117 */
+	    int c = z->c;
+	    while(1) { /* goto, line 117 */
+		int c = z->c;
+		if (!(in_grouping_U(z, g_v, 97, 121))) goto lab3;
+		z->bra = z->c; /* [, line 117 */
+		if (!(eq_s(z, 1, s_31))) goto lab3;
+		z->ket = z->c; /* ], line 117 */
+		z->c = c;
+		break;
+	    lab3:
+		z->c = c;
+		{   int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		    if (c < 0) goto lab2;
+		    z->c = c; /* goto, line 117 */
+		}
+	    }
+	    {	int ret;
+		ret = slice_from_s(z, 1, s_32); /* <-, line 117 */
+		if (ret < 0) return ret;
+	    }
+	    z->B[0] = 1; /* set Y_found, line 117 */
+	    continue;
+	lab2:
+	    z->c = c;
+	    break;
+	}
+	z->c = c;
+    }
+    z->I[0] = z->l;
+    z->I[1] = z->l;
+    {	int c = z->c; /* do, line 121 */
+	while(1) { /* gopast, line 122 */
+	    if (!(in_grouping_U(z, g_v, 97, 121))) goto lab5;
+	    break;
+	lab5:
+	    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		if (c < 0) goto lab4;
+		z->c = c; /* gopast, line 122 */
+	    }
+	}
+	while(1) { /* gopast, line 122 */
+	    if (!(out_grouping_U(z, g_v, 97, 121))) goto lab6;
+	    break;
+	lab6:
+	    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		if (c < 0) goto lab4;
+		z->c = c; /* gopast, line 122 */
+	    }
+	}
+	z->I[0] = z->c; /* setmark p1, line 122 */
+	while(1) { /* gopast, line 123 */
+	    if (!(in_grouping_U(z, g_v, 97, 121))) goto lab7;
+	    break;
+	lab7:
+	    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		if (c < 0) goto lab4;
+		z->c = c; /* gopast, line 123 */
+	    }
+	}
+	while(1) { /* gopast, line 123 */
+	    if (!(out_grouping_U(z, g_v, 97, 121))) goto lab8;
+	    break;
+	lab8:
+	    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		if (c < 0) goto lab4;
+		z->c = c; /* gopast, line 123 */
+	    }
+	}
+	z->I[1] = z->c; /* setmark p2, line 123 */
+    lab4:
+	z->c = c;
+    }
+    z->lb = z->c; z->c = z->l; /* backwards, line 126 */
+
+    {	int m = z->l - z->c; (void) m; /* do, line 127 */
+	{   int ret = r_Step_1a(z);
+	    if (ret == 0) goto lab9; /* call Step_1a, line 127 */
+	    if (ret < 0) return ret;
+	}
+    lab9:
+	z->c = z->l - m;
+    }
+    {	int m = z->l - z->c; (void) m; /* do, line 128 */
+	{   int ret = r_Step_1b(z);
+	    if (ret == 0) goto lab10; /* call Step_1b, line 128 */
+	    if (ret < 0) return ret;
+	}
+    lab10:
+	z->c = z->l - m;
+    }
+    {	int m = z->l - z->c; (void) m; /* do, line 129 */
+	{   int ret = r_Step_1c(z);
+	    if (ret == 0) goto lab11; /* call Step_1c, line 129 */
+	    if (ret < 0) return ret;
+	}
+    lab11:
+	z->c = z->l - m;
+    }
+    {	int m = z->l - z->c; (void) m; /* do, line 130 */
+	{   int ret = r_Step_2(z);
+	    if (ret == 0) goto lab12; /* call Step_2, line 130 */
+	    if (ret < 0) return ret;
+	}
+    lab12:
+	z->c = z->l - m;
+    }
+    {	int m = z->l - z->c; (void) m; /* do, line 131 */
+	{   int ret = r_Step_3(z);
+	    if (ret == 0) goto lab13; /* call Step_3, line 131 */
+	    if (ret < 0) return ret;
+	}
+    lab13:
+	z->c = z->l - m;
+    }
+    {	int m = z->l - z->c; (void) m; /* do, line 132 */
+	{   int ret = r_Step_4(z);
+	    if (ret == 0) goto lab14; /* call Step_4, line 132 */
+	    if (ret < 0) return ret;
+	}
+    lab14:
+	z->c = z->l - m;
+    }
+    {	int m = z->l - z->c; (void) m; /* do, line 133 */
+	{   int ret = r_Step_5a(z);
+	    if (ret == 0) goto lab15; /* call Step_5a, line 133 */
+	    if (ret < 0) return ret;
+	}
+    lab15:
+	z->c = z->l - m;
+    }
+    {	int m = z->l - z->c; (void) m; /* do, line 134 */
+	{   int ret = r_Step_5b(z);
+	    if (ret == 0) goto lab16; /* call Step_5b, line 134 */
+	    if (ret < 0) return ret;
+	}
+    lab16:
+	z->c = z->l - m;
+    }
+    z->c = z->lb;
+    {	int c = z->c; /* do, line 137 */
+	if (!(z->B[0])) goto lab17; /* Boolean test Y_found, line 137 */
+	while(1) { /* repeat, line 137 */
+	    int c = z->c;
+	    while(1) { /* goto, line 137 */
+		int c = z->c;
+		z->bra = z->c; /* [, line 137 */
+		if (!(eq_s(z, 1, s_33))) goto lab19;
+		z->ket = z->c; /* ], line 137 */
+		z->c = c;
+		break;
+	    lab19:
+		z->c = c;
+		{   int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		    if (c < 0) goto lab18;
+		    z->c = c; /* goto, line 137 */
+		}
+	    }
+	    {	int ret;
+		ret = slice_from_s(z, 1, s_34); /* <-, line 137 */
+		if (ret < 0) return ret;
+	    }
+	    continue;
+	lab18:
+	    z->c = c;
+	    break;
+	}
+    lab17:
+	z->c = c;
+    }
+    return 1;
+}
+
+extern struct SN_env * porter_UTF_8_create_env(void) { return SN_create_env(0, 2, 1); }
+
+extern void porter_UTF_8_close_env(struct SN_env * z) { SN_close_env(z); }
+

Added: trunk/src/libstemmer/stem_UTF_8_porter.h
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/stem_UTF_8_porter.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2001, Dr Martin Porter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+/* This file was generated automatically by the Snowball to ANSI C compiler */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern struct SN_env * porter_UTF_8_create_env(void);
+extern void porter_UTF_8_close_env(struct SN_env * z);
+
+extern int porter_UTF_8_stem(struct SN_env * z);
+
+#ifdef __cplusplus
+}
+#endif
+

Added: trunk/src/libstemmer/stem_UTF_8_portuguese.c
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/stem_UTF_8_portuguese.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,1068 @@
+/*
+ * Copyright (c) 2001, Dr Martin Porter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+/* This file was generated automatically by the Snowball to ANSI C compiler */
+
+#include "header.h"
+
+extern int portuguese_UTF_8_stem(struct SN_env * z);
+static int r_residual_form(struct SN_env * z);
+static int r_residual_suffix(struct SN_env * z);
+static int r_verb_suffix(struct SN_env * z);
+static int r_standard_suffix(struct SN_env * z);
+static int r_R2(struct SN_env * z);
+static int r_R1(struct SN_env * z);
+static int r_RV(struct SN_env * z);
+static int r_mark_regions(struct SN_env * z);
+static int r_postlude(struct SN_env * z);
+static int r_prelude(struct SN_env * z);
+
+extern struct SN_env * portuguese_UTF_8_create_env(void);
+extern void portuguese_UTF_8_close_env(struct SN_env * z);
+
+static symbol s_0_1[2] = { 0xC3, 0xA3 };
+static symbol s_0_2[2] = { 0xC3, 0xB5 };
+
+static struct among a_0[3] =
+{
+/*  0 */ { 0, 0, -1, 3, 0},
+/*  1 */ { 2, s_0_1, 0, 1, 0},
+/*  2 */ { 2, s_0_2, 0, 2, 0}
+};
+
+static symbol s_1_1[2] = { 'a', '~' };
+static symbol s_1_2[2] = { 'o', '~' };
+
+static struct among a_1[3] =
+{
+/*  0 */ { 0, 0, -1, 3, 0},
+/*  1 */ { 2, s_1_1, 0, 1, 0},
+/*  2 */ { 2, s_1_2, 0, 2, 0}
+};
+
+static symbol s_2_0[2] = { 'i', 'c' };
+static symbol s_2_1[2] = { 'a', 'd' };
+static symbol s_2_2[2] = { 'o', 's' };
+static symbol s_2_3[2] = { 'i', 'v' };
+
+static struct among a_2[4] =
+{
+/*  0 */ { 2, s_2_0, -1, -1, 0},
+/*  1 */ { 2, s_2_1, -1, -1, 0},
+/*  2 */ { 2, s_2_2, -1, -1, 0},
+/*  3 */ { 2, s_2_3, -1, 1, 0}
+};
+
+static symbol s_3_0[4] = { 'a', 'n', 't', 'e' };
+static symbol s_3_1[4] = { 'a', 'v', 'e', 'l' };
+static symbol s_3_2[5] = { 0xC3, 0xAD, 'v', 'e', 'l' };
+
+static struct among a_3[3] =
+{
+/*  0 */ { 4, s_3_0, -1, 1, 0},
+/*  1 */ { 4, s_3_1, -1, 1, 0},
+/*  2 */ { 5, s_3_2, -1, 1, 0}
+};
+
+static symbol s_4_0[2] = { 'i', 'c' };
+static symbol s_4_1[4] = { 'a', 'b', 'i', 'l' };
+static symbol s_4_2[2] = { 'i', 'v' };
+
+static struct among a_4[3] =
+{
+/*  0 */ { 2, s_4_0, -1, 1, 0},
+/*  1 */ { 4, s_4_1, -1, 1, 0},
+/*  2 */ { 2, s_4_2, -1, 1, 0}
+};
+
+static symbol s_5_0[3] = { 'i', 'c', 'a' };
+static symbol s_5_1[6] = { 0xC3, 0xA2, 'n', 'c', 'i', 'a' };
+static symbol s_5_2[6] = { 0xC3, 0xAA, 'n', 'c', 'i', 'a' };
+static symbol s_5_3[3] = { 'i', 'r', 'a' };
+static symbol s_5_4[5] = { 'a', 'd', 'o', 'r', 'a' };
+static symbol s_5_5[3] = { 'o', 's', 'a' };
+static symbol s_5_6[4] = { 'i', 's', 't', 'a' };
+static symbol s_5_7[3] = { 'i', 'v', 'a' };
+static symbol s_5_8[3] = { 'e', 'z', 'a' };
+static symbol s_5_9[6] = { 'l', 'o', 'g', 0xC3, 0xAD, 'a' };
+static symbol s_5_10[5] = { 'i', 'd', 'a', 'd', 'e' };
+static symbol s_5_11[4] = { 'a', 'n', 't', 'e' };
+static symbol s_5_12[5] = { 'm', 'e', 'n', 't', 'e' };
+static symbol s_5_13[6] = { 'a', 'm', 'e', 'n', 't', 'e' };
+static symbol s_5_14[5] = { 0xC3, 0xA1, 'v', 'e', 'l' };
+static symbol s_5_15[5] = { 0xC3, 0xAD, 'v', 'e', 'l' };
+static symbol s_5_16[6] = { 'u', 'c', 'i', 0xC3, 0xB3, 'n' };
+static symbol s_5_17[3] = { 'i', 'c', 'o' };
+static symbol s_5_18[4] = { 'i', 's', 'm', 'o' };
+static symbol s_5_19[3] = { 'o', 's', 'o' };
+static symbol s_5_20[6] = { 'a', 'm', 'e', 'n', 't', 'o' };
+static symbol s_5_21[6] = { 'i', 'm', 'e', 'n', 't', 'o' };
+static symbol s_5_22[3] = { 'i', 'v', 'o' };
+static symbol s_5_23[6] = { 'a', 0xC3, 0xA7, 'a', '~', 'o' };
+static symbol s_5_24[4] = { 'a', 'd', 'o', 'r' };
+static symbol s_5_25[4] = { 'i', 'c', 'a', 's' };
+static symbol s_5_26[7] = { 0xC3, 0xAA, 'n', 'c', 'i', 'a', 's' };
+static symbol s_5_27[4] = { 'i', 'r', 'a', 's' };
+static symbol s_5_28[6] = { 'a', 'd', 'o', 'r', 'a', 's' };
+static symbol s_5_29[4] = { 'o', 's', 'a', 's' };
+static symbol s_5_30[5] = { 'i', 's', 't', 'a', 's' };
+static symbol s_5_31[4] = { 'i', 'v', 'a', 's' };
+static symbol s_5_32[4] = { 'e', 'z', 'a', 's' };
+static symbol s_5_33[7] = { 'l', 'o', 'g', 0xC3, 0xAD, 'a', 's' };
+static symbol s_5_34[6] = { 'i', 'd', 'a', 'd', 'e', 's' };
+static symbol s_5_35[7] = { 'u', 'c', 'i', 'o', 'n', 'e', 's' };
+static symbol s_5_36[6] = { 'a', 'd', 'o', 'r', 'e', 's' };
+static symbol s_5_37[5] = { 'a', 'n', 't', 'e', 's' };
+static symbol s_5_38[7] = { 'a', 0xC3, 0xA7, 'o', '~', 'e', 's' };
+static symbol s_5_39[4] = { 'i', 'c', 'o', 's' };
+static symbol s_5_40[5] = { 'i', 's', 'm', 'o', 's' };
+static symbol s_5_41[4] = { 'o', 's', 'o', 's' };
+static symbol s_5_42[7] = { 'a', 'm', 'e', 'n', 't', 'o', 's' };
+static symbol s_5_43[7] = { 'i', 'm', 'e', 'n', 't', 'o', 's' };
+static symbol s_5_44[4] = { 'i', 'v', 'o', 's' };
+
+static struct among a_5[45] =
+{
+/*  0 */ { 3, s_5_0, -1, 1, 0},
+/*  1 */ { 6, s_5_1, -1, 1, 0},
+/*  2 */ { 6, s_5_2, -1, 4, 0},
+/*  3 */ { 3, s_5_3, -1, 9, 0},
+/*  4 */ { 5, s_5_4, -1, 1, 0},
+/*  5 */ { 3, s_5_5, -1, 1, 0},
+/*  6 */ { 4, s_5_6, -1, 1, 0},
+/*  7 */ { 3, s_5_7, -1, 8, 0},
+/*  8 */ { 3, s_5_8, -1, 1, 0},
+/*  9 */ { 6, s_5_9, -1, 2, 0},
+/* 10 */ { 5, s_5_10, -1, 7, 0},
+/* 11 */ { 4, s_5_11, -1, 1, 0},
+/* 12 */ { 5, s_5_12, -1, 6, 0},
+/* 13 */ { 6, s_5_13, 12, 5, 0},
+/* 14 */ { 5, s_5_14, -1, 1, 0},
+/* 15 */ { 5, s_5_15, -1, 1, 0},
+/* 16 */ { 6, s_5_16, -1, 3, 0},
+/* 17 */ { 3, s_5_17, -1, 1, 0},
+/* 18 */ { 4, s_5_18, -1, 1, 0},
+/* 19 */ { 3, s_5_19, -1, 1, 0},
+/* 20 */ { 6, s_5_20, -1, 1, 0},
+/* 21 */ { 6, s_5_21, -1, 1, 0},
+/* 22 */ { 3, s_5_22, -1, 8, 0},
+/* 23 */ { 6, s_5_23, -1, 1, 0},
+/* 24 */ { 4, s_5_24, -1, 1, 0},
+/* 25 */ { 4, s_5_25, -1, 1, 0},
+/* 26 */ { 7, s_5_26, -1, 4, 0},
+/* 27 */ { 4, s_5_27, -1, 9, 0},
+/* 28 */ { 6, s_5_28, -1, 1, 0},
+/* 29 */ { 4, s_5_29, -1, 1, 0},
+/* 30 */ { 5, s_5_30, -1, 1, 0},
+/* 31 */ { 4, s_5_31, -1, 8, 0},
+/* 32 */ { 4, s_5_32, -1, 1, 0},
+/* 33 */ { 7, s_5_33, -1, 2, 0},
+/* 34 */ { 6, s_5_34, -1, 7, 0},
+/* 35 */ { 7, s_5_35, -1, 3, 0},
+/* 36 */ { 6, s_5_36, -1, 1, 0},
+/* 37 */ { 5, s_5_37, -1, 1, 0},
+/* 38 */ { 7, s_5_38, -1, 1, 0},
+/* 39 */ { 4, s_5_39, -1, 1, 0},
+/* 40 */ { 5, s_5_40, -1, 1, 0},
+/* 41 */ { 4, s_5_41, -1, 1, 0},
+/* 42 */ { 7, s_5_42, -1, 1, 0},
+/* 43 */ { 7, s_5_43, -1, 1, 0},
+/* 44 */ { 4, s_5_44, -1, 8, 0}
+};
+
+static symbol s_6_0[3] = { 'a', 'd', 'a' };
+static symbol s_6_1[3] = { 'i', 'd', 'a' };
+static symbol s_6_2[2] = { 'i', 'a' };
+static symbol s_6_3[4] = { 'a', 'r', 'i', 'a' };
+static symbol s_6_4[4] = { 'e', 'r', 'i', 'a' };
+static symbol s_6_5[4] = { 'i', 'r', 'i', 'a' };
+static symbol s_6_6[3] = { 'a', 'r', 'a' };
+static symbol s_6_7[3] = { 'e', 'r', 'a' };
+static symbol s_6_8[3] = { 'i', 'r', 'a' };
+static symbol s_6_9[3] = { 'a', 'v', 'a' };
+static symbol s_6_10[4] = { 'a', 's', 's', 'e' };
+static symbol s_6_11[4] = { 'e', 's', 's', 'e' };
+static symbol s_6_12[4] = { 'i', 's', 's', 'e' };
+static symbol s_6_13[4] = { 'a', 's', 't', 'e' };
+static symbol s_6_14[4] = { 'e', 's', 't', 'e' };
+static symbol s_6_15[4] = { 'i', 's', 't', 'e' };
+static symbol s_6_16[2] = { 'e', 'i' };
+static symbol s_6_17[4] = { 'a', 'r', 'e', 'i' };
+static symbol s_6_18[4] = { 'e', 'r', 'e', 'i' };
+static symbol s_6_19[4] = { 'i', 'r', 'e', 'i' };
+static symbol s_6_20[2] = { 'a', 'm' };
+static symbol s_6_21[3] = { 'i', 'a', 'm' };
+static symbol s_6_22[5] = { 'a', 'r', 'i', 'a', 'm' };
+static symbol s_6_23[5] = { 'e', 'r', 'i', 'a', 'm' };
+static symbol s_6_24[5] = { 'i', 'r', 'i', 'a', 'm' };
+static symbol s_6_25[4] = { 'a', 'r', 'a', 'm' };
+static symbol s_6_26[4] = { 'e', 'r', 'a', 'm' };
+static symbol s_6_27[4] = { 'i', 'r', 'a', 'm' };
+static symbol s_6_28[4] = { 'a', 'v', 'a', 'm' };
+static symbol s_6_29[2] = { 'e', 'm' };
+static symbol s_6_30[4] = { 'a', 'r', 'e', 'm' };
+static symbol s_6_31[4] = { 'e', 'r', 'e', 'm' };
+static symbol s_6_32[4] = { 'i', 'r', 'e', 'm' };
+static symbol s_6_33[5] = { 'a', 's', 's', 'e', 'm' };
+static symbol s_6_34[5] = { 'e', 's', 's', 'e', 'm' };
+static symbol s_6_35[5] = { 'i', 's', 's', 'e', 'm' };
+static symbol s_6_36[3] = { 'a', 'd', 'o' };
+static symbol s_6_37[3] = { 'i', 'd', 'o' };
+static symbol s_6_38[4] = { 'a', 'n', 'd', 'o' };
+static symbol s_6_39[4] = { 'e', 'n', 'd', 'o' };
+static symbol s_6_40[4] = { 'i', 'n', 'd', 'o' };
+static symbol s_6_41[5] = { 'a', 'r', 'a', '~', 'o' };
+static symbol s_6_42[5] = { 'e', 'r', 'a', '~', 'o' };
+static symbol s_6_43[5] = { 'i', 'r', 'a', '~', 'o' };
+static symbol s_6_44[2] = { 'a', 'r' };
+static symbol s_6_45[2] = { 'e', 'r' };
+static symbol s_6_46[2] = { 'i', 'r' };
+static symbol s_6_47[2] = { 'a', 's' };
+static symbol s_6_48[4] = { 'a', 'd', 'a', 's' };
+static symbol s_6_49[4] = { 'i', 'd', 'a', 's' };
+static symbol s_6_50[3] = { 'i', 'a', 's' };
+static symbol s_6_51[5] = { 'a', 'r', 'i', 'a', 's' };
+static symbol s_6_52[5] = { 'e', 'r', 'i', 'a', 's' };
+static symbol s_6_53[5] = { 'i', 'r', 'i', 'a', 's' };
+static symbol s_6_54[4] = { 'a', 'r', 'a', 's' };
+static symbol s_6_55[4] = { 'e', 'r', 'a', 's' };
+static symbol s_6_56[4] = { 'i', 'r', 'a', 's' };
+static symbol s_6_57[4] = { 'a', 'v', 'a', 's' };
+static symbol s_6_58[2] = { 'e', 's' };
+static symbol s_6_59[5] = { 'a', 'r', 'd', 'e', 's' };
+static symbol s_6_60[5] = { 'e', 'r', 'd', 'e', 's' };
+static symbol s_6_61[5] = { 'i', 'r', 'd', 'e', 's' };
+static symbol s_6_62[4] = { 'a', 'r', 'e', 's' };
+static symbol s_6_63[4] = { 'e', 'r', 'e', 's' };
+static symbol s_6_64[4] = { 'i', 'r', 'e', 's' };
+static symbol s_6_65[5] = { 'a', 's', 's', 'e', 's' };
+static symbol s_6_66[5] = { 'e', 's', 's', 'e', 's' };
+static symbol s_6_67[5] = { 'i', 's', 's', 'e', 's' };
+static symbol s_6_68[5] = { 'a', 's', 't', 'e', 's' };
+static symbol s_6_69[5] = { 'e', 's', 't', 'e', 's' };
+static symbol s_6_70[5] = { 'i', 's', 't', 'e', 's' };
+static symbol s_6_71[2] = { 'i', 's' };
+static symbol s_6_72[3] = { 'a', 'i', 's' };
+static symbol s_6_73[3] = { 'e', 'i', 's' };
+static symbol s_6_74[5] = { 'a', 'r', 'e', 'i', 's' };
+static symbol s_6_75[5] = { 'e', 'r', 'e', 'i', 's' };
+static symbol s_6_76[5] = { 'i', 'r', 'e', 'i', 's' };
+static symbol s_6_77[6] = { 0xC3, 0xA1, 'r', 'e', 'i', 's' };
+static symbol s_6_78[6] = { 0xC3, 0xA9, 'r', 'e', 'i', 's' };
+static symbol s_6_79[6] = { 0xC3, 0xAD, 'r', 'e', 'i', 's' };
+static symbol s_6_80[7] = { 0xC3, 0xA1, 's', 's', 'e', 'i', 's' };
+static symbol s_6_81[7] = { 0xC3, 0xA9, 's', 's', 'e', 'i', 's' };
+static symbol s_6_82[7] = { 0xC3, 0xAD, 's', 's', 'e', 'i', 's' };
+static symbol s_6_83[6] = { 0xC3, 0xA1, 'v', 'e', 'i', 's' };
+static symbol s_6_84[5] = { 0xC3, 0xAD, 'e', 'i', 's' };
+static symbol s_6_85[7] = { 'a', 'r', 0xC3, 0xAD, 'e', 'i', 's' };
+static symbol s_6_86[7] = { 'e', 'r', 0xC3, 0xAD, 'e', 'i', 's' };
+static symbol s_6_87[7] = { 'i', 'r', 0xC3, 0xAD, 'e', 'i', 's' };
+static symbol s_6_88[4] = { 'a', 'd', 'o', 's' };
+static symbol s_6_89[4] = { 'i', 'd', 'o', 's' };
+static symbol s_6_90[4] = { 'a', 'm', 'o', 's' };
+static symbol s_6_91[7] = { 0xC3, 0xA1, 'r', 'a', 'm', 'o', 's' };
+static symbol s_6_92[7] = { 0xC3, 0xA9, 'r', 'a', 'm', 'o', 's' };
+static symbol s_6_93[7] = { 0xC3, 0xAD, 'r', 'a', 'm', 'o', 's' };
+static symbol s_6_94[7] = { 0xC3, 0xA1, 'v', 'a', 'm', 'o', 's' };
+static symbol s_6_95[6] = { 0xC3, 0xAD, 'a', 'm', 'o', 's' };
+static symbol s_6_96[8] = { 'a', 'r', 0xC3, 0xAD, 'a', 'm', 'o', 's' };
+static symbol s_6_97[8] = { 'e', 'r', 0xC3, 0xAD, 'a', 'm', 'o', 's' };
+static symbol s_6_98[8] = { 'i', 'r', 0xC3, 0xAD, 'a', 'm', 'o', 's' };
+static symbol s_6_99[4] = { 'e', 'm', 'o', 's' };
+static symbol s_6_100[6] = { 'a', 'r', 'e', 'm', 'o', 's' };
+static symbol s_6_101[6] = { 'e', 'r', 'e', 'm', 'o', 's' };
+static symbol s_6_102[6] = { 'i', 'r', 'e', 'm', 'o', 's' };
+static symbol s_6_103[8] = { 0xC3, 0xA1, 's', 's', 'e', 'm', 'o', 's' };
+static symbol s_6_104[8] = { 0xC3, 0xAA, 's', 's', 'e', 'm', 'o', 's' };
+static symbol s_6_105[8] = { 0xC3, 0xAD, 's', 's', 'e', 'm', 'o', 's' };
+static symbol s_6_106[4] = { 'i', 'm', 'o', 's' };
+static symbol s_6_107[5] = { 'a', 'r', 'm', 'o', 's' };
+static symbol s_6_108[5] = { 'e', 'r', 'm', 'o', 's' };
+static symbol s_6_109[5] = { 'i', 'r', 'm', 'o', 's' };
+static symbol s_6_110[5] = { 0xC3, 0xA1, 'm', 'o', 's' };
+static symbol s_6_111[5] = { 'a', 'r', 0xC3, 0xA1, 's' };
+static symbol s_6_112[5] = { 'e', 'r', 0xC3, 0xA1, 's' };
+static symbol s_6_113[5] = { 'i', 'r', 0xC3, 0xA1, 's' };
+static symbol s_6_114[2] = { 'e', 'u' };
+static symbol s_6_115[2] = { 'i', 'u' };
+static symbol s_6_116[2] = { 'o', 'u' };
+static symbol s_6_117[4] = { 'a', 'r', 0xC3, 0xA1 };
+static symbol s_6_118[4] = { 'e', 'r', 0xC3, 0xA1 };
+static symbol s_6_119[4] = { 'i', 'r', 0xC3, 0xA1 };
+
+static struct among a_6[120] =
+{
+/*  0 */ { 3, s_6_0, -1, 1, 0},
+/*  1 */ { 3, s_6_1, -1, 1, 0},
+/*  2 */ { 2, s_6_2, -1, 1, 0},
+/*  3 */ { 4, s_6_3, 2, 1, 0},
+/*  4 */ { 4, s_6_4, 2, 1, 0},
+/*  5 */ { 4, s_6_5, 2, 1, 0},
+/*  6 */ { 3, s_6_6, -1, 1, 0},
+/*  7 */ { 3, s_6_7, -1, 1, 0},
+/*  8 */ { 3, s_6_8, -1, 1, 0},
+/*  9 */ { 3, s_6_9, -1, 1, 0},
+/* 10 */ { 4, s_6_10, -1, 1, 0},
+/* 11 */ { 4, s_6_11, -1, 1, 0},
+/* 12 */ { 4, s_6_12, -1, 1, 0},
+/* 13 */ { 4, s_6_13, -1, 1, 0},
+/* 14 */ { 4, s_6_14, -1, 1, 0},
+/* 15 */ { 4, s_6_15, -1, 1, 0},
+/* 16 */ { 2, s_6_16, -1, 1, 0},
+/* 17 */ { 4, s_6_17, 16, 1, 0},
+/* 18 */ { 4, s_6_18, 16, 1, 0},
+/* 19 */ { 4, s_6_19, 16, 1, 0},
+/* 20 */ { 2, s_6_20, -1, 1, 0},
+/* 21 */ { 3, s_6_21, 20, 1, 0},
+/* 22 */ { 5, s_6_22, 21, 1, 0},
+/* 23 */ { 5, s_6_23, 21, 1, 0},
+/* 24 */ { 5, s_6_24, 21, 1, 0},
+/* 25 */ { 4, s_6_25, 20, 1, 0},
+/* 26 */ { 4, s_6_26, 20, 1, 0},
+/* 27 */ { 4, s_6_27, 20, 1, 0},
+/* 28 */ { 4, s_6_28, 20, 1, 0},
+/* 29 */ { 2, s_6_29, -1, 1, 0},
+/* 30 */ { 4, s_6_30, 29, 1, 0},
+/* 31 */ { 4, s_6_31, 29, 1, 0},
+/* 32 */ { 4, s_6_32, 29, 1, 0},
+/* 33 */ { 5, s_6_33, 29, 1, 0},
+/* 34 */ { 5, s_6_34, 29, 1, 0},
+/* 35 */ { 5, s_6_35, 29, 1, 0},
+/* 36 */ { 3, s_6_36, -1, 1, 0},
+/* 37 */ { 3, s_6_37, -1, 1, 0},
+/* 38 */ { 4, s_6_38, -1, 1, 0},
+/* 39 */ { 4, s_6_39, -1, 1, 0},
+/* 40 */ { 4, s_6_40, -1, 1, 0},
+/* 41 */ { 5, s_6_41, -1, 1, 0},
+/* 42 */ { 5, s_6_42, -1, 1, 0},
+/* 43 */ { 5, s_6_43, -1, 1, 0},
+/* 44 */ { 2, s_6_44, -1, 1, 0},
+/* 45 */ { 2, s_6_45, -1, 1, 0},
+/* 46 */ { 2, s_6_46, -1, 1, 0},
+/* 47 */ { 2, s_6_47, -1, 1, 0},
+/* 48 */ { 4, s_6_48, 47, 1, 0},
+/* 49 */ { 4, s_6_49, 47, 1, 0},
+/* 50 */ { 3, s_6_50, 47, 1, 0},
+/* 51 */ { 5, s_6_51, 50, 1, 0},
+/* 52 */ { 5, s_6_52, 50, 1, 0},
+/* 53 */ { 5, s_6_53, 50, 1, 0},
+/* 54 */ { 4, s_6_54, 47, 1, 0},
+/* 55 */ { 4, s_6_55, 47, 1, 0},
+/* 56 */ { 4, s_6_56, 47, 1, 0},
+/* 57 */ { 4, s_6_57, 47, 1, 0},
+/* 58 */ { 2, s_6_58, -1, 1, 0},
+/* 59 */ { 5, s_6_59, 58, 1, 0},
+/* 60 */ { 5, s_6_60, 58, 1, 0},
+/* 61 */ { 5, s_6_61, 58, 1, 0},
+/* 62 */ { 4, s_6_62, 58, 1, 0},
+/* 63 */ { 4, s_6_63, 58, 1, 0},
+/* 64 */ { 4, s_6_64, 58, 1, 0},
+/* 65 */ { 5, s_6_65, 58, 1, 0},
+/* 66 */ { 5, s_6_66, 58, 1, 0},
+/* 67 */ { 5, s_6_67, 58, 1, 0},
+/* 68 */ { 5, s_6_68, 58, 1, 0},
+/* 69 */ { 5, s_6_69, 58, 1, 0},
+/* 70 */ { 5, s_6_70, 58, 1, 0},
+/* 71 */ { 2, s_6_71, -1, 1, 0},
+/* 72 */ { 3, s_6_72, 71, 1, 0},
+/* 73 */ { 3, s_6_73, 71, 1, 0},
+/* 74 */ { 5, s_6_74, 73, 1, 0},
+/* 75 */ { 5, s_6_75, 73, 1, 0},
+/* 76 */ { 5, s_6_76, 73, 1, 0},
+/* 77 */ { 6, s_6_77, 73, 1, 0},
+/* 78 */ { 6, s_6_78, 73, 1, 0},
+/* 79 */ { 6, s_6_79, 73, 1, 0},
+/* 80 */ { 7, s_6_80, 73, 1, 0},
+/* 81 */ { 7, s_6_81, 73, 1, 0},
+/* 82 */ { 7, s_6_82, 73, 1, 0},
+/* 83 */ { 6, s_6_83, 73, 1, 0},
+/* 84 */ { 5, s_6_84, 73, 1, 0},
+/* 85 */ { 7, s_6_85, 84, 1, 0},
+/* 86 */ { 7, s_6_86, 84, 1, 0},
+/* 87 */ { 7, s_6_87, 84, 1, 0},
+/* 88 */ { 4, s_6_88, -1, 1, 0},
+/* 89 */ { 4, s_6_89, -1, 1, 0},
+/* 90 */ { 4, s_6_90, -1, 1, 0},
+/* 91 */ { 7, s_6_91, 90, 1, 0},
+/* 92 */ { 7, s_6_92, 90, 1, 0},
+/* 93 */ { 7, s_6_93, 90, 1, 0},
+/* 94 */ { 7, s_6_94, 90, 1, 0},
+/* 95 */ { 6, s_6_95, 90, 1, 0},
+/* 96 */ { 8, s_6_96, 95, 1, 0},
+/* 97 */ { 8, s_6_97, 95, 1, 0},
+/* 98 */ { 8, s_6_98, 95, 1, 0},
+/* 99 */ { 4, s_6_99, -1, 1, 0},
+/*100 */ { 6, s_6_100, 99, 1, 0},
+/*101 */ { 6, s_6_101, 99, 1, 0},
+/*102 */ { 6, s_6_102, 99, 1, 0},
+/*103 */ { 8, s_6_103, 99, 1, 0},
+/*104 */ { 8, s_6_104, 99, 1, 0},
+/*105 */ { 8, s_6_105, 99, 1, 0},
+/*106 */ { 4, s_6_106, -1, 1, 0},
+/*107 */ { 5, s_6_107, -1, 1, 0},
+/*108 */ { 5, s_6_108, -1, 1, 0},
+/*109 */ { 5, s_6_109, -1, 1, 0},
+/*110 */ { 5, s_6_110, -1, 1, 0},
+/*111 */ { 5, s_6_111, -1, 1, 0},
+/*112 */ { 5, s_6_112, -1, 1, 0},
+/*113 */ { 5, s_6_113, -1, 1, 0},
+/*114 */ { 2, s_6_114, -1, 1, 0},
+/*115 */ { 2, s_6_115, -1, 1, 0},
+/*116 */ { 2, s_6_116, -1, 1, 0},
+/*117 */ { 4, s_6_117, -1, 1, 0},
+/*118 */ { 4, s_6_118, -1, 1, 0},
+/*119 */ { 4, s_6_119, -1, 1, 0}
+};
+
+static symbol s_7_0[1] = { 'a' };
+static symbol s_7_1[1] = { 'i' };
+static symbol s_7_2[1] = { 'o' };
+static symbol s_7_3[2] = { 'o', 's' };
+static symbol s_7_4[2] = { 0xC3, 0xA1 };
+static symbol s_7_5[2] = { 0xC3, 0xAD };
+static symbol s_7_6[2] = { 0xC3, 0xB3 };
+
+static struct among a_7[7] =
+{
+/*  0 */ { 1, s_7_0, -1, 1, 0},
+/*  1 */ { 1, s_7_1, -1, 1, 0},
+/*  2 */ { 1, s_7_2, -1, 1, 0},
+/*  3 */ { 2, s_7_3, -1, 1, 0},
+/*  4 */ { 2, s_7_4, -1, 1, 0},
+/*  5 */ { 2, s_7_5, -1, 1, 0},
+/*  6 */ { 2, s_7_6, -1, 1, 0}
+};
+
+static symbol s_8_0[1] = { 'e' };
+static symbol s_8_1[2] = { 0xC3, 0xA7 };
+static symbol s_8_2[2] = { 0xC3, 0xA9 };
+static symbol s_8_3[2] = { 0xC3, 0xAA };
+
+static struct among a_8[4] =
+{
+/*  0 */ { 1, s_8_0, -1, 1, 0},
+/*  1 */ { 2, s_8_1, -1, 2, 0},
+/*  2 */ { 2, s_8_2, -1, 1, 0},
+/*  3 */ { 2, s_8_3, -1, 1, 0}
+};
+
+static unsigned char g_v[] = { 17, 65, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 19, 12, 2 };
+
+static symbol s_0[] = { 'a', '~' };
+static symbol s_1[] = { 'o', '~' };
+static symbol s_2[] = { 0xC3, 0xA3 };
+static symbol s_3[] = { 0xC3, 0xB5 };
+static symbol s_4[] = { 'l', 'o', 'g' };
+static symbol s_5[] = { 'u' };
+static symbol s_6[] = { 'e', 'n', 't', 'e' };
+static symbol s_7[] = { 'a', 't' };
+static symbol s_8[] = { 'a', 't' };
+static symbol s_9[] = { 'e' };
+static symbol s_10[] = { 'i', 'r' };
+static symbol s_11[] = { 'u' };
+static symbol s_12[] = { 'g' };
+static symbol s_13[] = { 'i' };
+static symbol s_14[] = { 'c' };
+static symbol s_15[] = { 'c' };
+static symbol s_16[] = { 'i' };
+static symbol s_17[] = { 'c' };
+
+static int r_prelude(struct SN_env * z) {
+    int among_var;
+    while(1) { /* repeat, line 36 */
+	int c = z->c;
+	z->bra = z->c; /* [, line 37 */
+	among_var = find_among(z, a_0, 3); /* substring, line 37 */
+	if (!(among_var)) goto lab0;
+	z->ket = z->c; /* ], line 37 */
+	switch(among_var) {
+	    case 0: goto lab0;
+	    case 1:
+		{   int ret;
+		    ret = slice_from_s(z, 2, s_0); /* <-, line 38 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 2:
+		{   int ret;
+		    ret = slice_from_s(z, 2, s_1); /* <-, line 39 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 3:
+		{   int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		    if (c < 0) goto lab0;
+		    z->c = c; /* next, line 40 */
+		}
+		break;
+	}
+	continue;
+    lab0:
+	z->c = c;
+	break;
+    }
+    return 1;
+}
+
+static int r_mark_regions(struct SN_env * z) {
+    z->I[0] = z->l;
+    z->I[1] = z->l;
+    z->I[2] = z->l;
+    {	int c = z->c; /* do, line 50 */
+	{   int c = z->c; /* or, line 52 */
+	    if (!(in_grouping_U(z, g_v, 97, 250))) goto lab2;
+	    {	int c = z->c; /* or, line 51 */
+		if (!(out_grouping_U(z, g_v, 97, 250))) goto lab4;
+		while(1) { /* gopast, line 51 */
+		    if (!(in_grouping_U(z, g_v, 97, 250))) goto lab5;
+		    break;
+		lab5:
+		    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+			if (c < 0) goto lab4;
+			z->c = c; /* gopast, line 51 */
+		    }
+		}
+		goto lab3;
+	    lab4:
+		z->c = c;
+		if (!(in_grouping_U(z, g_v, 97, 250))) goto lab2;
+		while(1) { /* gopast, line 51 */
+		    if (!(out_grouping_U(z, g_v, 97, 250))) goto lab6;
+		    break;
+		lab6:
+		    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+			if (c < 0) goto lab2;
+			z->c = c; /* gopast, line 51 */
+		    }
+		}
+	    }
+	lab3:
+	    goto lab1;
+	lab2:
+	    z->c = c;
+	    if (!(out_grouping_U(z, g_v, 97, 250))) goto lab0;
+	    {	int c = z->c; /* or, line 53 */
+		if (!(out_grouping_U(z, g_v, 97, 250))) goto lab8;
+		while(1) { /* gopast, line 53 */
+		    if (!(in_grouping_U(z, g_v, 97, 250))) goto lab9;
+		    break;
+		lab9:
+		    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+			if (c < 0) goto lab8;
+			z->c = c; /* gopast, line 53 */
+		    }
+		}
+		goto lab7;
+	    lab8:
+		z->c = c;
+		if (!(in_grouping_U(z, g_v, 97, 250))) goto lab0;
+		{   int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		    if (c < 0) goto lab0;
+		    z->c = c; /* next, line 53 */
+		}
+	    }
+	lab7:
+	    ;
+	}
+    lab1:
+	z->I[0] = z->c; /* setmark pV, line 54 */
+    lab0:
+	z->c = c;
+    }
+    {	int c = z->c; /* do, line 56 */
+	while(1) { /* gopast, line 57 */
+	    if (!(in_grouping_U(z, g_v, 97, 250))) goto lab11;
+	    break;
+	lab11:
+	    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		if (c < 0) goto lab10;
+		z->c = c; /* gopast, line 57 */
+	    }
+	}
+	while(1) { /* gopast, line 57 */
+	    if (!(out_grouping_U(z, g_v, 97, 250))) goto lab12;
+	    break;
+	lab12:
+	    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		if (c < 0) goto lab10;
+		z->c = c; /* gopast, line 57 */
+	    }
+	}
+	z->I[1] = z->c; /* setmark p1, line 57 */
+	while(1) { /* gopast, line 58 */
+	    if (!(in_grouping_U(z, g_v, 97, 250))) goto lab13;
+	    break;
+	lab13:
+	    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		if (c < 0) goto lab10;
+		z->c = c; /* gopast, line 58 */
+	    }
+	}
+	while(1) { /* gopast, line 58 */
+	    if (!(out_grouping_U(z, g_v, 97, 250))) goto lab14;
+	    break;
+	lab14:
+	    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		if (c < 0) goto lab10;
+		z->c = c; /* gopast, line 58 */
+	    }
+	}
+	z->I[2] = z->c; /* setmark p2, line 58 */
+    lab10:
+	z->c = c;
+    }
+    return 1;
+}
+
+static int r_postlude(struct SN_env * z) {
+    int among_var;
+    while(1) { /* repeat, line 62 */
+	int c = z->c;
+	z->bra = z->c; /* [, line 63 */
+	among_var = find_among(z, a_1, 3); /* substring, line 63 */
+	if (!(among_var)) goto lab0;
+	z->ket = z->c; /* ], line 63 */
+	switch(among_var) {
+	    case 0: goto lab0;
+	    case 1:
+		{   int ret;
+		    ret = slice_from_s(z, 2, s_2); /* <-, line 64 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 2:
+		{   int ret;
+		    ret = slice_from_s(z, 2, s_3); /* <-, line 65 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 3:
+		{   int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		    if (c < 0) goto lab0;
+		    z->c = c; /* next, line 66 */
+		}
+		break;
+	}
+	continue;
+    lab0:
+	z->c = c;
+	break;
+    }
+    return 1;
+}
+
+static int r_RV(struct SN_env * z) {
+    if (!(z->I[0] <= z->c)) return 0;
+    return 1;
+}
+
+static int r_R1(struct SN_env * z) {
+    if (!(z->I[1] <= z->c)) return 0;
+    return 1;
+}
+
+static int r_R2(struct SN_env * z) {
+    if (!(z->I[2] <= z->c)) return 0;
+    return 1;
+}
+
+static int r_standard_suffix(struct SN_env * z) {
+    int among_var;
+    z->ket = z->c; /* [, line 77 */
+    among_var = find_among_b(z, a_5, 45); /* substring, line 77 */
+    if (!(among_var)) return 0;
+    z->bra = z->c; /* ], line 77 */
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret = r_R2(z);
+		if (ret == 0) return 0; /* call R2, line 93 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 93 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    {	int ret = r_R2(z);
+		if (ret == 0) return 0; /* call R2, line 98 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_from_s(z, 3, s_4); /* <-, line 98 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 3:
+	    {	int ret = r_R2(z);
+		if (ret == 0) return 0; /* call R2, line 102 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_from_s(z, 1, s_5); /* <-, line 102 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 4:
+	    {	int ret = r_R2(z);
+		if (ret == 0) return 0; /* call R2, line 106 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_from_s(z, 4, s_6); /* <-, line 106 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 5:
+	    {	int ret = r_R1(z);
+		if (ret == 0) return 0; /* call R1, line 110 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 110 */
+		if (ret < 0) return ret;
+	    }
+	    {	int m = z->l - z->c; (void) m; /* try, line 111 */
+		z->ket = z->c; /* [, line 112 */
+		among_var = find_among_b(z, a_2, 4); /* substring, line 112 */
+		if (!(among_var)) { z->c = z->l - m; goto lab0; }
+		z->bra = z->c; /* ], line 112 */
+		{   int ret = r_R2(z);
+		    if (ret == 0) { z->c = z->l - m; goto lab0; } /* call R2, line 112 */
+		    if (ret < 0) return ret;
+		}
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 112 */
+		    if (ret < 0) return ret;
+		}
+		switch(among_var) {
+		    case 0: { z->c = z->l - m; goto lab0; }
+		    case 1:
+			z->ket = z->c; /* [, line 113 */
+			if (!(eq_s_b(z, 2, s_7))) { z->c = z->l - m; goto lab0; }
+			z->bra = z->c; /* ], line 113 */
+			{   int ret = r_R2(z);
+			    if (ret == 0) { z->c = z->l - m; goto lab0; } /* call R2, line 113 */
+			    if (ret < 0) return ret;
+			}
+			{   int ret;
+			    ret = slice_del(z); /* delete, line 113 */
+			    if (ret < 0) return ret;
+			}
+			break;
+		}
+	    lab0:
+		;
+	    }
+	    break;
+	case 6:
+	    {	int ret = r_R2(z);
+		if (ret == 0) return 0; /* call R2, line 122 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 122 */
+		if (ret < 0) return ret;
+	    }
+	    {	int m = z->l - z->c; (void) m; /* try, line 123 */
+		z->ket = z->c; /* [, line 124 */
+		among_var = find_among_b(z, a_3, 3); /* substring, line 124 */
+		if (!(among_var)) { z->c = z->l - m; goto lab1; }
+		z->bra = z->c; /* ], line 124 */
+		switch(among_var) {
+		    case 0: { z->c = z->l - m; goto lab1; }
+		    case 1:
+			{   int ret = r_R2(z);
+			    if (ret == 0) { z->c = z->l - m; goto lab1; } /* call R2, line 127 */
+			    if (ret < 0) return ret;
+			}
+			{   int ret;
+			    ret = slice_del(z); /* delete, line 127 */
+			    if (ret < 0) return ret;
+			}
+			break;
+		}
+	    lab1:
+		;
+	    }
+	    break;
+	case 7:
+	    {	int ret = r_R2(z);
+		if (ret == 0) return 0; /* call R2, line 134 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 134 */
+		if (ret < 0) return ret;
+	    }
+	    {	int m = z->l - z->c; (void) m; /* try, line 135 */
+		z->ket = z->c; /* [, line 136 */
+		among_var = find_among_b(z, a_4, 3); /* substring, line 136 */
+		if (!(among_var)) { z->c = z->l - m; goto lab2; }
+		z->bra = z->c; /* ], line 136 */
+		switch(among_var) {
+		    case 0: { z->c = z->l - m; goto lab2; }
+		    case 1:
+			{   int ret = r_R2(z);
+			    if (ret == 0) { z->c = z->l - m; goto lab2; } /* call R2, line 139 */
+			    if (ret < 0) return ret;
+			}
+			{   int ret;
+			    ret = slice_del(z); /* delete, line 139 */
+			    if (ret < 0) return ret;
+			}
+			break;
+		}
+	    lab2:
+		;
+	    }
+	    break;
+	case 8:
+	    {	int ret = r_R2(z);
+		if (ret == 0) return 0; /* call R2, line 146 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 146 */
+		if (ret < 0) return ret;
+	    }
+	    {	int m = z->l - z->c; (void) m; /* try, line 147 */
+		z->ket = z->c; /* [, line 148 */
+		if (!(eq_s_b(z, 2, s_8))) { z->c = z->l - m; goto lab3; }
+		z->bra = z->c; /* ], line 148 */
+		{   int ret = r_R2(z);
+		    if (ret == 0) { z->c = z->l - m; goto lab3; } /* call R2, line 148 */
+		    if (ret < 0) return ret;
+		}
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 148 */
+		    if (ret < 0) return ret;
+		}
+	    lab3:
+		;
+	    }
+	    break;
+	case 9:
+	    {	int ret = r_RV(z);
+		if (ret == 0) return 0; /* call RV, line 153 */
+		if (ret < 0) return ret;
+	    }
+	    if (!(eq_s_b(z, 1, s_9))) return 0;
+	    {	int ret;
+		ret = slice_from_s(z, 2, s_10); /* <-, line 154 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_verb_suffix(struct SN_env * z) {
+    int among_var;
+    {	int m3; /* setlimit, line 159 */
+	int m = z->l - z->c; (void) m;
+	if (z->c < z->I[0]) return 0;
+	z->c = z->I[0]; /* tomark, line 159 */
+	m3 = z->lb; z->lb = z->c;
+	z->c = z->l - m;
+	z->ket = z->c; /* [, line 160 */
+	among_var = find_among_b(z, a_6, 120); /* substring, line 160 */
+	if (!(among_var)) { z->lb = m3; return 0; }
+	z->bra = z->c; /* ], line 160 */
+	switch(among_var) {
+	    case 0: { z->lb = m3; return 0; }
+	    case 1:
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 179 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	}
+	z->lb = m3;
+    }
+    return 1;
+}
+
+static int r_residual_suffix(struct SN_env * z) {
+    int among_var;
+    z->ket = z->c; /* [, line 184 */
+    among_var = find_among_b(z, a_7, 7); /* substring, line 184 */
+    if (!(among_var)) return 0;
+    z->bra = z->c; /* ], line 184 */
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret = r_RV(z);
+		if (ret == 0) return 0; /* call RV, line 187 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 187 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_residual_form(struct SN_env * z) {
+    int among_var;
+    z->ket = z->c; /* [, line 192 */
+    among_var = find_among_b(z, a_8, 4); /* substring, line 192 */
+    if (!(among_var)) return 0;
+    z->bra = z->c; /* ], line 192 */
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret = r_RV(z);
+		if (ret == 0) return 0; /* call RV, line 194 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 194 */
+		if (ret < 0) return ret;
+	    }
+	    z->ket = z->c; /* [, line 194 */
+	    {	int m = z->l - z->c; (void) m; /* or, line 194 */
+		if (!(eq_s_b(z, 1, s_11))) goto lab1;
+		z->bra = z->c; /* ], line 194 */
+		{   int m_test = z->l - z->c; /* test, line 194 */
+		    if (!(eq_s_b(z, 1, s_12))) goto lab1;
+		    z->c = z->l - m_test;
+		}
+		goto lab0;
+	    lab1:
+		z->c = z->l - m;
+		if (!(eq_s_b(z, 1, s_13))) return 0;
+		z->bra = z->c; /* ], line 195 */
+		{   int m_test = z->l - z->c; /* test, line 195 */
+		    if (!(eq_s_b(z, 1, s_14))) return 0;
+		    z->c = z->l - m_test;
+		}
+	    }
+	lab0:
+	    {	int ret = r_RV(z);
+		if (ret == 0) return 0; /* call RV, line 195 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 195 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    {	int ret;
+		ret = slice_from_s(z, 1, s_15); /* <-, line 196 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+extern int portuguese_UTF_8_stem(struct SN_env * z) {
+    {	int c = z->c; /* do, line 202 */
+	{   int ret = r_prelude(z);
+	    if (ret == 0) goto lab0; /* call prelude, line 202 */
+	    if (ret < 0) return ret;
+	}
+    lab0:
+	z->c = c;
+    }
+    {	int c = z->c; /* do, line 203 */
+	{   int ret = r_mark_regions(z);
+	    if (ret == 0) goto lab1; /* call mark_regions, line 203 */
+	    if (ret < 0) return ret;
+	}
+    lab1:
+	z->c = c;
+    }
+    z->lb = z->c; z->c = z->l; /* backwards, line 204 */
+
+    {	int m = z->l - z->c; (void) m; /* do, line 205 */
+	{   int m = z->l - z->c; (void) m; /* or, line 209 */
+	    {	int m = z->l - z->c; (void) m; /* and, line 207 */
+		{   int m = z->l - z->c; (void) m; /* or, line 206 */
+		    {	int ret = r_standard_suffix(z);
+			if (ret == 0) goto lab6; /* call standard_suffix, line 206 */
+			if (ret < 0) return ret;
+		    }
+		    goto lab5;
+		lab6:
+		    z->c = z->l - m;
+		    {	int ret = r_verb_suffix(z);
+			if (ret == 0) goto lab4; /* call verb_suffix, line 206 */
+			if (ret < 0) return ret;
+		    }
+		}
+	    lab5:
+		z->c = z->l - m;
+		{   int m = z->l - z->c; (void) m; /* do, line 207 */
+		    z->ket = z->c; /* [, line 207 */
+		    if (!(eq_s_b(z, 1, s_16))) goto lab7;
+		    z->bra = z->c; /* ], line 207 */
+		    {	int m_test = z->l - z->c; /* test, line 207 */
+			if (!(eq_s_b(z, 1, s_17))) goto lab7;
+			z->c = z->l - m_test;
+		    }
+		    {	int ret = r_RV(z);
+			if (ret == 0) goto lab7; /* call RV, line 207 */
+			if (ret < 0) return ret;
+		    }
+		    {	int ret;
+			ret = slice_del(z); /* delete, line 207 */
+			if (ret < 0) return ret;
+		    }
+		lab7:
+		    z->c = z->l - m;
+		}
+	    }
+	    goto lab3;
+	lab4:
+	    z->c = z->l - m;
+	    {	int ret = r_residual_suffix(z);
+		if (ret == 0) goto lab2; /* call residual_suffix, line 209 */
+		if (ret < 0) return ret;
+	    }
+	}
+    lab3:
+    lab2:
+	z->c = z->l - m;
+    }
+    {	int m = z->l - z->c; (void) m; /* do, line 211 */
+	{   int ret = r_residual_form(z);
+	    if (ret == 0) goto lab8; /* call residual_form, line 211 */
+	    if (ret < 0) return ret;
+	}
+    lab8:
+	z->c = z->l - m;
+    }
+    z->c = z->lb;
+    {	int c = z->c; /* do, line 213 */
+	{   int ret = r_postlude(z);
+	    if (ret == 0) goto lab9; /* call postlude, line 213 */
+	    if (ret < 0) return ret;
+	}
+    lab9:
+	z->c = c;
+    }
+    return 1;
+}
+
+extern struct SN_env * portuguese_UTF_8_create_env(void) { return SN_create_env(0, 3, 0); }
+
+extern void portuguese_UTF_8_close_env(struct SN_env * z) { SN_close_env(z); }
+

Added: trunk/src/libstemmer/stem_UTF_8_portuguese.h
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/stem_UTF_8_portuguese.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2001, Dr Martin Porter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+/* This file was generated automatically by the Snowball to ANSI C compiler */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern struct SN_env * portuguese_UTF_8_create_env(void);
+extern void portuguese_UTF_8_close_env(struct SN_env * z);
+
+extern int portuguese_UTF_8_stem(struct SN_env * z);
+
+#ifdef __cplusplus
+}
+#endif
+

Added: trunk/src/libstemmer/stem_UTF_8_russian.c
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/stem_UTF_8_russian.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,722 @@
+/*
+ * Copyright (c) 2001, Dr Martin Porter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+/* This file was generated automatically by the Snowball to ANSI C compiler */
+
+#include "header.h"
+
+extern int russian_UTF_8_stem(struct SN_env * z);
+static int r_tidy_up(struct SN_env * z);
+static int r_derivational(struct SN_env * z);
+static int r_noun(struct SN_env * z);
+static int r_verb(struct SN_env * z);
+static int r_reflexive(struct SN_env * z);
+static int r_adjectival(struct SN_env * z);
+static int r_adjective(struct SN_env * z);
+static int r_perfective_gerund(struct SN_env * z);
+static int r_R2(struct SN_env * z);
+static int r_mark_regions(struct SN_env * z);
+
+extern struct SN_env * russian_UTF_8_create_env(void);
+extern void russian_UTF_8_close_env(struct SN_env * z);
+
+static symbol s_0_0[10] = { 0xD0, 0xB2, 0xD1, 0x88, 0xD0, 0xB8, 0xD1, 0x81, 0xD1, 0x8C };
+static symbol s_0_1[12] = { 0xD1, 0x8B, 0xD0, 0xB2, 0xD1, 0x88, 0xD0, 0xB8, 0xD1, 0x81, 0xD1, 0x8C };
+static symbol s_0_2[12] = { 0xD0, 0xB8, 0xD0, 0xB2, 0xD1, 0x88, 0xD0, 0xB8, 0xD1, 0x81, 0xD1, 0x8C };
+static symbol s_0_3[2] = { 0xD0, 0xB2 };
+static symbol s_0_4[4] = { 0xD1, 0x8B, 0xD0, 0xB2 };
+static symbol s_0_5[4] = { 0xD0, 0xB8, 0xD0, 0xB2 };
+static symbol s_0_6[6] = { 0xD0, 0xB2, 0xD1, 0x88, 0xD0, 0xB8 };
+static symbol s_0_7[8] = { 0xD1, 0x8B, 0xD0, 0xB2, 0xD1, 0x88, 0xD0, 0xB8 };
+static symbol s_0_8[8] = { 0xD0, 0xB8, 0xD0, 0xB2, 0xD1, 0x88, 0xD0, 0xB8 };
+
+static struct among a_0[9] =
+{
+/*  0 */ { 10, s_0_0, -1, 1, 0},
+/*  1 */ { 12, s_0_1, 0, 2, 0},
+/*  2 */ { 12, s_0_2, 0, 2, 0},
+/*  3 */ { 2, s_0_3, -1, 1, 0},
+/*  4 */ { 4, s_0_4, 3, 2, 0},
+/*  5 */ { 4, s_0_5, 3, 2, 0},
+/*  6 */ { 6, s_0_6, -1, 1, 0},
+/*  7 */ { 8, s_0_7, 6, 2, 0},
+/*  8 */ { 8, s_0_8, 6, 2, 0}
+};
+
+static symbol s_1_0[6] = { 0xD0, 0xB5, 0xD0, 0xBC, 0xD1, 0x83 };
+static symbol s_1_1[6] = { 0xD0, 0xBE, 0xD0, 0xBC, 0xD1, 0x83 };
+static symbol s_1_2[4] = { 0xD1, 0x8B, 0xD1, 0x85 };
+static symbol s_1_3[4] = { 0xD0, 0xB8, 0xD1, 0x85 };
+static symbol s_1_4[4] = { 0xD1, 0x83, 0xD1, 0x8E };
+static symbol s_1_5[4] = { 0xD1, 0x8E, 0xD1, 0x8E };
+static symbol s_1_6[4] = { 0xD0, 0xB5, 0xD1, 0x8E };
+static symbol s_1_7[4] = { 0xD0, 0xBE, 0xD1, 0x8E };
+static symbol s_1_8[4] = { 0xD1, 0x8F, 0xD1, 0x8F };
+static symbol s_1_9[4] = { 0xD0, 0xB0, 0xD1, 0x8F };
+static symbol s_1_10[4] = { 0xD1, 0x8B, 0xD0, 0xB5 };
+static symbol s_1_11[4] = { 0xD0, 0xB5, 0xD0, 0xB5 };
+static symbol s_1_12[4] = { 0xD0, 0xB8, 0xD0, 0xB5 };
+static symbol s_1_13[4] = { 0xD0, 0xBE, 0xD0, 0xB5 };
+static symbol s_1_14[6] = { 0xD1, 0x8B, 0xD0, 0xBC, 0xD0, 0xB8 };
+static symbol s_1_15[6] = { 0xD0, 0xB8, 0xD0, 0xBC, 0xD0, 0xB8 };
+static symbol s_1_16[4] = { 0xD1, 0x8B, 0xD0, 0xB9 };
+static symbol s_1_17[4] = { 0xD0, 0xB5, 0xD0, 0xB9 };
+static symbol s_1_18[4] = { 0xD0, 0xB8, 0xD0, 0xB9 };
+static symbol s_1_19[4] = { 0xD0, 0xBE, 0xD0, 0xB9 };
+static symbol s_1_20[4] = { 0xD1, 0x8B, 0xD0, 0xBC };
+static symbol s_1_21[4] = { 0xD0, 0xB5, 0xD0, 0xBC };
+static symbol s_1_22[4] = { 0xD0, 0xB8, 0xD0, 0xBC };
+static symbol s_1_23[4] = { 0xD0, 0xBE, 0xD0, 0xBC };
+static symbol s_1_24[6] = { 0xD0, 0xB5, 0xD0, 0xB3, 0xD0, 0xBE };
+static symbol s_1_25[6] = { 0xD0, 0xBE, 0xD0, 0xB3, 0xD0, 0xBE };
+
+static struct among a_1[26] =
+{
+/*  0 */ { 6, s_1_0, -1, 1, 0},
+/*  1 */ { 6, s_1_1, -1, 1, 0},
+/*  2 */ { 4, s_1_2, -1, 1, 0},
+/*  3 */ { 4, s_1_3, -1, 1, 0},
+/*  4 */ { 4, s_1_4, -1, 1, 0},
+/*  5 */ { 4, s_1_5, -1, 1, 0},
+/*  6 */ { 4, s_1_6, -1, 1, 0},
+/*  7 */ { 4, s_1_7, -1, 1, 0},
+/*  8 */ { 4, s_1_8, -1, 1, 0},
+/*  9 */ { 4, s_1_9, -1, 1, 0},
+/* 10 */ { 4, s_1_10, -1, 1, 0},
+/* 11 */ { 4, s_1_11, -1, 1, 0},
+/* 12 */ { 4, s_1_12, -1, 1, 0},
+/* 13 */ { 4, s_1_13, -1, 1, 0},
+/* 14 */ { 6, s_1_14, -1, 1, 0},
+/* 15 */ { 6, s_1_15, -1, 1, 0},
+/* 16 */ { 4, s_1_16, -1, 1, 0},
+/* 17 */ { 4, s_1_17, -1, 1, 0},
+/* 18 */ { 4, s_1_18, -1, 1, 0},
+/* 19 */ { 4, s_1_19, -1, 1, 0},
+/* 20 */ { 4, s_1_20, -1, 1, 0},
+/* 21 */ { 4, s_1_21, -1, 1, 0},
+/* 22 */ { 4, s_1_22, -1, 1, 0},
+/* 23 */ { 4, s_1_23, -1, 1, 0},
+/* 24 */ { 6, s_1_24, -1, 1, 0},
+/* 25 */ { 6, s_1_25, -1, 1, 0}
+};
+
+static symbol s_2_0[4] = { 0xD0, 0xB2, 0xD1, 0x88 };
+static symbol s_2_1[6] = { 0xD1, 0x8B, 0xD0, 0xB2, 0xD1, 0x88 };
+static symbol s_2_2[6] = { 0xD0, 0xB8, 0xD0, 0xB2, 0xD1, 0x88 };
+static symbol s_2_3[2] = { 0xD1, 0x89 };
+static symbol s_2_4[4] = { 0xD1, 0x8E, 0xD1, 0x89 };
+static symbol s_2_5[6] = { 0xD1, 0x83, 0xD1, 0x8E, 0xD1, 0x89 };
+static symbol s_2_6[4] = { 0xD0, 0xB5, 0xD0, 0xBC };
+static symbol s_2_7[4] = { 0xD0, 0xBD, 0xD0, 0xBD };
+
+static struct among a_2[8] =
+{
+/*  0 */ { 4, s_2_0, -1, 1, 0},
+/*  1 */ { 6, s_2_1, 0, 2, 0},
+/*  2 */ { 6, s_2_2, 0, 2, 0},
+/*  3 */ { 2, s_2_3, -1, 1, 0},
+/*  4 */ { 4, s_2_4, 3, 1, 0},
+/*  5 */ { 6, s_2_5, 4, 2, 0},
+/*  6 */ { 4, s_2_6, -1, 1, 0},
+/*  7 */ { 4, s_2_7, -1, 1, 0}
+};
+
+static symbol s_3_0[4] = { 0xD1, 0x81, 0xD1, 0x8C };
+static symbol s_3_1[4] = { 0xD1, 0x81, 0xD1, 0x8F };
+
+static struct among a_3[2] =
+{
+/*  0 */ { 4, s_3_0, -1, 1, 0},
+/*  1 */ { 4, s_3_1, -1, 1, 0}
+};
+
+static symbol s_4_0[4] = { 0xD1, 0x8B, 0xD1, 0x82 };
+static symbol s_4_1[4] = { 0xD1, 0x8E, 0xD1, 0x82 };
+static symbol s_4_2[6] = { 0xD1, 0x83, 0xD1, 0x8E, 0xD1, 0x82 };
+static symbol s_4_3[4] = { 0xD1, 0x8F, 0xD1, 0x82 };
+static symbol s_4_4[4] = { 0xD0, 0xB5, 0xD1, 0x82 };
+static symbol s_4_5[6] = { 0xD1, 0x83, 0xD0, 0xB5, 0xD1, 0x82 };
+static symbol s_4_6[4] = { 0xD0, 0xB8, 0xD1, 0x82 };
+static symbol s_4_7[4] = { 0xD0, 0xBD, 0xD1, 0x8B };
+static symbol s_4_8[6] = { 0xD0, 0xB5, 0xD0, 0xBD, 0xD1, 0x8B };
+static symbol s_4_9[4] = { 0xD1, 0x82, 0xD1, 0x8C };
+static symbol s_4_10[6] = { 0xD1, 0x8B, 0xD1, 0x82, 0xD1, 0x8C };
+static symbol s_4_11[6] = { 0xD0, 0xB8, 0xD1, 0x82, 0xD1, 0x8C };
+static symbol s_4_12[6] = { 0xD0, 0xB5, 0xD1, 0x88, 0xD1, 0x8C };
+static symbol s_4_13[6] = { 0xD0, 0xB8, 0xD1, 0x88, 0xD1, 0x8C };
+static symbol s_4_14[2] = { 0xD1, 0x8E };
+static symbol s_4_15[4] = { 0xD1, 0x83, 0xD1, 0x8E };
+static symbol s_4_16[4] = { 0xD0, 0xBB, 0xD0, 0xB0 };
+static symbol s_4_17[6] = { 0xD1, 0x8B, 0xD0, 0xBB, 0xD0, 0xB0 };
+static symbol s_4_18[6] = { 0xD0, 0xB8, 0xD0, 0xBB, 0xD0, 0xB0 };
+static symbol s_4_19[4] = { 0xD0, 0xBD, 0xD0, 0xB0 };
+static symbol s_4_20[6] = { 0xD0, 0xB5, 0xD0, 0xBD, 0xD0, 0xB0 };
+static symbol s_4_21[6] = { 0xD0, 0xB5, 0xD1, 0x82, 0xD0, 0xB5 };
+static symbol s_4_22[6] = { 0xD0, 0xB8, 0xD1, 0x82, 0xD0, 0xB5 };
+static symbol s_4_23[6] = { 0xD0, 0xB9, 0xD1, 0x82, 0xD0, 0xB5 };
+static symbol s_4_24[8] = { 0xD1, 0x83, 0xD0, 0xB9, 0xD1, 0x82, 0xD0, 0xB5 };
+static symbol s_4_25[8] = { 0xD0, 0xB5, 0xD0, 0xB9, 0xD1, 0x82, 0xD0, 0xB5 };
+static symbol s_4_26[4] = { 0xD0, 0xBB, 0xD0, 0xB8 };
+static symbol s_4_27[6] = { 0xD1, 0x8B, 0xD0, 0xBB, 0xD0, 0xB8 };
+static symbol s_4_28[6] = { 0xD0, 0xB8, 0xD0, 0xBB, 0xD0, 0xB8 };
+static symbol s_4_29[2] = { 0xD0, 0xB9 };
+static symbol s_4_30[4] = { 0xD1, 0x83, 0xD0, 0xB9 };
+static symbol s_4_31[4] = { 0xD0, 0xB5, 0xD0, 0xB9 };
+static symbol s_4_32[2] = { 0xD0, 0xBB };
+static symbol s_4_33[4] = { 0xD1, 0x8B, 0xD0, 0xBB };
+static symbol s_4_34[4] = { 0xD0, 0xB8, 0xD0, 0xBB };
+static symbol s_4_35[4] = { 0xD1, 0x8B, 0xD0, 0xBC };
+static symbol s_4_36[4] = { 0xD0, 0xB5, 0xD0, 0xBC };
+static symbol s_4_37[4] = { 0xD0, 0xB8, 0xD0, 0xBC };
+static symbol s_4_38[2] = { 0xD0, 0xBD };
+static symbol s_4_39[4] = { 0xD0, 0xB5, 0xD0, 0xBD };
+static symbol s_4_40[4] = { 0xD0, 0xBB, 0xD0, 0xBE };
+static symbol s_4_41[6] = { 0xD1, 0x8B, 0xD0, 0xBB, 0xD0, 0xBE };
+static symbol s_4_42[6] = { 0xD0, 0xB8, 0xD0, 0xBB, 0xD0, 0xBE };
+static symbol s_4_43[4] = { 0xD0, 0xBD, 0xD0, 0xBE };
+static symbol s_4_44[6] = { 0xD0, 0xB5, 0xD0, 0xBD, 0xD0, 0xBE };
+static symbol s_4_45[6] = { 0xD0, 0xBD, 0xD0, 0xBD, 0xD0, 0xBE };
+
+static struct among a_4[46] =
+{
+/*  0 */ { 4, s_4_0, -1, 2, 0},
+/*  1 */ { 4, s_4_1, -1, 1, 0},
+/*  2 */ { 6, s_4_2, 1, 2, 0},
+/*  3 */ { 4, s_4_3, -1, 2, 0},
+/*  4 */ { 4, s_4_4, -1, 1, 0},
+/*  5 */ { 6, s_4_5, 4, 2, 0},
+/*  6 */ { 4, s_4_6, -1, 2, 0},
+/*  7 */ { 4, s_4_7, -1, 1, 0},
+/*  8 */ { 6, s_4_8, 7, 2, 0},
+/*  9 */ { 4, s_4_9, -1, 1, 0},
+/* 10 */ { 6, s_4_10, 9, 2, 0},
+/* 11 */ { 6, s_4_11, 9, 2, 0},
+/* 12 */ { 6, s_4_12, -1, 1, 0},
+/* 13 */ { 6, s_4_13, -1, 2, 0},
+/* 14 */ { 2, s_4_14, -1, 2, 0},
+/* 15 */ { 4, s_4_15, 14, 2, 0},
+/* 16 */ { 4, s_4_16, -1, 1, 0},
+/* 17 */ { 6, s_4_17, 16, 2, 0},
+/* 18 */ { 6, s_4_18, 16, 2, 0},
+/* 19 */ { 4, s_4_19, -1, 1, 0},
+/* 20 */ { 6, s_4_20, 19, 2, 0},
+/* 21 */ { 6, s_4_21, -1, 1, 0},
+/* 22 */ { 6, s_4_22, -1, 2, 0},
+/* 23 */ { 6, s_4_23, -1, 1, 0},
+/* 24 */ { 8, s_4_24, 23, 2, 0},
+/* 25 */ { 8, s_4_25, 23, 2, 0},
+/* 26 */ { 4, s_4_26, -1, 1, 0},
+/* 27 */ { 6, s_4_27, 26, 2, 0},
+/* 28 */ { 6, s_4_28, 26, 2, 0},
+/* 29 */ { 2, s_4_29, -1, 1, 0},
+/* 30 */ { 4, s_4_30, 29, 2, 0},
+/* 31 */ { 4, s_4_31, 29, 2, 0},
+/* 32 */ { 2, s_4_32, -1, 1, 0},
+/* 33 */ { 4, s_4_33, 32, 2, 0},
+/* 34 */ { 4, s_4_34, 32, 2, 0},
+/* 35 */ { 4, s_4_35, -1, 2, 0},
+/* 36 */ { 4, s_4_36, -1, 1, 0},
+/* 37 */ { 4, s_4_37, -1, 2, 0},
+/* 38 */ { 2, s_4_38, -1, 1, 0},
+/* 39 */ { 4, s_4_39, 38, 2, 0},
+/* 40 */ { 4, s_4_40, -1, 1, 0},
+/* 41 */ { 6, s_4_41, 40, 2, 0},
+/* 42 */ { 6, s_4_42, 40, 2, 0},
+/* 43 */ { 4, s_4_43, -1, 1, 0},
+/* 44 */ { 6, s_4_44, 43, 2, 0},
+/* 45 */ { 6, s_4_45, 43, 1, 0}
+};
+
+static symbol s_5_0[2] = { 0xD1, 0x83 };
+static symbol s_5_1[4] = { 0xD1, 0x8F, 0xD1, 0x85 };
+static symbol s_5_2[6] = { 0xD0, 0xB8, 0xD1, 0x8F, 0xD1, 0x85 };
+static symbol s_5_3[4] = { 0xD0, 0xB0, 0xD1, 0x85 };
+static symbol s_5_4[2] = { 0xD1, 0x8B };
+static symbol s_5_5[2] = { 0xD1, 0x8C };
+static symbol s_5_6[2] = { 0xD1, 0x8E };
+static symbol s_5_7[4] = { 0xD1, 0x8C, 0xD1, 0x8E };
+static symbol s_5_8[4] = { 0xD0, 0xB8, 0xD1, 0x8E };
+static symbol s_5_9[2] = { 0xD1, 0x8F };
+static symbol s_5_10[4] = { 0xD1, 0x8C, 0xD1, 0x8F };
+static symbol s_5_11[4] = { 0xD0, 0xB8, 0xD1, 0x8F };
+static symbol s_5_12[2] = { 0xD0, 0xB0 };
+static symbol s_5_13[4] = { 0xD0, 0xB5, 0xD0, 0xB2 };
+static symbol s_5_14[4] = { 0xD0, 0xBE, 0xD0, 0xB2 };
+static symbol s_5_15[2] = { 0xD0, 0xB5 };
+static symbol s_5_16[4] = { 0xD1, 0x8C, 0xD0, 0xB5 };
+static symbol s_5_17[4] = { 0xD0, 0xB8, 0xD0, 0xB5 };
+static symbol s_5_18[2] = { 0xD0, 0xB8 };
+static symbol s_5_19[4] = { 0xD0, 0xB5, 0xD0, 0xB8 };
+static symbol s_5_20[4] = { 0xD0, 0xB8, 0xD0, 0xB8 };
+static symbol s_5_21[6] = { 0xD1, 0x8F, 0xD0, 0xBC, 0xD0, 0xB8 };
+static symbol s_5_22[8] = { 0xD0, 0xB8, 0xD1, 0x8F, 0xD0, 0xBC, 0xD0, 0xB8 };
+static symbol s_5_23[6] = { 0xD0, 0xB0, 0xD0, 0xBC, 0xD0, 0xB8 };
+static symbol s_5_24[2] = { 0xD0, 0xB9 };
+static symbol s_5_25[4] = { 0xD0, 0xB5, 0xD0, 0xB9 };
+static symbol s_5_26[6] = { 0xD0, 0xB8, 0xD0, 0xB5, 0xD0, 0xB9 };
+static symbol s_5_27[4] = { 0xD0, 0xB8, 0xD0, 0xB9 };
+static symbol s_5_28[4] = { 0xD0, 0xBE, 0xD0, 0xB9 };
+static symbol s_5_29[4] = { 0xD1, 0x8F, 0xD0, 0xBC };
+static symbol s_5_30[6] = { 0xD0, 0xB8, 0xD1, 0x8F, 0xD0, 0xBC };
+static symbol s_5_31[4] = { 0xD0, 0xB0, 0xD0, 0xBC };
+static symbol s_5_32[4] = { 0xD0, 0xB5, 0xD0, 0xBC };
+static symbol s_5_33[6] = { 0xD0, 0xB8, 0xD0, 0xB5, 0xD0, 0xBC };
+static symbol s_5_34[4] = { 0xD0, 0xBE, 0xD0, 0xBC };
+static symbol s_5_35[2] = { 0xD0, 0xBE };
+
+static struct among a_5[36] =
+{
+/*  0 */ { 2, s_5_0, -1, 1, 0},
+/*  1 */ { 4, s_5_1, -1, 1, 0},
+/*  2 */ { 6, s_5_2, 1, 1, 0},
+/*  3 */ { 4, s_5_3, -1, 1, 0},
+/*  4 */ { 2, s_5_4, -1, 1, 0},
+/*  5 */ { 2, s_5_5, -1, 1, 0},
+/*  6 */ { 2, s_5_6, -1, 1, 0},
+/*  7 */ { 4, s_5_7, 6, 1, 0},
+/*  8 */ { 4, s_5_8, 6, 1, 0},
+/*  9 */ { 2, s_5_9, -1, 1, 0},
+/* 10 */ { 4, s_5_10, 9, 1, 0},
+/* 11 */ { 4, s_5_11, 9, 1, 0},
+/* 12 */ { 2, s_5_12, -1, 1, 0},
+/* 13 */ { 4, s_5_13, -1, 1, 0},
+/* 14 */ { 4, s_5_14, -1, 1, 0},
+/* 15 */ { 2, s_5_15, -1, 1, 0},
+/* 16 */ { 4, s_5_16, 15, 1, 0},
+/* 17 */ { 4, s_5_17, 15, 1, 0},
+/* 18 */ { 2, s_5_18, -1, 1, 0},
+/* 19 */ { 4, s_5_19, 18, 1, 0},
+/* 20 */ { 4, s_5_20, 18, 1, 0},
+/* 21 */ { 6, s_5_21, 18, 1, 0},
+/* 22 */ { 8, s_5_22, 21, 1, 0},
+/* 23 */ { 6, s_5_23, 18, 1, 0},
+/* 24 */ { 2, s_5_24, -1, 1, 0},
+/* 25 */ { 4, s_5_25, 24, 1, 0},
+/* 26 */ { 6, s_5_26, 25, 1, 0},
+/* 27 */ { 4, s_5_27, 24, 1, 0},
+/* 28 */ { 4, s_5_28, 24, 1, 0},
+/* 29 */ { 4, s_5_29, -1, 1, 0},
+/* 30 */ { 6, s_5_30, 29, 1, 0},
+/* 31 */ { 4, s_5_31, -1, 1, 0},
+/* 32 */ { 4, s_5_32, -1, 1, 0},
+/* 33 */ { 6, s_5_33, 32, 1, 0},
+/* 34 */ { 4, s_5_34, -1, 1, 0},
+/* 35 */ { 2, s_5_35, -1, 1, 0}
+};
+
+static symbol s_6_0[6] = { 0xD0, 0xBE, 0xD1, 0x81, 0xD1, 0x82 };
+static symbol s_6_1[8] = { 0xD0, 0xBE, 0xD1, 0x81, 0xD1, 0x82, 0xD1, 0x8C };
+
+static struct among a_6[2] =
+{
+/*  0 */ { 6, s_6_0, -1, 1, 0},
+/*  1 */ { 8, s_6_1, -1, 1, 0}
+};
+
+static symbol s_7_0[6] = { 0xD0, 0xB5, 0xD0, 0xB9, 0xD1, 0x88 };
+static symbol s_7_1[2] = { 0xD1, 0x8C };
+static symbol s_7_2[8] = { 0xD0, 0xB5, 0xD0, 0xB9, 0xD1, 0x88, 0xD0, 0xB5 };
+static symbol s_7_3[2] = { 0xD0, 0xBD };
+
+static struct among a_7[4] =
+{
+/*  0 */ { 6, s_7_0, -1, 1, 0},
+/*  1 */ { 2, s_7_1, -1, 3, 0},
+/*  2 */ { 8, s_7_2, -1, 1, 0},
+/*  3 */ { 2, s_7_3, -1, 2, 0}
+};
+
+static unsigned char g_v[] = { 33, 65, 8, 232 };
+
+static symbol s_0[] = { 0xD0, 0xB0 };
+static symbol s_1[] = { 0xD1, 0x8F };
+static symbol s_2[] = { 0xD0, 0xB0 };
+static symbol s_3[] = { 0xD1, 0x8F };
+static symbol s_4[] = { 0xD0, 0xB0 };
+static symbol s_5[] = { 0xD1, 0x8F };
+static symbol s_6[] = { 0xD0, 0xBD };
+static symbol s_7[] = { 0xD0, 0xBD };
+static symbol s_8[] = { 0xD0, 0xBD };
+static symbol s_9[] = { 0xD0, 0xB8 };
+
+static int r_mark_regions(struct SN_env * z) {
+    z->I[0] = z->l;
+    z->I[1] = z->l;
+    {	int c = z->c; /* do, line 61 */
+	while(1) { /* gopast, line 62 */
+	    if (!(in_grouping_U(z, g_v, 1072, 1103))) goto lab1;
+	    break;
+	lab1:
+	    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		if (c < 0) goto lab0;
+		z->c = c; /* gopast, line 62 */
+	    }
+	}
+	z->I[0] = z->c; /* setmark pV, line 62 */
+	while(1) { /* gopast, line 62 */
+	    if (!(out_grouping_U(z, g_v, 1072, 1103))) goto lab2;
+	    break;
+	lab2:
+	    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		if (c < 0) goto lab0;
+		z->c = c; /* gopast, line 62 */
+	    }
+	}
+	while(1) { /* gopast, line 63 */
+	    if (!(in_grouping_U(z, g_v, 1072, 1103))) goto lab3;
+	    break;
+	lab3:
+	    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		if (c < 0) goto lab0;
+		z->c = c; /* gopast, line 63 */
+	    }
+	}
+	while(1) { /* gopast, line 63 */
+	    if (!(out_grouping_U(z, g_v, 1072, 1103))) goto lab4;
+	    break;
+	lab4:
+	    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		if (c < 0) goto lab0;
+		z->c = c; /* gopast, line 63 */
+	    }
+	}
+	z->I[1] = z->c; /* setmark p2, line 63 */
+    lab0:
+	z->c = c;
+    }
+    return 1;
+}
+
+static int r_R2(struct SN_env * z) {
+    if (!(z->I[1] <= z->c)) return 0;
+    return 1;
+}
+
+static int r_perfective_gerund(struct SN_env * z) {
+    int among_var;
+    z->ket = z->c; /* [, line 72 */
+    among_var = find_among_b(z, a_0, 9); /* substring, line 72 */
+    if (!(among_var)) return 0;
+    z->bra = z->c; /* ], line 72 */
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int m = z->l - z->c; (void) m; /* or, line 76 */
+		if (!(eq_s_b(z, 2, s_0))) goto lab1;
+		goto lab0;
+	    lab1:
+		z->c = z->l - m;
+		if (!(eq_s_b(z, 2, s_1))) return 0;
+	    }
+	lab0:
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 76 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 83 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_adjective(struct SN_env * z) {
+    int among_var;
+    z->ket = z->c; /* [, line 88 */
+    among_var = find_among_b(z, a_1, 26); /* substring, line 88 */
+    if (!(among_var)) return 0;
+    z->bra = z->c; /* ], line 88 */
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 97 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_adjectival(struct SN_env * z) {
+    int among_var;
+    {	int ret = r_adjective(z);
+	if (ret == 0) return 0; /* call adjective, line 102 */
+	if (ret < 0) return ret;
+    }
+    {	int m = z->l - z->c; (void) m; /* try, line 109 */
+	z->ket = z->c; /* [, line 110 */
+	among_var = find_among_b(z, a_2, 8); /* substring, line 110 */
+	if (!(among_var)) { z->c = z->l - m; goto lab0; }
+	z->bra = z->c; /* ], line 110 */
+	switch(among_var) {
+	    case 0: { z->c = z->l - m; goto lab0; }
+	    case 1:
+		{   int m = z->l - z->c; (void) m; /* or, line 115 */
+		    if (!(eq_s_b(z, 2, s_2))) goto lab2;
+		    goto lab1;
+		lab2:
+		    z->c = z->l - m;
+		    if (!(eq_s_b(z, 2, s_3))) { z->c = z->l - m; goto lab0; }
+		}
+	    lab1:
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 115 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 2:
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 122 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	}
+    lab0:
+	;
+    }
+    return 1;
+}
+
+static int r_reflexive(struct SN_env * z) {
+    int among_var;
+    z->ket = z->c; /* [, line 129 */
+    among_var = find_among_b(z, a_3, 2); /* substring, line 129 */
+    if (!(among_var)) return 0;
+    z->bra = z->c; /* ], line 129 */
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 132 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_verb(struct SN_env * z) {
+    int among_var;
+    z->ket = z->c; /* [, line 137 */
+    among_var = find_among_b(z, a_4, 46); /* substring, line 137 */
+    if (!(among_var)) return 0;
+    z->bra = z->c; /* ], line 137 */
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int m = z->l - z->c; (void) m; /* or, line 143 */
+		if (!(eq_s_b(z, 2, s_4))) goto lab1;
+		goto lab0;
+	    lab1:
+		z->c = z->l - m;
+		if (!(eq_s_b(z, 2, s_5))) return 0;
+	    }
+	lab0:
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 143 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 151 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_noun(struct SN_env * z) {
+    int among_var;
+    z->ket = z->c; /* [, line 160 */
+    among_var = find_among_b(z, a_5, 36); /* substring, line 160 */
+    if (!(among_var)) return 0;
+    z->bra = z->c; /* ], line 160 */
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 167 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_derivational(struct SN_env * z) {
+    int among_var;
+    z->ket = z->c; /* [, line 176 */
+    among_var = find_among_b(z, a_6, 2); /* substring, line 176 */
+    if (!(among_var)) return 0;
+    z->bra = z->c; /* ], line 176 */
+    {	int ret = r_R2(z);
+	if (ret == 0) return 0; /* call R2, line 176 */
+	if (ret < 0) return ret;
+    }
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 179 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_tidy_up(struct SN_env * z) {
+    int among_var;
+    z->ket = z->c; /* [, line 184 */
+    among_var = find_among_b(z, a_7, 4); /* substring, line 184 */
+    if (!(among_var)) return 0;
+    z->bra = z->c; /* ], line 184 */
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 188 */
+		if (ret < 0) return ret;
+	    }
+	    z->ket = z->c; /* [, line 189 */
+	    if (!(eq_s_b(z, 2, s_6))) return 0;
+	    z->bra = z->c; /* ], line 189 */
+	    if (!(eq_s_b(z, 2, s_7))) return 0;
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 189 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    if (!(eq_s_b(z, 2, s_8))) return 0;
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 192 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 3:
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 194 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+extern int russian_UTF_8_stem(struct SN_env * z) {
+    {	int c = z->c; /* do, line 201 */
+	{   int ret = r_mark_regions(z);
+	    if (ret == 0) goto lab0; /* call mark_regions, line 201 */
+	    if (ret < 0) return ret;
+	}
+    lab0:
+	z->c = c;
+    }
+    z->lb = z->c; z->c = z->l; /* backwards, line 202 */
+
+    {	int m3; /* setlimit, line 202 */
+	int m = z->l - z->c; (void) m;
+	if (z->c < z->I[0]) return 0;
+	z->c = z->I[0]; /* tomark, line 202 */
+	m3 = z->lb; z->lb = z->c;
+	z->c = z->l - m;
+	{   int m = z->l - z->c; (void) m; /* do, line 203 */
+	    {	int m = z->l - z->c; (void) m; /* or, line 204 */
+		{   int ret = r_perfective_gerund(z);
+		    if (ret == 0) goto lab3; /* call perfective_gerund, line 204 */
+		    if (ret < 0) return ret;
+		}
+		goto lab2;
+	    lab3:
+		z->c = z->l - m;
+		{   int m = z->l - z->c; (void) m; /* try, line 205 */
+		    {	int ret = r_reflexive(z);
+			if (ret == 0) { z->c = z->l - m; goto lab4; } /* call reflexive, line 205 */
+			if (ret < 0) return ret;
+		    }
+		lab4:
+		    ;
+		}
+		{   int m = z->l - z->c; (void) m; /* or, line 206 */
+		    {	int ret = r_adjectival(z);
+			if (ret == 0) goto lab6; /* call adjectival, line 206 */
+			if (ret < 0) return ret;
+		    }
+		    goto lab5;
+		lab6:
+		    z->c = z->l - m;
+		    {	int ret = r_verb(z);
+			if (ret == 0) goto lab7; /* call verb, line 206 */
+			if (ret < 0) return ret;
+		    }
+		    goto lab5;
+		lab7:
+		    z->c = z->l - m;
+		    {	int ret = r_noun(z);
+			if (ret == 0) goto lab1; /* call noun, line 206 */
+			if (ret < 0) return ret;
+		    }
+		}
+	    lab5:
+		;
+	    }
+	lab2:
+	lab1:
+	    z->c = z->l - m;
+	}
+	{   int m = z->l - z->c; (void) m; /* try, line 209 */
+	    z->ket = z->c; /* [, line 209 */
+	    if (!(eq_s_b(z, 2, s_9))) { z->c = z->l - m; goto lab8; }
+	    z->bra = z->c; /* ], line 209 */
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 209 */
+		if (ret < 0) return ret;
+	    }
+	lab8:
+	    ;
+	}
+	{   int m = z->l - z->c; (void) m; /* do, line 212 */
+	    {	int ret = r_derivational(z);
+		if (ret == 0) goto lab9; /* call derivational, line 212 */
+		if (ret < 0) return ret;
+	    }
+	lab9:
+	    z->c = z->l - m;
+	}
+	{   int m = z->l - z->c; (void) m; /* do, line 213 */
+	    {	int ret = r_tidy_up(z);
+		if (ret == 0) goto lab10; /* call tidy_up, line 213 */
+		if (ret < 0) return ret;
+	    }
+	lab10:
+	    z->c = z->l - m;
+	}
+	z->lb = m3;
+    }
+    z->c = z->lb;
+    return 1;
+}
+
+extern struct SN_env * russian_UTF_8_create_env(void) { return SN_create_env(0, 2, 0); }
+
+extern void russian_UTF_8_close_env(struct SN_env * z) { SN_close_env(z); }
+

Added: trunk/src/libstemmer/stem_UTF_8_russian.h
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/stem_UTF_8_russian.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2001, Dr Martin Porter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+/* This file was generated automatically by the Snowball to ANSI C compiler */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern struct SN_env * russian_UTF_8_create_env(void);
+extern void russian_UTF_8_close_env(struct SN_env * z);
+
+extern int russian_UTF_8_stem(struct SN_env * z);
+
+#ifdef __cplusplus
+}
+#endif
+

Added: trunk/src/libstemmer/stem_UTF_8_spanish.c
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/stem_UTF_8_spanish.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,1150 @@
+/*
+ * Copyright (c) 2001, Dr Martin Porter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+/* This file was generated automatically by the Snowball to ANSI C compiler */
+
+#include "header.h"
+
+extern int spanish_UTF_8_stem(struct SN_env * z);
+static int r_residual_suffix(struct SN_env * z);
+static int r_verb_suffix(struct SN_env * z);
+static int r_y_verb_suffix(struct SN_env * z);
+static int r_standard_suffix(struct SN_env * z);
+static int r_attached_pronoun(struct SN_env * z);
+static int r_R2(struct SN_env * z);
+static int r_R1(struct SN_env * z);
+static int r_RV(struct SN_env * z);
+static int r_mark_regions(struct SN_env * z);
+static int r_postlude(struct SN_env * z);
+
+extern struct SN_env * spanish_UTF_8_create_env(void);
+extern void spanish_UTF_8_close_env(struct SN_env * z);
+
+static symbol s_0_1[2] = { 0xC3, 0xA1 };
+static symbol s_0_2[2] = { 0xC3, 0xA9 };
+static symbol s_0_3[2] = { 0xC3, 0xAD };
+static symbol s_0_4[2] = { 0xC3, 0xB3 };
+static symbol s_0_5[2] = { 0xC3, 0xBA };
+
+static struct among a_0[6] =
+{
+/*  0 */ { 0, 0, -1, 6, 0},
+/*  1 */ { 2, s_0_1, 0, 1, 0},
+/*  2 */ { 2, s_0_2, 0, 2, 0},
+/*  3 */ { 2, s_0_3, 0, 3, 0},
+/*  4 */ { 2, s_0_4, 0, 4, 0},
+/*  5 */ { 2, s_0_5, 0, 5, 0}
+};
+
+static symbol s_1_0[2] = { 'l', 'a' };
+static symbol s_1_1[4] = { 's', 'e', 'l', 'a' };
+static symbol s_1_2[2] = { 'l', 'e' };
+static symbol s_1_3[2] = { 'm', 'e' };
+static symbol s_1_4[2] = { 's', 'e' };
+static symbol s_1_5[2] = { 'l', 'o' };
+static symbol s_1_6[4] = { 's', 'e', 'l', 'o' };
+static symbol s_1_7[3] = { 'l', 'a', 's' };
+static symbol s_1_8[5] = { 's', 'e', 'l', 'a', 's' };
+static symbol s_1_9[3] = { 'l', 'e', 's' };
+static symbol s_1_10[3] = { 'l', 'o', 's' };
+static symbol s_1_11[5] = { 's', 'e', 'l', 'o', 's' };
+static symbol s_1_12[3] = { 'n', 'o', 's' };
+
+static struct among a_1[13] =
+{
+/*  0 */ { 2, s_1_0, -1, -1, 0},
+/*  1 */ { 4, s_1_1, 0, -1, 0},
+/*  2 */ { 2, s_1_2, -1, -1, 0},
+/*  3 */ { 2, s_1_3, -1, -1, 0},
+/*  4 */ { 2, s_1_4, -1, -1, 0},
+/*  5 */ { 2, s_1_5, -1, -1, 0},
+/*  6 */ { 4, s_1_6, 5, -1, 0},
+/*  7 */ { 3, s_1_7, -1, -1, 0},
+/*  8 */ { 5, s_1_8, 7, -1, 0},
+/*  9 */ { 3, s_1_9, -1, -1, 0},
+/* 10 */ { 3, s_1_10, -1, -1, 0},
+/* 11 */ { 5, s_1_11, 10, -1, 0},
+/* 12 */ { 3, s_1_12, -1, -1, 0}
+};
+
+static symbol s_2_0[4] = { 'a', 'n', 'd', 'o' };
+static symbol s_2_1[5] = { 'i', 'e', 'n', 'd', 'o' };
+static symbol s_2_2[5] = { 'y', 'e', 'n', 'd', 'o' };
+static symbol s_2_3[5] = { 0xC3, 0xA1, 'n', 'd', 'o' };
+static symbol s_2_4[6] = { 'i', 0xC3, 0xA9, 'n', 'd', 'o' };
+static symbol s_2_5[2] = { 'a', 'r' };
+static symbol s_2_6[2] = { 'e', 'r' };
+static symbol s_2_7[2] = { 'i', 'r' };
+static symbol s_2_8[3] = { 0xC3, 0xA1, 'r' };
+static symbol s_2_9[3] = { 0xC3, 0xA9, 'r' };
+static symbol s_2_10[3] = { 0xC3, 0xAD, 'r' };
+
+static struct among a_2[11] =
+{
+/*  0 */ { 4, s_2_0, -1, 6, 0},
+/*  1 */ { 5, s_2_1, -1, 6, 0},
+/*  2 */ { 5, s_2_2, -1, 7, 0},
+/*  3 */ { 5, s_2_3, -1, 2, 0},
+/*  4 */ { 6, s_2_4, -1, 1, 0},
+/*  5 */ { 2, s_2_5, -1, 6, 0},
+/*  6 */ { 2, s_2_6, -1, 6, 0},
+/*  7 */ { 2, s_2_7, -1, 6, 0},
+/*  8 */ { 3, s_2_8, -1, 3, 0},
+/*  9 */ { 3, s_2_9, -1, 4, 0},
+/* 10 */ { 3, s_2_10, -1, 5, 0}
+};
+
+static symbol s_3_0[2] = { 'i', 'c' };
+static symbol s_3_1[2] = { 'a', 'd' };
+static symbol s_3_2[2] = { 'o', 's' };
+static symbol s_3_3[2] = { 'i', 'v' };
+
+static struct among a_3[4] =
+{
+/*  0 */ { 2, s_3_0, -1, -1, 0},
+/*  1 */ { 2, s_3_1, -1, -1, 0},
+/*  2 */ { 2, s_3_2, -1, -1, 0},
+/*  3 */ { 2, s_3_3, -1, 1, 0}
+};
+
+static symbol s_4_0[4] = { 'a', 'b', 'l', 'e' };
+static symbol s_4_1[4] = { 'i', 'b', 'l', 'e' };
+static symbol s_4_2[4] = { 'a', 'n', 't', 'e' };
+
+static struct among a_4[3] =
+{
+/*  0 */ { 4, s_4_0, -1, 1, 0},
+/*  1 */ { 4, s_4_1, -1, 1, 0},
+/*  2 */ { 4, s_4_2, -1, 1, 0}
+};
+
+static symbol s_5_0[2] = { 'i', 'c' };
+static symbol s_5_1[4] = { 'a', 'b', 'i', 'l' };
+static symbol s_5_2[2] = { 'i', 'v' };
+
+static struct among a_5[3] =
+{
+/*  0 */ { 2, s_5_0, -1, 1, 0},
+/*  1 */ { 4, s_5_1, -1, 1, 0},
+/*  2 */ { 2, s_5_2, -1, 1, 0}
+};
+
+static symbol s_6_0[3] = { 'i', 'c', 'a' };
+static symbol s_6_1[5] = { 'a', 'n', 'c', 'i', 'a' };
+static symbol s_6_2[5] = { 'e', 'n', 'c', 'i', 'a' };
+static symbol s_6_3[5] = { 'a', 'd', 'o', 'r', 'a' };
+static symbol s_6_4[3] = { 'o', 's', 'a' };
+static symbol s_6_5[4] = { 'i', 's', 't', 'a' };
+static symbol s_6_6[3] = { 'i', 'v', 'a' };
+static symbol s_6_7[4] = { 'a', 'n', 'z', 'a' };
+static symbol s_6_8[6] = { 'l', 'o', 'g', 0xC3, 0xAD, 'a' };
+static symbol s_6_9[4] = { 'i', 'd', 'a', 'd' };
+static symbol s_6_10[4] = { 'a', 'b', 'l', 'e' };
+static symbol s_6_11[4] = { 'i', 'b', 'l', 'e' };
+static symbol s_6_12[4] = { 'a', 'n', 't', 'e' };
+static symbol s_6_13[5] = { 'm', 'e', 'n', 't', 'e' };
+static symbol s_6_14[6] = { 'a', 'm', 'e', 'n', 't', 'e' };
+static symbol s_6_15[6] = { 'a', 'c', 'i', 0xC3, 0xB3, 'n' };
+static symbol s_6_16[6] = { 'u', 'c', 'i', 0xC3, 0xB3, 'n' };
+static symbol s_6_17[3] = { 'i', 'c', 'o' };
+static symbol s_6_18[4] = { 'i', 's', 'm', 'o' };
+static symbol s_6_19[3] = { 'o', 's', 'o' };
+static symbol s_6_20[7] = { 'a', 'm', 'i', 'e', 'n', 't', 'o' };
+static symbol s_6_21[7] = { 'i', 'm', 'i', 'e', 'n', 't', 'o' };
+static symbol s_6_22[3] = { 'i', 'v', 'o' };
+static symbol s_6_23[4] = { 'a', 'd', 'o', 'r' };
+static symbol s_6_24[4] = { 'i', 'c', 'a', 's' };
+static symbol s_6_25[6] = { 'a', 'n', 'c', 'i', 'a', 's' };
+static symbol s_6_26[6] = { 'e', 'n', 'c', 'i', 'a', 's' };
+static symbol s_6_27[6] = { 'a', 'd', 'o', 'r', 'a', 's' };
+static symbol s_6_28[4] = { 'o', 's', 'a', 's' };
+static symbol s_6_29[5] = { 'i', 's', 't', 'a', 's' };
+static symbol s_6_30[4] = { 'i', 'v', 'a', 's' };
+static symbol s_6_31[5] = { 'a', 'n', 'z', 'a', 's' };
+static symbol s_6_32[7] = { 'l', 'o', 'g', 0xC3, 0xAD, 'a', 's' };
+static symbol s_6_33[6] = { 'i', 'd', 'a', 'd', 'e', 's' };
+static symbol s_6_34[5] = { 'a', 'b', 'l', 'e', 's' };
+static symbol s_6_35[5] = { 'i', 'b', 'l', 'e', 's' };
+static symbol s_6_36[7] = { 'a', 'c', 'i', 'o', 'n', 'e', 's' };
+static symbol s_6_37[7] = { 'u', 'c', 'i', 'o', 'n', 'e', 's' };
+static symbol s_6_38[6] = { 'a', 'd', 'o', 'r', 'e', 's' };
+static symbol s_6_39[5] = { 'a', 'n', 't', 'e', 's' };
+static symbol s_6_40[4] = { 'i', 'c', 'o', 's' };
+static symbol s_6_41[5] = { 'i', 's', 'm', 'o', 's' };
+static symbol s_6_42[4] = { 'o', 's', 'o', 's' };
+static symbol s_6_43[8] = { 'a', 'm', 'i', 'e', 'n', 't', 'o', 's' };
+static symbol s_6_44[8] = { 'i', 'm', 'i', 'e', 'n', 't', 'o', 's' };
+static symbol s_6_45[4] = { 'i', 'v', 'o', 's' };
+
+static struct among a_6[46] =
+{
+/*  0 */ { 3, s_6_0, -1, 1, 0},
+/*  1 */ { 5, s_6_1, -1, 2, 0},
+/*  2 */ { 5, s_6_2, -1, 5, 0},
+/*  3 */ { 5, s_6_3, -1, 2, 0},
+/*  4 */ { 3, s_6_4, -1, 1, 0},
+/*  5 */ { 4, s_6_5, -1, 1, 0},
+/*  6 */ { 3, s_6_6, -1, 9, 0},
+/*  7 */ { 4, s_6_7, -1, 1, 0},
+/*  8 */ { 6, s_6_8, -1, 3, 0},
+/*  9 */ { 4, s_6_9, -1, 8, 0},
+/* 10 */ { 4, s_6_10, -1, 1, 0},
+/* 11 */ { 4, s_6_11, -1, 1, 0},
+/* 12 */ { 4, s_6_12, -1, 2, 0},
+/* 13 */ { 5, s_6_13, -1, 7, 0},
+/* 14 */ { 6, s_6_14, 13, 6, 0},
+/* 15 */ { 6, s_6_15, -1, 2, 0},
+/* 16 */ { 6, s_6_16, -1, 4, 0},
+/* 17 */ { 3, s_6_17, -1, 1, 0},
+/* 18 */ { 4, s_6_18, -1, 1, 0},
+/* 19 */ { 3, s_6_19, -1, 1, 0},
+/* 20 */ { 7, s_6_20, -1, 1, 0},
+/* 21 */ { 7, s_6_21, -1, 1, 0},
+/* 22 */ { 3, s_6_22, -1, 9, 0},
+/* 23 */ { 4, s_6_23, -1, 2, 0},
+/* 24 */ { 4, s_6_24, -1, 1, 0},
+/* 25 */ { 6, s_6_25, -1, 2, 0},
+/* 26 */ { 6, s_6_26, -1, 5, 0},
+/* 27 */ { 6, s_6_27, -1, 2, 0},
+/* 28 */ { 4, s_6_28, -1, 1, 0},
+/* 29 */ { 5, s_6_29, -1, 1, 0},
+/* 30 */ { 4, s_6_30, -1, 9, 0},
+/* 31 */ { 5, s_6_31, -1, 1, 0},
+/* 32 */ { 7, s_6_32, -1, 3, 0},
+/* 33 */ { 6, s_6_33, -1, 8, 0},
+/* 34 */ { 5, s_6_34, -1, 1, 0},
+/* 35 */ { 5, s_6_35, -1, 1, 0},
+/* 36 */ { 7, s_6_36, -1, 2, 0},
+/* 37 */ { 7, s_6_37, -1, 4, 0},
+/* 38 */ { 6, s_6_38, -1, 2, 0},
+/* 39 */ { 5, s_6_39, -1, 2, 0},
+/* 40 */ { 4, s_6_40, -1, 1, 0},
+/* 41 */ { 5, s_6_41, -1, 1, 0},
+/* 42 */ { 4, s_6_42, -1, 1, 0},
+/* 43 */ { 8, s_6_43, -1, 1, 0},
+/* 44 */ { 8, s_6_44, -1, 1, 0},
+/* 45 */ { 4, s_6_45, -1, 9, 0}
+};
+
+static symbol s_7_0[2] = { 'y', 'a' };
+static symbol s_7_1[2] = { 'y', 'e' };
+static symbol s_7_2[3] = { 'y', 'a', 'n' };
+static symbol s_7_3[3] = { 'y', 'e', 'n' };
+static symbol s_7_4[5] = { 'y', 'e', 'r', 'o', 'n' };
+static symbol s_7_5[5] = { 'y', 'e', 'n', 'd', 'o' };
+static symbol s_7_6[2] = { 'y', 'o' };
+static symbol s_7_7[3] = { 'y', 'a', 's' };
+static symbol s_7_8[3] = { 'y', 'e', 's' };
+static symbol s_7_9[4] = { 'y', 'a', 'i', 's' };
+static symbol s_7_10[5] = { 'y', 'a', 'm', 'o', 's' };
+static symbol s_7_11[3] = { 'y', 0xC3, 0xB3 };
+
+static struct among a_7[12] =
+{
+/*  0 */ { 2, s_7_0, -1, 1, 0},
+/*  1 */ { 2, s_7_1, -1, 1, 0},
+/*  2 */ { 3, s_7_2, -1, 1, 0},
+/*  3 */ { 3, s_7_3, -1, 1, 0},
+/*  4 */ { 5, s_7_4, -1, 1, 0},
+/*  5 */ { 5, s_7_5, -1, 1, 0},
+/*  6 */ { 2, s_7_6, -1, 1, 0},
+/*  7 */ { 3, s_7_7, -1, 1, 0},
+/*  8 */ { 3, s_7_8, -1, 1, 0},
+/*  9 */ { 4, s_7_9, -1, 1, 0},
+/* 10 */ { 5, s_7_10, -1, 1, 0},
+/* 11 */ { 3, s_7_11, -1, 1, 0}
+};
+
+static symbol s_8_0[3] = { 'a', 'b', 'a' };
+static symbol s_8_1[3] = { 'a', 'd', 'a' };
+static symbol s_8_2[3] = { 'i', 'd', 'a' };
+static symbol s_8_3[3] = { 'a', 'r', 'a' };
+static symbol s_8_4[4] = { 'i', 'e', 'r', 'a' };
+static symbol s_8_5[3] = { 0xC3, 0xAD, 'a' };
+static symbol s_8_6[5] = { 'a', 'r', 0xC3, 0xAD, 'a' };
+static symbol s_8_7[5] = { 'e', 'r', 0xC3, 0xAD, 'a' };
+static symbol s_8_8[5] = { 'i', 'r', 0xC3, 0xAD, 'a' };
+static symbol s_8_9[2] = { 'a', 'd' };
+static symbol s_8_10[2] = { 'e', 'd' };
+static symbol s_8_11[2] = { 'i', 'd' };
+static symbol s_8_12[3] = { 'a', 's', 'e' };
+static symbol s_8_13[4] = { 'i', 'e', 's', 'e' };
+static symbol s_8_14[4] = { 'a', 's', 't', 'e' };
+static symbol s_8_15[4] = { 'i', 's', 't', 'e' };
+static symbol s_8_16[2] = { 'a', 'n' };
+static symbol s_8_17[4] = { 'a', 'b', 'a', 'n' };
+static symbol s_8_18[4] = { 'a', 'r', 'a', 'n' };
+static symbol s_8_19[5] = { 'i', 'e', 'r', 'a', 'n' };
+static symbol s_8_20[4] = { 0xC3, 0xAD, 'a', 'n' };
+static symbol s_8_21[6] = { 'a', 'r', 0xC3, 0xAD, 'a', 'n' };
+static symbol s_8_22[6] = { 'e', 'r', 0xC3, 0xAD, 'a', 'n' };
+static symbol s_8_23[6] = { 'i', 'r', 0xC3, 0xAD, 'a', 'n' };
+static symbol s_8_24[2] = { 'e', 'n' };
+static symbol s_8_25[4] = { 'a', 's', 'e', 'n' };
+static symbol s_8_26[5] = { 'i', 'e', 's', 'e', 'n' };
+static symbol s_8_27[4] = { 'a', 'r', 'o', 'n' };
+static symbol s_8_28[5] = { 'i', 'e', 'r', 'o', 'n' };
+static symbol s_8_29[5] = { 'a', 'r', 0xC3, 0xA1, 'n' };
+static symbol s_8_30[5] = { 'e', 'r', 0xC3, 0xA1, 'n' };
+static symbol s_8_31[5] = { 'i', 'r', 0xC3, 0xA1, 'n' };
+static symbol s_8_32[3] = { 'a', 'd', 'o' };
+static symbol s_8_33[3] = { 'i', 'd', 'o' };
+static symbol s_8_34[4] = { 'a', 'n', 'd', 'o' };
+static symbol s_8_35[5] = { 'i', 'e', 'n', 'd', 'o' };
+static symbol s_8_36[2] = { 'a', 'r' };
+static symbol s_8_37[2] = { 'e', 'r' };
+static symbol s_8_38[2] = { 'i', 'r' };
+static symbol s_8_39[2] = { 'a', 's' };
+static symbol s_8_40[4] = { 'a', 'b', 'a', 's' };
+static symbol s_8_41[4] = { 'a', 'd', 'a', 's' };
+static symbol s_8_42[4] = { 'i', 'd', 'a', 's' };
+static symbol s_8_43[4] = { 'a', 'r', 'a', 's' };
+static symbol s_8_44[5] = { 'i', 'e', 'r', 'a', 's' };
+static symbol s_8_45[4] = { 0xC3, 0xAD, 'a', 's' };
+static symbol s_8_46[6] = { 'a', 'r', 0xC3, 0xAD, 'a', 's' };
+static symbol s_8_47[6] = { 'e', 'r', 0xC3, 0xAD, 'a', 's' };
+static symbol s_8_48[6] = { 'i', 'r', 0xC3, 0xAD, 'a', 's' };
+static symbol s_8_49[2] = { 'e', 's' };
+static symbol s_8_50[4] = { 'a', 's', 'e', 's' };
+static symbol s_8_51[5] = { 'i', 'e', 's', 'e', 's' };
+static symbol s_8_52[5] = { 'a', 'b', 'a', 'i', 's' };
+static symbol s_8_53[5] = { 'a', 'r', 'a', 'i', 's' };
+static symbol s_8_54[6] = { 'i', 'e', 'r', 'a', 'i', 's' };
+static symbol s_8_55[5] = { 0xC3, 0xAD, 'a', 'i', 's' };
+static symbol s_8_56[7] = { 'a', 'r', 0xC3, 0xAD, 'a', 'i', 's' };
+static symbol s_8_57[7] = { 'e', 'r', 0xC3, 0xAD, 'a', 'i', 's' };
+static symbol s_8_58[7] = { 'i', 'r', 0xC3, 0xAD, 'a', 'i', 's' };
+static symbol s_8_59[5] = { 'a', 's', 'e', 'i', 's' };
+static symbol s_8_60[6] = { 'i', 'e', 's', 'e', 'i', 's' };
+static symbol s_8_61[6] = { 'a', 's', 't', 'e', 'i', 's' };
+static symbol s_8_62[6] = { 'i', 's', 't', 'e', 'i', 's' };
+static symbol s_8_63[4] = { 0xC3, 0xA1, 'i', 's' };
+static symbol s_8_64[4] = { 0xC3, 0xA9, 'i', 's' };
+static symbol s_8_65[6] = { 'a', 'r', 0xC3, 0xA9, 'i', 's' };
+static symbol s_8_66[6] = { 'e', 'r', 0xC3, 0xA9, 'i', 's' };
+static symbol s_8_67[6] = { 'i', 'r', 0xC3, 0xA9, 'i', 's' };
+static symbol s_8_68[4] = { 'a', 'd', 'o', 's' };
+static symbol s_8_69[4] = { 'i', 'd', 'o', 's' };
+static symbol s_8_70[4] = { 'a', 'm', 'o', 's' };
+static symbol s_8_71[7] = { 0xC3, 0xA1, 'b', 'a', 'm', 'o', 's' };
+static symbol s_8_72[7] = { 0xC3, 0xA1, 'r', 'a', 'm', 'o', 's' };
+static symbol s_8_73[8] = { 'i', 0xC3, 0xA9, 'r', 'a', 'm', 'o', 's' };
+static symbol s_8_74[6] = { 0xC3, 0xAD, 'a', 'm', 'o', 's' };
+static symbol s_8_75[8] = { 'a', 'r', 0xC3, 0xAD, 'a', 'm', 'o', 's' };
+static symbol s_8_76[8] = { 'e', 'r', 0xC3, 0xAD, 'a', 'm', 'o', 's' };
+static symbol s_8_77[8] = { 'i', 'r', 0xC3, 0xAD, 'a', 'm', 'o', 's' };
+static symbol s_8_78[4] = { 'e', 'm', 'o', 's' };
+static symbol s_8_79[6] = { 'a', 'r', 'e', 'm', 'o', 's' };
+static symbol s_8_80[6] = { 'e', 'r', 'e', 'm', 'o', 's' };
+static symbol s_8_81[6] = { 'i', 'r', 'e', 'm', 'o', 's' };
+static symbol s_8_82[7] = { 0xC3, 0xA1, 's', 'e', 'm', 'o', 's' };
+static symbol s_8_83[8] = { 'i', 0xC3, 0xA9, 's', 'e', 'm', 'o', 's' };
+static symbol s_8_84[4] = { 'i', 'm', 'o', 's' };
+static symbol s_8_85[5] = { 'a', 'r', 0xC3, 0xA1, 's' };
+static symbol s_8_86[5] = { 'e', 'r', 0xC3, 0xA1, 's' };
+static symbol s_8_87[5] = { 'i', 'r', 0xC3, 0xA1, 's' };
+static symbol s_8_88[3] = { 0xC3, 0xAD, 's' };
+static symbol s_8_89[4] = { 'a', 'r', 0xC3, 0xA1 };
+static symbol s_8_90[4] = { 'e', 'r', 0xC3, 0xA1 };
+static symbol s_8_91[4] = { 'i', 'r', 0xC3, 0xA1 };
+static symbol s_8_92[4] = { 'a', 'r', 0xC3, 0xA9 };
+static symbol s_8_93[4] = { 'e', 'r', 0xC3, 0xA9 };
+static symbol s_8_94[4] = { 'i', 'r', 0xC3, 0xA9 };
+static symbol s_8_95[3] = { 'i', 0xC3, 0xB3 };
+
+static struct among a_8[96] =
+{
+/*  0 */ { 3, s_8_0, -1, 2, 0},
+/*  1 */ { 3, s_8_1, -1, 2, 0},
+/*  2 */ { 3, s_8_2, -1, 2, 0},
+/*  3 */ { 3, s_8_3, -1, 2, 0},
+/*  4 */ { 4, s_8_4, -1, 2, 0},
+/*  5 */ { 3, s_8_5, -1, 2, 0},
+/*  6 */ { 5, s_8_6, 5, 2, 0},
+/*  7 */ { 5, s_8_7, 5, 2, 0},
+/*  8 */ { 5, s_8_8, 5, 2, 0},
+/*  9 */ { 2, s_8_9, -1, 2, 0},
+/* 10 */ { 2, s_8_10, -1, 2, 0},
+/* 11 */ { 2, s_8_11, -1, 2, 0},
+/* 12 */ { 3, s_8_12, -1, 2, 0},
+/* 13 */ { 4, s_8_13, -1, 2, 0},
+/* 14 */ { 4, s_8_14, -1, 2, 0},
+/* 15 */ { 4, s_8_15, -1, 2, 0},
+/* 16 */ { 2, s_8_16, -1, 2, 0},
+/* 17 */ { 4, s_8_17, 16, 2, 0},
+/* 18 */ { 4, s_8_18, 16, 2, 0},
+/* 19 */ { 5, s_8_19, 16, 2, 0},
+/* 20 */ { 4, s_8_20, 16, 2, 0},
+/* 21 */ { 6, s_8_21, 20, 2, 0},
+/* 22 */ { 6, s_8_22, 20, 2, 0},
+/* 23 */ { 6, s_8_23, 20, 2, 0},
+/* 24 */ { 2, s_8_24, -1, 1, 0},
+/* 25 */ { 4, s_8_25, 24, 2, 0},
+/* 26 */ { 5, s_8_26, 24, 2, 0},
+/* 27 */ { 4, s_8_27, -1, 2, 0},
+/* 28 */ { 5, s_8_28, -1, 2, 0},
+/* 29 */ { 5, s_8_29, -1, 2, 0},
+/* 30 */ { 5, s_8_30, -1, 2, 0},
+/* 31 */ { 5, s_8_31, -1, 2, 0},
+/* 32 */ { 3, s_8_32, -1, 2, 0},
+/* 33 */ { 3, s_8_33, -1, 2, 0},
+/* 34 */ { 4, s_8_34, -1, 2, 0},
+/* 35 */ { 5, s_8_35, -1, 2, 0},
+/* 36 */ { 2, s_8_36, -1, 2, 0},
+/* 37 */ { 2, s_8_37, -1, 2, 0},
+/* 38 */ { 2, s_8_38, -1, 2, 0},
+/* 39 */ { 2, s_8_39, -1, 2, 0},
+/* 40 */ { 4, s_8_40, 39, 2, 0},
+/* 41 */ { 4, s_8_41, 39, 2, 0},
+/* 42 */ { 4, s_8_42, 39, 2, 0},
+/* 43 */ { 4, s_8_43, 39, 2, 0},
+/* 44 */ { 5, s_8_44, 39, 2, 0},
+/* 45 */ { 4, s_8_45, 39, 2, 0},
+/* 46 */ { 6, s_8_46, 45, 2, 0},
+/* 47 */ { 6, s_8_47, 45, 2, 0},
+/* 48 */ { 6, s_8_48, 45, 2, 0},
+/* 49 */ { 2, s_8_49, -1, 1, 0},
+/* 50 */ { 4, s_8_50, 49, 2, 0},
+/* 51 */ { 5, s_8_51, 49, 2, 0},
+/* 52 */ { 5, s_8_52, -1, 2, 0},
+/* 53 */ { 5, s_8_53, -1, 2, 0},
+/* 54 */ { 6, s_8_54, -1, 2, 0},
+/* 55 */ { 5, s_8_55, -1, 2, 0},
+/* 56 */ { 7, s_8_56, 55, 2, 0},
+/* 57 */ { 7, s_8_57, 55, 2, 0},
+/* 58 */ { 7, s_8_58, 55, 2, 0},
+/* 59 */ { 5, s_8_59, -1, 2, 0},
+/* 60 */ { 6, s_8_60, -1, 2, 0},
+/* 61 */ { 6, s_8_61, -1, 2, 0},
+/* 62 */ { 6, s_8_62, -1, 2, 0},
+/* 63 */ { 4, s_8_63, -1, 2, 0},
+/* 64 */ { 4, s_8_64, -1, 1, 0},
+/* 65 */ { 6, s_8_65, 64, 2, 0},
+/* 66 */ { 6, s_8_66, 64, 2, 0},
+/* 67 */ { 6, s_8_67, 64, 2, 0},
+/* 68 */ { 4, s_8_68, -1, 2, 0},
+/* 69 */ { 4, s_8_69, -1, 2, 0},
+/* 70 */ { 4, s_8_70, -1, 2, 0},
+/* 71 */ { 7, s_8_71, 70, 2, 0},
+/* 72 */ { 7, s_8_72, 70, 2, 0},
+/* 73 */ { 8, s_8_73, 70, 2, 0},
+/* 74 */ { 6, s_8_74, 70, 2, 0},
+/* 75 */ { 8, s_8_75, 74, 2, 0},
+/* 76 */ { 8, s_8_76, 74, 2, 0},
+/* 77 */ { 8, s_8_77, 74, 2, 0},
+/* 78 */ { 4, s_8_78, -1, 1, 0},
+/* 79 */ { 6, s_8_79, 78, 2, 0},
+/* 80 */ { 6, s_8_80, 78, 2, 0},
+/* 81 */ { 6, s_8_81, 78, 2, 0},
+/* 82 */ { 7, s_8_82, 78, 2, 0},
+/* 83 */ { 8, s_8_83, 78, 2, 0},
+/* 84 */ { 4, s_8_84, -1, 2, 0},
+/* 85 */ { 5, s_8_85, -1, 2, 0},
+/* 86 */ { 5, s_8_86, -1, 2, 0},
+/* 87 */ { 5, s_8_87, -1, 2, 0},
+/* 88 */ { 3, s_8_88, -1, 2, 0},
+/* 89 */ { 4, s_8_89, -1, 2, 0},
+/* 90 */ { 4, s_8_90, -1, 2, 0},
+/* 91 */ { 4, s_8_91, -1, 2, 0},
+/* 92 */ { 4, s_8_92, -1, 2, 0},
+/* 93 */ { 4, s_8_93, -1, 2, 0},
+/* 94 */ { 4, s_8_94, -1, 2, 0},
+/* 95 */ { 3, s_8_95, -1, 2, 0}
+};
+
+static symbol s_9_0[1] = { 'a' };
+static symbol s_9_1[1] = { 'e' };
+static symbol s_9_2[1] = { 'o' };
+static symbol s_9_3[2] = { 'o', 's' };
+static symbol s_9_4[2] = { 0xC3, 0xA1 };
+static symbol s_9_5[2] = { 0xC3, 0xA9 };
+static symbol s_9_6[2] = { 0xC3, 0xAD };
+static symbol s_9_7[2] = { 0xC3, 0xB3 };
+
+static struct among a_9[8] =
+{
+/*  0 */ { 1, s_9_0, -1, 1, 0},
+/*  1 */ { 1, s_9_1, -1, 2, 0},
+/*  2 */ { 1, s_9_2, -1, 1, 0},
+/*  3 */ { 2, s_9_3, -1, 1, 0},
+/*  4 */ { 2, s_9_4, -1, 1, 0},
+/*  5 */ { 2, s_9_5, -1, 2, 0},
+/*  6 */ { 2, s_9_6, -1, 1, 0},
+/*  7 */ { 2, s_9_7, -1, 1, 0}
+};
+
+static unsigned char g_v[] = { 17, 65, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 17, 4, 10 };
+
+static symbol s_0[] = { 'a' };
+static symbol s_1[] = { 'e' };
+static symbol s_2[] = { 'i' };
+static symbol s_3[] = { 'o' };
+static symbol s_4[] = { 'u' };
+static symbol s_5[] = { 'i', 'e', 'n', 'd', 'o' };
+static symbol s_6[] = { 'a', 'n', 'd', 'o' };
+static symbol s_7[] = { 'a', 'r' };
+static symbol s_8[] = { 'e', 'r' };
+static symbol s_9[] = { 'i', 'r' };
+static symbol s_10[] = { 'u' };
+static symbol s_11[] = { 'i', 'c' };
+static symbol s_12[] = { 'l', 'o', 'g' };
+static symbol s_13[] = { 'u' };
+static symbol s_14[] = { 'e', 'n', 't', 'e' };
+static symbol s_15[] = { 'a', 't' };
+static symbol s_16[] = { 'a', 't' };
+static symbol s_17[] = { 'u' };
+static symbol s_18[] = { 'u' };
+static symbol s_19[] = { 'g' };
+static symbol s_20[] = { 'u' };
+static symbol s_21[] = { 'g' };
+
+static int r_mark_regions(struct SN_env * z) {
+    z->I[0] = z->l;
+    z->I[1] = z->l;
+    z->I[2] = z->l;
+    {	int c = z->c; /* do, line 37 */
+	{   int c = z->c; /* or, line 39 */
+	    if (!(in_grouping_U(z, g_v, 97, 252))) goto lab2;
+	    {	int c = z->c; /* or, line 38 */
+		if (!(out_grouping_U(z, g_v, 97, 252))) goto lab4;
+		while(1) { /* gopast, line 38 */
+		    if (!(in_grouping_U(z, g_v, 97, 252))) goto lab5;
+		    break;
+		lab5:
+		    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+			if (c < 0) goto lab4;
+			z->c = c; /* gopast, line 38 */
+		    }
+		}
+		goto lab3;
+	    lab4:
+		z->c = c;
+		if (!(in_grouping_U(z, g_v, 97, 252))) goto lab2;
+		while(1) { /* gopast, line 38 */
+		    if (!(out_grouping_U(z, g_v, 97, 252))) goto lab6;
+		    break;
+		lab6:
+		    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+			if (c < 0) goto lab2;
+			z->c = c; /* gopast, line 38 */
+		    }
+		}
+	    }
+	lab3:
+	    goto lab1;
+	lab2:
+	    z->c = c;
+	    if (!(out_grouping_U(z, g_v, 97, 252))) goto lab0;
+	    {	int c = z->c; /* or, line 40 */
+		if (!(out_grouping_U(z, g_v, 97, 252))) goto lab8;
+		while(1) { /* gopast, line 40 */
+		    if (!(in_grouping_U(z, g_v, 97, 252))) goto lab9;
+		    break;
+		lab9:
+		    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+			if (c < 0) goto lab8;
+			z->c = c; /* gopast, line 40 */
+		    }
+		}
+		goto lab7;
+	    lab8:
+		z->c = c;
+		if (!(in_grouping_U(z, g_v, 97, 252))) goto lab0;
+		{   int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		    if (c < 0) goto lab0;
+		    z->c = c; /* next, line 40 */
+		}
+	    }
+	lab7:
+	    ;
+	}
+    lab1:
+	z->I[0] = z->c; /* setmark pV, line 41 */
+    lab0:
+	z->c = c;
+    }
+    {	int c = z->c; /* do, line 43 */
+	while(1) { /* gopast, line 44 */
+	    if (!(in_grouping_U(z, g_v, 97, 252))) goto lab11;
+	    break;
+	lab11:
+	    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		if (c < 0) goto lab10;
+		z->c = c; /* gopast, line 44 */
+	    }
+	}
+	while(1) { /* gopast, line 44 */
+	    if (!(out_grouping_U(z, g_v, 97, 252))) goto lab12;
+	    break;
+	lab12:
+	    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		if (c < 0) goto lab10;
+		z->c = c; /* gopast, line 44 */
+	    }
+	}
+	z->I[1] = z->c; /* setmark p1, line 44 */
+	while(1) { /* gopast, line 45 */
+	    if (!(in_grouping_U(z, g_v, 97, 252))) goto lab13;
+	    break;
+	lab13:
+	    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		if (c < 0) goto lab10;
+		z->c = c; /* gopast, line 45 */
+	    }
+	}
+	while(1) { /* gopast, line 45 */
+	    if (!(out_grouping_U(z, g_v, 97, 252))) goto lab14;
+	    break;
+	lab14:
+	    {	int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		if (c < 0) goto lab10;
+		z->c = c; /* gopast, line 45 */
+	    }
+	}
+	z->I[2] = z->c; /* setmark p2, line 45 */
+    lab10:
+	z->c = c;
+    }
+    return 1;
+}
+
+static int r_postlude(struct SN_env * z) {
+    int among_var;
+    while(1) { /* repeat, line 49 */
+	int c = z->c;
+	z->bra = z->c; /* [, line 50 */
+	among_var = find_among(z, a_0, 6); /* substring, line 50 */
+	if (!(among_var)) goto lab0;
+	z->ket = z->c; /* ], line 50 */
+	switch(among_var) {
+	    case 0: goto lab0;
+	    case 1:
+		{   int ret;
+		    ret = slice_from_s(z, 1, s_0); /* <-, line 51 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 2:
+		{   int ret;
+		    ret = slice_from_s(z, 1, s_1); /* <-, line 52 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 3:
+		{   int ret;
+		    ret = slice_from_s(z, 1, s_2); /* <-, line 53 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 4:
+		{   int ret;
+		    ret = slice_from_s(z, 1, s_3); /* <-, line 54 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 5:
+		{   int ret;
+		    ret = slice_from_s(z, 1, s_4); /* <-, line 55 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 6:
+		{   int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+		    if (c < 0) goto lab0;
+		    z->c = c; /* next, line 57 */
+		}
+		break;
+	}
+	continue;
+    lab0:
+	z->c = c;
+	break;
+    }
+    return 1;
+}
+
+static int r_RV(struct SN_env * z) {
+    if (!(z->I[0] <= z->c)) return 0;
+    return 1;
+}
+
+static int r_R1(struct SN_env * z) {
+    if (!(z->I[1] <= z->c)) return 0;
+    return 1;
+}
+
+static int r_R2(struct SN_env * z) {
+    if (!(z->I[2] <= z->c)) return 0;
+    return 1;
+}
+
+static int r_attached_pronoun(struct SN_env * z) {
+    int among_var;
+    z->ket = z->c; /* [, line 68 */
+    if (!(find_among_b(z, a_1, 13))) return 0; /* substring, line 68 */
+    z->bra = z->c; /* ], line 68 */
+    among_var = find_among_b(z, a_2, 11); /* substring, line 72 */
+    if (!(among_var)) return 0;
+    {	int ret = r_RV(z);
+	if (ret == 0) return 0; /* call RV, line 72 */
+	if (ret < 0) return ret;
+    }
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    z->bra = z->c; /* ], line 73 */
+	    {	int ret;
+		ret = slice_from_s(z, 5, s_5); /* <-, line 73 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    z->bra = z->c; /* ], line 74 */
+	    {	int ret;
+		ret = slice_from_s(z, 4, s_6); /* <-, line 74 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 3:
+	    z->bra = z->c; /* ], line 75 */
+	    {	int ret;
+		ret = slice_from_s(z, 2, s_7); /* <-, line 75 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 4:
+	    z->bra = z->c; /* ], line 76 */
+	    {	int ret;
+		ret = slice_from_s(z, 2, s_8); /* <-, line 76 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 5:
+	    z->bra = z->c; /* ], line 77 */
+	    {	int ret;
+		ret = slice_from_s(z, 2, s_9); /* <-, line 77 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 6:
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 81 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 7:
+	    if (!(eq_s_b(z, 1, s_10))) return 0;
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 82 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_standard_suffix(struct SN_env * z) {
+    int among_var;
+    z->ket = z->c; /* [, line 87 */
+    among_var = find_among_b(z, a_6, 46); /* substring, line 87 */
+    if (!(among_var)) return 0;
+    z->bra = z->c; /* ], line 87 */
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret = r_R2(z);
+		if (ret == 0) return 0; /* call R2, line 99 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 99 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    {	int ret = r_R2(z);
+		if (ret == 0) return 0; /* call R2, line 105 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 105 */
+		if (ret < 0) return ret;
+	    }
+	    {	int m = z->l - z->c; (void) m; /* try, line 106 */
+		z->ket = z->c; /* [, line 106 */
+		if (!(eq_s_b(z, 2, s_11))) { z->c = z->l - m; goto lab0; }
+		z->bra = z->c; /* ], line 106 */
+		{   int ret = r_R2(z);
+		    if (ret == 0) { z->c = z->l - m; goto lab0; } /* call R2, line 106 */
+		    if (ret < 0) return ret;
+		}
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 106 */
+		    if (ret < 0) return ret;
+		}
+	    lab0:
+		;
+	    }
+	    break;
+	case 3:
+	    {	int ret = r_R2(z);
+		if (ret == 0) return 0; /* call R2, line 111 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_from_s(z, 3, s_12); /* <-, line 111 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 4:
+	    {	int ret = r_R2(z);
+		if (ret == 0) return 0; /* call R2, line 115 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_from_s(z, 1, s_13); /* <-, line 115 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 5:
+	    {	int ret = r_R2(z);
+		if (ret == 0) return 0; /* call R2, line 119 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_from_s(z, 4, s_14); /* <-, line 119 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 6:
+	    {	int ret = r_R1(z);
+		if (ret == 0) return 0; /* call R1, line 123 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 123 */
+		if (ret < 0) return ret;
+	    }
+	    {	int m = z->l - z->c; (void) m; /* try, line 124 */
+		z->ket = z->c; /* [, line 125 */
+		among_var = find_among_b(z, a_3, 4); /* substring, line 125 */
+		if (!(among_var)) { z->c = z->l - m; goto lab1; }
+		z->bra = z->c; /* ], line 125 */
+		{   int ret = r_R2(z);
+		    if (ret == 0) { z->c = z->l - m; goto lab1; } /* call R2, line 125 */
+		    if (ret < 0) return ret;
+		}
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 125 */
+		    if (ret < 0) return ret;
+		}
+		switch(among_var) {
+		    case 0: { z->c = z->l - m; goto lab1; }
+		    case 1:
+			z->ket = z->c; /* [, line 126 */
+			if (!(eq_s_b(z, 2, s_15))) { z->c = z->l - m; goto lab1; }
+			z->bra = z->c; /* ], line 126 */
+			{   int ret = r_R2(z);
+			    if (ret == 0) { z->c = z->l - m; goto lab1; } /* call R2, line 126 */
+			    if (ret < 0) return ret;
+			}
+			{   int ret;
+			    ret = slice_del(z); /* delete, line 126 */
+			    if (ret < 0) return ret;
+			}
+			break;
+		}
+	    lab1:
+		;
+	    }
+	    break;
+	case 7:
+	    {	int ret = r_R2(z);
+		if (ret == 0) return 0; /* call R2, line 135 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 135 */
+		if (ret < 0) return ret;
+	    }
+	    {	int m = z->l - z->c; (void) m; /* try, line 136 */
+		z->ket = z->c; /* [, line 137 */
+		among_var = find_among_b(z, a_4, 3); /* substring, line 137 */
+		if (!(among_var)) { z->c = z->l - m; goto lab2; }
+		z->bra = z->c; /* ], line 137 */
+		switch(among_var) {
+		    case 0: { z->c = z->l - m; goto lab2; }
+		    case 1:
+			{   int ret = r_R2(z);
+			    if (ret == 0) { z->c = z->l - m; goto lab2; } /* call R2, line 140 */
+			    if (ret < 0) return ret;
+			}
+			{   int ret;
+			    ret = slice_del(z); /* delete, line 140 */
+			    if (ret < 0) return ret;
+			}
+			break;
+		}
+	    lab2:
+		;
+	    }
+	    break;
+	case 8:
+	    {	int ret = r_R2(z);
+		if (ret == 0) return 0; /* call R2, line 147 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 147 */
+		if (ret < 0) return ret;
+	    }
+	    {	int m = z->l - z->c; (void) m; /* try, line 148 */
+		z->ket = z->c; /* [, line 149 */
+		among_var = find_among_b(z, a_5, 3); /* substring, line 149 */
+		if (!(among_var)) { z->c = z->l - m; goto lab3; }
+		z->bra = z->c; /* ], line 149 */
+		switch(among_var) {
+		    case 0: { z->c = z->l - m; goto lab3; }
+		    case 1:
+			{   int ret = r_R2(z);
+			    if (ret == 0) { z->c = z->l - m; goto lab3; } /* call R2, line 152 */
+			    if (ret < 0) return ret;
+			}
+			{   int ret;
+			    ret = slice_del(z); /* delete, line 152 */
+			    if (ret < 0) return ret;
+			}
+			break;
+		}
+	    lab3:
+		;
+	    }
+	    break;
+	case 9:
+	    {	int ret = r_R2(z);
+		if (ret == 0) return 0; /* call R2, line 159 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 159 */
+		if (ret < 0) return ret;
+	    }
+	    {	int m = z->l - z->c; (void) m; /* try, line 160 */
+		z->ket = z->c; /* [, line 161 */
+		if (!(eq_s_b(z, 2, s_16))) { z->c = z->l - m; goto lab4; }
+		z->bra = z->c; /* ], line 161 */
+		{   int ret = r_R2(z);
+		    if (ret == 0) { z->c = z->l - m; goto lab4; } /* call R2, line 161 */
+		    if (ret < 0) return ret;
+		}
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 161 */
+		    if (ret < 0) return ret;
+		}
+	    lab4:
+		;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_y_verb_suffix(struct SN_env * z) {
+    int among_var;
+    {	int m3; /* setlimit, line 168 */
+	int m = z->l - z->c; (void) m;
+	if (z->c < z->I[0]) return 0;
+	z->c = z->I[0]; /* tomark, line 168 */
+	m3 = z->lb; z->lb = z->c;
+	z->c = z->l - m;
+	z->ket = z->c; /* [, line 168 */
+	among_var = find_among_b(z, a_7, 12); /* substring, line 168 */
+	if (!(among_var)) { z->lb = m3; return 0; }
+	z->bra = z->c; /* ], line 168 */
+	z->lb = m3;
+    }
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    if (!(eq_s_b(z, 1, s_17))) return 0;
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 171 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_verb_suffix(struct SN_env * z) {
+    int among_var;
+    {	int m3; /* setlimit, line 176 */
+	int m = z->l - z->c; (void) m;
+	if (z->c < z->I[0]) return 0;
+	z->c = z->I[0]; /* tomark, line 176 */
+	m3 = z->lb; z->lb = z->c;
+	z->c = z->l - m;
+	z->ket = z->c; /* [, line 176 */
+	among_var = find_among_b(z, a_8, 96); /* substring, line 176 */
+	if (!(among_var)) { z->lb = m3; return 0; }
+	z->bra = z->c; /* ], line 176 */
+	z->lb = m3;
+    }
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int m = z->l - z->c; (void) m; /* try, line 179 */
+		if (!(eq_s_b(z, 1, s_18))) { z->c = z->l - m; goto lab0; }
+		{   int m_test = z->l - z->c; /* test, line 179 */
+		    if (!(eq_s_b(z, 1, s_19))) { z->c = z->l - m; goto lab0; }
+		    z->c = z->l - m_test;
+		}
+	    lab0:
+		;
+	    }
+	    z->bra = z->c; /* ], line 179 */
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 179 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 200 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_residual_suffix(struct SN_env * z) {
+    int among_var;
+    z->ket = z->c; /* [, line 205 */
+    among_var = find_among_b(z, a_9, 8); /* substring, line 205 */
+    if (!(among_var)) return 0;
+    z->bra = z->c; /* ], line 205 */
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret = r_RV(z);
+		if (ret == 0) return 0; /* call RV, line 208 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 208 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    {	int ret = r_RV(z);
+		if (ret == 0) return 0; /* call RV, line 210 */
+		if (ret < 0) return ret;
+	    }
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 210 */
+		if (ret < 0) return ret;
+	    }
+	    {	int m = z->l - z->c; (void) m; /* try, line 210 */
+		z->ket = z->c; /* [, line 210 */
+		if (!(eq_s_b(z, 1, s_20))) { z->c = z->l - m; goto lab0; }
+		z->bra = z->c; /* ], line 210 */
+		{   int m_test = z->l - z->c; /* test, line 210 */
+		    if (!(eq_s_b(z, 1, s_21))) { z->c = z->l - m; goto lab0; }
+		    z->c = z->l - m_test;
+		}
+		{   int ret = r_RV(z);
+		    if (ret == 0) { z->c = z->l - m; goto lab0; } /* call RV, line 210 */
+		    if (ret < 0) return ret;
+		}
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 210 */
+		    if (ret < 0) return ret;
+		}
+	    lab0:
+		;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+extern int spanish_UTF_8_stem(struct SN_env * z) {
+    {	int c = z->c; /* do, line 216 */
+	{   int ret = r_mark_regions(z);
+	    if (ret == 0) goto lab0; /* call mark_regions, line 216 */
+	    if (ret < 0) return ret;
+	}
+    lab0:
+	z->c = c;
+    }
+    z->lb = z->c; z->c = z->l; /* backwards, line 217 */
+
+    {	int m = z->l - z->c; (void) m; /* do, line 218 */
+	{   int ret = r_attached_pronoun(z);
+	    if (ret == 0) goto lab1; /* call attached_pronoun, line 218 */
+	    if (ret < 0) return ret;
+	}
+    lab1:
+	z->c = z->l - m;
+    }
+    {	int m = z->l - z->c; (void) m; /* do, line 219 */
+	{   int m = z->l - z->c; (void) m; /* or, line 219 */
+	    {	int ret = r_standard_suffix(z);
+		if (ret == 0) goto lab4; /* call standard_suffix, line 219 */
+		if (ret < 0) return ret;
+	    }
+	    goto lab3;
+	lab4:
+	    z->c = z->l - m;
+	    {	int ret = r_y_verb_suffix(z);
+		if (ret == 0) goto lab5; /* call y_verb_suffix, line 220 */
+		if (ret < 0) return ret;
+	    }
+	    goto lab3;
+	lab5:
+	    z->c = z->l - m;
+	    {	int ret = r_verb_suffix(z);
+		if (ret == 0) goto lab2; /* call verb_suffix, line 221 */
+		if (ret < 0) return ret;
+	    }
+	}
+    lab3:
+    lab2:
+	z->c = z->l - m;
+    }
+    {	int m = z->l - z->c; (void) m; /* do, line 223 */
+	{   int ret = r_residual_suffix(z);
+	    if (ret == 0) goto lab6; /* call residual_suffix, line 223 */
+	    if (ret < 0) return ret;
+	}
+    lab6:
+	z->c = z->l - m;
+    }
+    z->c = z->lb;
+    {	int c = z->c; /* do, line 225 */
+	{   int ret = r_postlude(z);
+	    if (ret == 0) goto lab7; /* call postlude, line 225 */
+	    if (ret < 0) return ret;
+	}
+    lab7:
+	z->c = c;
+    }
+    return 1;
+}
+
+extern struct SN_env * spanish_UTF_8_create_env(void) { return SN_create_env(0, 3, 0); }
+
+extern void spanish_UTF_8_close_env(struct SN_env * z) { SN_close_env(z); }
+

Added: trunk/src/libstemmer/stem_UTF_8_spanish.h
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/stem_UTF_8_spanish.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2001, Dr Martin Porter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+/* This file was generated automatically by the Snowball to ANSI C compiler */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern struct SN_env * spanish_UTF_8_create_env(void);
+extern void spanish_UTF_8_close_env(struct SN_env * z);
+
+extern int spanish_UTF_8_stem(struct SN_env * z);
+
+#ifdef __cplusplus
+}
+#endif
+

Added: trunk/src/libstemmer/stem_UTF_8_swedish.c
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/stem_UTF_8_swedish.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,326 @@
+/*
+ * Copyright (c) 2001, Dr Martin Porter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+/* This file was generated automatically by the Snowball to ANSI C compiler */
+
+#include "header.h"
+
+extern int swedish_UTF_8_stem(struct SN_env * z);
+static int r_other_suffix(struct SN_env * z);
+static int r_consonant_pair(struct SN_env * z);
+static int r_main_suffix(struct SN_env * z);
+static int r_mark_regions(struct SN_env * z);
+
+extern struct SN_env * swedish_UTF_8_create_env(void);
+extern void swedish_UTF_8_close_env(struct SN_env * z);
+
+static symbol s_0_0[1] = { 'a' };
+static symbol s_0_1[4] = { 'a', 'r', 'n', 'a' };
+static symbol s_0_2[4] = { 'e', 'r', 'n', 'a' };
+static symbol s_0_3[7] = { 'h', 'e', 't', 'e', 'r', 'n', 'a' };
+static symbol s_0_4[4] = { 'o', 'r', 'n', 'a' };
+static symbol s_0_5[2] = { 'a', 'd' };
+static symbol s_0_6[1] = { 'e' };
+static symbol s_0_7[3] = { 'a', 'd', 'e' };
+static symbol s_0_8[4] = { 'a', 'n', 'd', 'e' };
+static symbol s_0_9[4] = { 'a', 'r', 'n', 'e' };
+static symbol s_0_10[3] = { 'a', 'r', 'e' };
+static symbol s_0_11[4] = { 'a', 's', 't', 'e' };
+static symbol s_0_12[2] = { 'e', 'n' };
+static symbol s_0_13[5] = { 'a', 'n', 'd', 'e', 'n' };
+static symbol s_0_14[4] = { 'a', 'r', 'e', 'n' };
+static symbol s_0_15[5] = { 'h', 'e', 't', 'e', 'n' };
+static symbol s_0_16[3] = { 'e', 'r', 'n' };
+static symbol s_0_17[2] = { 'a', 'r' };
+static symbol s_0_18[2] = { 'e', 'r' };
+static symbol s_0_19[5] = { 'h', 'e', 't', 'e', 'r' };
+static symbol s_0_20[2] = { 'o', 'r' };
+static symbol s_0_21[1] = { 's' };
+static symbol s_0_22[2] = { 'a', 's' };
+static symbol s_0_23[5] = { 'a', 'r', 'n', 'a', 's' };
+static symbol s_0_24[5] = { 'e', 'r', 'n', 'a', 's' };
+static symbol s_0_25[5] = { 'o', 'r', 'n', 'a', 's' };
+static symbol s_0_26[2] = { 'e', 's' };
+static symbol s_0_27[4] = { 'a', 'd', 'e', 's' };
+static symbol s_0_28[5] = { 'a', 'n', 'd', 'e', 's' };
+static symbol s_0_29[3] = { 'e', 'n', 's' };
+static symbol s_0_30[5] = { 'a', 'r', 'e', 'n', 's' };
+static symbol s_0_31[6] = { 'h', 'e', 't', 'e', 'n', 's' };
+static symbol s_0_32[4] = { 'e', 'r', 'n', 's' };
+static symbol s_0_33[2] = { 'a', 't' };
+static symbol s_0_34[5] = { 'a', 'n', 'd', 'e', 't' };
+static symbol s_0_35[3] = { 'h', 'e', 't' };
+static symbol s_0_36[3] = { 'a', 's', 't' };
+
+static struct among a_0[37] =
+{
+/*  0 */ { 1, s_0_0, -1, 1, 0},
+/*  1 */ { 4, s_0_1, 0, 1, 0},
+/*  2 */ { 4, s_0_2, 0, 1, 0},
+/*  3 */ { 7, s_0_3, 2, 1, 0},
+/*  4 */ { 4, s_0_4, 0, 1, 0},
+/*  5 */ { 2, s_0_5, -1, 1, 0},
+/*  6 */ { 1, s_0_6, -1, 1, 0},
+/*  7 */ { 3, s_0_7, 6, 1, 0},
+/*  8 */ { 4, s_0_8, 6, 1, 0},
+/*  9 */ { 4, s_0_9, 6, 1, 0},
+/* 10 */ { 3, s_0_10, 6, 1, 0},
+/* 11 */ { 4, s_0_11, 6, 1, 0},
+/* 12 */ { 2, s_0_12, -1, 1, 0},
+/* 13 */ { 5, s_0_13, 12, 1, 0},
+/* 14 */ { 4, s_0_14, 12, 1, 0},
+/* 15 */ { 5, s_0_15, 12, 1, 0},
+/* 16 */ { 3, s_0_16, -1, 1, 0},
+/* 17 */ { 2, s_0_17, -1, 1, 0},
+/* 18 */ { 2, s_0_18, -1, 1, 0},
+/* 19 */ { 5, s_0_19, 18, 1, 0},
+/* 20 */ { 2, s_0_20, -1, 1, 0},
+/* 21 */ { 1, s_0_21, -1, 2, 0},
+/* 22 */ { 2, s_0_22, 21, 1, 0},
+/* 23 */ { 5, s_0_23, 22, 1, 0},
+/* 24 */ { 5, s_0_24, 22, 1, 0},
+/* 25 */ { 5, s_0_25, 22, 1, 0},
+/* 26 */ { 2, s_0_26, 21, 1, 0},
+/* 27 */ { 4, s_0_27, 26, 1, 0},
+/* 28 */ { 5, s_0_28, 26, 1, 0},
+/* 29 */ { 3, s_0_29, 21, 1, 0},
+/* 30 */ { 5, s_0_30, 29, 1, 0},
+/* 31 */ { 6, s_0_31, 29, 1, 0},
+/* 32 */ { 4, s_0_32, 21, 1, 0},
+/* 33 */ { 2, s_0_33, -1, 1, 0},
+/* 34 */ { 5, s_0_34, -1, 1, 0},
+/* 35 */ { 3, s_0_35, -1, 1, 0},
+/* 36 */ { 3, s_0_36, -1, 1, 0}
+};
+
+static symbol s_1_0[2] = { 'd', 'd' };
+static symbol s_1_1[2] = { 'g', 'd' };
+static symbol s_1_2[2] = { 'n', 'n' };
+static symbol s_1_3[2] = { 'd', 't' };
+static symbol s_1_4[2] = { 'g', 't' };
+static symbol s_1_5[2] = { 'k', 't' };
+static symbol s_1_6[2] = { 't', 't' };
+
+static struct among a_1[7] =
+{
+/*  0 */ { 2, s_1_0, -1, -1, 0},
+/*  1 */ { 2, s_1_1, -1, -1, 0},
+/*  2 */ { 2, s_1_2, -1, -1, 0},
+/*  3 */ { 2, s_1_3, -1, -1, 0},
+/*  4 */ { 2, s_1_4, -1, -1, 0},
+/*  5 */ { 2, s_1_5, -1, -1, 0},
+/*  6 */ { 2, s_1_6, -1, -1, 0}
+};
+
+static symbol s_2_0[2] = { 'i', 'g' };
+static symbol s_2_1[3] = { 'l', 'i', 'g' };
+static symbol s_2_2[3] = { 'e', 'l', 's' };
+static symbol s_2_3[5] = { 'f', 'u', 'l', 'l', 't' };
+static symbol s_2_4[5] = { 'l', 0xC3, 0xB6, 's', 't' };
+
+static struct among a_2[5] =
+{
+/*  0 */ { 2, s_2_0, -1, 1, 0},
+/*  1 */ { 3, s_2_1, 0, 1, 0},
+/*  2 */ { 3, s_2_2, -1, 1, 0},
+/*  3 */ { 5, s_2_3, -1, 3, 0},
+/*  4 */ { 5, s_2_4, -1, 2, 0}
+};
+
+static unsigned char g_v[] = { 17, 65, 16, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 32 };
+
+static unsigned char g_s_ending[] = { 119, 127, 149 };
+
+static symbol s_0[] = { 'l', 0xC3, 0xB6, 's' };
+static symbol s_1[] = { 'f', 'u', 'l', 'l' };
+
+static int r_mark_regions(struct SN_env * z) {
+    z->I[0] = z->l;
+    {	int c_test = z->c; /* test, line 29 */
+	{   int c = skip_utf8(z->p, z->c, 0, z->l, + 3);
+	    if (c < 0) return 0;
+	    z->c = c; /* hop, line 29 */
+	}
+	z->I[1] = z->c; /* setmark x, line 29 */
+	z->c = c_test;
+    }
+    while(1) { /* goto, line 30 */
+	int c = z->c;
+	if (!(in_grouping_U(z, g_v, 97, 246))) goto lab0;
+	z->c = c;
+	break;
+    lab0:
+	z->c = c;
+	{   int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+	    if (c < 0) return 0;
+	    z->c = c; /* goto, line 30 */
+	}
+    }
+    while(1) { /* gopast, line 30 */
+	if (!(out_grouping_U(z, g_v, 97, 246))) goto lab1;
+	break;
+    lab1:
+	{   int c = skip_utf8(z->p, z->c, 0, z->l, 1);
+	    if (c < 0) return 0;
+	    z->c = c; /* gopast, line 30 */
+	}
+    }
+    z->I[0] = z->c; /* setmark p1, line 30 */
+     /* try, line 31 */
+    if (!(z->I[0] < z->I[1])) goto lab2;
+    z->I[0] = z->I[1];
+lab2:
+    return 1;
+}
+
+static int r_main_suffix(struct SN_env * z) {
+    int among_var;
+    {	int m3; /* setlimit, line 37 */
+	int m = z->l - z->c; (void) m;
+	if (z->c < z->I[0]) return 0;
+	z->c = z->I[0]; /* tomark, line 37 */
+	m3 = z->lb; z->lb = z->c;
+	z->c = z->l - m;
+	z->ket = z->c; /* [, line 37 */
+	among_var = find_among_b(z, a_0, 37); /* substring, line 37 */
+	if (!(among_var)) { z->lb = m3; return 0; }
+	z->bra = z->c; /* ], line 37 */
+	z->lb = m3;
+    }
+    switch(among_var) {
+	case 0: return 0;
+	case 1:
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 44 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+	case 2:
+	    if (!(in_grouping_b_U(z, g_s_ending, 98, 121))) return 0;
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 46 */
+		if (ret < 0) return ret;
+	    }
+	    break;
+    }
+    return 1;
+}
+
+static int r_consonant_pair(struct SN_env * z) {
+    {	int m3; /* setlimit, line 50 */
+	int m = z->l - z->c; (void) m;
+	if (z->c < z->I[0]) return 0;
+	z->c = z->I[0]; /* tomark, line 50 */
+	m3 = z->lb; z->lb = z->c;
+	z->c = z->l - m;
+	{   int m = z->l - z->c; (void) m; /* and, line 52 */
+	    if (!(find_among_b(z, a_1, 7))) { z->lb = m3; return 0; } /* among, line 51 */
+	    z->c = z->l - m;
+	    z->ket = z->c; /* [, line 52 */
+	    {	int c = skip_utf8(z->p, z->c, z->lb, 0, -1);
+		if (c < 0) { z->lb = m3; return 0; }
+		z->c = c; /* next, line 52 */
+	    }
+	    z->bra = z->c; /* ], line 52 */
+	    {	int ret;
+		ret = slice_del(z); /* delete, line 52 */
+		if (ret < 0) return ret;
+	    }
+	}
+	z->lb = m3;
+    }
+    return 1;
+}
+
+static int r_other_suffix(struct SN_env * z) {
+    int among_var;
+    {	int m3; /* setlimit, line 55 */
+	int m = z->l - z->c; (void) m;
+	if (z->c < z->I[0]) return 0;
+	z->c = z->I[0]; /* tomark, line 55 */
+	m3 = z->lb; z->lb = z->c;
+	z->c = z->l - m;
+	z->ket = z->c; /* [, line 56 */
+	among_var = find_among_b(z, a_2, 5); /* substring, line 56 */
+	if (!(among_var)) { z->lb = m3; return 0; }
+	z->bra = z->c; /* ], line 56 */
+	switch(among_var) {
+	    case 0: { z->lb = m3; return 0; }
+	    case 1:
+		{   int ret;
+		    ret = slice_del(z); /* delete, line 57 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 2:
+		{   int ret;
+		    ret = slice_from_s(z, 4, s_0); /* <-, line 58 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	    case 3:
+		{   int ret;
+		    ret = slice_from_s(z, 4, s_1); /* <-, line 59 */
+		    if (ret < 0) return ret;
+		}
+		break;
+	}
+	z->lb = m3;
+    }
+    return 1;
+}
+
+extern int swedish_UTF_8_stem(struct SN_env * z) {
+    {	int c = z->c; /* do, line 66 */
+	{   int ret = r_mark_regions(z);
+	    if (ret == 0) goto lab0; /* call mark_regions, line 66 */
+	    if (ret < 0) return ret;
+	}
+    lab0:
+	z->c = c;
+    }
+    z->lb = z->c; z->c = z->l; /* backwards, line 67 */
+
+    {	int m = z->l - z->c; (void) m; /* do, line 68 */
+	{   int ret = r_main_suffix(z);
+	    if (ret == 0) goto lab1; /* call main_suffix, line 68 */
+	    if (ret < 0) return ret;
+	}
+    lab1:
+	z->c = z->l - m;
+    }
+    {	int m = z->l - z->c; (void) m; /* do, line 69 */
+	{   int ret = r_consonant_pair(z);
+	    if (ret == 0) goto lab2; /* call consonant_pair, line 69 */
+	    if (ret < 0) return ret;
+	}
+    lab2:
+	z->c = z->l - m;
+    }
+    {	int m = z->l - z->c; (void) m; /* do, line 70 */
+	{   int ret = r_other_suffix(z);
+	    if (ret == 0) goto lab3; /* call other_suffix, line 70 */
+	    if (ret < 0) return ret;
+	}
+    lab3:
+	z->c = z->l - m;
+    }
+    z->c = z->lb;
+    return 1;
+}
+
+extern struct SN_env * swedish_UTF_8_create_env(void) { return SN_create_env(0, 2, 0); }
+
+extern void swedish_UTF_8_close_env(struct SN_env * z) { SN_close_env(z); }
+

Added: trunk/src/libstemmer/stem_UTF_8_swedish.h
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/stem_UTF_8_swedish.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2001, Dr Martin Porter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+/* This file was generated automatically by the Snowball to ANSI C compiler */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern struct SN_env * swedish_UTF_8_create_env(void);
+extern void swedish_UTF_8_close_env(struct SN_env * z);
+
+extern int swedish_UTF_8_stem(struct SN_env * z);
+
+#ifdef __cplusplus
+}
+#endif
+

Added: trunk/src/libstemmer/utilities.c
==============================================================================
--- (empty file)
+++ trunk/src/libstemmer/utilities.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,459 @@
+/*
+ * Copyright (c) 2001, Dr Martin Porter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "header.h"
+
+#define unless(C) if(!(C))
+
+#define CREATE_SIZE 1
+
+extern symbol * create_s(void) {
+    symbol * p;
+    void * mem = malloc(HEAD + (CREATE_SIZE + 1) * sizeof(symbol));
+    if (mem == NULL) return NULL;
+    p = (symbol *) (HEAD + (char *) mem);
+    CAPACITY(p) = CREATE_SIZE;
+    SET_SIZE(p, CREATE_SIZE);
+    return p;
+}
+
+extern void lose_s(symbol * p) {
+    if (p == NULL) return;
+    free((char *) p - HEAD);
+}
+
+/*
+   new_p = X_skip_utf8(p, c, lb, l, n); skips n characters forwards from p + c
+   if n +ve, or n characters backwards from p +c - 1 if n -ve. new_p is the new
+   position, or 0 on failure.
+
+   -- used to implement hop and next in the utf8 case.
+*/
+
+extern int skip_utf8(const symbol * p, int c, int lb, int l, int n) {
+    int b;
+    if (n >= 0) {
+	for (; n > 0; n--) {
+	    if (c >= l) return -1;
+	    b = p[c++];
+	    if (b >= 0xC0) {   /* 1100 0000 */
+		while (c < l) {
+		    b = p[c];
+		    if (b >= 0xC0 || b < 0x80) break;
+		    /* break unless b is 10------ */
+		    c++;
+		}
+	    }
+	}
+    } else {
+	for (; n < 0; n++) {
+	    if (c <= lb) return -1;
+	    b = p[--c];
+	    if (b >= 0x80) {   /* 1000 0000 */
+		while (c > lb) {
+		    b = p[c];
+		    if (b >= 0xC0) break; /* 1100 0000 */
+		    c--;
+		}
+	    }
+	}
+    }
+    return c;
+}
+
+/* Code for character groupings: utf8 cases */
+
+static int get_utf8(const symbol * p, int c, int l, int * slot) {
+    int b0, b1;
+    if (c >= l) return 0;
+    b0 = p[c++];
+    if (b0 < 0xC0 || c == l) {	 /* 1100 0000 */
+	* slot = b0; return 1;
+    }
+    b1 = p[c++];
+    if (b0 < 0xE0 || c == l) {	 /* 1110 0000 */
+	* slot = (b0 & 0x1F) << 6 | (b1 & 0x3F); return 2;
+    }
+    * slot = (b0 & 0xF) << 12 | (b1 & 0x3F) << 6 | (*p & 0x3F); return 3;
+}
+
+static int get_b_utf8(const symbol * p, int c, int lb, int * slot) {
+    int b0, b1;
+    if (c <= lb) return 0;
+    b0 = p[--c];
+    if (b0 < 0x80 || c == lb) {   /* 1000 0000 */
+	* slot = b0; return 1;
+    }
+    b1 = p[--c];
+    if (b1 >= 0xC0 || c == lb) {   /* 1100 0000 */
+	* slot = (b1 & 0x1F) << 6 | (b0 & 0x3F); return 2;
+    }
+    * slot = (*p & 0xF) << 12 | (b1 & 0x3F) << 6 | (b0 & 0x3F); return 3;
+}
+
+extern int in_grouping_U(struct SN_env * z, unsigned char * s, int min, int max) {
+    int ch;
+    int w = get_utf8(z->p, z->c, z->l, & ch);
+    unless (w) return 0;
+    if (ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0) return 0;
+    z->c += w; return 1;
+}
+
+extern int in_grouping_b_U(struct SN_env * z, unsigned char * s, int min, int max) {
+    int ch;
+    int w = get_b_utf8(z->p, z->c, z->lb, & ch);
+    unless (w) return 0;
+    if (ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0) return 0;
+    z->c -= w; return 1;
+}
+
+extern int out_grouping_U(struct SN_env * z, unsigned char * s, int min, int max) {
+    int ch;
+    int w = get_utf8(z->p, z->c, z->l, & ch);
+    unless (w) return 0;
+    unless (ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0) return 0;
+    z->c += w; return 1;
+}
+
+extern int out_grouping_b_U(struct SN_env * z, unsigned char * s, int min, int max) {
+    int ch;
+    int w = get_b_utf8(z->p, z->c, z->lb, & ch);
+    unless (w) return 0;
+    unless (ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0) return 0;
+    z->c -= w; return 1;
+}
+
+/* Code for character groupings: non-utf8 cases */
+
+extern int in_grouping(struct SN_env * z, unsigned char * s, int min, int max) {
+    int ch;
+    if (z->c >= z->l) return 0;
+    ch = z->p[z->c];
+    if (ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0) return 0;
+    z->c++; return 1;
+}
+
+extern int in_grouping_b(struct SN_env * z, unsigned char * s, int min, int max) {
+    int ch;
+    if (z->c <= z->lb) return 0;
+    ch = z->p[z->c - 1];
+    if (ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0) return 0;
+    z->c--; return 1;
+}
+
+extern int out_grouping(struct SN_env * z, unsigned char * s, int min, int max) {
+    int ch;
+    if (z->c >= z->l) return 0;
+    ch = z->p[z->c];
+    unless (ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0) return 0;
+    z->c++; return 1;
+}
+
+extern int out_grouping_b(struct SN_env * z, unsigned char * s, int min, int max) {
+    int ch;
+    if (z->c <= z->lb) return 0;
+    ch = z->p[z->c - 1];
+    unless (ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0) return 0;
+    z->c--; return 1;
+}
+
+extern int eq_s(struct SN_env * z, int s_size, symbol * s) {
+    if (z->l - z->c < s_size || memcmp(z->p + z->c, s, s_size * sizeof(symbol)) != 0) return 0;
+    z->c += s_size; return 1;
+}
+
+extern int eq_s_b(struct SN_env * z, int s_size, symbol * s) {
+    if (z->c - z->lb < s_size || memcmp(z->p + z->c - s_size, s, s_size * sizeof(symbol)) != 0) return 0;
+    z->c -= s_size; return 1;
+}
+
+extern int eq_v(struct SN_env * z, symbol * p) {
+    return eq_s(z, SIZE(p), p);
+}
+
+extern int eq_v_b(struct SN_env * z, symbol * p) {
+    return eq_s_b(z, SIZE(p), p);
+}
+
+extern int find_among(struct SN_env * z, struct among * v, int v_size) {
+
+    int i = 0;
+    int j = v_size;
+
+    int c = z->c; int l = z->l;
+    symbol * q = z->p + c;
+
+    struct among * w;
+
+    int common_i = 0;
+    int common_j = 0;
+
+    int first_key_inspected = 0;
+
+    while(1) {
+	int k = i + ((j - i) >> 1);
+	int diff = 0;
+	int common = common_i < common_j ? common_i : common_j; /* smaller */
+	w = v + k;
+	{
+	    int i; for (i = common; i < w->s_size; i++) {
+		if (c + common == l) { diff = -1; break; }
+		diff = q[common] - w->s[i];
+		if (diff != 0) break;
+		common++;
+	    }
+	}
+	if (diff < 0) { j = k; common_j = common; }
+		 else { i = k; common_i = common; }
+	if (j - i <= 1) {
+	    if (i > 0) break; /* v->s has been inspected */
+	    if (j == i) break; /* only one item in v */
+
+	    /* - but now we need to go round once more to get
+	       v->s inspected. This looks messy, but is actually
+	       the optimal approach.  */
+
+	    if (first_key_inspected) break;
+	    first_key_inspected = 1;
+	}
+    }
+    while(1) {
+	w = v + i;
+	if (common_i >= w->s_size) {
+	    z->c = c + w->s_size;
+	    if (w->function == 0) return w->result;
+	    {
+		int res = w->function(z);
+		z->c = c + w->s_size;
+		if (res) return w->result;
+	    }
+	}
+	i = w->substring_i;
+	if (i < 0) return 0;
+    }
+}
+
+/* find_among_b is for backwards processing. Same comments apply */
+
+extern int find_among_b(struct SN_env * z, struct among * v, int v_size) {
+
+    int i = 0;
+    int j = v_size;
+
+    int c = z->c; int lb = z->lb;
+    symbol * q = z->p + c - 1;
+
+    struct among * w;
+
+    int common_i = 0;
+    int common_j = 0;
+
+    int first_key_inspected = 0;
+
+    while(1) {
+	int k = i + ((j - i) >> 1);
+	int diff = 0;
+	int common = common_i < common_j ? common_i : common_j;
+	w = v + k;
+	{
+	    int i; for (i = w->s_size - 1 - common; i >= 0; i--) {
+		if (c - common == lb) { diff = -1; break; }
+		diff = q[- common] - w->s[i];
+		if (diff != 0) break;
+		common++;
+	    }
+	}
+	if (diff < 0) { j = k; common_j = common; }
+		 else { i = k; common_i = common; }
+	if (j - i <= 1) {
+	    if (i > 0) break;
+	    if (j == i) break;
+	    if (first_key_inspected) break;
+	    first_key_inspected = 1;
+	}
+    }
+    while(1) {
+	w = v + i;
+	if (common_i >= w->s_size) {
+	    z->c = c - w->s_size;
+	    if (w->function == 0) return w->result;
+	    {
+		int res = w->function(z);
+		z->c = c - w->s_size;
+		if (res) return w->result;
+	    }
+	}
+	i = w->substring_i;
+	if (i < 0) return 0;
+    }
+}
+
+
+/* Increase the size of the buffer pointed to by p to at least n symbols.
+ * If insufficient memory, returns NULL and frees the old buffer.
+ */
+static symbol * increase_size(symbol * p, int n) {
+    symbol * q;
+    int new_size = n + 20;
+    void * mem = realloc((char *) p - HEAD,
+			 HEAD + (new_size + 1) * sizeof(symbol));
+    if (mem == NULL) {
+	lose_s(p);
+	return NULL;
+    }
+    q = (symbol *) (HEAD + (char *)mem);
+    CAPACITY(q) = new_size;
+    return q;
+}
+
+/* to replace symbols between c_bra and c_ket in z->p by the
+   s_size symbols at s.
+   Returns 0 on success, -1 on error.
+   Also, frees z->p (and sets it to NULL) on error.
+*/
+extern int replace_s(struct SN_env * z, int c_bra, int c_ket, int s_size, const symbol * s, int * adjptr)
+{
+    int adjustment;
+    int len;
+    if (z->p == NULL) {
+	z->p = create_s();
+	if (z->p == NULL) return -1;
+    }
+    adjustment = s_size - (c_ket - c_bra);
+    len = SIZE(z->p);
+    if (adjustment != 0) {
+	if (adjustment + len > CAPACITY(z->p)) {
+	    z->p = increase_size(z->p, adjustment + len);
+	    if (z->p == NULL) return -1;
+	}
+	memmove(z->p + c_ket + adjustment,
+		z->p + c_ket,
+		(len - c_ket) * sizeof(symbol));
+	SET_SIZE(z->p, adjustment + len);
+	z->l += adjustment;
+	if (z->c >= c_ket)
+	    z->c += adjustment;
+	else
+	    if (z->c > c_bra)
+		z->c = c_bra;
+    }
+    unless (s_size == 0) memmove(z->p + c_bra, s, s_size * sizeof(symbol));
+    if (adjptr != NULL)
+	*adjptr = adjustment;
+    return 0;
+}
+
+static int slice_check(struct SN_env * z) {
+
+    if (z->bra < 0 ||
+	z->bra > z->ket ||
+	z->ket > z->l ||
+	z->p == NULL ||
+	z->l > SIZE(z->p)) /* this line could be removed */
+    {
+#if 0
+	fprintf(stderr, "faulty slice operation:\n");
+	debug(z, -1, 0);
+#endif
+	return -1;
+    }
+    return 0;
+}
+
+extern int slice_from_s(struct SN_env * z, int s_size, symbol * s) {
+    if (slice_check(z)) return -1;
+    return replace_s(z, z->bra, z->ket, s_size, s, NULL);
+}
+
+extern int slice_from_v(struct SN_env * z, symbol * p) {
+    return slice_from_s(z, SIZE(p), p);
+}
+
+extern int slice_del(struct SN_env * z) {
+    return slice_from_s(z, 0, 0);
+}
+
+extern int insert_s(struct SN_env * z, int bra, int ket, int s_size, symbol * s) {
+    int adjustment;
+    if (replace_s(z, bra, ket, s_size, s, &adjustment))
+	return -1;
+    if (bra <= z->bra) z->bra += adjustment;
+    if (bra <= z->ket) z->ket += adjustment;
+    return 0;
+}
+
+extern int insert_v(struct SN_env * z, int bra, int ket, symbol * p) {
+    int adjustment;
+    if (replace_s(z, bra, ket, SIZE(p), p, &adjustment))
+	return -1;
+    if (bra <= z->bra) z->bra += adjustment;
+    if (bra <= z->ket) z->ket += adjustment;
+    return 0;
+}
+
+extern symbol * slice_to(struct SN_env * z, symbol * p) {
+    if (slice_check(z)) {
+	lose_s(p);
+	return NULL;
+    }
+    {
+	int len = z->ket - z->bra;
+	if (CAPACITY(p) < len) {
+	    p = increase_size(p, len);
+	    if (p == NULL)
+		return NULL;
+	}
+	memmove(p, z->p + z->bra, len * sizeof(symbol));
+	SET_SIZE(p, len);
+    }
+    return p;
+}
+
+extern symbol * assign_to(struct SN_env * z, symbol * p) {
+    int len = z->l;
+    if (CAPACITY(p) < len) {
+	p = increase_size(p, len);
+	if (p == NULL)
+	    return NULL;
+    }
+    memmove(p, z->p, len * sizeof(symbol));
+    SET_SIZE(p, len);
+    return p;
+}
+
+#if 0
+extern void debug(struct SN_env * z, int number, int line_count) {
+    int i;
+    int limit = SIZE(z->p);
+    /*if (number >= 0) printf("%3d (line %4d): '", number, line_count);*/
+    if (number >= 0) printf("%3d (line %4d): [%d]'", number, line_count,limit);
+    for (i = 0; i <= limit; i++) {
+	if (z->lb == i) printf("{");
+	if (z->bra == i) printf("[");
+	if (z->c == i) printf("|");
+	if (z->ket == i) printf("]");
+	if (z->l == i) printf("}");
+	if (i < limit)
+	{   int ch = z->p[i];
+	    if (ch == 0) ch = '#';
+	    printf("%c", ch);
+	}
+    }
+    printf("'\n");
+}
+#endif

Added: trunk/src/libtracker-common/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,79 @@
+include $(top_srcdir)/Makefile.decl
+
+INCLUDES =						\
+	-DSHAREDIR=\""$(datadir)"\"			\
+	-DG_LOG_DOMAIN=\"Tracker\"			\
+	-I$(top_srcdir)/src				\
+	$(HAL_CFLAGS)					\
+	$(DBUS_CFLAGS)					\
+	$(UNAC_CFLAGS)					\
+	$(PANGO_CFLAGS)					\
+	$(GIO_CFLAGS)					\
+	$(GLIB2_CFLAGS)
+
+libtracker_commondir = $(libdir)/tracker
+libtracker_common_LTLIBRARIES = libtracker-common.la
+
+if OS_WIN32
+os_sources = tracker-os-dependant-win.c
+else
+os_sources = tracker-os-dependant-unix.c
+endif
+
+if HAVE_HAL
+hal_sources = 						\
+	tracker-hal.c
+hal_headers = 						\
+	tracker-hal.h
+endif
+
+libtracker_common_la_SOURCES =	 			\
+	$(os_sources)					\
+	$(hal_sources)					\
+	tracker-config.c 				\
+	tracker-configuration.c 			\
+	tracker-dbus.c	 				\
+	tracker-field.c					\
+	tracker-field-data.c				\
+	tracker-file-utils.c				\
+	tracker-ioprio.c				\
+	tracker-language.c				\
+	tracker-log.c	 				\
+	tracker-module-config.c				\
+	tracker-nfs-lock.c				\
+	tracker-ontology.c				\
+	tracker-parser.c				\
+	tracker-service.c				\
+	tracker-type-utils.c				\
+	tracker-utils.c
+
+noinst_HEADERS =					\
+	$(hal_headers)					\
+	tracker-config.h				\
+	tracker-configuration.h				\
+	tracker-dbus.h					\
+	tracker-field-data.h				\
+	tracker-field.h					\
+	tracker-file-utils.h				\
+	tracker-ioprio.h				\
+	tracker-language.h				\
+	tracker-log.h					\
+	tracker-module-config.h				\
+	tracker-nfs-lock.h				\
+	tracker-ontology.h				\
+	tracker-os-dependant.h				\
+	tracker-parser.h				\
+	tracker-service.h				\
+	tracker-type-utils.h				\
+	tracker-utils.h
+
+libtracker_common_la_LDFLAGS = -version-info 0:0:0
+libtracker_common_la_LIBADD = 				\
+	$(top_builddir)/src/libstemmer/libstemmer.la	\
+	$(HAL_LIBS)					\
+	$(DBUS_LIBS)					\
+	$(UNAC_LIBS)					\
+	$(PANGO_LIBS)					\
+	$(GIO_LIBS)					\
+	$(GLIB2_LIBS)
+

Added: trunk/src/libtracker-common/tracker-config.c
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-config.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,2211 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2007, Michal Pryc (Michal Pryc Sun Com)
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include "tracker-language.h"
+#include "tracker-config.h"
+#include "tracker-file-utils.h"
+
+#define TRACKER_CONFIG_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TRACKER_TYPE_CONFIG, TrackerConfigPrivate))
+
+/* GKeyFile defines */
+#define GROUP_GENERAL				 "General"
+#define KEY_VERBOSITY				 "Verbosity"
+#define KEY_INITIAL_SLEEP			 "InitialSleep"
+#define KEY_LOW_MEMORY_MODE			 "LowMemoryMode"
+#define KEY_NFS_LOCKING				 "NFSLocking"
+#define GROUP_WATCHES				 "Watches"
+#define KEY_WATCH_DIRECTORY_ROOTS		 "WatchDirectoryRoots"
+#define KEY_CRAWL_DIRECTORY_ROOTS		 "CrawlDirectory"
+#define KEY_NO_WATCH_DIRECTORY_ROOTS		 "NoWatchDirectory"
+#define KEY_ENABLE_WATCHES			 "EnableWatching"
+
+#define GROUP_INDEXING				 "Indexing"
+#define KEY_THROTTLE				 "Throttle"
+#define KEY_ENABLE_INDEXING			 "EnableIndexing"
+#define KEY_ENABLE_CONTENT_INDEXING		 "EnableFileContentIndexing"
+#define KEY_ENABLE_THUMBNAILS			 "EnableThumbnails"
+#define KEY_DISABLED_MODULES			 "DisabledModules"
+#define KEY_FAST_MERGES				 "FastMerges"
+#define KEY_NO_INDEX_FILE_TYPES			 "NoIndexFileTypes"
+#define KEY_MIN_WORD_LENGTH			 "MinWorldLength"
+#define KEY_MAX_WORD_LENGTH			 "MaxWorldLength"
+#define KEY_LANGUAGE				 "Language"
+#define KEY_ENABLE_STEMMER			 "EnableStemmer"
+#define KEY_DISABLE_INDEXING_ON_BATTERY		 "BatteryIndex"
+#define KEY_DISABLE_INDEXING_ON_BATTERY_INIT	 "BatteryIndexInitial"
+#define KEY_LOW_DISK_SPACE_LIMIT		 "LowDiskSpaceLimit"
+#define KEY_INDEX_MOUNTED_DIRECTORIES		 "IndexMountedDirectories"
+#define KEY_INDEX_REMOVABLE_DEVICES		 "IndexRemovableMedia"
+
+#define GROUP_PERFORMANCE			 "Performance"
+#define KEY_MAX_TEXT_TO_INDEX			 "MaxTextToIndex"
+#define KEY_MAX_WORDS_TO_INDEX			 "MaxWordsToIndex"
+#define KEY_MAX_BUCKET_COUNT			 "MaxBucketCount"
+#define KEY_MIN_BUCKET_COUNT			 "MinBucketCount"
+
+#define GROUP_SERVICES				 "Services"
+#define KEY_ENABLE_XESAM			 "EnableXesam"
+
+/* Default values */
+#define DEFAULT_VERBOSITY			 0
+#define DEFAULT_INITIAL_SLEEP			 45	  /* 0->1000 */
+#define DEFAULT_LOW_MEMORY_MODE			 FALSE
+#define DEFAULT_NFS_LOCKING			 FALSE
+#define DEFAULT_ENABLE_WATCHES			 TRUE
+#define DEFAULT_THROTTLE			 0	  /* 0->20 */
+#define DEFAULT_ENABLE_INDEXING			 TRUE
+#define DEFAULT_ENABLE_XESAM			 FALSE
+#define DEFAULT_ENABLE_CONTENT_INDEXING		 TRUE
+#define DEFAULT_ENABLE_THUMBNAILS		 TRUE
+#define DEFAULT_FAST_MERGES			 FALSE
+#define DEFAULT_MIN_WORD_LENGTH			 3	  /* 0->30 */
+#define DEFAULT_MAX_WORD_LENGTH			 30	  /* 0->200 */
+#define DEFAULT_ENABLE_STEMMER			 TRUE
+#define DEFAULT_DISABLE_INDEXING_ON_BATTERY	 TRUE
+#define DEFAULT_DISABLE_INDEXING_ON_BATTERY_INIT FALSE
+#define DEFAULT_INDEX_MOUNTED_DIRECTORIES	 TRUE
+#define DEFAULT_INDEX_REMOVABLE_DEVICES		 TRUE
+#define DEFAULT_LOW_DISK_SPACE_LIMIT		 1	  /* 0->100 / -1 */
+#define DEFAULT_MAX_TEXT_TO_INDEX		 1048576  /* Bytes */
+#define DEFAULT_MAX_WORDS_TO_INDEX		 10000
+#define DEFAULT_MAX_BUCKET_COUNT		 524288
+#define DEFAULT_MIN_BUCKET_COUNT		 65536
+
+typedef struct _TrackerConfigPrivate TrackerConfigPrivate;
+
+struct _TrackerConfigPrivate {
+	GFile	     *file;
+	GFileMonitor *monitor;
+
+	/* General */
+	gint	      verbosity;
+	gint	      initial_sleep;
+	gboolean      low_memory_mode;
+	gboolean      nfs_locking;
+
+	/* Watches */
+	GSList	     *watch_directory_roots;
+	GSList	     *crawl_directory_roots;
+	GSList	     *no_watch_directory_roots;
+	gboolean      enable_watches;
+
+	/* Indexing */
+	gint	      throttle;
+	gboolean      enable_indexing;
+	gboolean      enable_content_indexing;
+	gboolean      enable_thumbnails;
+	GSList	     *disabled_modules;
+	gboolean      fast_merges;
+	GSList	     *no_index_file_types;
+	gint	      min_word_length;
+	gint	      max_word_length;
+	gchar	     *language;
+	gboolean      enable_stemmer;
+	gboolean      disable_indexing_on_battery;
+	gboolean      disable_indexing_on_battery_init;
+	gint	      low_disk_space_limit;
+	gboolean      index_mounted_directories;
+	gboolean      index_removable_devices;
+
+	/* Performance */
+	gint	      max_text_to_index;
+	gint	      max_words_to_index;
+	gint	      max_bucket_count;
+	gint	      min_bucket_count;
+
+	/* Services*/
+	gboolean      enable_xesam;
+};
+
+static void config_finalize	(GObject      *object);
+static void config_get_property (GObject      *object,
+				 guint	       param_id,
+				 GValue	      *value,
+				 GParamSpec   *pspec);
+static void config_set_property (GObject      *object,
+				 guint	       param_id,
+				 const GValue *value,
+				 GParamSpec   *pspec);
+static void config_load		(TrackerConfig *config);
+
+enum {
+	PROP_0,
+
+	/* General */
+	PROP_VERBOSITY,
+	PROP_INITIAL_SLEEP,
+	PROP_LOW_MEMORY_MODE,
+	PROP_NFS_LOCKING,
+
+	/* Watches */
+	PROP_WATCH_DIRECTORY_ROOTS,
+	PROP_CRAWL_DIRECTORY_ROOTS,
+	PROP_NO_WATCH_DIRECTORY_ROOTS,
+	PROP_ENABLE_WATCHES,
+
+	/* Indexing */
+	PROP_THROTTLE,
+	PROP_ENABLE_INDEXING,
+	PROP_ENABLE_CONTENT_INDEXING,
+	PROP_ENABLE_THUMBNAILS,
+	PROP_DISABLED_MODULES,
+	PROP_FAST_MERGES,
+	PROP_NO_INDEX_FILE_TYPES,
+	PROP_MIN_WORD_LENGTH,
+	PROP_MAX_WORD_LENGTH,
+	PROP_LANGUAGE,
+	PROP_ENABLE_STEMMER,
+	PROP_DISABLE_INDEXING_ON_BATTERY,
+	PROP_DISABLE_INDEXING_ON_BATTERY_INIT,
+	PROP_LOW_DISK_SPACE_LIMIT,
+	PROP_INDEX_MOUNTED_DIRECTORIES,
+	PROP_INDEX_REMOVABLE_DEVICES,
+
+	/* Performance */
+	PROP_MAX_TEXT_TO_INDEX,
+	PROP_MAX_WORDS_TO_INDEX,
+	PROP_MAX_BUCKET_COUNT,
+	PROP_MIN_BUCKET_COUNT,
+
+	/* Services*/
+	PROP_ENABLE_XESAM
+};
+
+G_DEFINE_TYPE (TrackerConfig, tracker_config, G_TYPE_OBJECT);
+
+static void
+tracker_config_class_init (TrackerConfigClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	object_class->finalize	   = config_finalize;
+	object_class->get_property = config_get_property;
+	object_class->set_property = config_set_property;
+
+	/* General */
+	g_object_class_install_property (object_class,
+					 PROP_VERBOSITY,
+					 g_param_spec_int ("verbosity",
+							   "Log verbosity",
+							   "How much logging we have "
+							   "(0=errors, 1=minimal, 2=detailed, 3=debug)",
+							   0,
+							   3,
+							   DEFAULT_VERBOSITY,
+							   G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_class_install_property (object_class,
+					 PROP_INITIAL_SLEEP,
+					 g_param_spec_int ("initial-sleep",
+							   "Initial sleep",
+							   "Initial sleep time in seconds "
+							   "(0->1000)",
+							   0,
+							   1000,
+							   DEFAULT_INITIAL_SLEEP,
+							   G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_class_install_property (object_class,
+					 PROP_LOW_MEMORY_MODE,
+					 g_param_spec_boolean ("low-memory-mode",
+							       "Use extra memory",
+							       "Use extra memory at the "
+							       "expense of indexing speed",
+							       DEFAULT_LOW_MEMORY_MODE,
+							       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_class_install_property (object_class,
+					 PROP_NFS_LOCKING,
+					 g_param_spec_boolean ("nfs-locking",
+							       "Use NFS friendly location for lock file",
+							       "In NFS filesystems is not safe to have "
+							       "the lock file in the home directory",
+							       DEFAULT_NFS_LOCKING,
+							       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+	/* Watches */
+	g_object_class_install_property (object_class,
+					 PROP_WATCH_DIRECTORY_ROOTS,
+					 g_param_spec_pointer ("watch-directory-roots",
+							       "Watched directory roots",
+							       "This is a GSList of directory roots "
+							       "to index and watch",
+							       G_PARAM_READABLE));
+	g_object_class_install_property (object_class,
+					 PROP_CRAWL_DIRECTORY_ROOTS,
+					 g_param_spec_pointer ("crawl-directory-roots",
+							       "Crawl directory roots",
+							       "This is a GSList of directory roots "
+							       "to index but NOT watch",
+							       G_PARAM_READABLE));
+	g_object_class_install_property (object_class,
+					 PROP_NO_WATCH_DIRECTORY_ROOTS,
+					 g_param_spec_pointer ("no-watch-directory-roots",
+							       "Not watched directory roots",
+							       "This is a GSList of directory roots "
+							       "to NOT index and NOT watch",
+							       G_PARAM_READABLE));
+	g_object_class_install_property (object_class,
+					 PROP_ENABLE_WATCHES,
+					 g_param_spec_boolean ("enable-watches",
+							       "Enable watches",
+							       "You can disable all watches "
+							       "by setting this FALSE",
+							       DEFAULT_ENABLE_WATCHES,
+							       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+	/* Indexing */
+	g_object_class_install_property (object_class,
+					 PROP_THROTTLE,
+					 g_param_spec_int ("throttle",
+							   "Throttle",
+							   "Throttle indexing, higher value "
+							   "is slower (0->20)",
+							   0,
+							   20,
+							   DEFAULT_THROTTLE,
+							   G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_class_install_property (object_class,
+					 PROP_ENABLE_INDEXING,
+					 g_param_spec_boolean ("enable-indexing",
+							       "Enable indexing",
+							       "All indexing",
+							       DEFAULT_ENABLE_INDEXING,
+							       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_class_install_property (object_class,
+					 PROP_ENABLE_CONTENT_INDEXING,
+					 g_param_spec_boolean ("enable-content-indexing",
+							       "Enable content indexing",
+							       "Content specific indexing "
+							       "(i.e. file content)",
+							       DEFAULT_ENABLE_CONTENT_INDEXING,
+							       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_class_install_property (object_class,
+					 PROP_ENABLE_THUMBNAILS,
+					 g_param_spec_boolean ("enable-thumbnails",
+							       "Enable thumbnails",
+							       "Create thumbnails from image based files",
+							       DEFAULT_ENABLE_THUMBNAILS,
+							       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_class_install_property (object_class,
+					 PROP_DISABLED_MODULES,
+					 g_param_spec_pointer ("disabled-modules",
+							       "Disabled modules",
+							       "Modules to disable, like 'files', etc.",
+							       G_PARAM_READABLE));
+	g_object_class_install_property (object_class,
+					 PROP_FAST_MERGES,
+					 g_param_spec_boolean ("fast-merges",
+							       "Fast merges",
+							       "Spends more disk usage if TRUE",
+							       DEFAULT_FAST_MERGES,
+							       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_class_install_property (object_class,
+					 PROP_NO_INDEX_FILE_TYPES,
+					 g_param_spec_pointer ("no-index-file-types",
+							       "File types to not index",
+							       "This is a GSList of file types "
+							       "to NOT index",
+							       G_PARAM_READABLE));
+	g_object_class_install_property (object_class,
+					 PROP_MIN_WORD_LENGTH,
+					 g_param_spec_int ("min-word-length",
+							   "Minimum word length",
+							   "Minimum word length used to index "
+							   "(0->30)",
+							   0,
+							   30,
+							   DEFAULT_MIN_WORD_LENGTH,
+							   G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_class_install_property (object_class,
+					 PROP_MAX_WORD_LENGTH,
+					 g_param_spec_int ("max-word-length",
+							   "Maximum word length",
+							   "Maximum word length used to index",
+							   0,
+							   200, /* Is this a reasonable limit? */
+							   DEFAULT_MAX_WORD_LENGTH,
+							   G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_class_install_property (object_class,
+					 PROP_LANGUAGE,
+					 g_param_spec_string ("language",
+							      "Language",
+							      "Language to use with stemming "
+							      "('en', 'fr', 'sv', etc)",
+							      "en",
+							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_class_install_property (object_class,
+					 PROP_ENABLE_STEMMER,
+					 g_param_spec_boolean ("enable-stemmer",
+							       "Enable stemmer",
+							       "Language specific stemmer",
+							       DEFAULT_ENABLE_STEMMER,
+							       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_class_install_property (object_class,
+					 PROP_DISABLE_INDEXING_ON_BATTERY,
+					 g_param_spec_boolean ("disable-indexing-on-battery",
+							       "Disable indexing on battery",
+							       "Don't index when using AC battery",
+							       DEFAULT_DISABLE_INDEXING_ON_BATTERY,
+							       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_class_install_property (object_class,
+					 PROP_DISABLE_INDEXING_ON_BATTERY_INIT,
+					 g_param_spec_boolean ("disable-indexing-on-battery-init",
+							       "Disable indexing on battery",
+							       "Don't index when using AC "
+							       "battery initially",
+							       DEFAULT_DISABLE_INDEXING_ON_BATTERY_INIT,
+							       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_class_install_property (object_class,
+					 PROP_LOW_DISK_SPACE_LIMIT,
+					 g_param_spec_int ("low-disk-space-limit",
+							   "Low disk space limit",
+							   "Pause the indexer when the "
+							   "disk space is below this percentage "
+							   "(-1=off, 0->100)",
+							   -1,
+							   100,
+							   DEFAULT_LOW_DISK_SPACE_LIMIT,
+							   G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_class_install_property (object_class,
+					 PROP_INDEX_MOUNTED_DIRECTORIES,
+					 g_param_spec_boolean ("index-mounted-directories",
+							       "Index mounted directories",
+							       "Don't traverse mounted directories "
+							       "which are not on the same file system",
+							       DEFAULT_INDEX_MOUNTED_DIRECTORIES,
+							       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_class_install_property (object_class,
+					 PROP_INDEX_REMOVABLE_DEVICES,
+					 g_param_spec_boolean ("index-removable-devices",
+							       "index removable devices",
+							       "Don't traverse mounted directories "
+							       "which are for removable devices",
+							       DEFAULT_INDEX_REMOVABLE_DEVICES,
+							       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+	/* Performance */
+	g_object_class_install_property (object_class,
+					 PROP_MAX_TEXT_TO_INDEX,
+					 g_param_spec_int ("max-text-to-index",
+							   "Maximum text to index",
+							   "Maximum text in bytes to index "
+							   "from file's content",
+							   0,
+							   G_MAXINT,
+							   DEFAULT_MAX_TEXT_TO_INDEX,
+							   G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_class_install_property (object_class,
+					 PROP_MAX_WORDS_TO_INDEX,
+					 g_param_spec_int ("max-words-to-index",
+							   "Maximum words to index",
+							   "Maximum unique words to index "
+							   "from file's content",
+							   0,
+							   G_MAXINT,
+							   DEFAULT_MAX_WORDS_TO_INDEX,
+							   G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_class_install_property (object_class,
+					 PROP_MAX_BUCKET_COUNT,
+					 g_param_spec_int ("max-bucket-count",
+							   "Maximum bucket count",
+							   "Maximum bucket count (1000->524288)",
+							   1000,
+							   G_MAXINT, /* FIXME: Is this reasonable? */
+							   DEFAULT_MAX_BUCKET_COUNT,
+							   G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_class_install_property (object_class,
+					 PROP_MIN_BUCKET_COUNT,
+					 g_param_spec_int ("min-bucket-count",
+							   "Minimum bucket count",
+							   "Minimum bucket count (1000->65536)",
+							   1000,
+							   G_MAXINT,
+							   DEFAULT_MIN_BUCKET_COUNT,
+							   G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+	/* Services */
+	g_object_class_install_property (object_class,
+					 PROP_ENABLE_XESAM,
+					 g_param_spec_boolean ("enable-xesam",
+							       "Enable Xesam",
+							       "Xesam DBus service",
+							       DEFAULT_ENABLE_XESAM,
+							       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+	g_type_class_add_private (object_class, sizeof (TrackerConfigPrivate));
+}
+
+static void
+tracker_config_init (TrackerConfig *object)
+{
+}
+
+static void
+config_finalize (GObject *object)
+{
+	TrackerConfigPrivate *priv;
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (object);
+
+	g_slist_foreach (priv->watch_directory_roots, (GFunc) g_free, NULL);
+	g_slist_free (priv->watch_directory_roots);
+
+	g_slist_foreach (priv->crawl_directory_roots, (GFunc) g_free, NULL);
+	g_slist_free (priv->crawl_directory_roots);
+
+	g_slist_foreach (priv->no_watch_directory_roots, (GFunc) g_free, NULL);
+	g_slist_free (priv->no_watch_directory_roots);
+
+	g_slist_foreach (priv->no_index_file_types, (GFunc) g_free, NULL);
+	g_slist_free (priv->no_index_file_types);
+
+	g_free (priv->language);
+
+	if (priv->monitor) {
+		g_object_unref (priv->monitor);
+	}
+
+	if (priv->file) {
+		g_object_unref (priv->file);
+	}
+
+	(G_OBJECT_CLASS (tracker_config_parent_class)->finalize) (object);
+}
+
+static void
+config_get_property (GObject	*object,
+		     guint	 param_id,
+		     GValue	*value,
+		     GParamSpec *pspec)
+{
+	TrackerConfigPrivate *priv;
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (object);
+
+	switch (param_id) {
+		/* General */
+	case PROP_VERBOSITY:
+		g_value_set_int (value, priv->verbosity);
+		break;
+	case PROP_INITIAL_SLEEP:
+		g_value_set_int (value, priv->initial_sleep);
+		break;
+	case PROP_LOW_MEMORY_MODE:
+		g_value_set_boolean (value, priv->low_memory_mode);
+		break;
+	case PROP_NFS_LOCKING:
+		g_value_set_boolean (value, priv->nfs_locking);
+		break;
+
+		/* Watches */
+	case PROP_WATCH_DIRECTORY_ROOTS:
+		g_value_set_pointer (value, priv->watch_directory_roots);
+		break;
+	case PROP_CRAWL_DIRECTORY_ROOTS:
+		g_value_set_pointer (value, priv->crawl_directory_roots);
+		break;
+	case PROP_NO_WATCH_DIRECTORY_ROOTS:
+		g_value_set_pointer (value, priv->no_watch_directory_roots);
+		break;
+	case PROP_ENABLE_WATCHES:
+		g_value_set_boolean (value, priv->enable_watches);
+		break;
+
+		/* Indexing */
+	case PROP_THROTTLE:
+		g_value_set_int (value, priv->throttle);
+		break;
+	case PROP_ENABLE_INDEXING:
+		g_value_set_boolean (value, priv->enable_indexing);
+		break;
+	case PROP_ENABLE_CONTENT_INDEXING:
+		g_value_set_boolean (value, priv->enable_content_indexing);
+		break;
+	case PROP_ENABLE_THUMBNAILS:
+		g_value_set_boolean (value, priv->enable_thumbnails);
+		break;
+	case PROP_DISABLED_MODULES:
+		g_value_set_pointer (value, priv->disabled_modules);
+		break;
+	case PROP_FAST_MERGES:
+		g_value_set_boolean (value, priv->fast_merges);
+		break;
+	case PROP_NO_INDEX_FILE_TYPES:
+		g_value_set_pointer (value, priv->no_index_file_types);
+		break;
+	case PROP_MIN_WORD_LENGTH:
+		g_value_set_int (value, priv->min_word_length);
+		break;
+	case PROP_MAX_WORD_LENGTH:
+		g_value_set_int (value, priv->max_word_length);
+		break;
+	case PROP_LANGUAGE:
+		g_value_set_string (value, priv->language);
+		break;
+	case PROP_ENABLE_STEMMER:
+		g_value_set_boolean (value, priv->enable_stemmer);
+		break;
+	case PROP_DISABLE_INDEXING_ON_BATTERY:
+		g_value_set_boolean (value, priv->disable_indexing_on_battery);
+		break;
+	case PROP_DISABLE_INDEXING_ON_BATTERY_INIT:
+		g_value_set_boolean (value, priv->disable_indexing_on_battery_init);
+		break;
+	case PROP_LOW_DISK_SPACE_LIMIT:
+		g_value_set_int (value, priv->low_disk_space_limit);
+		break;
+	case PROP_INDEX_MOUNTED_DIRECTORIES:
+		g_value_set_boolean (value, priv->index_mounted_directories);
+		break;
+	case PROP_INDEX_REMOVABLE_DEVICES:
+		g_value_set_boolean (value, priv->index_removable_devices);
+		break;
+
+		/* Performance */
+	case PROP_MAX_TEXT_TO_INDEX:
+		g_value_set_int (value, priv->max_text_to_index);
+		break;
+	case PROP_MAX_WORDS_TO_INDEX:
+		g_value_set_int (value, priv->max_words_to_index);
+		break;
+	case PROP_MAX_BUCKET_COUNT:
+		g_value_set_int (value, priv->max_bucket_count);
+		break;
+	case PROP_MIN_BUCKET_COUNT:
+		g_value_set_int (value, priv->min_bucket_count);
+		break;
+
+	/* Services */
+	case PROP_ENABLE_XESAM:
+		g_value_set_boolean (value, priv->enable_xesam);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	};
+}
+
+static void
+config_set_property (GObject	  *object,
+		     guint	   param_id,
+		     const GValue *value,
+		     GParamSpec	  *pspec)
+{
+	TrackerConfigPrivate *priv;
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (object);
+
+	switch (param_id) {
+		/* General */
+	case PROP_VERBOSITY:
+		tracker_config_set_verbosity (TRACKER_CONFIG (object),
+					      g_value_get_int (value));
+		break;
+	case PROP_INITIAL_SLEEP:
+		tracker_config_set_initial_sleep (TRACKER_CONFIG (object),
+						  g_value_get_int (value));
+		break;
+	case PROP_LOW_MEMORY_MODE:
+		tracker_config_set_low_memory_mode (TRACKER_CONFIG (object),
+						    g_value_get_boolean (value));
+		break;
+	case PROP_NFS_LOCKING:
+		tracker_config_set_nfs_locking (TRACKER_CONFIG (object),
+						g_value_get_boolean (value));
+		break;
+
+		/* Watches */
+	case PROP_WATCH_DIRECTORY_ROOTS:    /* Not writable */
+	case PROP_CRAWL_DIRECTORY_ROOTS:    /* Not writable */
+	case PROP_NO_WATCH_DIRECTORY_ROOTS: /* Not writable */
+		break;
+	case PROP_ENABLE_WATCHES:
+		tracker_config_set_enable_watches (TRACKER_CONFIG (object),
+						   g_value_get_boolean (value));
+		break;
+
+		/* Indexing */
+	case PROP_THROTTLE:
+		tracker_config_set_throttle (TRACKER_CONFIG (object),
+					     g_value_get_int (value));
+		break;
+	case PROP_ENABLE_INDEXING:
+		tracker_config_set_enable_indexing (TRACKER_CONFIG (object),
+						    g_value_get_boolean (value));
+		break;
+	case PROP_ENABLE_CONTENT_INDEXING:
+		tracker_config_set_enable_content_indexing (TRACKER_CONFIG (object),
+							    g_value_get_boolean (value));
+		break;
+	case PROP_ENABLE_THUMBNAILS:
+		tracker_config_set_enable_thumbnails (TRACKER_CONFIG (object),
+						      g_value_get_boolean (value));
+		break;
+	case PROP_DISABLED_MODULES:
+		/* Not writable */
+		break;
+	case PROP_FAST_MERGES:
+		tracker_config_set_fast_merges (TRACKER_CONFIG (object),
+						g_value_get_boolean (value));
+		break;
+	case PROP_NO_INDEX_FILE_TYPES:
+		/* Not writable */
+		break;
+	case PROP_MIN_WORD_LENGTH:
+		tracker_config_set_min_word_length (TRACKER_CONFIG (object),
+						    g_value_get_int (value));
+		break;
+	case PROP_MAX_WORD_LENGTH:
+		tracker_config_set_max_word_length (TRACKER_CONFIG (object),
+						    g_value_get_int (value));
+		break;
+	case PROP_LANGUAGE:
+		tracker_config_set_language (TRACKER_CONFIG (object),
+					     g_value_get_string (value));
+		break;
+	case PROP_ENABLE_STEMMER:
+		tracker_config_set_enable_stemmer (TRACKER_CONFIG (object),
+						   g_value_get_boolean (value));
+		break;
+	case PROP_DISABLE_INDEXING_ON_BATTERY:
+		tracker_config_set_disable_indexing_on_battery (TRACKER_CONFIG (object),
+								g_value_get_boolean (value));
+		break;
+	case PROP_DISABLE_INDEXING_ON_BATTERY_INIT:
+		tracker_config_set_disable_indexing_on_battery_init (TRACKER_CONFIG (object),
+								     g_value_get_boolean (value));
+		break;
+	case PROP_LOW_DISK_SPACE_LIMIT:
+		tracker_config_set_low_disk_space_limit (TRACKER_CONFIG (object),
+							 g_value_get_int (value));
+		break;
+	case PROP_INDEX_MOUNTED_DIRECTORIES:
+		tracker_config_set_index_mounted_directories (TRACKER_CONFIG (object),
+							      g_value_get_boolean (value));
+		break;
+	case PROP_INDEX_REMOVABLE_DEVICES:
+		tracker_config_set_index_removable_devices (TRACKER_CONFIG (object),
+								g_value_get_boolean (value));
+		break;
+
+		/* Performance */
+	case PROP_MAX_TEXT_TO_INDEX:
+		tracker_config_set_max_text_to_index (TRACKER_CONFIG (object),
+						      g_value_get_int (value));
+		break;
+	case PROP_MAX_WORDS_TO_INDEX:
+		tracker_config_set_max_words_to_index (TRACKER_CONFIG (object),
+						       g_value_get_int (value));
+		break;
+	case PROP_MAX_BUCKET_COUNT:
+		tracker_config_set_max_bucket_count (TRACKER_CONFIG (object),
+						     g_value_get_int (value));
+		break;
+	case PROP_MIN_BUCKET_COUNT:
+		tracker_config_set_min_bucket_count (TRACKER_CONFIG (object),
+						     g_value_get_int (value));
+		break;
+
+	/* Services */
+	case PROP_ENABLE_XESAM:
+		tracker_config_set_enable_xesam (TRACKER_CONFIG (object),
+						    g_value_get_boolean (value));
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	};
+}
+
+static gchar *
+config_dir_ensure_exists_and_return (void)
+{
+	gchar *directory;
+
+	directory = g_build_filename (g_get_user_config_dir (),
+				      "tracker",
+				      NULL);
+
+	if (!g_file_test (directory, G_FILE_TEST_EXISTS)) {
+		g_print ("Creating config directory:'%s'\n", directory);
+
+		if (g_mkdir_with_parents (directory, 0700) == -1) {
+			g_warning ("Could not create configuration directory");
+			g_free (directory);
+			return NULL;
+		}
+	}
+
+	return directory;
+}
+
+static gboolean
+config_create_with_defaults (const gchar *filename,
+			     GKeyFile	 *key_file)
+{
+	GError	     *error = NULL;
+	gchar	     *content = NULL;
+	gchar	     *language;
+	const gchar  *watch_directory_roots[2] = { NULL, NULL };
+	const gchar  *empty_string_list[] = { NULL };
+
+	/* Get default values */
+	language = tracker_language_get_default_code ();
+
+	watch_directory_roots[0] = g_get_home_dir ();
+
+	/* General */
+	g_key_file_set_integer (key_file, GROUP_GENERAL, KEY_VERBOSITY, DEFAULT_VERBOSITY);
+	g_key_file_set_comment (key_file, GROUP_GENERAL, KEY_VERBOSITY,
+				" Log Verbosity (0=errors, 1=minimal, 2=detailed, 3=debug)",
+				NULL);
+	g_key_file_set_integer (key_file, GROUP_GENERAL, KEY_INITIAL_SLEEP, DEFAULT_INITIAL_SLEEP);
+	g_key_file_set_comment (key_file, GROUP_GENERAL, KEY_INITIAL_SLEEP,
+				" Initial sleep time in seconds (0->1000)",
+				NULL);
+	g_key_file_set_boolean (key_file, GROUP_GENERAL, KEY_LOW_MEMORY_MODE, DEFAULT_LOW_MEMORY_MODE);
+	g_key_file_set_comment (key_file, GROUP_GENERAL, KEY_LOW_MEMORY_MODE,
+				" Minimizes memory use at the expense of indexing speed",
+				NULL);
+	g_key_file_set_boolean (key_file, GROUP_GENERAL, KEY_NFS_LOCKING, DEFAULT_NFS_LOCKING);
+	g_key_file_set_comment (key_file, GROUP_GENERAL, KEY_NFS_LOCKING,
+				" Set to TRUE when the home directory is in a NFS filesystem",
+				NULL);
+
+
+	/* Watches */
+	g_key_file_set_string_list (key_file, GROUP_WATCHES, KEY_WATCH_DIRECTORY_ROOTS,
+				    watch_directory_roots, 1);
+	g_key_file_set_comment (key_file, GROUP_WATCHES, KEY_WATCH_DIRECTORY_ROOTS,
+				" List of directory roots to index and watch (separator=;)",
+				NULL);
+	g_key_file_set_string_list (key_file, GROUP_WATCHES, KEY_CRAWL_DIRECTORY_ROOTS,
+				    empty_string_list, 0);
+	g_key_file_set_comment (key_file, GROUP_WATCHES, KEY_CRAWL_DIRECTORY_ROOTS,
+				" List of directory roots to index but NOT watch (separator=;)",
+				NULL);
+	g_key_file_set_string_list (key_file, GROUP_WATCHES, KEY_NO_WATCH_DIRECTORY_ROOTS,
+				    empty_string_list, 0);
+	g_key_file_set_comment (key_file, GROUP_WATCHES, KEY_NO_WATCH_DIRECTORY_ROOTS,
+				" List of directory roots NOT to index and NOT to watch (separator=;)",
+				NULL);
+	g_key_file_set_boolean (key_file, GROUP_WATCHES, KEY_ENABLE_WATCHES, DEFAULT_ENABLE_WATCHES);
+	g_key_file_set_comment (key_file, GROUP_WATCHES, KEY_ENABLE_WATCHES,
+				" Set to false to completely disable any watching",
+				NULL);
+
+	/* Indexing */
+	g_key_file_set_integer (key_file, GROUP_INDEXING, KEY_THROTTLE, DEFAULT_THROTTLE);
+	g_key_file_set_comment (key_file, GROUP_INDEXING, KEY_THROTTLE,
+				" Sets the indexing speed (0->20, where 20=slowest speed)",
+				NULL);
+
+	g_key_file_set_boolean (key_file, GROUP_INDEXING, KEY_ENABLE_INDEXING, DEFAULT_ENABLE_INDEXING);
+	g_key_file_set_comment (key_file, GROUP_INDEXING, KEY_ENABLE_INDEXING,
+				" Set to false to completely disable any indexing",
+				NULL);
+	g_key_file_set_boolean (key_file, GROUP_INDEXING, KEY_ENABLE_CONTENT_INDEXING, DEFAULT_ENABLE_CONTENT_INDEXING);
+	g_key_file_set_comment (key_file, GROUP_INDEXING, KEY_ENABLE_CONTENT_INDEXING,
+				" Set to false to completely disable file content indexing",
+				NULL);
+	g_key_file_set_boolean (key_file, GROUP_INDEXING, KEY_ENABLE_THUMBNAILS, DEFAULT_ENABLE_THUMBNAILS);
+	g_key_file_set_comment (key_file, GROUP_INDEXING, KEY_ENABLE_THUMBNAILS,
+				" Set to false to completely disable thumbnail generation",
+				NULL);
+	g_key_file_set_string_list (key_file, GROUP_INDEXING, KEY_DISABLED_MODULES,
+				    empty_string_list, 0);
+	g_key_file_set_comment (key_file, GROUP_INDEXING, KEY_DISABLED_MODULES,
+				" List of disabled modules (separator=;)\n"
+				" The modules that are indexed are kept in $prefix/share/tracker/modules",
+				NULL);
+	g_key_file_set_boolean (key_file, GROUP_INDEXING, KEY_FAST_MERGES, DEFAULT_FAST_MERGES);
+	g_key_file_set_comment (key_file, GROUP_INDEXING, KEY_FAST_MERGES,
+				" Set to false to NOT hog the disk for extended periods",
+				NULL);
+	g_key_file_set_string_list (key_file, GROUP_INDEXING, KEY_NO_INDEX_FILE_TYPES,
+				    empty_string_list, 0);
+	g_key_file_set_comment (key_file, GROUP_INDEXING, KEY_NO_INDEX_FILE_TYPES,
+				" List of partial file pattern globs (separator=;)\n"
+				" This is for files to NOT index\n"
+				" (basic stat info is only extended for files that match the patterns)",
+				NULL);
+	g_key_file_set_integer (key_file, GROUP_INDEXING, KEY_MIN_WORD_LENGTH, DEFAULT_MIN_WORD_LENGTH);
+	g_key_file_set_comment (key_file, GROUP_INDEXING, KEY_MIN_WORD_LENGTH,
+				" Set the minimum length of words to index (0->30, default=3)",
+				NULL);
+	g_key_file_set_integer (key_file, GROUP_INDEXING, KEY_MAX_WORD_LENGTH, DEFAULT_MAX_WORD_LENGTH);
+	g_key_file_set_comment (key_file, GROUP_INDEXING, KEY_MAX_WORD_LENGTH,
+				" Set the maximum length of words to index (0->200, default=30)",
+				NULL);
+	g_key_file_set_string (key_file, GROUP_INDEXING, KEY_LANGUAGE, language);
+	g_key_file_set_comment (key_file, GROUP_INDEXING, KEY_LANGUAGE,
+				" Set the language specific stemmer and stopword list to use\n"
+				" Values include:\n"
+				" - en (English)\n"
+				" - da (Danish)\n"
+				" - nl (Dutch)\n"
+				" - fi (Finish)\n"
+				" - fr (French)\n"
+				" - de (German)\n"
+				" - it (Italian)\n"
+				" - nb (Norwegian)\n"
+				" - pt (Portugese)\n"
+				" - ru (Russian)\n"
+				" - es (Spanish)\n"
+				" - sv (Swedish)",
+				NULL);
+	g_key_file_set_boolean (key_file, GROUP_INDEXING, KEY_ENABLE_STEMMER, DEFAULT_ENABLE_STEMMER);
+	g_key_file_set_comment (key_file, GROUP_INDEXING, KEY_ENABLE_STEMMER,
+				" Set to false to disable language specific stemmer",
+				NULL);
+	g_key_file_set_boolean (key_file, GROUP_INDEXING, KEY_DISABLE_INDEXING_ON_BATTERY, DEFAULT_DISABLE_INDEXING_ON_BATTERY);
+	g_key_file_set_comment (key_file, GROUP_INDEXING, KEY_DISABLE_INDEXING_ON_BATTERY,
+				" Set to true to disable indexing when running on battery",
+				NULL);
+	g_key_file_set_boolean (key_file, GROUP_INDEXING, KEY_DISABLE_INDEXING_ON_BATTERY_INIT, DEFAULT_DISABLE_INDEXING_ON_BATTERY_INIT);
+	g_key_file_set_comment (key_file, GROUP_INDEXING, KEY_DISABLE_INDEXING_ON_BATTERY_INIT,
+				" Set to true to disable initial indexing when running on battery",
+				NULL);
+	g_key_file_set_integer (key_file, GROUP_INDEXING, KEY_LOW_DISK_SPACE_LIMIT, DEFAULT_LOW_DISK_SPACE_LIMIT);
+	g_key_file_set_comment (key_file, GROUP_INDEXING, KEY_LOW_DISK_SPACE_LIMIT,
+				" Pause indexer when disk space is <= this value\n"
+				" (0->100, value is in % of $HOME file system, -1=disable pausing)",
+				NULL);
+	g_key_file_set_boolean (key_file, GROUP_INDEXING, KEY_INDEX_MOUNTED_DIRECTORIES, DEFAULT_INDEX_MOUNTED_DIRECTORIES);
+	g_key_file_set_comment (key_file, GROUP_INDEXING, KEY_INDEX_MOUNTED_DIRECTORIES,
+				" Set to true to enable traversing mounted directories on other file systems\n"
+				" (this excludes removable devices)",
+				NULL);
+	g_key_file_set_boolean (key_file, GROUP_INDEXING, KEY_INDEX_REMOVABLE_DEVICES, DEFAULT_INDEX_REMOVABLE_DEVICES);
+	g_key_file_set_comment (key_file, GROUP_INDEXING, KEY_INDEX_REMOVABLE_DEVICES,
+				" Set to true to enable traversing mounted directories for removable devices",
+				NULL);
+
+	/* Performance */
+	g_key_file_set_integer (key_file, GROUP_PERFORMANCE, KEY_MAX_TEXT_TO_INDEX, DEFAULT_MAX_TEXT_TO_INDEX);
+	g_key_file_set_comment (key_file, GROUP_PERFORMANCE, KEY_MAX_TEXT_TO_INDEX,
+				" Maximum text size in bytes to index from a file's content",
+				NULL);
+	g_key_file_set_integer (key_file, GROUP_PERFORMANCE, KEY_MAX_WORDS_TO_INDEX, DEFAULT_MAX_WORDS_TO_INDEX);
+	g_key_file_set_comment (key_file, GROUP_PERFORMANCE, KEY_MAX_WORDS_TO_INDEX,
+				" Maximum unique words to index from a file's content",
+				NULL);
+	g_key_file_set_integer (key_file, GROUP_PERFORMANCE, KEY_MAX_BUCKET_COUNT, DEFAULT_MAX_BUCKET_COUNT);
+	g_key_file_set_integer (key_file, GROUP_PERFORMANCE, KEY_MIN_BUCKET_COUNT, DEFAULT_MIN_BUCKET_COUNT);
+
+	/* Services */
+	g_key_file_set_boolean (key_file, GROUP_SERVICES, KEY_ENABLE_XESAM, DEFAULT_ENABLE_XESAM);
+	g_key_file_set_comment (key_file, GROUP_SERVICES, KEY_ENABLE_XESAM,
+				" Xesam DBus service.\n",
+				NULL);
+
+	content = g_key_file_to_data (key_file, NULL, &error);
+	g_free (language);
+
+	if (error) {
+		g_warning ("Couldn't produce default configuration, %s", error->message);
+		g_clear_error (&error);
+		return FALSE;
+	}
+
+	if (!g_file_set_contents (filename, content, -1, &error)) {
+		g_warning ("Couldn't write default configuration, %s", error->message);
+		g_clear_error (&error);
+		g_free (content);
+		return FALSE;
+	}
+
+	g_print ("Writting default configuration to file:'%s'\n", filename);
+	g_free (content);
+
+	return TRUE;
+}
+
+static GSList *
+config_string_list_to_gslist (const gchar **value,
+			      gboolean	    is_directory_list)
+{
+	GSList *list = NULL;
+	gint	i;
+
+	for (i = 0; value[i]; i++) {
+		const gchar *str;
+		gchar	    *validated;
+
+		str = value[i];
+		if (!str || str[0] == '\0') {
+			continue;
+		}
+
+		if (!is_directory_list) {
+			list = g_slist_prepend (list, g_strdup (str));
+			continue;
+		}
+
+		/* For directories we validate any special characters,
+		 * for example '~' and '../../'
+		 */
+		validated = tracker_path_evaluate_name (str);
+		if (validated) {
+			list = g_slist_prepend (list, validated);
+		}
+	}
+
+	return g_slist_reverse (list);
+}
+
+static void
+config_load_int (TrackerConfig *config,
+		 const gchar   *property,
+		 GKeyFile      *key_file,
+		 const gchar   *group,
+		 const gchar   *key)
+{
+	GError *error = NULL;
+	gint	value;
+
+	value = g_key_file_get_integer (key_file, group, key, &error);
+	if (!error) {
+		g_object_set (G_OBJECT (config), property, value, NULL);
+	} else {
+		g_message ("Couldn't load config option '%s' (int) in group '%s', %s",
+			   property, group, error->message);
+		g_error_free (error);
+	}
+}
+
+static void
+config_load_boolean (TrackerConfig *config,
+		     const gchar   *property,
+		     GKeyFile	   *key_file,
+		     const gchar   *group,
+		     const gchar   *key)
+{
+	GError	 *error = NULL;
+	gboolean  value;
+
+	value = g_key_file_get_boolean (key_file, group, key, &error);
+	if (!error) {
+		g_object_set (G_OBJECT (config), property, value, NULL);
+	} else {
+		g_message ("Couldn't load config option '%s' (bool) in group '%s', %s",
+			   property, group, error->message);
+		g_error_free (error);
+	}
+}
+
+static void
+config_load_string (TrackerConfig *config,
+		    const gchar	  *property,
+		    GKeyFile	  *key_file,
+		    const gchar	  *group,
+		    const gchar	  *key)
+{
+	GError *error = NULL;
+	gchar  *value;
+
+	value = g_key_file_get_string (key_file, group, key, &error);
+	if (!error) {
+		g_object_set (G_OBJECT (config), property, value, NULL);
+	} else {
+		g_message ("Couldn't load config option '%s' (string) in group '%s', %s",
+			   property, group, error->message);
+		g_error_free (error);
+	}
+
+	g_free (value);
+}
+
+static void
+config_load_string_list (TrackerConfig *config,
+			 const gchar   *property,
+			 GKeyFile      *key_file,
+			 const gchar   *group,
+			 const gchar   *key)
+{
+	TrackerConfigPrivate  *priv;
+	GSList		      *l;
+	gchar		     **value;
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	value = g_key_file_get_string_list (key_file, group, key, NULL, NULL);
+
+	if (strcmp (property, "watch-directory-roots") == 0) {
+		if (value) {
+			priv->watch_directory_roots = l =
+				config_string_list_to_gslist ((const gchar **) value, TRUE);
+			priv->watch_directory_roots =
+				tracker_path_list_filter_duplicates (priv->watch_directory_roots);
+
+			g_slist_foreach (l, (GFunc) g_free, NULL);
+			g_slist_free (l);
+		}
+	}
+	else if (strcmp (property, "crawl-directory-roots") == 0) {
+		if (value) {
+			priv->crawl_directory_roots = l =
+				config_string_list_to_gslist ((const gchar **) value, TRUE);
+			priv->crawl_directory_roots =
+				tracker_path_list_filter_duplicates (priv->crawl_directory_roots);
+
+			g_slist_foreach (l, (GFunc) g_free, NULL);
+			g_slist_free (l);
+		}
+	}
+	else if (strcmp (property, "no-watch-directory-roots") == 0) {
+		if (value) {
+			priv->no_watch_directory_roots = l =
+				config_string_list_to_gslist ((const gchar **) value, TRUE);
+			priv->no_watch_directory_roots =
+				tracker_path_list_filter_duplicates (priv->no_watch_directory_roots);
+
+			g_slist_foreach (l, (GFunc) g_free, NULL);
+			g_slist_free (l);
+		}
+	}
+	else if (strcmp (property, "no-index-file-types") == 0) {
+		if (value) {
+			priv->no_index_file_types =
+				config_string_list_to_gslist ((const gchar **) value, FALSE);
+		}
+	}
+	else if (strcmp (property, "disabled-modules") == 0) {
+		if (value) {
+			priv->disabled_modules =
+				config_string_list_to_gslist ((const gchar **) value, FALSE);
+		}
+	}
+	else {
+		g_warning ("Property '%s' not recognized to set string list from key '%s'",
+			   property, key);
+	}
+
+	g_strfreev (value);
+}
+
+static void
+config_changed_cb (GFileMonitor     *monitor,
+		   GFile	    *file,
+		   GFile	    *other_file,
+		   GFileMonitorEvent event_type,
+		   gpointer	     user_data)
+{
+	TrackerConfig *config;
+	gchar	      *filename;
+
+	config = TRACKER_CONFIG (user_data);
+
+	/* Do we recreate if the file is deleted? */
+
+	switch (event_type) {
+	case G_FILE_MONITOR_EVENT_CHANGED:
+	case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
+		filename = g_file_get_path (file);
+		g_message ("Config file changed:'%s', reloading settings...",
+			   filename);
+		g_free (filename);
+
+		config_load (config);
+		break;
+
+	default:
+		break;
+	}
+}
+
+static void
+config_load (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+	GKeyFile	     *key_file;
+	GError		     *error = NULL;
+	gchar		     *filename;
+	gchar		     *directory;
+	gboolean	      value;
+
+	key_file = g_key_file_new ();
+
+	/* Check we have a config file and if not, create it based on
+	 * the default settings.
+	 */
+	directory = config_dir_ensure_exists_and_return ();
+	if (!directory) {
+		return;
+	}
+
+	filename = g_build_filename (directory, "tracker.cfg", NULL);
+	g_free (directory);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	/* Add file monitoring for changes */
+	if (!priv->file) {
+		priv->file = g_file_new_for_path (filename);
+	}
+
+	if (!priv->monitor) {
+		g_message ("Setting up monitor for changes to config file:'%s'",
+			   filename);
+
+		priv->monitor = g_file_monitor_file (priv->file,
+						     G_FILE_MONITOR_NONE,
+						     NULL,
+						     NULL);
+
+		g_signal_connect (priv->monitor, "changed",
+				  G_CALLBACK (config_changed_cb),
+				  config);
+	}
+
+	/* Load options */
+	g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &error);
+	if (error) {
+		config_create_with_defaults (filename, key_file);
+		g_clear_error (&error);
+	}
+
+	g_free (filename);
+
+	/* General */
+	config_load_int (config, "verbosity", key_file, GROUP_GENERAL, KEY_VERBOSITY);
+	config_load_int (config, "initial-sleep", key_file, GROUP_GENERAL, KEY_INITIAL_SLEEP);
+	config_load_boolean (config, "low-memory-mode", key_file, GROUP_GENERAL, KEY_LOW_MEMORY_MODE);
+	config_load_boolean (config, "nfs-locking", key_file, GROUP_GENERAL, KEY_NFS_LOCKING);
+
+	/* Watches */
+	config_load_string_list (config, "watch-directory-roots", key_file, GROUP_WATCHES, KEY_WATCH_DIRECTORY_ROOTS);
+	config_load_string_list (config, "crawl-directory-roots", key_file, GROUP_WATCHES, KEY_CRAWL_DIRECTORY_ROOTS);
+	config_load_string_list (config, "no-watch-directory-roots", key_file, GROUP_WATCHES, KEY_NO_WATCH_DIRECTORY_ROOTS);
+	config_load_boolean (config, "enable-watches", key_file, GROUP_WATCHES, KEY_ENABLE_WATCHES);
+
+	/* Indexing */
+	config_load_int (config, "throttle", key_file, GROUP_INDEXING, KEY_THROTTLE);
+	config_load_boolean (config, "enable-indexing", key_file, GROUP_INDEXING, KEY_ENABLE_INDEXING);
+	config_load_boolean (config, "enable-content-indexing", key_file, GROUP_INDEXING, KEY_ENABLE_CONTENT_INDEXING);
+	config_load_boolean (config, "enable-thumbnails", key_file, GROUP_INDEXING, KEY_ENABLE_THUMBNAILS);
+	config_load_string_list (config, "disabled-modules", key_file, GROUP_INDEXING, KEY_DISABLED_MODULES);
+	config_load_boolean (config, "fast-merges", key_file, GROUP_INDEXING, KEY_FAST_MERGES);
+	config_load_string_list (config, "no-index-file-types", key_file, GROUP_INDEXING, KEY_NO_INDEX_FILE_TYPES);
+	config_load_int (config, "min-word-length", key_file, GROUP_INDEXING, KEY_MIN_WORD_LENGTH);
+	config_load_int (config, "max-word-length", key_file, GROUP_INDEXING, KEY_MAX_WORD_LENGTH);
+	config_load_string (config, "language", key_file, GROUP_INDEXING, KEY_LANGUAGE);
+	config_load_boolean (config, "enable-stemmer", key_file, GROUP_INDEXING, KEY_ENABLE_STEMMER);
+	config_load_boolean (config, "disable-indexing-on-battery", key_file, GROUP_INDEXING, KEY_DISABLE_INDEXING_ON_BATTERY);
+	config_load_boolean (config, "disable-indexing-on-battery-init", key_file, GROUP_INDEXING, KEY_DISABLE_INDEXING_ON_BATTERY_INIT);
+	config_load_int (config, "low-disk-space-limit", key_file, GROUP_INDEXING, KEY_LOW_DISK_SPACE_LIMIT);
+	config_load_boolean (config, "index-mounted-directories", key_file, GROUP_INDEXING, KEY_INDEX_MOUNTED_DIRECTORIES);
+	config_load_boolean (config, "index-removable-devices", key_file, GROUP_INDEXING, KEY_INDEX_REMOVABLE_DEVICES);
+
+	/* Performance */
+	config_load_int (config, "max-text-to-index", key_file, GROUP_PERFORMANCE, KEY_MAX_TEXT_TO_INDEX);
+	config_load_int (config, "max-words-to-index", key_file, GROUP_PERFORMANCE, KEY_MAX_WORDS_TO_INDEX);
+	config_load_int (config, "max-bucket-count", key_file, GROUP_PERFORMANCE, KEY_MAX_BUCKET_COUNT);
+	config_load_int (config, "min-bucket-count", key_file, GROUP_PERFORMANCE, KEY_MIN_BUCKET_COUNT);
+
+	/* Services */
+	config_load_boolean (config, "enable-xesam", key_file, GROUP_SERVICES, KEY_ENABLE_XESAM);
+
+	/*
+	 * Legacy options no longer supported:
+	 */
+	value = g_key_file_get_boolean (key_file, "Emails", "IndexEvolutionEmails", &error);
+	if (!error) {
+		gchar * const modules[2] = { "evolution", NULL };
+
+		g_message ("Legacy config option 'IndexEvolutionEmails' found");
+		g_message ("  This option has been replaced by 'DisabledModules'");
+
+		if (!value) {
+			tracker_config_add_disabled_modules (config, modules);
+			g_message ("  Option 'DisabledModules' added '%s'", modules[0]);
+		} else {
+			tracker_config_remove_disabled_modules (config, modules[0]);
+			g_message ("  Option 'DisabledModules' removed '%s'", modules[0]);
+		}
+	} else {
+		g_clear_error (&error);
+	}
+
+	value = g_key_file_get_boolean (key_file, "Emails", "IndexThunderbirdEmails", &error);
+	if (!error) {
+		g_message ("Legacy config option 'IndexThunderbirdEmails' found");
+		g_message ("  This option is no longer supported and has no effect");
+	} else {
+		g_clear_error (&error);
+	}
+
+	value = g_key_file_get_boolean (key_file, "Indexing", "SkipMountPoints", &error);
+	if (!error) {
+		g_message ("Legacy config option 'SkipMountPoints' found");
+		tracker_config_set_index_mounted_directories (config, !value);
+		g_message ("  Option 'IndexMountedDirectories' set to %s", !value ? "true" : "false");
+	} else {
+		g_clear_error (&error);
+	}
+
+	g_key_file_free (key_file);
+}
+
+static gboolean
+config_int_validate (TrackerConfig *config,
+		     const gchar   *property,
+		     gint	    value)
+{
+#ifdef G_DISABLE_CHECKS
+	GParamSpec *spec;
+	GValue	    value = { 0 };
+	gboolean    valid;
+
+	spec = g_object_class_find_property (G_OBJECT_CLASS (config), property);
+	g_return_val_if_fail (spec != NULL, FALSE);
+
+	g_value_init (&value, spec->value_type);
+	g_value_set_int (&value, verbosity);
+	valid = g_param_value_validate (spec, &value);
+	g_value_unset (&value);
+
+	g_return_val_if_fail (valid != TRUE, FALSE);
+#endif
+
+	return TRUE;
+}
+
+TrackerConfig *
+tracker_config_new (void)
+{
+	TrackerConfig *config;
+
+	config = g_object_new (TRACKER_TYPE_CONFIG, NULL);
+	config_load (config);
+
+	return config;
+}
+
+gint
+tracker_config_get_verbosity (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_VERBOSITY);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->verbosity;
+}
+
+gint
+tracker_config_get_initial_sleep (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_INITIAL_SLEEP);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->initial_sleep;
+}
+
+gboolean
+tracker_config_get_low_memory_mode (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_LOW_MEMORY_MODE);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->low_memory_mode;
+}
+
+gboolean
+tracker_config_get_nfs_locking (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_NFS_LOCKING);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->nfs_locking;
+}
+
+
+GSList *
+tracker_config_get_watch_directory_roots (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), NULL);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->watch_directory_roots;
+}
+
+GSList *
+tracker_config_get_crawl_directory_roots (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), NULL);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->crawl_directory_roots;
+}
+
+GSList *
+tracker_config_get_no_watch_directory_roots (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), NULL);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->no_watch_directory_roots;
+}
+
+gboolean
+tracker_config_get_enable_watches (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_ENABLE_WATCHES);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->enable_watches;
+}
+
+gint
+tracker_config_get_throttle (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_THROTTLE);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->throttle;
+}
+
+gboolean
+tracker_config_get_enable_indexing (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_ENABLE_INDEXING);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->enable_indexing;
+}
+
+gboolean
+tracker_config_get_enable_xesam (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_ENABLE_XESAM);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->enable_xesam;
+}
+
+gboolean
+tracker_config_get_enable_content_indexing (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_ENABLE_CONTENT_INDEXING);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->enable_content_indexing;
+}
+
+gboolean
+tracker_config_get_enable_thumbnails (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_ENABLE_THUMBNAILS);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->enable_thumbnails;
+}
+
+GSList *
+tracker_config_get_disabled_modules (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), NULL);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->disabled_modules;
+}
+
+gboolean
+tracker_config_get_fast_merges (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_FAST_MERGES);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->fast_merges;
+}
+
+GSList *
+tracker_config_get_no_index_file_types (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), NULL);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->no_index_file_types;
+}
+
+gint
+tracker_config_get_min_word_length (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_MIN_WORD_LENGTH);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->min_word_length;
+}
+
+gint
+tracker_config_get_max_word_length (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_MAX_WORD_LENGTH);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->max_word_length;
+}
+
+const gchar *
+tracker_config_get_language (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), "en");
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->language;
+}
+
+gboolean
+tracker_config_get_enable_stemmer (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_ENABLE_STEMMER);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->enable_stemmer;
+}
+
+gboolean
+tracker_config_get_disable_indexing_on_battery (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_DISABLE_INDEXING_ON_BATTERY);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->disable_indexing_on_battery;
+}
+
+gboolean
+tracker_config_get_disable_indexing_on_battery_init (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_DISABLE_INDEXING_ON_BATTERY_INIT);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->disable_indexing_on_battery_init;
+}
+
+gint
+tracker_config_get_low_disk_space_limit (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_LOW_DISK_SPACE_LIMIT);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->low_disk_space_limit;
+}
+
+gboolean
+tracker_config_get_index_mounted_directories (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_INDEX_MOUNTED_DIRECTORIES);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->index_mounted_directories;
+}
+
+gboolean
+tracker_config_get_index_removable_devices (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_INDEX_REMOVABLE_DEVICES);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->index_removable_devices;
+}
+
+gint
+tracker_config_get_max_text_to_index (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_MAX_TEXT_TO_INDEX);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->max_text_to_index;
+}
+
+gint
+tracker_config_get_max_words_to_index (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_MAX_WORDS_TO_INDEX);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->max_words_to_index;
+}
+
+gint
+tracker_config_get_max_bucket_count (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_MAX_BUCKET_COUNT);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->max_bucket_count;
+}
+
+gint
+tracker_config_get_min_bucket_count (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_MIN_BUCKET_COUNT);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->min_bucket_count;
+}
+
+void
+tracker_config_set_verbosity (TrackerConfig *config,
+			      gint	     value)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	if (!config_int_validate (config, "verbosity", value)) {
+		return;
+	}
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	priv->verbosity = value;
+	g_object_notify (G_OBJECT (config), "verbosity");
+}
+
+void
+tracker_config_set_initial_sleep (TrackerConfig *config,
+				  gint		 value)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	if (!config_int_validate (config, "initial-sleep", value)) {
+		return;
+	}
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	priv->initial_sleep = value;
+	g_object_notify (G_OBJECT (config), "initial-sleep");
+}
+
+void
+tracker_config_set_low_memory_mode (TrackerConfig *config,
+				    gboolean	   value)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	priv->low_memory_mode = value;
+	g_object_notify (G_OBJECT (config), "low-memory-mode");
+}
+
+void
+tracker_config_set_nfs_locking (TrackerConfig *config,
+				gboolean      value)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	priv->nfs_locking = value;
+	g_object_notify (G_OBJECT (config), "nfs-locking");
+}
+
+
+void
+tracker_config_set_enable_watches (TrackerConfig *config,
+				   gboolean	  value)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	priv->enable_watches = value;
+	g_object_notify (G_OBJECT (config), "enable-watches");
+}
+
+void
+tracker_config_set_throttle (TrackerConfig *config,
+			     gint	    value)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	if (!config_int_validate (config, "throttle", value)) {
+		return;
+	}
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	priv->throttle = value;
+	g_object_notify (G_OBJECT (config), "throttle");
+}
+
+void
+tracker_config_set_enable_indexing (TrackerConfig *config,
+				    gboolean	   value)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	priv->enable_indexing = value;
+	g_object_notify (G_OBJECT (config), "enable-indexing");
+}
+
+void
+tracker_config_set_enable_xesam (TrackerConfig *config,
+				 gboolean	   value)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	priv->enable_xesam = value;
+	g_object_notify (G_OBJECT (config), "enable-xesam");
+}
+
+void
+tracker_config_set_enable_content_indexing (TrackerConfig *config,
+					    gboolean	   value)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	priv->enable_content_indexing = value;
+	g_object_notify (G_OBJECT (config), "enable-content-indexing");
+}
+
+void
+tracker_config_set_enable_thumbnails (TrackerConfig *config,
+				      gboolean	     value)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	priv->enable_thumbnails = value;
+	g_object_notify (G_OBJECT (config), "enable-thumbnails");
+}
+
+void
+tracker_config_set_fast_merges (TrackerConfig *config,
+				gboolean       value)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	priv->fast_merges = value;
+	g_object_notify (G_OBJECT (config), "fast-merges");
+}
+
+void
+tracker_config_set_min_word_length (TrackerConfig *config,
+				    gint	   value)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	if (!config_int_validate (config, "min-word-length", value)) {
+		return;
+	}
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	priv->min_word_length = value;
+	g_object_notify (G_OBJECT (config), "min-word-length");
+}
+
+void
+tracker_config_set_max_word_length (TrackerConfig *config,
+				    gint	   value)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	if (!config_int_validate (config, "max-word-length", value)) {
+		return;
+	}
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	priv->max_word_length = value;
+	g_object_notify (G_OBJECT (config), "max-word-length");
+}
+
+void
+tracker_config_set_language (TrackerConfig *config,
+			     const gchar   *value)
+{
+	TrackerConfigPrivate *priv;
+	gboolean	      use_default = FALSE;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	g_free (priv->language);
+
+	/* Validate language */
+	use_default |= !value;
+	use_default |= strlen (value) < 2;
+	use_default |= !tracker_language_check_exists (value);
+
+	if (use_default) {
+		priv->language = tracker_language_get_default_code ();
+	} else {
+		priv->language = g_strdup (value);
+	}
+
+	g_object_notify (G_OBJECT (config), "language");
+}
+
+void
+tracker_config_set_enable_stemmer (TrackerConfig *config,
+				   gboolean	  value)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	priv->enable_stemmer = value;
+	g_object_notify (G_OBJECT (config), "enable-stemmer");
+}
+
+void
+tracker_config_set_disable_indexing_on_battery (TrackerConfig *config,
+						gboolean       value)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	priv->disable_indexing_on_battery = value;
+	g_object_notify (G_OBJECT (config), "disable-indexing-on-battery");
+}
+
+void
+tracker_config_set_disable_indexing_on_battery_init (TrackerConfig *config,
+						     gboolean	    value)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	priv->disable_indexing_on_battery_init = value;
+	g_object_notify (G_OBJECT (config), "disable-indexing-on-battery-init");
+}
+
+void
+tracker_config_set_low_disk_space_limit (TrackerConfig *config,
+					 gint		value)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	if (!config_int_validate (config, "low-disk-space-limit", value)) {
+		return;
+	}
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	priv->low_disk_space_limit = value;
+	g_object_notify (G_OBJECT (config), "low-disk-space-limit");
+}
+
+void
+tracker_config_set_index_mounted_directories (TrackerConfig *config,
+					      gboolean	     value)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	priv->index_mounted_directories = value;
+	g_object_notify (G_OBJECT (config), "index-mounted-directories");
+}
+
+void
+tracker_config_set_index_removable_devices (TrackerConfig *config,
+					  gboolean	 value)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	priv->index_removable_devices = value;
+	g_object_notify (G_OBJECT (config), "index-removable-devices");
+}
+
+void
+tracker_config_set_max_text_to_index (TrackerConfig *config,
+				      gint	     value)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	if (!config_int_validate (config, "max-text-to-index", value)) {
+		return;
+	}
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	priv->max_text_to_index = value;
+	g_object_notify (G_OBJECT (config), "max-text-to-index");
+}
+
+void
+tracker_config_set_max_words_to_index (TrackerConfig *config,
+				       gint	      value)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	if (!config_int_validate (config, "max-words-to-index", value)) {
+		return;
+	}
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	priv->max_words_to_index = value;
+	g_object_notify (G_OBJECT (config), "max-words-to-index");
+}
+
+void
+tracker_config_set_max_bucket_count (TrackerConfig *config,
+				     gint	    value)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	if (!config_int_validate (config, "max-bucket-count", value)) {
+		return;
+	}
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	priv->max_bucket_count = value;
+	g_object_notify (G_OBJECT (config), "max-bucket-count");
+}
+
+void
+tracker_config_set_min_bucket_count (TrackerConfig *config,
+				     gint	    value)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	if (!config_int_validate (config, "min-bucket-count", value)) {
+		return;
+	}
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	priv->min_bucket_count = value;
+	g_object_notify (G_OBJECT (config), "min-bucket-count");
+}
+
+void
+tracker_config_add_watch_directory_roots (TrackerConfig *config,
+					  gchar * const *roots)
+{
+	TrackerConfigPrivate *priv;
+	GSList		     *l;
+	gchar		     *validated_root;
+	gchar * const	     *p;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+	g_return_if_fail (roots != NULL);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	for (p = roots; *p; p++) {
+		validated_root = tracker_path_evaluate_name (*p);
+		if (!validated_root) {
+			g_print ("Root '%s' is not valid to add to watch directory list\n",
+				 validated_root);
+			continue;
+		}
+
+		priv->watch_directory_roots = g_slist_append (priv->watch_directory_roots,
+							      validated_root);
+	}
+
+	l = priv->watch_directory_roots;
+	priv->watch_directory_roots =
+		tracker_path_list_filter_duplicates (priv->watch_directory_roots);
+
+	g_slist_foreach (l, (GFunc) g_free, NULL);
+	g_slist_free (l);
+
+	g_object_notify (G_OBJECT (config), "watch-directory-roots");
+}
+
+void
+tracker_config_add_crawl_directory_roots (TrackerConfig *config,
+					  gchar * const *roots)
+{
+	TrackerConfigPrivate *priv;
+	GSList		     *l;
+	gchar		     *validated_root;
+	gchar * const	     *p;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+	g_return_if_fail (roots != NULL);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	for (p = roots; *p; p++) {
+		validated_root = tracker_path_evaluate_name (*p);
+		if (!validated_root) {
+			g_print ("Root '%s' is not valid to add to crawl directory list\n",
+				 validated_root);
+			continue;
+		}
+
+		priv->crawl_directory_roots = g_slist_append (priv->crawl_directory_roots,
+							      validated_root);
+	}
+
+	l = priv->crawl_directory_roots;
+	priv->crawl_directory_roots =
+		tracker_path_list_filter_duplicates (priv->crawl_directory_roots);
+
+	g_slist_foreach (l, (GFunc) g_free, NULL);
+	g_slist_free (l);
+
+	g_object_notify (G_OBJECT (config), "crawl-directory-roots");
+}
+
+void
+tracker_config_add_no_watch_directory_roots (TrackerConfig *config,
+					     gchar * const *roots)
+{
+	TrackerConfigPrivate *priv;
+	GSList		     *l;
+	gchar		     *validated_root;
+	gchar * const	     *p;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+	g_return_if_fail (roots != NULL);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	for (p = roots; *p; p++) {
+		validated_root = tracker_path_evaluate_name (*p);
+		if (!validated_root) {
+			g_print ("Root '%s' is not valid to add to no_watch directory list\n",
+				 validated_root);
+			continue;
+		}
+
+		priv->no_watch_directory_roots = g_slist_append (priv->no_watch_directory_roots,
+								 validated_root);
+	}
+
+	l = priv->no_watch_directory_roots;
+	priv->no_watch_directory_roots =
+		tracker_path_list_filter_duplicates (priv->no_watch_directory_roots);
+
+	g_slist_foreach (l, (GFunc) g_free, NULL);
+	g_slist_free (l);
+
+	g_object_notify (G_OBJECT (config), "no-watch-directory-roots");
+}
+
+void
+tracker_config_add_disabled_modules (TrackerConfig *config,
+				     gchar * const *modules)
+{
+	TrackerConfigPrivate *priv;
+	GSList		     *new_modules;
+	gchar * const	     *p;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+	g_return_if_fail (modules != NULL);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	new_modules = NULL;
+
+	for (p = modules; *p; p++) {
+		if (g_slist_find_custom (priv->disabled_modules,
+					 *p,
+					 (GCompareFunc) strcmp)) {
+			continue;
+		}
+
+		new_modules = g_slist_append (new_modules, g_strdup (*p));
+	}
+
+	priv->disabled_modules = g_slist_concat (priv->disabled_modules,
+						 new_modules);
+
+	g_object_notify (G_OBJECT (config), "disabled-modules");
+}
+
+void
+tracker_config_remove_disabled_modules (TrackerConfig *config,
+					const gchar   *module)
+{
+	TrackerConfigPrivate *priv;
+	GSList		     *l;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+	g_return_if_fail (module != NULL);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	l = g_slist_find_custom (priv->disabled_modules,
+				 module,
+				 (GCompareFunc) strcmp);
+
+	if (l) {
+		g_free (l->data);
+		priv->disabled_modules = g_slist_delete_link (priv->disabled_modules, l);
+		g_object_notify (G_OBJECT (config), "disabled-modules");
+	}
+}

Added: trunk/src/libtracker-common/tracker-config.h
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-config.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,146 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2007, Michal Pryc (Michal Pryc Sun Com)
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __LIBTRACKER_COMMON_CONFIG_H__
+#define __LIBTRACKER_COMMON_CONFIG_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_CONFIG	    (tracker_config_get_type ())
+#define TRACKER_CONFIG(o)	    (G_TYPE_CHECK_INSTANCE_CAST ((o), TRACKER_TYPE_CONFIG, TrackerConfig))
+#define TRACKER_CONFIG_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), TRACKER_TYPE_CONFIG, TrackerConfigClass))
+#define TRACKER_IS_CONFIG(o)	    (G_TYPE_CHECK_INSTANCE_TYPE ((o), TRACKER_TYPE_CONFIG))
+#define TRACKER_IS_CONFIG_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), TRACKER_TYPE_CONFIG))
+#define TRACKER_CONFIG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), TRACKER_TYPE_CONFIG, TrackerConfigClass))
+
+typedef struct _TrackerConfig	   TrackerConfig;
+typedef struct _TrackerConfigClass TrackerConfigClass;
+
+struct _TrackerConfig {
+	GObject      parent;
+};
+
+struct _TrackerConfigClass {
+	GObjectClass parent_class;
+};
+
+GType	       tracker_config_get_type				   (void) G_GNUC_CONST;
+
+void	       tracker_config_load_file				   (void);
+
+TrackerConfig *tracker_config_new				   (void);
+gint	       tracker_config_get_verbosity			   (TrackerConfig *config);
+gint	       tracker_config_get_initial_sleep			   (TrackerConfig *config);
+gboolean       tracker_config_get_low_memory_mode		   (TrackerConfig *config);
+gboolean       tracker_config_get_nfs_locking			   (TrackerConfig *config);
+GSList *       tracker_config_get_watch_directory_roots		   (TrackerConfig *config);
+GSList *       tracker_config_get_crawl_directory_roots		   (TrackerConfig *config);
+GSList *       tracker_config_get_no_watch_directory_roots	   (TrackerConfig *config);
+gboolean       tracker_config_get_enable_watches		   (TrackerConfig *config);
+gint	       tracker_config_get_throttle			   (TrackerConfig *config);
+gboolean       tracker_config_get_enable_indexing		   (TrackerConfig *config);
+gboolean       tracker_config_get_enable_xesam			   (TrackerConfig *config);
+gboolean       tracker_config_get_enable_content_indexing	   (TrackerConfig *config);
+gboolean       tracker_config_get_enable_thumbnails		   (TrackerConfig *config);
+GSList *       tracker_config_get_disabled_modules		   (TrackerConfig *config);
+gboolean       tracker_config_get_fast_merges			   (TrackerConfig *config);
+GSList *       tracker_config_get_no_index_file_types		   (TrackerConfig *config);
+gint	       tracker_config_get_min_word_length		   (TrackerConfig *config);
+gint	       tracker_config_get_max_word_length		   (TrackerConfig *config);
+const gchar *  tracker_config_get_language			   (TrackerConfig *config);
+gboolean       tracker_config_get_enable_stemmer		   (TrackerConfig *config);
+gboolean       tracker_config_get_disable_indexing_on_battery	   (TrackerConfig *config);
+gboolean       tracker_config_get_disable_indexing_on_battery_init (TrackerConfig *config);
+gint	       tracker_config_get_low_disk_space_limit		   (TrackerConfig *config);
+gboolean       tracker_config_get_index_removable_devices	   (TrackerConfig *config);
+gboolean       tracker_config_get_index_mounted_directories	   (TrackerConfig *config);
+gint	       tracker_config_get_max_text_to_index		   (TrackerConfig *config);
+gint	       tracker_config_get_max_words_to_index		   (TrackerConfig *config);
+gint	       tracker_config_get_max_bucket_count		   (TrackerConfig *config);
+gint	       tracker_config_get_min_bucket_count		   (TrackerConfig *config);
+void	       tracker_config_set_verbosity			   (TrackerConfig *config,
+								    gint	   value);
+void	       tracker_config_set_initial_sleep			   (TrackerConfig *config,
+								    gint	   value);
+void	       tracker_config_set_low_memory_mode		   (TrackerConfig *config,
+								    gboolean	   value);
+void	       tracker_config_set_nfs_locking			   (TrackerConfig *config,
+								    gboolean	   value);
+void	       tracker_config_set_enable_watches		   (TrackerConfig *config,
+								    gboolean	   value);
+void	       tracker_config_set_throttle			   (TrackerConfig *config,
+								    gint	   value);
+void	       tracker_config_set_enable_indexing		   (TrackerConfig *config,
+								    gboolean	   value);
+void	       tracker_config_set_enable_xesam			   (TrackerConfig *config,
+								    gboolean	   value);
+void	       tracker_config_set_enable_content_indexing	   (TrackerConfig *config,
+								    gboolean	   value);
+void	       tracker_config_set_enable_thumbnails		   (TrackerConfig *config,
+								    gboolean	   value);
+void	       tracker_config_set_fast_merges			   (TrackerConfig *config,
+								    gboolean	   value);
+void	       tracker_config_set_min_word_length		   (TrackerConfig *config,
+								    gint	   value);
+void	       tracker_config_set_max_word_length		   (TrackerConfig *config,
+								    gint	   value);
+void	       tracker_config_set_language			   (TrackerConfig *config,
+								    const gchar   *value);
+void	       tracker_config_set_enable_stemmer		   (TrackerConfig *config,
+								    gboolean	   value);
+void	       tracker_config_set_disable_indexing_on_battery	   (TrackerConfig *config,
+								    gboolean	   value);
+void	       tracker_config_set_disable_indexing_on_battery_init (TrackerConfig *config,
+								    gboolean	   value);
+void	       tracker_config_set_low_disk_space_limit		   (TrackerConfig *config,
+								    gint	   value);
+void	       tracker_config_set_index_removable_devices	   (TrackerConfig *config,
+								    gboolean	   value);
+void	       tracker_config_set_index_mounted_directories	   (TrackerConfig *config,
+								    gboolean	   value);
+void	       tracker_config_set_max_text_to_index		   (TrackerConfig *config,
+								    gint	   value);
+void	       tracker_config_set_max_words_to_index		   (TrackerConfig *config,
+								    gint	   value);
+void	       tracker_config_set_max_bucket_count		   (TrackerConfig *config,
+								    gint	   value);
+void	       tracker_config_set_min_bucket_count		   (TrackerConfig *config,
+								    gint	   value);
+
+/* Directory root APIs*/
+void	       tracker_config_add_watch_directory_roots		   (TrackerConfig *config,
+								    gchar * const *roots);
+void	       tracker_config_add_crawl_directory_roots		   (TrackerConfig *config,
+								    gchar * const *roots);
+void	       tracker_config_add_no_watch_directory_roots	   (TrackerConfig *config,
+								    gchar * const *roots);
+void	       tracker_config_add_disabled_modules		   (TrackerConfig *config,
+								    gchar * const *modules);
+void	       tracker_config_remove_disabled_modules		   (TrackerConfig *config,
+								    const gchar   *module);
+
+G_END_DECLS
+
+#endif /* __LIBTRACKER_COMMON_CONFIG_H__ */
+

Added: trunk/src/libtracker-common/tracker-configuration.c
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-configuration.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,480 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Saleem Abdulrasool (compnerd compnerd org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "tracker-configuration.h"
+
+#include <string.h>
+
+static gboolean dirty = FALSE;
+static gchar *filename = NULL;
+static GKeyFile *configuration = NULL;
+
+const LanguageMapEntry LanguageMap[] = {
+	{ "da", "Danish" },
+	{ "nl", "Duch" },
+	{ "en", "English" },
+	{ "fi", "Finnish" },
+	{ "fr", "French" },
+	{ "de", "German" },
+	{ "it", "Italian" },
+	{ "nb", "Norwegian" },
+	{ "pt", "Portugese" },
+	{ "ru", "Russian" },
+	{ "es", "Spanish" },
+	{ "sv", "Swedish" },
+	{ NULL, NULL },
+};
+
+
+static gchar *
+get_default_language_code (void)
+{
+	gchar **langs, **lang;
+
+	/* get languages for user's locale */
+	langs = (char **)g_get_language_names ();
+
+	for (lang = langs; *lang; lang++) {
+		if (strlen (*lang) > 1) {
+			gint i;
+			for (i = 0; LanguageMap[i].language; i++) {
+				if (g_str_has_prefix (*lang, LanguageMap[i].language)) {
+					return g_strndup (*lang, 2);
+				}
+			}
+		}
+	}
+
+	return g_strdup ("en");
+}
+
+static void
+write_default_config (void)
+{
+	gchar * contents = NULL, * language = NULL;
+
+	language = get_default_language_code ();
+	contents = g_strconcat ("[General]\n",
+				"# Log verbosity"
+				"# Valid values are:\n"
+				"#    0 (displays/logs only errors)\n"
+				"#    1 (minimal)\n"
+				"#    2 (detailed)\n"
+				"#    3 (debug)\n",
+				"Verbosity = 0\n",
+				"# Set the initial sleeping time, in seconds\n",
+				"InitialSleep = 45\n\n",
+				"# Minimizes the use of memory but may slow indexing down\n",
+				"LowMemoryMode = false\n",
+				"[Watches]\n",
+				"# List of directory roots to index and watch separated by semicolons\n",
+				"WatchDirectoryRoots =", g_get_home_dir (), ";\n",
+				"# List of directory roots to index but not watch (no live updates but are\n"
+				"# refreshed when trackerd is next restarted) separated by semicolons\n",
+				"CrawlDirectory =\n",
+				"# List of directory roots to not index and not watch separated by semicolons\n",
+				"NoWatchDirectory =\n",
+				"# Set to false to prevent watching of any kind\n",
+				"EnableWatching = true\n\n",
+				"[Indexing]\n",
+				"# Throttles the indexing process.  Allowable values are 0 - 20.  Higher values\n"
+				"# decrease indexing speed\n",
+				"Throttle = 0\n",
+				"# Disables the indexing process\n",
+				"EnableIndexing = true\n",
+				"# Enables indexing of a file's text contents\n",
+				"EnableFileContentIndexing = true\n",
+				"# Enables generation of thumbnails\n",
+				"EnableThumbnails = false\n",
+				"# Enables fast index merges but may hog the disk for extended periods.\n",
+				"EnableFastMerges = false\n",
+				"# List of partial file patterns (glob) separated by semicolons that specify files\n"
+				"# to not index (basic stat info is only indexed for files that match these patterns)\n",
+				"NoIndexFileTypes =;\n",
+				"# Sets minimum length of words to index\n",
+				"MinWordLength = 3\n",
+				"# Sets the maximum length of words to index (words are cropped if bigger than this)\n",
+				"MaxWordLength = 30\n",
+				"# Sets the language specific stemmer and stopword list to use\n"
+				"# Valid values are:\n"
+				"#   'en'  (english)\n"
+				"#   'da'  (danish)\n"
+				"#   'nl'  (dutch)\n"
+				"#   'fi'  (finnish)\n"
+				"#   'fr'  (french)\n"
+				"#   'de'  (german)\n"
+				"#   'it'  (italian)\n"
+				"#   'nb'  (norwegian)\n"
+				"#   'pt'  (portugese)\n"
+				"#   'ru'  (russian)\n"
+				"#   'es'  (spanish)\n"
+				"#   'sv'  (swedish)\n",
+				"Language = ", language, "\n",
+				"# Enables use of language-specific stemmer\n",
+				"EnableStemmer = true\n",
+				"# Set to true to prevent tracker from descending into mounted directory trees\n",
+				"SkipMountPoints = false\n",
+				"# Disable all indexing when on battery\n",
+				"BatteryIndex = true\n",
+				"# Disable initial index sweep when on battery\n",
+				"BatteryIndexInitial = false\n",
+				"# Pause indexer when disk space equals or goes below this value in %% of the $HOME filesystem\n"
+				"# Set it to a value smaller than zero to disable pausing at all.\n",
+				"LowDiskSpaceLimit = 1\n\n",
+				"[Emails]\n",
+				"# Index email messages from Evolution\n",
+				"IndexEvolutionEmails = true\n",
+				"# Index email messages from Modest\n",
+				"IndexModestEmails = false\n",
+				"# Index email messages from Thunderbird\n",
+				"IndexThunderbirdEmails = true\n\n",
+				"[Performance]\n",
+				"# Maximum size of text in bytes to index from a file's text contents\n",
+				"MaxTextToIndex = 1048576\n",
+				"# Maximum number of unique words to index from a file's text contents\n",
+				"MaxWordsToIndex = 10000\n",
+				"# Specifies the number of entities to index before determining whether to perform\n"
+				"# index optimization\n",
+				"OptimizationSweepCount = 10000\n",
+				"# Sets the maximum bucket count for the indexer\n",
+				"MaxBucketCount = 524288\n",
+				"# Sets the minimum bucket count for the indexer\n",
+				"MinBucketCount = 65536\n",
+				"# Sets number of divisions of the index file\n",
+				"Divisions = 4\n",
+				"# Selects the desired ratio of used records to buckets to be used when optimizing\n"
+				"# the index (should be a value between 0 and 4)\n",
+				"BucketRatio = 1\n",
+				"# Alters how much padding is used to prevent index relocations.  Higher values improve\n"
+				"# indexing speed but waste more disk space.  Values should be in the range 1-8.\n",
+				"Padding = 2\n",
+				"# sets stack size of trackerd threads in bytes.  The default on Linux is 8Mb (0 will use\n"
+				"# the system default).\n",
+				"ThreadStackSize = 0\n",
+				NULL);
+
+	g_file_set_contents (filename, contents, strlen (contents), NULL);
+	g_free (contents);
+}
+
+static gchar *
+string_replace (const gchar * haystack, gchar * needle, gchar * replacement)
+{
+	GString *str;
+	char *start_pos = NULL, *end_pos = NULL;
+	gint needle_len = 0;
+
+	g_return_val_if_fail (haystack, NULL);
+	g_return_val_if_fail (needle, NULL);
+	g_return_val_if_fail (g_utf8_validate (haystack, -1, NULL), NULL);
+
+	str = g_string_new ("");
+	needle_len = g_utf8_strlen(needle, -1);
+
+	while ((end_pos = strstr (start_pos, needle)) != NULL) {
+		str = g_string_append_len (str, start_pos, start_pos - end_pos);
+		if (replacement) {
+			str = g_string_append (str, replacement);
+		}
+
+		start_pos = end_pos + needle_len;
+	}
+
+	return g_string_free (str, FALSE);
+}
+
+
+void
+tracker_configuration_load (void)
+{
+	GError *error = NULL;
+
+	filename = g_build_filename (g_get_user_config_dir (), "tracker", "tracker.cfg", NULL);
+
+	if (! g_file_test (filename, G_FILE_TEST_EXISTS)) {
+		gchar *tracker_dir = g_build_filename (g_get_user_config_dir (), "tracker", NULL);
+
+		if (! g_file_test (tracker_dir, G_FILE_TEST_EXISTS)) {
+			g_mkdir_with_parents (tracker_dir, 0700);
+		}
+
+		g_free (tracker_dir);
+
+		write_default_config();
+	}
+
+	g_key_file_load_from_file (configuration, filename, G_KEY_FILE_KEEP_COMMENTS, &error);
+	if (error)
+		g_error ("failed: g_key_file_load_from_file(): %s\n", error->message);
+}
+
+void
+tracker_configuration_save (void)
+{
+	gsize length = 0;
+	GError *error = NULL;
+	gchar *data = NULL, *contents = NULL;
+
+	if (! dirty)
+		return;
+
+	data = g_key_file_to_data (configuration, &length, &error);
+	if (error)
+		g_error ("failed: g_key_file_to_data(): %s\n", error->message);
+
+	contents = string_replace (data, "\n\n\n", "\n\n");
+	g_free (data);
+
+	g_file_set_contents (filename, contents, -1, NULL);
+	g_free (contents);
+
+	dirty = FALSE;
+}
+
+void
+tracker_configuration_free (void)
+{
+	if (dirty)
+		tracker_configuration_save ();
+
+	g_free (filename);
+	g_key_file_free (configuration);
+}
+
+#define TRACKER_CONFIGURATION_GET_(TypeName, GTypeName, Null)					\
+GTypeName											\
+tracker_configuration_get_##TypeName (const gchar * const key, GError ** error)			\
+{												\
+	gchar **data = g_strsplit (key, "/", 3);						\
+	GTypeName value = Null;									\
+												\
+	if (g_key_file_has_key (configuration, data[1], data[2], error)) {			\
+		value = g_key_file_get_##TypeName (configuration, data[1], data[2], error);	\
+	}											\
+												\
+	g_strfreev (data);									\
+	return value;										\
+}
+
+#define TRACKER_CONFIGURATION_SET_(TypeName, GTypeName)						\
+void												\
+tracker_configuration_set_##TypeName (const gchar * const key, const GTypeName value)		\
+{												\
+	gchar **data = g_strsplit (key, "/", 3);						\
+												\
+	g_key_file_set_##TypeName (configuration, data[1], data[2], value);			\
+												\
+	g_strfreev (data);									\
+	dirty = TRUE;										\
+}
+
+TRACKER_CONFIGURATION_GET_(boolean, gboolean, FALSE)
+TRACKER_CONFIGURATION_SET_(boolean, gboolean)
+
+TRACKER_CONFIGURATION_GET_(integer, gint, 0)
+TRACKER_CONFIGURATION_SET_(integer, gint)
+
+TRACKER_CONFIGURATION_GET_(string, gchar *, NULL)
+TRACKER_CONFIGURATION_SET_(string, gchar *)
+
+#undef TRACKER_CONFIGURATION_GET_
+#undef TRACKER_CONFIGURATION_SET_
+
+#define TRACKER_CONFIGURATION_LIST_GET_(TypeName, GTypeName)							\
+static GSList *													\
+tracker_configuration_get_##TypeName##_list (const gchar * const key, GError ** error)				\
+{														\
+	gchar **data = NULL;											\
+	GSList *retval = NULL;											\
+														\
+	g_return_val_if_fail (key, NULL);									\
+														\
+	data = g_strsplit (key, "/", 3);									\
+														\
+	if (g_key_file_has_key (configuration, data[1], data[2], error)) {					\
+		gsize length = 0;										\
+		GTypeName *values = NULL;									\
+														\
+		values = g_key_file_get_##TypeName##_list (configuration, data[1], data[2], &length, error);	\
+														\
+		if (values) {											\
+			gsize i = 0;										\
+														\
+			for (i = 0; i < length; i++) {								\
+				GTypeName *value = g_new0 (GTypeName, 1);					\
+				*value = values[i];								\
+														\
+				retval = g_slist_prepend (retval, value);					\
+			}											\
+														\
+			g_free (values);									\
+		}												\
+	}													\
+														\
+	g_strfreev (data);											\
+	return g_slist_reverse (retval);									\
+}
+
+#define TRACKER_CONFIGURATION_LIST_SET_(TypeName, GTypeName)					\
+static void											\
+tracker_configuration_set_##TypeName##_list (const gchar * const key, GSList * value)		\
+{												\
+	gchar **data = NULL;									\
+	GTypeName *list = NULL;									\
+	guint length = 0;									\
+												\
+	g_return_if_fail (key);									\
+	g_return_if_fail (value);								\
+												\
+	data = g_strsplit (key, "/", 3);							\
+	length = g_slist_length (value);							\
+	list = g_new0 (GTypeName, length);							\
+												\
+	guint i;										\
+	const GSList *tmp;									\
+	for (i = 0, tmp = value; tmp; tmp = tmp->next, i++) {					\
+		if (tmp->data) {								\
+			GTypeName *n = tmp->data;						\
+			list[i] = *n;								\
+		}										\
+	}											\
+												\
+	g_key_file_set_##TypeName##_list (configuration, data[1], data[2], list, length);	\
+												\
+	g_free (list);										\
+	g_strfreev (data);									\
+}
+
+TRACKER_CONFIGURATION_LIST_GET_(boolean, gboolean)
+TRACKER_CONFIGURATION_LIST_SET_(boolean, gboolean)
+
+TRACKER_CONFIGURATION_LIST_GET_(double, gdouble)
+TRACKER_CONFIGURATION_LIST_SET_(double, gdouble)
+
+TRACKER_CONFIGURATION_LIST_GET_(integer, gint)
+TRACKER_CONFIGURATION_LIST_SET_(integer, gint)
+
+#undef TRACKER_CONFIGURATION_LIST_GET_
+#undef TRACKER_CONFIGURATION_LIST_SET_
+
+/* Implement string lists manually for strings are special you see */
+
+static GSList *
+tracker_configuration_get_string_list (const gchar * const key, GError ** error)
+{
+	gchar **data = NULL;
+	GSList *retval = NULL;
+
+	g_return_val_if_fail (key, NULL);
+
+	data = g_strsplit (key, "/", 3);
+
+	if (g_key_file_has_key (configuration, data[1], data[2], error)) {
+		gsize length = 0;
+		gchar **values = NULL;
+
+		values = g_key_file_get_string_list (configuration, data[1], data[2], &length, error);
+
+		if (values) {
+			gsize i = 0;
+
+			for (i = 0; i < length; i++) {
+				gchar *value = g_strdup (values[i]);
+				retval = g_slist_prepend (retval, value);
+			}
+
+			g_strfreev (values);
+		}
+	}
+
+	g_strfreev (data);
+	return g_slist_reverse (retval);
+}
+
+static void
+tracker_configuration_set_string_list (const gchar * const key, GSList * value)
+{
+	gchar **data = NULL;
+	gchar **list = NULL;
+	guint length = 0;
+
+	g_return_if_fail (key);
+	g_return_if_fail (value);
+
+	data = g_strsplit (key, "/", 3);
+	length = g_slist_length (value);
+	list = g_new0 (gchar *, length + 1);
+
+	guint i;
+	const GSList *tmp;
+	for (i = 0, tmp = value; tmp; tmp = tmp->next, i++) {
+		if (tmp->data) {
+			gchar *value = g_strdup (tmp->data);
+			list[i] = value;
+		}
+	}
+
+	g_key_file_set_string_list (configuration, data[1], data[2], (const gchar * const *)list, length);
+
+	g_strfreev (list);
+	g_strfreev (data);
+}
+
+GSList *
+tracker_configuration_get_list (const gchar * const key, GType g_type, GError ** error)
+{
+	switch (g_type) {
+		case G_TYPE_BOOLEAN:
+			return tracker_configuration_get_boolean_list (key, error);
+			break;
+		case G_TYPE_DOUBLE:
+			return tracker_configuration_get_double_list (key, error);
+			break;
+		case G_TYPE_INT:
+			return tracker_configuration_get_integer_list (key, error);
+			break;
+		case G_TYPE_STRING:
+			return tracker_configuration_get_string_list (key, error);
+			break;
+		default:
+			g_assert_not_reached ();
+	}
+}
+
+void
+tracker_configuration_set_list (const gchar * const key, GType g_type, GSList * value)
+{
+	switch (g_type) {
+		case G_TYPE_BOOLEAN:
+			tracker_configuration_set_boolean_list (key, value);
+			break;
+		case G_TYPE_DOUBLE:
+			tracker_configuration_set_double_list (key, value);
+			break;
+		case G_TYPE_INT:
+			tracker_configuration_set_integer_list (key, value);
+			break;
+		case G_TYPE_STRING:
+			tracker_configuration_set_string_list (key, value);
+			break;
+		default:
+			g_assert_not_reached ();
+	}
+}

Added: trunk/src/libtracker-common/tracker-configuration.h
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-configuration.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Saleem Abdulrasool (compnerd compnerd org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __LIBTRACKER_COMMON_CONFIGURATION_H__
+#define __LIBTRACKER_COMMON_CONFIGURATION_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef struct _LanguageMapEntry {
+	const gchar * const code;
+	const gchar * const language;
+} LanguageMapEntry;
+
+extern const LanguageMapEntry LanguageMap[];
+
+void	 tracker_configuration_load	   (void);
+void	 tracker_configuration_save	   (void);
+void	 tracker_configuration_free	   (void);
+gboolean tracker_configuration_get_boolean (const gchar * const   key,
+					    GError		**error);
+void	 tracker_configuration_set_boolean (const gchar * const   key,
+					    const gboolean	  value);
+gint	 tracker_configuration_get_integer (const gchar * const   key,
+					    GError		**error);
+void	 tracker_configuration_set_integer (const gchar * const   key,
+					    const gint		  value);
+gchar *  tracker_configuration_get_string  (const gchar * const   key,
+					    GError		**error);
+void	 tracker_configuration_set_string  (const gchar * const   key,
+					    const gchar * const   value);
+GSList * tracker_configuration_get_list    (const gchar * const   key,
+					    GType		  g_type,
+					    GError		**error);
+void	 tracker_configuration_set_list    (const gchar * const   key,
+					    GType		  g_type,
+					    GSList		 *value);
+
+G_END_DECLS
+
+#endif /* LIBTRACKER_COMMON_CONFIGURATION */

Added: trunk/src/libtracker-common/tracker-dbus.c
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-dbus.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,332 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "tracker-dbus.h"
+
+#include <gio/gio.h>
+
+struct TrackerDBusRequestHandler {
+	TrackerDBusRequestFunc new;
+	TrackerDBusRequestFunc done;
+	gpointer	       user_data;
+};
+
+static GSList *hooks;
+
+static void
+request_handler_call_for_new (guint request_id)
+{
+	GSList *l;
+
+	for (l = hooks; l; l = l->next) {
+		TrackerDBusRequestHandler *handler;
+
+		handler = l->data;
+
+		if (handler->new) {
+			(handler->new)(request_id, handler->user_data);
+		}
+	}
+}
+
+static void
+request_handler_call_for_done (guint request_id)
+{
+	GSList *l;
+
+	for (l = hooks; l; l = l->next) {
+		TrackerDBusRequestHandler *handler;
+
+		handler = l->data;
+
+		if (handler->done) {
+			(handler->done)(request_id, handler->user_data);
+		}
+	}
+}
+
+GValue *
+tracker_dbus_gvalue_slice_new (GType type)
+{
+	GValue *value;
+
+	value = g_slice_new0 (GValue);
+	g_value_init (value, type);
+
+	return value;
+}
+
+void
+tracker_dbus_gvalue_slice_free (GValue *value)
+{
+	g_value_unset (value);
+	g_slice_free (GValue, value);
+}
+
+GQuark
+tracker_dbus_error_quark (void)
+{
+	return g_quark_from_static_string (TRACKER_DBUS_ERROR_DOMAIN);
+}
+
+TrackerDBusData *
+tracker_dbus_data_new (const gpointer arg1,
+		       const gpointer arg2)
+{
+	TrackerDBusData *data;
+
+	data = g_new0 (TrackerDBusData, 1);
+
+	data->id = tracker_dbus_get_next_request_id ();
+
+	data->data1 = arg1;
+	data->data2 = arg2;
+
+	return data;
+}
+
+gchar **
+tracker_dbus_slist_to_strv (GSList *list)
+{
+	GSList	*l;
+	gchar  **strv;
+	gint	 i = 0;
+
+	strv = g_new0 (gchar*, g_slist_length (list) + 1);
+
+	for (l = list; l != NULL; l = l->next) {
+		if (!g_utf8_validate (l->data, -1, NULL)) {
+			g_message ("Could not add string:'%s' to GStrv, invalid UTF-8",
+				   (gchar*) l->data);
+			continue;
+		}
+
+		strv[i++] = g_strdup (l->data);
+	}
+
+	strv[i] = NULL;
+
+	return strv;
+}
+
+gchar **
+tracker_dbus_queue_str_to_strv (GQueue *queue,
+				gint	max)
+{
+	gchar **strv;
+	gchar  *str;
+	gint	i, j;
+	gint	length;
+
+	length = g_queue_get_length (queue);
+
+	if (max > 0) {
+		length = MIN (max, length);
+	}
+
+	strv = g_new0 (gchar*, length + 1);
+
+	for (i = 0, j = 0; i < length; i++) {
+		str = g_queue_pop_head (queue);
+
+		if (!str) {
+			break;
+		}
+
+		if (!g_utf8_validate (str, -1, NULL)) {
+			g_message ("Could not add string:'%s' to GStrv, invalid UTF-8", str);
+			g_free (str);
+			continue;
+		}
+
+		strv[j++] = str;
+	}
+
+	strv[j] = NULL;
+
+	return strv;
+}
+
+gchar **
+tracker_dbus_queue_gfile_to_strv (GQueue *queue,
+				  gint	  max)
+{
+	gchar **strv;
+	gchar  *str;
+	GFile  *file;
+	gint	i, j;
+	gint	length;
+
+	length = g_queue_get_length (queue);
+
+	if (max > 0) {
+		length = MIN (max, length);
+	}
+
+	strv = g_new0 (gchar*, length + 1);
+
+	for (i = 0, j = 0; i < length; i++) {
+		file = g_queue_pop_head (queue);
+
+		if (!file) {
+			break;
+		}
+
+		str = g_file_get_path (file);
+		g_object_unref (file);
+
+		if (!g_utf8_validate (str, -1, NULL)) {
+			g_message ("Could not add string:'%s' to GStrv, invalid UTF-8", str);
+			g_free (str);
+			continue;
+		}
+
+		strv[j++] = str;
+	}
+
+	strv[j] = NULL;
+
+	return strv;
+}
+
+void
+tracker_dbus_results_ptr_array_free (GPtrArray **ptr_array)
+{
+	if (!ptr_array || !(*ptr_array)) {
+		return;
+	}
+
+	g_ptr_array_foreach (*ptr_array, (GFunc) g_strfreev, NULL);
+	g_ptr_array_free (*ptr_array, TRUE);
+	*ptr_array = NULL;
+}
+
+guint
+tracker_dbus_get_next_request_id (void)
+{
+	static guint request_id = 1;
+
+	return request_id++;
+}
+
+TrackerDBusRequestHandler *
+tracker_dbus_request_add_hook (TrackerDBusRequestFunc new,
+			       TrackerDBusRequestFunc done,
+			       gpointer		      user_data)
+{
+	TrackerDBusRequestHandler *handler;
+
+	handler = g_slice_new0 (TrackerDBusRequestHandler);
+	handler->new = new;
+	handler->done = done;
+	handler->user_data = user_data;
+
+	hooks = g_slist_append (hooks, handler);
+
+	return handler;
+}
+
+void
+tracker_dbus_request_remove_hook (TrackerDBusRequestHandler *handler)
+{
+	g_return_if_fail (handler != NULL);
+
+	hooks = g_slist_remove (hooks, handler);
+	g_slice_free (TrackerDBusRequestHandler, handler);
+}
+
+void
+tracker_dbus_request_new (gint		request_id,
+			  const gchar  *format,
+			  ...)
+{
+	gchar	*str;
+	va_list  args;
+
+	va_start (args, format);
+	str = g_strdup_vprintf (format, args);
+	va_end (args);
+
+	g_message ("<--- [%d] %s",
+		   request_id,
+		   str);
+
+	g_free (str);
+
+	request_handler_call_for_new (request_id);
+}
+
+void
+tracker_dbus_request_success (gint request_id)
+{
+	request_handler_call_for_done (request_id);
+
+	g_message ("---> [%d] Success, no error given",
+		   request_id);
+}
+
+void
+tracker_dbus_request_failed (gint	   request_id,
+			     GError	 **error,
+			     const gchar  *format,
+			     ...)
+{
+	gchar	*str;
+	va_list  args;
+
+	request_handler_call_for_done (request_id);
+
+	if (format) {
+		va_start (args, format);
+		str = g_strdup_vprintf (format, args);
+		va_end (args);
+
+		g_set_error (error, TRACKER_DBUS_ERROR, 0, str);
+	} else if (*error != NULL) {
+		str = g_strdup ((*error)->message);
+	} else {
+		str = g_strdup (_("No error given"));
+		g_warning ("Unset error and no error message.");
+	}
+
+	g_message ("---> [%d] Failed, %s",
+		   request_id,
+		   str);
+	g_free (str);
+}
+
+void
+tracker_dbus_request_comment (gint	   request_id,
+			      const gchar *format,
+			      ...)
+{
+	gchar	*str;
+	va_list  args;
+
+	va_start (args, format);
+	str = g_strdup_vprintf (format, args);
+	va_end (args);
+
+	g_message ("---- [%d] %s",
+		   request_id,
+		   str);
+	g_free (str);
+}

Added: trunk/src/libtracker-common/tracker-dbus.h
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-dbus.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,119 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __LIBTRACKER_DBUS_H__
+#define __LIBTRACKER_DBUS_H__
+
+#ifndef DBUS_API_SUBJECT_TO_CHANGE
+#define DBUS_API_SUBJECT_TO_CHANGE
+#endif
+
+#include <glib/gi18n.h>
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <dbus/dbus-glib.h>
+
+G_BEGIN_DECLS
+
+#define TRACKER_DBUS_ERROR_DOMAIN "TrackerDBus"
+#define TRACKER_DBUS_ERROR	  tracker_dbus_error_quark()
+
+#define tracker_dbus_async_return_if_fail(expr,context)			\
+	G_STMT_START {							\
+		if G_LIKELY(expr) { } else {				\
+			GError *error = NULL;				\
+									\
+			g_set_error (&error,				\
+				     TRACKER_DBUS_ERROR,		\
+				     0,					\
+				     _("Assertion `%s' failed"),	\
+				     #expr);				\
+									\
+			dbus_g_method_return_error (context, error);	\
+			g_clear_error (&error);				\
+									\
+			return;						\
+		};							\
+	} G_STMT_END
+
+#define tracker_dbus_return_val_if_fail(expr,val,error)			\
+	G_STMT_START {							\
+		if G_LIKELY(expr) { } else {				\
+			g_set_error (error,				\
+				     TRACKER_DBUS_ERROR,		\
+				     0,					\
+				     _("Assertion `%s' failed"),	\
+				     #expr);				\
+									\
+			return val;					\
+		};							\
+	} G_STMT_END
+
+typedef struct TrackerDBusRequestHandler TrackerDBusRequestHandler;
+
+typedef void (*TrackerDBusRequestFunc)	 (guint    request_id,
+					  gpointer user_data);
+
+typedef struct {
+	guint	 id;
+	gpointer data1;
+	gpointer data2;
+} TrackerDBusData;
+
+GQuark		 tracker_dbus_error_quark	     (void);
+TrackerDBusData *tracker_dbus_data_new		     (const gpointer		  arg1,
+						      const gpointer		  arg2);
+
+/* Utils */
+GValue *	 tracker_dbus_gvalue_slice_new	     (GType			  type);
+void		 tracker_dbus_gvalue_slice_free      (GValue			 *value);
+gchar **	 tracker_dbus_slist_to_strv	     (GSList			 *list);
+gchar **	 tracker_dbus_queue_str_to_strv      (GQueue			 *queue,
+						      gint			  max);
+gchar **	 tracker_dbus_queue_gfile_to_strv    (GQueue			 *queue,
+						      gint			  max);
+void		 tracker_dbus_results_ptr_array_free (GPtrArray			**ptr_array);
+
+/* Requests */
+guint		 tracker_dbus_get_next_request_id    (void);
+
+TrackerDBusRequestHandler *
+		 tracker_dbus_request_add_hook	     (TrackerDBusRequestFunc	  new,
+						      TrackerDBusRequestFunc	  done,
+						      gpointer			  user_data);
+void		 tracker_dbus_request_remove_hook    (TrackerDBusRequestHandler  *handler);
+
+void		 tracker_dbus_request_new	     (gint			  request_id,
+						      const gchar		 *format,
+						      ...);
+void		 tracker_dbus_request_success	     (gint			  request_id);
+void		 tracker_dbus_request_failed	     (gint			  request_id,
+						      GError			**error,
+						      const gchar		 *format,
+						      ...);
+void		 tracker_dbus_request_comment	     (gint			  request_id,
+						      const gchar		 *format,
+						      ...);
+
+G_END_DECLS
+
+#endif /* __LIBTRACKER_DBUS_H__ */

Added: trunk/src/libtracker-common/tracker-field-data.c
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-field-data.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,637 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <glib.h>
+
+#include "tracker-field-data.h"
+
+#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TRACKER_TYPE_FIELD_DATA, TrackerFieldDataPriv))
+
+typedef struct _TrackerFieldDataPriv TrackerFieldDataPriv;
+
+struct _TrackerFieldDataPriv {
+	gchar		 *alias;
+
+	gchar		 *table_name;
+	gchar		 *field_name;
+
+	gchar		 *select_field;
+	gchar		 *where_field;
+	gchar		 *id_field;
+
+	TrackerFieldType  data_type;
+
+	gboolean	  multiple_values;
+	gboolean	  is_select;
+	gboolean	  is_condition;
+	gboolean	  needs_join;
+};
+
+static void field_data_finalize     (GObject	  *object);
+static void field_data_get_property (GObject	  *object,
+				     guint	   param_id,
+				     GValue	  *value,
+				     GParamSpec   *pspec);
+static void field_data_set_property (GObject	  *object,
+				     guint	   param_id,
+				     const GValue *value,
+				     GParamSpec   *pspec);
+
+enum {
+	PROP_0,
+	PROP_ALIAS,
+	PROP_TABLE_NAME,
+	PROP_FIELD_NAME,
+	PROP_SELECT_FIELD,
+	PROP_WHERE_FIELD,
+	PROP_ID_FIELD,
+	PROP_DATA_TYPE,
+	PROP_MULTIPLE_VALUES,
+	PROP_IS_SELECT,
+	PROP_IS_CONDITION,
+	PROP_NEEDS_JOIN
+};
+
+G_DEFINE_TYPE (TrackerFieldData, tracker_field_data, G_TYPE_OBJECT);
+
+static void
+tracker_field_data_class_init (TrackerFieldDataClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	object_class->finalize	   = field_data_finalize;
+	object_class->get_property = field_data_get_property;
+	object_class->set_property = field_data_set_property;
+
+	g_object_class_install_property (object_class,
+					 PROP_ALIAS,
+					 g_param_spec_string ("alias",
+							      "alias",
+							      "A name for this field data",
+							      NULL,
+							      G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_TABLE_NAME,
+					 g_param_spec_string ("table-name",
+							      "Table name",
+							      "Table name",
+							      NULL,
+							      G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_FIELD_NAME,
+					 g_param_spec_string ("field-name",
+							      "Field name",
+							      "Field name",
+							      NULL,
+							      G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_SELECT_FIELD,
+					 g_param_spec_string ("select-field",
+							      "Select field",
+							      "Select field",
+							      NULL,
+							      G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_WHERE_FIELD,
+					 g_param_spec_string ("where-field",
+							      "Where field",
+							      "Where field",
+							      NULL,
+							      G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_ID_FIELD,
+					 g_param_spec_string ("id-field",
+							      "ID field",
+							      "ID field",
+							      NULL,
+							      G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_DATA_TYPE,
+					 g_param_spec_enum ("data-type",
+							    "Data type",
+							    "TrackerField type",
+							    tracker_field_type_get_type (),
+							    TRACKER_FIELD_TYPE_INDEX,
+							    G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_MULTIPLE_VALUES,
+					 g_param_spec_boolean ("multiple-values",
+							       "Multiple values",
+							       "Multiple values",
+							       FALSE,
+							       G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_IS_SELECT,
+					 g_param_spec_boolean ("is-select",
+							       "Is select",
+							       "Is select",
+							       FALSE,
+							       G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_IS_CONDITION,
+					 g_param_spec_boolean ("is-condition",
+							       "Is condition",
+							       "Is condition",
+							       FALSE,
+							       G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_NEEDS_JOIN,
+					 g_param_spec_boolean ("needs-join",
+							       "Needs join",
+							       "Needs join",
+							       FALSE,
+							       G_PARAM_READWRITE));
+
+	g_type_class_add_private (object_class, sizeof (TrackerFieldDataPriv));
+}
+
+static void
+tracker_field_data_init (TrackerFieldData *field_data)
+{
+}
+
+static void
+field_data_finalize (GObject *object)
+{
+	TrackerFieldDataPriv *priv;
+
+	priv = GET_PRIV (object);
+
+	g_free (priv->alias);
+
+	g_free (priv->table_name);
+	g_free (priv->field_name);
+
+	g_free (priv->select_field);
+	g_free (priv->where_field);
+	g_free (priv->id_field);
+
+	(G_OBJECT_CLASS (tracker_field_data_parent_class)->finalize) (object);
+}
+
+static void
+field_data_get_property (GObject    *object,
+			 guint	     param_id,
+			 GValue     *value,
+			 GParamSpec *pspec)
+{
+	TrackerFieldDataPriv *priv;
+
+	priv = GET_PRIV (object);
+
+	switch (param_id) {
+	case PROP_ALIAS:
+		g_value_set_string (value, priv->alias);
+		break;
+	case PROP_TABLE_NAME:
+		g_value_set_string (value, priv->table_name);
+		break;
+	case PROP_FIELD_NAME:
+		g_value_set_string (value, priv->field_name);
+		break;
+	case PROP_SELECT_FIELD:
+		g_value_set_string (value, priv->select_field);
+		break;
+	case PROP_WHERE_FIELD:
+		g_value_set_string (value, priv->where_field);
+		break;
+	case PROP_ID_FIELD:
+		g_value_set_string (value, priv->id_field);
+		break;
+	case PROP_DATA_TYPE:
+		g_value_set_enum (value, priv->data_type);
+		break;
+	case PROP_MULTIPLE_VALUES:
+		g_value_set_boolean (value, priv->multiple_values);
+		break;
+	case PROP_IS_SELECT:
+		g_value_set_boolean (value, priv->is_select);
+		break;
+	case PROP_IS_CONDITION:
+		g_value_set_boolean (value, priv->is_condition);
+		break;
+	case PROP_NEEDS_JOIN:
+		g_value_set_boolean (value, priv->needs_join);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	};
+}
+
+static void
+field_data_set_property (GObject      *object,
+			 guint	       param_id,
+			 const GValue *value,
+			 GParamSpec   *pspec)
+{
+	switch (param_id) {
+	case PROP_ALIAS:
+		tracker_field_data_set_alias (TRACKER_FIELD_DATA (object),
+					      g_value_get_string (value));
+		break;
+	case PROP_TABLE_NAME:
+		tracker_field_data_set_table_name (TRACKER_FIELD_DATA (object),
+						   g_value_get_string (value));
+		break;
+	case PROP_FIELD_NAME:
+		tracker_field_data_set_field_name (TRACKER_FIELD_DATA (object),
+						   g_value_get_string (value));
+		break;
+	case PROP_SELECT_FIELD:
+		tracker_field_data_set_select_field (TRACKER_FIELD_DATA (object),
+						     g_value_get_string (value));
+		break;
+	case PROP_WHERE_FIELD:
+		tracker_field_data_set_where_field (TRACKER_FIELD_DATA (object),
+						    g_value_get_string (value));
+		break;
+	case PROP_ID_FIELD:
+		tracker_field_data_set_id_field (TRACKER_FIELD_DATA (object),
+						 g_value_get_string (value));
+		break;
+	case PROP_DATA_TYPE:
+		tracker_field_data_set_data_type (TRACKER_FIELD_DATA (object),
+						 g_value_get_enum (value));
+		break;
+	case PROP_MULTIPLE_VALUES:
+		tracker_field_data_set_multiple_values (TRACKER_FIELD_DATA (object),
+							g_value_get_boolean (value));
+		break;
+	case PROP_IS_SELECT:
+		tracker_field_data_set_is_select (TRACKER_FIELD_DATA (object),
+						  g_value_get_boolean (value));
+		break;
+	case PROP_IS_CONDITION:
+		tracker_field_data_set_is_condition (TRACKER_FIELD_DATA (object),
+						     g_value_get_boolean (value));
+		break;
+	case PROP_NEEDS_JOIN:
+		tracker_field_data_set_needs_join (TRACKER_FIELD_DATA (object),
+						   g_value_get_boolean (value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	};
+}
+
+TrackerFieldData *
+tracker_field_data_new (void)
+{
+	TrackerFieldData *field_data;
+
+	field_data = g_object_new (TRACKER_TYPE_FIELD_DATA, NULL);
+
+	return field_data;
+}
+
+const gchar *
+tracker_field_data_get_alias (TrackerFieldData *field_data)
+{
+	TrackerFieldDataPriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_FIELD_DATA (field_data), NULL);
+
+	priv = GET_PRIV (field_data);
+
+	return priv->alias;
+}
+
+const gchar *
+tracker_field_data_get_table_name (TrackerFieldData *field_data)
+{
+	TrackerFieldDataPriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_FIELD_DATA (field_data), NULL);
+
+	priv = GET_PRIV (field_data);
+
+	return priv->table_name;
+}
+
+const gchar *
+tracker_field_data_get_field_name (TrackerFieldData *field_data)
+{
+	TrackerFieldDataPriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_FIELD_DATA (field_data), NULL);
+
+	priv = GET_PRIV (field_data);
+
+	return priv->field_name;
+}
+
+const gchar *
+tracker_field_data_get_select_field (TrackerFieldData *field_data)
+{
+	TrackerFieldDataPriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_FIELD_DATA (field_data), NULL);
+
+	priv = GET_PRIV (field_data);
+
+	return priv->select_field;
+}
+
+const gchar *
+tracker_field_data_get_where_field (TrackerFieldData *field_data)
+{
+	TrackerFieldDataPriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_FIELD_DATA (field_data), NULL);
+
+	priv = GET_PRIV (field_data);
+
+	return priv->where_field;
+}
+
+const gchar *
+tracker_field_data_get_id_field (TrackerFieldData *field_data)
+{
+	TrackerFieldDataPriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_FIELD_DATA (field_data), NULL);
+
+	priv = GET_PRIV (field_data);
+
+	return priv->id_field;
+}
+
+TrackerFieldType
+tracker_field_data_get_data_type (TrackerFieldData *field_data)
+{
+	TrackerFieldDataPriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_FIELD_DATA (field_data), TRACKER_FIELD_TYPE_INDEX);
+
+	priv = GET_PRIV (field_data);
+
+	return priv->data_type;
+}
+
+gboolean
+tracker_field_data_get_multiple_values (TrackerFieldData *field_data)
+{
+	TrackerFieldDataPriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_FIELD_DATA (field_data), FALSE);
+
+	priv = GET_PRIV (field_data);
+
+	return priv->multiple_values;
+}
+
+
+gboolean
+tracker_field_data_get_is_select (TrackerFieldData *field_data)
+{
+	TrackerFieldDataPriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_FIELD_DATA (field_data), FALSE);
+
+	priv = GET_PRIV (field_data);
+
+	return priv->is_select;
+}
+
+gboolean
+tracker_field_data_get_is_condition (TrackerFieldData *field_data)
+{
+	TrackerFieldDataPriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_FIELD_DATA (field_data), FALSE);
+
+	priv = GET_PRIV (field_data);
+
+	return priv->is_condition;
+}
+
+gboolean
+tracker_field_data_get_needs_join (TrackerFieldData *field_data)
+{
+	TrackerFieldDataPriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_FIELD_DATA (field_data), FALSE);
+
+	priv = GET_PRIV (field_data);
+
+	return priv->needs_join;
+}
+
+void
+tracker_field_data_set_alias (TrackerFieldData *field_data,
+			      const gchar      *value)
+{
+	TrackerFieldDataPriv *priv;
+
+	g_return_if_fail (TRACKER_IS_FIELD_DATA (field_data));
+
+	priv = GET_PRIV (field_data);
+
+	g_free (priv->alias);
+
+	if (value) {
+		priv->alias = g_strdup (value);
+	} else {
+		priv->alias = NULL;
+	}
+
+	g_object_notify (G_OBJECT (field_data), "alias");
+}
+
+void
+tracker_field_data_set_table_name (TrackerFieldData *field_data,
+				   const gchar	    *value)
+{
+	TrackerFieldDataPriv *priv;
+
+	g_return_if_fail (TRACKER_IS_FIELD_DATA (field_data));
+
+	priv = GET_PRIV (field_data);
+
+	g_free (priv->table_name);
+
+	if (value) {
+		priv->table_name = g_strdup (value);
+	} else {
+		priv->table_name = NULL;
+	}
+
+	g_object_notify (G_OBJECT (field_data), "table-name");
+}
+
+void
+tracker_field_data_set_field_name (TrackerFieldData *field_data,
+				   const gchar	    *value)
+{
+	TrackerFieldDataPriv *priv;
+
+	g_return_if_fail (TRACKER_IS_FIELD_DATA (field_data));
+
+	priv = GET_PRIV (field_data);
+
+	g_free (priv->field_name);
+
+	if (value) {
+		priv->field_name = g_strdup (value);
+	} else {
+		priv->field_name = NULL;
+	}
+
+	g_object_notify (G_OBJECT (field_data), "field-name");
+}
+
+void
+tracker_field_data_set_select_field (TrackerFieldData *field_data,
+				     const gchar      *value)
+{
+	TrackerFieldDataPriv *priv;
+
+	g_return_if_fail (TRACKER_IS_FIELD_DATA (field_data));
+
+	priv = GET_PRIV (field_data);
+
+	g_free (priv->select_field);
+
+	if (value) {
+		priv->select_field = g_strdup (value);
+	} else {
+		priv->select_field = NULL;
+	}
+
+	g_object_notify (G_OBJECT (field_data), "select-field");
+}
+
+void
+tracker_field_data_set_where_field (TrackerFieldData *field_data,
+				    const gchar      *value)
+{
+	TrackerFieldDataPriv *priv;
+
+	g_return_if_fail (TRACKER_IS_FIELD_DATA (field_data));
+
+	priv = GET_PRIV (field_data);
+
+	g_free (priv->where_field);
+
+	if (value) {
+		priv->where_field = g_strdup (value);
+	} else {
+		priv->where_field = NULL;
+	}
+
+	g_object_notify (G_OBJECT (field_data), "where-field");
+}
+
+void
+tracker_field_data_set_id_field (TrackerFieldData *field_data,
+				 const gchar	  *value)
+{
+	TrackerFieldDataPriv *priv;
+
+	g_return_if_fail (TRACKER_IS_FIELD_DATA (field_data));
+
+	priv = GET_PRIV (field_data);
+
+	g_free (priv->id_field);
+
+	if (value) {
+		priv->id_field = g_strdup (value);
+	} else {
+		priv->id_field = NULL;
+	}
+
+	g_object_notify (G_OBJECT (field_data), "id-field");
+}
+
+void
+tracker_field_data_set_data_type (TrackerFieldData *field_data,
+				  TrackerFieldType  value)
+{
+	TrackerFieldDataPriv *priv;
+
+	g_return_if_fail (TRACKER_IS_FIELD_DATA (field_data));
+
+	priv = GET_PRIV (field_data);
+
+	priv->data_type = value;
+	g_object_notify (G_OBJECT (field_data), "data-type");
+}
+
+void
+tracker_field_data_set_multiple_values (TrackerFieldData *field_data,
+					gboolean	  value)
+{
+	TrackerFieldDataPriv *priv;
+
+	g_return_if_fail (TRACKER_IS_FIELD_DATA (field_data));
+
+	priv = GET_PRIV (field_data);
+
+	priv->multiple_values = value;
+	g_object_notify (G_OBJECT (field_data), "multiple-values");
+}
+
+void
+tracker_field_data_set_is_select (TrackerFieldData *field_data,
+				  gboolean	    value)
+{
+	TrackerFieldDataPriv *priv;
+
+	g_return_if_fail (TRACKER_IS_FIELD_DATA (field_data));
+
+	priv = GET_PRIV (field_data);
+
+	priv->is_select = value;
+	g_object_notify (G_OBJECT (field_data), "is-select");
+}
+
+void
+tracker_field_data_set_is_condition (TrackerFieldData *field_data,
+				     gboolean	       value)
+{
+	TrackerFieldDataPriv *priv;
+
+	g_return_if_fail (TRACKER_IS_FIELD_DATA (field_data));
+
+	priv = GET_PRIV (field_data);
+
+	priv->is_condition = value;
+	g_object_notify (G_OBJECT (field_data), "is-condition");
+}
+
+void
+tracker_field_data_set_needs_join (TrackerFieldData *field_data,
+				   gboolean	     value)
+{
+	TrackerFieldDataPriv *priv;
+
+	g_return_if_fail (TRACKER_IS_FIELD_DATA (field_data));
+
+	priv = GET_PRIV (field_data);
+
+	priv->needs_join = value;
+	g_object_notify (G_OBJECT (field_data), "needs-join");
+}

Added: trunk/src/libtracker-common/tracker-field-data.h
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-field-data.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,91 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKERD_FIELD_DATA_H__
+#define __TRACKERD_FIELD_DATA_H__
+
+#include <glib-object.h>
+
+#include "tracker-field.h"
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_FIELD_DATA		(tracker_field_data_get_type ())
+#define TRACKER_FIELD_DATA(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), TRACKER_TYPE_FIELD_DATA, TrackerFieldData))
+#define TRACKER_FIELD_DATA_CLASS(k)	(G_TYPE_CHECK_CLASS_CAST ((k), TRACKER_TYPE_FIELD_DATA, TrackerFieldDataClass))
+#define TRACKER_IS_FIELD_DATA(o)	(G_TYPE_CHECK_INSTANCE_TYPE ((o), TRACKER_TYPE_FIELD_DATA))
+#define TRACKER_IS_FIELD_DATA_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), TRACKER_TYPE_FIELD_DATA))
+#define TRACKER_FIELD_DATA_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), TRACKER_TYPE_FIELD_DATA, TrackerFieldDataClass))
+
+typedef struct _TrackerFieldData      TrackerFieldData;
+typedef struct _TrackerFieldDataClass TrackerFieldDataClass;
+
+struct _TrackerFieldData {
+	GObject      parent;
+};
+
+struct _TrackerFieldDataClass {
+	GObjectClass parent_class;
+};
+
+GType		  tracker_field_data_get_type		 (void) G_GNUC_CONST;
+
+TrackerFieldData *tracker_field_data_new		 (void);
+
+const gchar *	  tracker_field_data_get_alias		 (TrackerFieldData *field_data);
+const gchar *	  tracker_field_data_get_table_name	 (TrackerFieldData *field_data);
+const gchar *	  tracker_field_data_get_field_name	 (TrackerFieldData *field_data);
+const gchar *	  tracker_field_data_get_select_field	 (TrackerFieldData *field_data);
+const gchar *	  tracker_field_data_get_where_field	 (TrackerFieldData *field_data);
+const gchar *	  tracker_field_data_get_id_field	 (TrackerFieldData *field_data);
+TrackerFieldType  tracker_field_data_get_data_type	 (TrackerFieldData *field_data);
+gboolean	  tracker_field_data_get_multiple_values (TrackerFieldData *field_data);
+gboolean	  tracker_field_data_get_is_select	 (TrackerFieldData *field_data);
+gboolean	  tracker_field_data_get_is_condition	 (TrackerFieldData *field_data);
+gboolean	  tracker_field_data_get_needs_join	 (TrackerFieldData *field_data);
+
+void		  tracker_field_data_set_alias		 (TrackerFieldData *field_data,
+							  const gchar	   *value);
+void		  tracker_field_data_set_table_name	 (TrackerFieldData *field_data,
+							  const gchar	   *value);
+void		  tracker_field_data_set_field_name	 (TrackerFieldData *field_data,
+							  const gchar	   *value);
+void		  tracker_field_data_set_select_field	 (TrackerFieldData *field_data,
+							  const gchar	   *value);
+void		  tracker_field_data_set_where_field	 (TrackerFieldData *field_data,
+							  const gchar	   *value);
+void		  tracker_field_data_set_id_field	 (TrackerFieldData *field_data,
+							  const gchar	   *value);
+void		  tracker_field_data_set_data_type	 (TrackerFieldData *field_data,
+							  TrackerFieldType  value);
+void		  tracker_field_data_set_multiple_values (TrackerFieldData *field_data,
+							  gboolean	    value);
+void		  tracker_field_data_set_is_select	 (TrackerFieldData *field_data,
+							  gboolean	    value);
+void		  tracker_field_data_set_is_condition	 (TrackerFieldData *field_data,
+							  gboolean	    value);
+void		  tracker_field_data_set_needs_join	 (TrackerFieldData *field_data,
+							  gboolean	    value);
+
+G_END_DECLS
+
+#endif /* __TRACKERD_FIELD_DATA_H__ */
+

Added: trunk/src/libtracker-common/tracker-field.c
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-field.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,746 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <glib.h>
+
+#include "tracker-field.h"
+
+#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TRACKER_TYPE_FIELD, TrackerFieldPriv))
+
+typedef struct _TrackerFieldPriv TrackerFieldPriv;
+
+struct _TrackerFieldPriv {
+	gchar	      *id;
+	gchar	      *name;
+
+	TrackerFieldType  data_type;
+	gchar	      *field_name;
+	gint	       weight;
+	gboolean       embedded;
+	gboolean       multiple_values;
+	gboolean       delimited;
+	gboolean       filtered;
+	gboolean       store_metadata;
+
+	GSList	      *child_ids;
+};
+
+static void field_finalize     (GObject      *object);
+static void field_get_property (GObject      *object,
+				guint	      param_id,
+				GValue	     *value,
+				GParamSpec   *pspec);
+static void field_set_property (GObject      *object,
+				guint	      param_id,
+				const GValue *value,
+				GParamSpec   *pspec);
+
+enum {
+	PROP_0,
+	PROP_ID,
+	PROP_NAME,
+	PROP_DATA_TYPE,
+	PROP_FIELD_NAME,
+	PROP_WEIGHT,
+	PROP_EMBEDDED,
+	PROP_MULTIPLE_VALUES,
+	PROP_DELIMITED,
+	PROP_FILTERED,
+	PROP_STORE_METADATA,
+	PROP_CHILD_IDS
+};
+
+GType
+tracker_field_type_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			{ TRACKER_FIELD_TYPE_KEYWORD,
+			  "TRACKER_FIELD_TYPE_KEYWORD",
+			  "keyword" },
+			{ TRACKER_FIELD_TYPE_INDEX,
+			  "TRACKER_FIELD_TYPE_INDEX",
+			  "index" },
+			{ TRACKER_FIELD_TYPE_FULLTEXT,
+			  "TRACKER_FIELD_TYPE_FULLTEXT",
+			  "fulltext" },
+			{ TRACKER_FIELD_TYPE_STRING,
+			  "TRACKER_FIELD_TYPE_STRING",
+			  "string" },
+			{ TRACKER_FIELD_TYPE_INTEGER,
+			  "TRACKER_FIELD_TYPE_INTEGER",
+			  "integer" },
+			{ TRACKER_FIELD_TYPE_DOUBLE,
+			  "TRACKER_FIELD_TYPE_DOUBLE",
+			  "double" },
+			{ TRACKER_FIELD_TYPE_DATE,
+			  "TRACKER_FIELD_TYPE_DATE",
+			  "date" },
+			{ TRACKER_FIELD_TYPE_BLOB,
+			  "TRACKER_FIELD_TYPE_BLOB",
+			  "blob" },
+			{ TRACKER_FIELD_TYPE_STRUCT,
+			  "TRACKER_FIELD_TYPE_STRUCT",
+			  "struct" },
+			{ TRACKER_FIELD_TYPE_LINK,
+			  "TRACKER_FIELD_TYPE_LINK",
+			  "link" },
+			{ 0, NULL, NULL }
+		};
+
+		etype = g_enum_register_static ("TrackerFieldType", values);
+	}
+
+	return etype;
+}
+
+G_DEFINE_TYPE (TrackerField, tracker_field, G_TYPE_OBJECT);
+
+const gchar *
+tracker_field_type_to_string (TrackerFieldType fieldtype)
+{
+	GType	    type;
+	GEnumClass *enum_class;
+	GEnumValue *enum_value;
+
+	type = tracker_field_type_get_type ();
+	enum_class = G_ENUM_CLASS (g_type_class_peek (type));
+	enum_value = g_enum_get_value (enum_class, fieldtype);
+
+	if (!enum_value) {
+		return NULL;
+	}
+
+	return enum_value->value_nick;
+}
+
+static void
+tracker_field_class_init (TrackerFieldClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	object_class->finalize	   = field_finalize;
+	object_class->get_property = field_get_property;
+	object_class->set_property = field_set_property;
+
+	g_object_class_install_property (object_class,
+					 PROP_ID,
+					 g_param_spec_string ("id",
+							      "id",
+							      "Unique identifier for this field",
+							      NULL,
+							      G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_NAME,
+					 g_param_spec_string ("name",
+							      "name",
+							      "Field name",
+							      NULL,
+							      G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_DATA_TYPE,
+					 g_param_spec_enum ("data-type",
+							    "data-type",
+							    "Field data type",
+							    tracker_field_type_get_type (),
+							    TRACKER_FIELD_TYPE_INDEX,
+							    G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_FIELD_NAME,
+					 g_param_spec_string ("field-name",
+							      "field-name",
+							      "Column in services table with the contents of this metadata",
+							      NULL,
+							      G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_WEIGHT,
+					 g_param_spec_int ("weight",
+							   "weight",
+							   "Boost to the score",
+							   0,
+							   G_MAXINT,
+							   0,
+							   G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_EMBEDDED,
+					 g_param_spec_boolean ("embedded",
+							       "embedded",
+							       "Embedded",
+							       TRUE,
+							       G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_MULTIPLE_VALUES,
+					 g_param_spec_boolean ("multiple-values",
+							       "multiple-values",
+							       "Multiple values",
+							       TRUE,
+							       G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_DELIMITED,
+					 g_param_spec_boolean ("delimited",
+							       "delimited",
+							       "Delimited",
+							       FALSE,
+							       G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_FILTERED,
+					 g_param_spec_boolean ("filtered",
+							       "filtered",
+							       "Filtered",
+							       FALSE,
+							       G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_STORE_METADATA,
+					 g_param_spec_boolean ("store-metadata",
+							       "store-metadata",
+							       "Store metadata",
+							       FALSE,
+							       G_PARAM_READWRITE));
+
+	g_object_class_install_property (object_class,
+					 PROP_CHILD_IDS,
+					 g_param_spec_pointer ("child-ids",
+							       "child-ids",
+							       "Child ids",
+							       G_PARAM_READWRITE));
+
+	g_type_class_add_private (object_class, sizeof (TrackerFieldPriv));
+}
+
+static void
+tracker_field_init (TrackerField *field)
+{
+}
+
+static void
+field_finalize (GObject *object)
+{
+	TrackerFieldPriv *priv;
+
+	priv = GET_PRIV (object);
+
+	g_free (priv->id);
+	g_free (priv->name);
+
+	if (priv->field_name) {
+		g_free (priv->field_name);
+	}
+
+	g_slist_foreach (priv->child_ids, (GFunc) g_free, NULL);
+	g_slist_free (priv->child_ids);
+
+	(G_OBJECT_CLASS (tracker_field_parent_class)->finalize) (object);
+}
+
+static void
+field_get_property (GObject    *object,
+		    guint	param_id,
+		    GValue     *value,
+		    GParamSpec *pspec)
+{
+	TrackerFieldPriv *priv;
+
+	priv = GET_PRIV (object);
+
+	switch (param_id) {
+	case PROP_ID:
+		g_value_set_string (value, priv->id);
+		break;
+	case PROP_NAME:
+		g_value_set_string (value, priv->name);
+		break;
+	case PROP_DATA_TYPE:
+		g_value_set_enum (value, priv->data_type);
+		break;
+	case PROP_FIELD_NAME:
+		g_value_set_string (value, priv->field_name);
+		break;
+	case PROP_WEIGHT:
+		g_value_set_int (value, priv->weight);
+		break;
+	case PROP_EMBEDDED:
+		g_value_set_boolean (value, priv->embedded);
+		break;
+	case PROP_MULTIPLE_VALUES:
+		g_value_set_boolean (value, priv->multiple_values);
+		break;
+	case PROP_DELIMITED:
+		g_value_set_boolean (value, priv->delimited);
+		break;
+	case PROP_FILTERED:
+		g_value_set_boolean (value, priv->filtered);
+		break;
+	case PROP_STORE_METADATA:
+		g_value_set_boolean (value, priv->store_metadata);
+		break;
+	case PROP_CHILD_IDS:
+		g_value_set_pointer (value, priv->child_ids);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	};
+}
+
+static void
+field_set_property (GObject	 *object,
+		    guint	  param_id,
+		    const GValue *value,
+		    GParamSpec	 *pspec)
+{
+	switch (param_id) {
+	case PROP_ID:
+		tracker_field_set_id (TRACKER_FIELD (object),
+				      g_value_get_string (value));
+		break;
+	case PROP_NAME:
+		tracker_field_set_name (TRACKER_FIELD (object),
+					g_value_get_string (value));
+		break;
+	case PROP_DATA_TYPE:
+		tracker_field_set_data_type (TRACKER_FIELD (object),
+					     g_value_get_enum (value));
+		break;
+	case PROP_FIELD_NAME:
+		tracker_field_set_field_name (TRACKER_FIELD (object),
+					      g_value_get_string (value));
+		break;
+	case PROP_WEIGHT:
+		tracker_field_set_weight (TRACKER_FIELD (object),
+					  g_value_get_int (value));
+		break;
+	case PROP_EMBEDDED:
+		tracker_field_set_embedded (TRACKER_FIELD (object),
+					    g_value_get_boolean (value));
+		break;
+	case PROP_MULTIPLE_VALUES:
+		tracker_field_set_multiple_values (TRACKER_FIELD (object),
+						   g_value_get_boolean (value));
+		break;
+	case PROP_DELIMITED:
+		tracker_field_set_delimited (TRACKER_FIELD (object),
+					     g_value_get_boolean (value));
+		break;
+	case PROP_FILTERED:
+		tracker_field_set_filtered (TRACKER_FIELD (object),
+					    g_value_get_boolean (value));
+		break;
+	case PROP_STORE_METADATA:
+		tracker_field_set_store_metadata (TRACKER_FIELD (object),
+						  g_value_get_boolean (value));
+		break;
+	case PROP_CHILD_IDS:
+		tracker_field_set_child_ids (TRACKER_FIELD (object),
+					     g_value_get_pointer (value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	};
+}
+
+static gboolean
+field_int_validate (TrackerField *field,
+		    const gchar   *property,
+		    gint	    value)
+{
+#ifdef G_DISABLE_CHECKS
+	GParamSpec *spec;
+	GValue	    value = { 0 };
+	gboolean    valid;
+
+	spec = g_object_class_find_property (G_OBJECT_CLASS (field), property);
+	g_return_val_if_fail (spec != NULL, FALSE);
+
+	g_value_init (&value, spec->value_type);
+	g_value_set_int (&value, verbosity);
+	valid = g_param_value_validate (spec, &value);
+	g_value_unset (&value);
+
+	g_return_val_if_fail (valid != TRUE, FALSE);
+#endif
+
+	return TRUE;
+}
+
+TrackerField *
+tracker_field_new (void)
+{
+	TrackerField *field;
+
+	field = g_object_new (TRACKER_TYPE_FIELD, NULL);
+
+	return field;
+}
+
+const gchar *
+tracker_field_get_id (TrackerField *field)
+{
+	TrackerFieldPriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_FIELD (field), NULL);
+
+	priv = GET_PRIV (field);
+
+	return priv->id;
+}
+
+const gchar *
+tracker_field_get_name (TrackerField *field)
+{
+	TrackerFieldPriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_FIELD (field), NULL);
+
+	priv = GET_PRIV (field);
+
+	return priv->name;
+}
+
+TrackerFieldType
+tracker_field_get_data_type (TrackerField *field)
+{
+	TrackerFieldPriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_FIELD (field), TRACKER_FIELD_TYPE_STRING); //FIXME
+
+	priv = GET_PRIV (field);
+
+	return priv->data_type;
+}
+
+const gchar *
+tracker_field_get_field_name (TrackerField *field)
+{
+	TrackerFieldPriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_FIELD (field), NULL);
+
+	priv = GET_PRIV (field);
+
+	return priv->field_name;
+}
+
+gint
+tracker_field_get_weight (TrackerField *field)
+{
+	TrackerFieldPriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_FIELD (field), -1);
+
+	priv = GET_PRIV (field);
+
+	return priv->weight;
+}
+
+
+gboolean
+tracker_field_get_embedded (TrackerField *field)
+{
+	TrackerFieldPriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_FIELD (field), FALSE);
+
+	priv = GET_PRIV (field);
+
+	return priv->embedded;
+}
+
+
+gboolean
+tracker_field_get_multiple_values (TrackerField *field)
+{
+	TrackerFieldPriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_FIELD (field), FALSE);
+
+	priv = GET_PRIV (field);
+
+	return priv->multiple_values;
+}
+
+gboolean
+tracker_field_get_delimited (TrackerField *field)
+{
+	TrackerFieldPriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_FIELD (field), FALSE);
+
+	priv = GET_PRIV (field);
+
+	return priv->delimited;
+}
+
+gboolean
+tracker_field_get_filtered (TrackerField *field)
+{
+	TrackerFieldPriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_FIELD (field), FALSE);
+
+	priv = GET_PRIV (field);
+
+	return priv->filtered;
+}
+
+gboolean
+tracker_field_get_store_metadata (TrackerField *field)
+{
+	TrackerFieldPriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_FIELD (field), FALSE);
+
+	priv = GET_PRIV (field);
+
+	return priv->store_metadata;
+}
+
+
+const GSList *
+tracker_field_get_child_ids (TrackerField *field)
+{
+	TrackerFieldPriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_FIELD (field), NULL);
+
+	priv = GET_PRIV (field);
+
+	return priv->child_ids;
+}
+
+
+void
+tracker_field_set_id (TrackerField *field,
+		      const gchar  *value)
+{
+	TrackerFieldPriv *priv;
+
+	g_return_if_fail (TRACKER_IS_FIELD (field));
+
+	priv = GET_PRIV (field);
+
+	g_free (priv->id);
+
+	if (value) {
+		priv->id = g_strdup (value);
+	} else {
+		priv->id = NULL;
+	}
+
+	g_object_notify (G_OBJECT (field), "id");
+}
+
+void
+tracker_field_set_name (TrackerField *field,
+			const gchar  *value)
+{
+	TrackerFieldPriv *priv;
+
+	g_return_if_fail (TRACKER_IS_FIELD (field));
+
+	priv = GET_PRIV (field);
+
+	g_free (priv->name);
+
+	if (value) {
+		priv->name = g_strdup (value);
+	} else {
+		priv->name = NULL;
+	}
+
+	g_object_notify (G_OBJECT (field), "name");
+}
+
+void
+tracker_field_set_data_type (TrackerField     *field,
+			     TrackerFieldType  value)
+{
+	TrackerFieldPriv *priv;
+
+	g_return_if_fail (TRACKER_IS_FIELD (field));
+
+	priv = GET_PRIV (field);
+
+	priv->data_type = value;
+	g_object_notify (G_OBJECT (field), "data-type");
+}
+
+void
+tracker_field_set_field_name (TrackerField *field,
+			      const gchar    *value)
+{
+	TrackerFieldPriv *priv;
+
+	g_return_if_fail (TRACKER_IS_FIELD (field));
+
+	priv = GET_PRIV (field);
+
+	g_free (priv->field_name);
+
+	if (value) {
+		priv->field_name = g_strdup (value);
+	} else {
+		priv->field_name = NULL;
+	}
+
+	g_object_notify (G_OBJECT (field), "field-name");
+}
+
+void
+tracker_field_set_weight (TrackerField *field,
+			  gint		value)
+{
+	TrackerFieldPriv *priv;
+	g_return_if_fail (TRACKER_IS_FIELD (field));
+
+	if (!field_int_validate (field, "weight", value)) {
+		return;
+	}
+
+	priv = GET_PRIV (field);
+
+	priv->weight = value;
+	g_object_notify (G_OBJECT (field), "weight");
+}
+
+void
+tracker_field_set_embedded (TrackerField *field,
+			    gboolean	  value)
+{
+	TrackerFieldPriv *priv;
+
+	g_return_if_fail (TRACKER_IS_FIELD (field));
+
+	priv = GET_PRIV (field);
+
+	priv->embedded = value;
+	g_object_notify (G_OBJECT (field), "embedded");
+}
+
+void
+tracker_field_set_multiple_values (TrackerField *field,
+				   gboolean	 value)
+{
+	TrackerFieldPriv *priv;
+
+	g_return_if_fail (TRACKER_IS_FIELD (field));
+
+	priv = GET_PRIV (field);
+
+	priv->multiple_values = value;
+	g_object_notify (G_OBJECT (field), "multiple-values");
+}
+
+void
+tracker_field_set_delimited (TrackerField *field,
+			     gboolean	   value)
+{
+	TrackerFieldPriv *priv;
+
+	g_return_if_fail (TRACKER_IS_FIELD (field));
+
+	priv = GET_PRIV (field);
+
+	priv->delimited = value;
+	g_object_notify (G_OBJECT (field), "delimited");
+}
+
+void
+tracker_field_set_filtered (TrackerField *field,
+			    gboolean	  value)
+{
+	TrackerFieldPriv *priv;
+
+	g_return_if_fail (TRACKER_IS_FIELD (field));
+
+	priv = GET_PRIV (field);
+
+	priv->filtered = value;
+	g_object_notify (G_OBJECT (field), "filtered");
+}
+
+void
+tracker_field_set_store_metadata (TrackerField *field,
+				  gboolean	value)
+{
+	TrackerFieldPriv *priv;
+
+	g_return_if_fail (TRACKER_IS_FIELD (field));
+
+	priv = GET_PRIV (field);
+
+	priv->store_metadata = value;
+	g_object_notify (G_OBJECT (field), "store-metadata");
+}
+
+void
+tracker_field_set_child_ids (TrackerField *field,
+			     const GSList *value)
+{
+	TrackerFieldPriv *priv;
+
+	g_return_if_fail (TRACKER_IS_FIELD (field));
+
+	priv = GET_PRIV (field);
+
+	g_slist_foreach (priv->child_ids, (GFunc) g_free, NULL);
+	g_slist_free (priv->child_ids);
+
+	if (value) {
+		GSList	     *new_list;
+		const GSList *l;
+
+		new_list = NULL;
+
+		for (l = value; l; l = l->next) {
+			new_list = g_slist_prepend (new_list, g_strdup (l->data));
+		}
+
+		new_list = g_slist_reverse (new_list);
+		priv->child_ids = new_list;
+	} else {
+		priv->child_ids = NULL;
+	}
+
+	g_object_notify (G_OBJECT (field), "child-ids");
+}
+
+void
+tracker_field_append_child_id (TrackerField *field,
+			       const gchar  *value)
+{
+	TrackerFieldPriv *priv;
+
+	g_return_if_fail (TRACKER_IS_FIELD (field));
+
+	priv = GET_PRIV (field);
+
+	if (value) {
+		priv->child_ids = g_slist_append (priv->child_ids, g_strdup (value));
+	}
+
+	g_object_notify (G_OBJECT (field), "child-ids");
+}

Added: trunk/src/libtracker-common/tracker-field.h
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-field.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,112 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKERD_FIELD_H__
+#define __TRACKERD_FIELD_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_FIELD_TYPE (tracker_field_type_get_type ())
+
+typedef enum {
+	TRACKER_FIELD_TYPE_KEYWORD,
+	TRACKER_FIELD_TYPE_INDEX,
+	TRACKER_FIELD_TYPE_FULLTEXT,
+	TRACKER_FIELD_TYPE_STRING,
+	TRACKER_FIELD_TYPE_INTEGER,
+	TRACKER_FIELD_TYPE_DOUBLE,
+	TRACKER_FIELD_TYPE_DATE,
+	TRACKER_FIELD_TYPE_BLOB,
+	TRACKER_FIELD_TYPE_STRUCT,
+	TRACKER_FIELD_TYPE_LINK,
+} TrackerFieldType;
+
+GType	     tracker_field_type_get_type  (void) G_GNUC_CONST;
+const gchar *tracker_field_type_to_string (TrackerFieldType fieldtype);
+
+
+
+#define TRACKER_TYPE_FIELD	   (tracker_field_get_type ())
+#define TRACKER_TYPE_FIELD_TYPE    (tracker_field_type_get_type ())
+#define TRACKER_FIELD(o)	   (G_TYPE_CHECK_INSTANCE_CAST ((o), TRACKER_TYPE_FIELD, TrackerField))
+#define TRACKER_FIELD_CLASS(k)	   (G_TYPE_CHECK_CLASS_CAST ((k), TRACKER_TYPE_FIELD, TrackerFieldClass))
+#define TRACKER_IS_FIELD(o)	   (G_TYPE_CHECK_INSTANCE_TYPE ((o), TRACKER_TYPE_FIELD))
+#define TRACKER_IS_FIELD_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), TRACKER_TYPE_FIELD))
+#define TRACKER_FIELD_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), TRACKER_TYPE_FIELD, TrackerFieldClass))
+
+typedef struct _TrackerField	  TrackerField;
+typedef struct _TrackerFieldClass TrackerFieldClass;
+
+struct _TrackerField {
+	GObject      parent;
+};
+
+struct _TrackerFieldClass {
+	GObjectClass parent_class;
+};
+
+GType		 tracker_field_get_type		   (void) G_GNUC_CONST;
+
+TrackerField *	 tracker_field_new		   (void);
+
+const gchar *	 tracker_field_get_id		   (TrackerField     *field);
+const gchar *	 tracker_field_get_name		   (TrackerField     *field);
+TrackerFieldType tracker_field_get_data_type	   (TrackerField     *field);
+const gchar *	 tracker_field_get_field_name	   (TrackerField     *field);
+gint		 tracker_field_get_weight	   (TrackerField     *service);
+gboolean	 tracker_field_get_embedded	   (TrackerField     *field);
+gboolean	 tracker_field_get_multiple_values (TrackerField     *field);
+gboolean	 tracker_field_get_delimited	   (TrackerField     *field);
+gboolean	 tracker_field_get_filtered	   (TrackerField     *field);
+gboolean	 tracker_field_get_store_metadata  (TrackerField     *field);
+const GSList *	 tracker_field_get_child_ids	   (TrackerField     *field);
+
+void		 tracker_field_set_id		   (TrackerField     *field,
+						    const gchar      *value);
+void		 tracker_field_set_name		   (TrackerField     *field,
+						    const gchar      *value);
+void		 tracker_field_set_data_type	   (TrackerField     *field,
+						    TrackerFieldType  value);
+void		 tracker_field_set_field_name	   (TrackerField     *field,
+						    const gchar      *value);
+void		 tracker_field_set_weight	   (TrackerField     *field,
+						    gint	      value);
+void		 tracker_field_set_embedded	   (TrackerField     *field,
+						    gboolean	      value);
+void		 tracker_field_set_multiple_values (TrackerField     *field,
+						    gboolean	      value);
+void		 tracker_field_set_delimited	   (TrackerField     *field,
+						    gboolean	      value);
+void		 tracker_field_set_filtered	   (TrackerField     *field,
+						    gboolean	      value);
+void		 tracker_field_set_store_metadata  (TrackerField     *field,
+						    gboolean	      value);
+void		 tracker_field_set_child_ids	   (TrackerField     *field,
+						    const GSList     *value);
+void		 tracker_field_append_child_id	   (TrackerField     *field,
+						    const gchar      *id);
+
+G_END_DECLS
+
+#endif /* __TRACKERD_FIELD_H__ */
+

Added: trunk/src/libtracker-common/tracker-file-utils.c
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-file-utils.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,821 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <limits.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include "tracker-log.h"
+#include "tracker-os-dependant.h"
+#include "tracker-file-utils.h"
+
+#define TEXT_SNIFF_SIZE 4096
+
+gint
+tracker_file_open (const gchar *uri,
+		   gboolean	readahead)
+{
+	gint fd;
+
+#if defined(__linux__)
+	fd = open (uri, O_RDONLY | O_NOATIME);
+
+	if (fd == -1) {
+		fd = open (uri, O_RDONLY);
+	}
+#else
+	fd = open (uri, O_RDONLY);
+#endif
+
+	if (fd == -1) {
+		return -1;
+	}
+
+#ifdef HAVE_POSIX_FADVISE
+	if (readahead) {
+		posix_fadvise (fd, 0, 0, POSIX_FADV_SEQUENTIAL);
+	} else {
+		posix_fadvise (fd, 0, 0, POSIX_FADV_RANDOM);
+	}
+#endif
+
+	return fd;
+}
+
+void
+tracker_file_close (gint     fd,
+		    gboolean no_longer_needed)
+{
+
+#ifdef HAVE_POSIX_FADVISE
+	if (no_longer_needed) {
+		posix_fadvise (fd, 0, 0, POSIX_FADV_DONTNEED);
+	}
+#endif
+
+	close (fd);
+}
+
+gboolean
+tracker_file_unlink (const gchar *uri)
+{
+	gchar	 *str;
+	gboolean  result;
+
+	str = g_filename_from_utf8 (uri, -1, NULL, NULL, NULL);
+	result = g_unlink (str) == 0;
+	g_free (str);
+
+	return result;
+}
+
+guint32
+tracker_file_get_size (const gchar *uri)
+{
+	struct stat finfo;
+
+	if (g_lstat (uri, &finfo) == -1) {
+		return 0;
+	} else {
+		return (guint32) finfo.st_size;
+	}
+}
+
+static inline gboolean
+is_utf8 (const gchar *buffer,
+	 gint	      buffer_length)
+{
+	gchar *end;
+
+	/* Code in this function modified from gnome-vfs */
+	if (g_utf8_validate ((gchar*) buffer,
+			     buffer_length,
+			     (const gchar**) &end)) {
+		return TRUE;
+	} else {
+		/* Check whether the string was truncated in the middle of
+		 * a valid UTF8 char, or if we really have an invalid
+		 * UTF8 string.
+		 */
+		gunichar validated;
+		gint	 remaining_bytes;
+
+		remaining_bytes  = buffer_length;
+		remaining_bytes -= end - ((gchar *) buffer);
+
+		if (remaining_bytes > 4) {
+			return FALSE;
+		}
+
+		validated = g_utf8_get_char_validated (end, (gsize) remaining_bytes);
+
+		if (validated == (gunichar) - 2) {
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+gboolean
+tracker_file_is_valid (const gchar *uri)
+{
+	gchar	 *str;
+	gboolean  is_valid = TRUE;
+
+	str = g_filename_from_utf8 (uri, -1, NULL, NULL, NULL);
+
+	if (!str) {
+		g_warning ("URI:'%s' could not be converted to locale format",
+			   uri);
+		return FALSE;
+	}
+
+	/* g_file_test (file,G_FILE_TEST_EXISTS) uses the access ()
+	 * system call and so needs locale filenames.
+	 */
+	is_valid &= uri != NULL;
+	is_valid &= g_file_test (str,
+				 G_FILE_TEST_IS_REGULAR |
+				 G_FILE_TEST_IS_DIR |
+				 G_FILE_TEST_IS_SYMLINK);
+
+	g_free (str);
+
+	return is_valid;
+}
+
+gboolean
+tracker_file_is_directory (const gchar *uri)
+{
+	gchar	 *str;
+	gboolean  is_directory;
+
+	str = g_filename_from_utf8 (uri, -1, NULL, NULL, NULL);
+
+	if (!str) {
+		g_warning ("URI:'%s' could not be converted to locale format",
+			   str);
+		return FALSE;
+	}
+
+	is_directory = g_file_test (str, G_FILE_TEST_IS_DIR);
+	g_free (str);
+
+	return is_directory;
+}
+
+gboolean
+tracker_file_is_indexable (const gchar *uri)
+{
+	gchar	    *str;
+	struct stat  finfo;
+	gboolean     is_indexable;
+
+	g_return_val_if_fail (uri != NULL, FALSE);
+
+	str = g_filename_from_utf8 (uri, -1, NULL, NULL, NULL);
+
+	if (!str) {
+		g_warning ("URI:'%s' could not be converted to locale format",
+			   str);
+		return FALSE;
+	}
+
+	g_lstat (str, &finfo);
+	g_free (str);
+
+	is_indexable  = TRUE;
+	is_indexable &= !S_ISDIR (finfo.st_mode);
+	is_indexable &= S_ISREG (finfo.st_mode);
+
+	g_debug ("URI:'%s' %s indexable",
+		 uri,
+		 is_indexable ? "is" : "is not");
+
+	return is_indexable;
+}
+
+gint32
+tracker_file_get_mtime (const gchar *uri)
+{
+	struct stat  finfo;
+	gchar	    *str;
+
+	str = g_filename_from_utf8 (uri, -1, NULL, NULL, NULL);
+
+	if (str) {
+		if (g_lstat (str, &finfo) == -1) {
+			g_free (str);
+			return 0;
+		}
+	} else {
+		g_warning ("URI:'%s' could not be converted to locale format",
+			   uri);
+		return 0;
+	}
+
+	g_free (str);
+
+	return (gint32) finfo.st_mtime;
+}
+
+gchar *
+tracker_file_get_mime_type (const gchar *path)
+{
+	GFileInfo *info;
+	GFile	  *file;
+	GError	  *error = NULL;
+	gchar	  *content_type;
+
+	file = g_file_new_for_path (path);
+	info = g_file_query_info (file,
+				  G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+				  G_FILE_QUERY_INFO_NONE,
+				  NULL,
+				  &error);
+
+	if (G_UNLIKELY (error)) {
+		g_warning ("Could not guess mimetype, %s\n",
+			   error->message);
+		g_error_free (error);
+		content_type = NULL;
+	} else {
+		content_type = g_strdup (g_file_info_get_content_type (info));
+	}
+
+	g_object_unref (info);
+	g_object_unref (file);
+
+	return content_type ? content_type : g_strdup ("unknown");
+}
+
+static gchar *
+tracker_file_get_vfs_path (const gchar *uri)
+{
+	gchar *p;
+
+	if (!uri || !strchr (uri, G_DIR_SEPARATOR)) {
+		return NULL;
+	}
+
+	p = (gchar*) uri + strlen (uri) - 1;
+
+	/* Skip trailing slash */
+	if (p != uri && *p == G_DIR_SEPARATOR) {
+		p--;
+	}
+
+	/* Search backwards to the next slash. */
+	while (p != uri && *p != G_DIR_SEPARATOR) {
+		p--;
+	}
+
+	if (p[0] != '\0') {
+		gchar *new_uri_text;
+		gint   length;
+
+		length = p - uri;
+
+		if (length == 0) {
+			new_uri_text = g_strdup (G_DIR_SEPARATOR_S);
+		} else {
+			new_uri_text = g_malloc (length + 1);
+			memcpy (new_uri_text, uri, length);
+			new_uri_text[length] = '\0';
+		}
+
+		return new_uri_text;
+	} else {
+		return g_strdup (G_DIR_SEPARATOR_S);
+	}
+}
+
+static gchar *
+tracker_file_get_vfs_name (const gchar *uri)
+{
+	gchar *p, *res, *tmp, *result;
+
+	if (!uri || !strchr (uri, G_DIR_SEPARATOR)) {
+		return g_strdup (" ");
+	}
+
+	tmp = g_strdup (uri);
+	p = tmp + strlen (uri) - 1;
+
+	/* Skip trailing slash */
+	if (p != tmp && *p == G_DIR_SEPARATOR) {
+		*p = '\0';
+	}
+
+	/* Search backwards to the next slash.	*/
+	while (p != tmp && *p != G_DIR_SEPARATOR) {
+		p--;
+	}
+
+	res = p + 1;
+
+	if (res && res[0] != '\0') {
+		result = g_strdup (res);
+		g_free (tmp);
+
+		return result;
+	}
+
+	g_free (tmp);
+
+	return g_strdup (" ");
+}
+
+
+static gchar *
+normalize_uri (const gchar *uri) {
+
+	GFile  *f;
+	gchar *normalized;
+
+	f = g_file_new_for_path (uri);
+	normalized =  g_file_get_path (f);
+	g_object_unref (f);
+
+	return normalized;
+}
+
+void
+tracker_file_get_path_and_name (const gchar *uri,
+				gchar **path,
+				gchar **name)
+{
+
+	g_return_if_fail (uri);
+	g_return_if_fail (path);
+	g_return_if_fail (name);
+
+	if (uri[0] == G_DIR_SEPARATOR) {
+		gchar *checked_uri;
+
+		checked_uri = normalize_uri (uri);
+		*name = g_path_get_basename (checked_uri);
+		*path = g_path_get_dirname (checked_uri);
+
+		g_free (checked_uri);
+	} else {
+		*name = tracker_file_get_vfs_name (uri);
+		*path = tracker_file_get_vfs_path (uri);
+	}
+
+}
+
+
+
+void
+tracker_path_remove (const gchar *uri)
+{
+	GQueue *dirs;
+	GSList *dirs_to_remove = NULL;
+
+	g_return_if_fail (uri != NULL);
+
+	dirs = g_queue_new ();
+
+	g_queue_push_tail (dirs, g_strdup (uri));
+
+	while (!g_queue_is_empty (dirs)) {
+		GDir  *p;
+		gchar *dir;
+
+		dir = g_queue_pop_head (dirs);
+		dirs_to_remove = g_slist_prepend (dirs_to_remove, dir);
+
+		if ((p = g_dir_open (dir, 0, NULL))) {
+			const gchar *file;
+
+			while ((file = g_dir_read_name (p))) {
+				gchar *full_filename;
+
+				full_filename = g_build_filename (dir, file, NULL);
+
+				if (g_file_test (full_filename, G_FILE_TEST_IS_DIR)) {
+					g_queue_push_tail (dirs, full_filename);
+				} else {
+					g_unlink (full_filename);
+					g_free (full_filename);
+				}
+			}
+
+			g_dir_close (p);
+		}
+	}
+
+	g_queue_free (dirs);
+
+	/* Remove directories (now they are empty) */
+	g_slist_foreach (dirs_to_remove, (GFunc) g_remove, NULL);
+	g_slist_foreach (dirs_to_remove, (GFunc) g_free, NULL);
+	g_slist_free (dirs_to_remove);
+}
+
+gboolean
+tracker_path_is_in_path (const gchar *path,
+			 const gchar *in_path)
+{
+	gchar	 *new_path;
+	gchar	 *new_in_path;
+	gboolean  is_in_path = FALSE;
+
+	g_return_val_if_fail (path != NULL, FALSE);
+	g_return_val_if_fail (in_path != NULL, FALSE);
+
+	if (!g_str_has_suffix (path, G_DIR_SEPARATOR_S)) {
+		new_path = g_strconcat (path, G_DIR_SEPARATOR_S, NULL);
+	} else {
+		new_path = g_strdup (path);
+	}
+
+	if (!g_str_has_suffix (in_path, G_DIR_SEPARATOR_S)) {
+		new_in_path = g_strconcat (in_path, G_DIR_SEPARATOR_S, NULL);
+	} else {
+		new_in_path = g_strdup (in_path);
+	}
+
+	if (g_str_has_prefix (new_path, new_in_path)) {
+		is_in_path = TRUE;
+	}
+
+	g_free (new_in_path);
+	g_free (new_path);
+
+	return is_in_path;
+}
+
+void
+tracker_path_hash_table_filter_duplicates (GHashTable *roots)
+{
+	GHashTableIter iter1, iter2;
+	gpointer       key;
+
+	g_debug ("Filtering duplicates in path hash table:");
+
+	g_hash_table_iter_init (&iter1, roots);
+	while (g_hash_table_iter_next (&iter1, &key, NULL)) {
+		const gchar *path;
+
+		path = (const gchar*) key;
+
+		g_hash_table_iter_init (&iter2, roots);
+		while (g_hash_table_iter_next (&iter2, &key, NULL)) {
+			const gchar *in_path;
+
+			in_path = (const gchar*) key;
+
+			if (path == in_path) {
+				continue;
+			}
+
+			if (tracker_path_is_in_path (path, in_path)) {
+				g_debug ("Removing path:'%s', it is in path:'%s'",
+					 path, in_path);
+
+				g_hash_table_iter_remove (&iter1);
+				g_hash_table_iter_init (&iter1, roots);
+				break;
+			} else if (tracker_path_is_in_path (in_path, path)) {
+				g_debug ("Removing path:'%s', it is in path:'%s'",
+					 in_path, path);
+
+				g_hash_table_iter_remove (&iter2);
+				g_hash_table_iter_init (&iter1, roots);
+				break;
+			}
+		}
+	}
+
+#ifdef TESTING
+	g_debug ("Using the following roots to crawl:");
+
+	if (TRUE) {
+		GList *keys, *l;
+
+		keys = g_hash_table_get_keys (roots);
+
+		for (l = keys; l; l = l->next) {
+			g_debug ("  %s", (gchar*) l->data);
+		}
+
+		g_list_free (keys);
+	}
+#endif /* TESTING */
+}
+
+GSList *
+tracker_path_list_filter_duplicates (GSList *roots)
+{
+	GSList *checked_roots = NULL;
+	GSList *l1, *l2;
+
+	/* This function CREATES a new list and the data in the list
+	 * is new too! g_free() must be called on the list data and
+	 * g_slist_free() on the list too when done with.
+	 */
+
+	/* ONLY HERE do we add separators on each location we check.
+	 * The reason for this is that these locations are user
+	 * entered in the configuration and we need to make sure we
+	 * don't include the same location more than once.
+	 */
+
+	for (l1 = roots; l1; l1 = l1->next) {
+		gchar	 *path;
+		gboolean  should_add = TRUE;
+
+		if (!g_str_has_suffix (l1->data, G_DIR_SEPARATOR_S)) {
+			path = g_strconcat (l1->data, G_DIR_SEPARATOR_S, NULL);
+		} else {
+			path = g_strdup (l1->data);
+		}
+
+		l2 = checked_roots;
+
+		while (l2 && should_add) {
+			/* If the new path exists as a lower level
+			 * path or is the same as an existing checked
+			 * root we disgard it, it will be checked
+			 * anyway.
+			 */
+			if (g_str_has_prefix (path, l2->data)) {
+				should_add = FALSE;
+			}
+
+			/* If the new path exists as a higher level
+			 * path to one already in the checked roots,
+			 * we remove the checked roots version
+			 */
+			if (g_str_has_prefix (l2->data, path)) {
+				checked_roots = g_slist_remove_link (checked_roots, l2);
+				g_free (l2->data);
+
+				l2 = checked_roots;
+				continue;
+			}
+
+			l2 = l2->next;
+		}
+
+		if (should_add) {
+			gint len;
+
+			/* Don't use the trailing '/' and make sure we
+			 * don't remove '/' if that is the content of
+			 * the string or it is '//'.
+			 */
+			len = strlen (path);
+			if (len > 2) {
+				path[len - 1] = '\0';
+			}
+
+			checked_roots = g_slist_prepend (checked_roots, path);
+			continue;
+		}
+
+		g_free (path);
+	}
+
+	checked_roots = g_slist_reverse (checked_roots);
+
+#ifdef TESTING
+	g_debug ("Using the following roots to crawl:");
+
+	for (l1 = checked_roots; l1; l1 = l1->next) {
+		g_debug ("  %s", (gchar*) l1->data);
+	}
+#endif /* TESTING */
+
+	return checked_roots;
+}
+
+gchar *
+tracker_path_evaluate_name (const gchar *uri)
+{
+	gchar	     *final_path;
+	gchar	    **tokens;
+	gchar	    **token;
+	gchar	     *start;
+	gchar	     *end;
+	const gchar  *env;
+	gchar	     *expanded;
+
+	if (!uri || uri[0] == '\0') {
+		return NULL;
+	}
+
+	/* First check the simple case of using tilder */
+	if (uri[0] == '~') {
+		const char *home = g_get_home_dir ();
+
+		if (!home || home[0] == '\0') {
+			return NULL;
+		}
+
+		return g_build_path (G_DIR_SEPARATOR_S,
+				     home,
+				     uri + 1,
+				     NULL);
+	}
+
+	/* Second try to find any environment variables and expand
+	 * them, like $HOME or ${FOO}
+	 */
+	tokens = g_strsplit (uri, G_DIR_SEPARATOR_S, -1);
+
+	for (token = tokens; *token; token++) {
+		if (**token != '$') {
+			continue;
+		}
+
+		start = *token + 1;
+
+		if (*start == '{') {
+			start++;
+			end = start + (strlen (start)) - 1;
+			*end='\0';
+		}
+
+		env = g_getenv (start);
+		g_free (*token);
+
+		/* Don't do g_strdup (s?s1:s2) as that doesn't work
+		 * with certain gcc 2.96 versions.
+		 */
+		*token = env ? g_strdup (env) : g_strdup ("");
+	}
+
+	/* Third get the real path removing any "../" and other
+	 * symbolic links to other places, returning only the REAL
+	 * location.
+	 */
+	if (tokens) {
+		expanded = g_strjoinv (G_DIR_SEPARATOR_S, tokens);
+		g_strfreev (tokens);
+	} else {
+		expanded = g_strdup (uri);
+	}
+
+	/* Only resolve relative paths if there is a directory
+	 * separator in the path, otherwise it is just a name.
+	 */
+	if (strchr (expanded, G_DIR_SEPARATOR)) {
+		GFile *file;
+
+		file = g_file_new_for_commandline_arg (expanded);
+		final_path = g_file_get_path (file);
+		g_object_unref (file);
+		g_free (expanded);
+	} else {
+		final_path = expanded;
+	}
+
+	return final_path;
+}
+
+static gboolean
+path_has_write_access (const gchar *path,
+		       gboolean    *exists)
+{
+	GFile	  *file;
+	GFileInfo *info;
+	GError	  *error = NULL;
+	gboolean   writable;
+
+	g_return_val_if_fail (path != NULL, FALSE);
+	g_return_val_if_fail (path[0] != '\0', FALSE);
+
+	file = g_file_new_for_path (path);
+	info = g_file_query_info (file,
+				  G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE,
+				  0,
+				  NULL,
+				  &error);
+	g_object_unref (file);
+
+	if (G_UNLIKELY (error)) {
+		if (error->code == G_IO_ERROR_NOT_FOUND) {
+			if (exists) {
+				*exists = FALSE;
+			}
+		} else {
+			g_warning ("Could not check if we have write access for "
+				   "path '%s', %s",
+				   path,
+				   error->message);
+		}
+
+		g_error_free (error);
+
+		writable = FALSE;
+	} else {
+		if (exists) {
+			*exists = TRUE;
+		}
+
+		writable = g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE);
+	}
+
+	g_object_unref (info);
+
+	return writable;
+}
+
+static gboolean
+path_has_write_access_or_was_created (const gchar *path)
+{
+	gboolean writable;
+	gboolean exists;
+
+	writable = path_has_write_access (path, &exists);
+	if (exists) {
+		if (writable) {
+			g_message ("  Path is OK");
+			return TRUE;
+		}
+
+		g_message ("  Path can not be written to");
+	} else {
+		g_message ("  Path does not exist, attempting to create...");
+
+		if (g_mkdir_with_parents (path, 0700) == 0) {
+			g_message ("  Path was created");
+			return TRUE;
+		}
+
+		g_message ("  Path could not be created");
+	}
+
+	return FALSE;
+}
+
+gboolean
+tracker_env_check_xdg_dirs (void)
+{
+	const gchar *user_data_dir;
+	gchar	    *new_dir;
+	gboolean     success;
+
+	g_message ("Checking XDG_DATA_HOME is writable and exists");
+
+	/* NOTE: We don't use g_get_user_data_dir() here because as
+	 * soon as we do, it sets the result and doesn't re-fetch the
+	 * XDG_DATA_HOME environment variable which we set below.
+	 */
+	user_data_dir = g_getenv ("XDG_DATA_HOME");
+
+	/* Check the default XDG_DATA_HOME location */
+	g_message ("  XDG_DATA_HOME is '%s'", user_data_dir);
+
+	if (user_data_dir && path_has_write_access_or_was_created (user_data_dir)) {
+		return TRUE;
+	}
+
+	/* Change environment, this is actually what we have on Ubuntu. */
+	new_dir = g_build_path (G_DIR_SEPARATOR_S, g_get_home_dir (), ".local", "share", NULL);
+
+	/* Check the new XDG_DATA_HOME location */
+	success = g_setenv ("XDG_DATA_HOME", new_dir, TRUE);
+
+	if (success) {
+		g_message ("  XDG_DATA_HOME set to '%s'", new_dir);
+		success = path_has_write_access_or_was_created (new_dir);
+	} else {
+		g_message ("  XDG_DATA_HOME could not be set");
+	}
+
+	g_free (new_dir);
+
+	return success;
+}

Added: trunk/src/libtracker-common/tracker-file-utils.h
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-file-utils.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,49 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __LIBTRACKER_COMMON_FILE_UTILS_H__
+#define __LIBTRACKER_COMMON_FILE_UTILS_H__
+
+#include <glib.h>
+
+gint	 tracker_file_open			   (const gchar *uri,
+						    gboolean	 readahead);
+void	 tracker_file_close			   (gint	 fd,
+						    gboolean	 no_longer_needed);
+gboolean tracker_file_unlink			   (const gchar *uri);
+gboolean tracker_file_is_valid			   (const gchar *uri);
+gboolean tracker_file_is_directory		   (const gchar *uri);
+gboolean tracker_file_is_indexable		   (const gchar *uri);
+guint32  tracker_file_get_size			   (const gchar *uri);
+gint32	 tracker_file_get_mtime			   (const gchar *uri);
+gchar *  tracker_file_get_mime_type		   (const gchar *uri);
+void	 tracker_file_get_path_and_name		   (const gchar *uri,
+						    gchar **path,
+						    gchar **name);
+void	 tracker_path_remove			   (const gchar *uri);
+gboolean tracker_path_is_in_path		   (const gchar *path,
+						    const gchar *in_path);
+void	 tracker_path_hash_table_filter_duplicates (GHashTable	*roots);
+GSList * tracker_path_list_filter_duplicates	   (GSList	*roots);
+gchar *  tracker_path_evaluate_name		   (const gchar *uri);
+gboolean tracker_env_check_xdg_dirs		   (void);
+
+#endif /* __LIBTRACKER_COMMON_FILE_UTILS_H__ */

Added: trunk/src/libtracker-common/tracker-hal.c
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-hal.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,924 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#ifdef HAVE_HAL
+
+#include <string.h>
+
+#include <libhal.h>
+#include <libhal-storage.h>
+
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include "tracker-log.h"
+#include "tracker-hal.h"
+#include "tracker-utils.h"
+
+#define DEVICE_AC_ADAPTER  "ac_adapter"
+#define DEVICE_VOLUME	   "volume"
+
+#define PROP_AC_ADAPTER_ON "ac_adapter.present"
+#define PROP_IS_MOUNTED    "volume.is_mounted"
+
+#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TRACKER_TYPE_HAL, TrackerHalPriv))
+
+typedef struct {
+	LibHalContext *context;
+
+	GHashTable    *all_devices;
+	GHashTable    *mounted_devices;
+	GHashTable    *removable_devices;
+
+	gchar	      *battery_udi;
+	gboolean       battery_in_use;
+} TrackerHalPriv;
+
+typedef struct {
+	LibHalContext *context;
+	GSList	      *roots;
+} GetRoots;
+
+static void	tracker_hal_class_init		(TrackerHalClass *klass);
+static void	tracker_hal_init		(TrackerHal	 *hal);
+static void	tracker_hal_finalize		(GObject	 *object);
+static void	hal_get_property		(GObject	 *object,
+						 guint		  param_id,
+						 GValue		 *value,
+						 GParamSpec	 *pspec);
+static gboolean hal_setup_devices		(TrackerHal	 *hal);
+static gboolean hal_setup_batteries		(TrackerHal	 *hal);
+
+static gboolean hal_device_add			(TrackerHal	 *hal,
+						 LibHalVolume	 *volume);
+static void	hal_device_added_cb		(LibHalContext	 *context,
+						 const gchar	 *udi);
+static void	hal_device_removed_cb		(LibHalContext	 *context,
+						 const gchar	 *udi);
+static void	hal_device_property_modified_cb (LibHalContext	 *context,
+						 const char	 *udi,
+						 const char	 *key,
+						 dbus_bool_t	  is_removed,
+						 dbus_bool_t	  is_added);
+
+enum {
+	PROP_0,
+	PROP_BATTERY_IN_USE,
+	PROP_BATTERY_EXISTS,
+};
+
+enum {
+	SIG_MOUNT_POINT_ADDED,
+	SIG_MOUNT_POINT_REMOVED,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = {0};
+
+G_DEFINE_TYPE (TrackerHal, tracker_hal, G_TYPE_OBJECT);
+
+static void
+tracker_hal_class_init (TrackerHalClass *klass)
+{
+	GObjectClass *object_class;
+
+	object_class = G_OBJECT_CLASS (klass);
+
+	object_class->finalize	   = tracker_hal_finalize;
+	object_class->get_property = hal_get_property;
+
+	signals[SIG_MOUNT_POINT_ADDED] =
+		g_signal_new ("mount-point-added",
+			      G_TYPE_FROM_CLASS (klass),
+			      G_SIGNAL_RUN_LAST,
+			      0,
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__STRING,
+			      G_TYPE_NONE,
+			      1, G_TYPE_STRING);
+
+	signals[SIG_MOUNT_POINT_REMOVED] =
+		g_signal_new ("mount-point-removed",
+			      G_TYPE_FROM_CLASS (klass),
+			      G_SIGNAL_RUN_LAST,
+			      0,
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__STRING,
+			      G_TYPE_NONE,
+			      1, G_TYPE_STRING);
+
+	g_object_class_install_property (object_class,
+					 PROP_BATTERY_IN_USE,
+					 g_param_spec_boolean ("battery-in-use",
+							       "Battery exists",
+							       "There is a battery on this machine",
+							       FALSE,
+							       G_PARAM_READABLE));
+
+	g_object_class_install_property (object_class,
+					 PROP_BATTERY_EXISTS,
+					 g_param_spec_boolean ("battery-exists",
+							       "Battery exists",
+							       "There is a battery on this machine",
+							       FALSE,
+							       G_PARAM_READABLE));
+
+	g_type_class_add_private (object_class, sizeof (TrackerHalPriv));
+}
+
+static void
+tracker_hal_init (TrackerHal *hal)
+{
+	TrackerHalPriv *priv;
+	DBusError	error;
+	DBusConnection *connection;
+
+	g_message ("Initializing HAL...");
+
+	priv = GET_PRIV (hal);
+
+	priv->all_devices = g_hash_table_new_full (g_str_hash,
+						   g_str_equal,
+						   (GDestroyNotify) g_free,
+						   (GDestroyNotify) g_free);
+
+	priv->mounted_devices = g_hash_table_new_full (g_str_hash,
+						       g_str_equal,
+						       (GDestroyNotify) g_free,
+						       (GDestroyNotify) g_free);
+
+	priv->removable_devices = g_hash_table_new_full (g_str_hash,
+							 g_str_equal,
+							 (GDestroyNotify) g_free,
+							 (GDestroyNotify) g_free);
+
+	dbus_error_init (&error);
+
+	connection = dbus_bus_get (DBUS_BUS_SYSTEM, &error);
+	if (dbus_error_is_set (&error)) {
+		g_critical ("Could not get the system DBus connection, %s",
+			    error.message);
+		dbus_error_free (&error);
+		return;
+	}
+
+	dbus_connection_setup_with_g_main (connection, NULL);
+
+	priv->context = libhal_ctx_new ();
+
+	if (!priv->context) {
+		g_critical ("Could not create HAL context");
+		return;
+	}
+
+	libhal_ctx_set_user_data (priv->context, hal);
+	libhal_ctx_set_dbus_connection (priv->context, connection);
+
+	if (!libhal_ctx_init (priv->context, &error)) {
+		if (dbus_error_is_set (&error)) {
+			g_critical ("Could not initialize the HAL context, %s",
+				    error.message);
+			dbus_error_free (&error);
+		} else {
+			g_critical ("Could not initialize the HAL context, "
+				    "no error, is hald running?");
+		}
+
+		libhal_ctx_free (priv->context);
+		return;
+	}
+
+
+	/* Volume and property notification callbacks */
+	g_message ("HAL monitors set for devices added/removed/mounted/umounted...");
+	libhal_ctx_set_device_added (priv->context, hal_device_added_cb);
+	libhal_ctx_set_device_removed (priv->context, hal_device_removed_cb);
+	libhal_ctx_set_device_property_modified (priv->context, hal_device_property_modified_cb);
+
+	/* Get all devices which are mountable and set them up */
+	if (!hal_setup_devices (hal)) {
+		return;
+	}
+
+	/* Get all battery devices and set them up */
+	if (!hal_setup_batteries (hal)) {
+		return;
+	}
+}
+
+static void
+tracker_hal_finalize (GObject *object)
+{
+	TrackerHalPriv *priv;
+
+	priv = GET_PRIV (object);
+
+	if (priv->removable_devices) {
+		g_hash_table_unref (priv->removable_devices);
+	}
+
+	if (priv->mounted_devices) {
+		g_hash_table_unref (priv->mounted_devices);
+	}
+
+	if (priv->all_devices) {
+		g_hash_table_unref (priv->all_devices);
+	}
+
+	g_free (priv->battery_udi);
+
+	if (priv->context) {
+		libhal_ctx_set_user_data (priv->context, NULL);
+		libhal_ctx_free (priv->context);
+	}
+
+	(G_OBJECT_CLASS (tracker_hal_parent_class)->finalize) (object);
+}
+
+static void
+hal_get_property (GObject    *object,
+		  guint       param_id,
+		  GValue     *value,
+		  GParamSpec *pspec)
+{
+	TrackerHalPriv *priv;
+
+	priv = GET_PRIV (object);
+
+	switch (param_id) {
+	case PROP_BATTERY_IN_USE:
+		g_value_set_boolean (value, priv->battery_in_use);
+		break;
+	case PROP_BATTERY_EXISTS:
+		g_value_set_boolean (value, priv->battery_udi != NULL);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	};
+}
+
+static gboolean
+hal_setup_devices (TrackerHal *hal)
+{
+	TrackerHalPriv	*priv;
+	DBusError	 error;
+	gchar	       **devices, **p;
+	gint		 num;
+
+	priv = GET_PRIV (hal);
+
+	dbus_error_init (&error);
+
+	devices = libhal_find_device_by_capability (priv->context,
+						    DEVICE_VOLUME,
+						    &num,
+						    &error);
+
+	if (dbus_error_is_set (&error)) {
+		g_critical ("Could not get devices with 'volume' capability, %s",
+			    error.message);
+		dbus_error_free (&error);
+		return FALSE;
+	}
+
+	if (!devices || !devices[0]) {
+		g_message ("HAL devices not found with 'volume' capability");
+		return TRUE;
+	}
+
+	for (p = devices; *p; p++) {
+		LibHalVolume *volume;
+
+		volume = libhal_volume_from_udi (priv->context, *p);
+		if (!volume) {
+			continue;
+		}
+
+		g_message ("HAL device found:\n"
+			   " - udi	  : %s\n"
+			   " - mount point: %s\n"
+			   " - device file: %s\n"
+			   " - uuid	  : %s\n"
+			   " - mounted	  : %s\n"
+			   " - file system: %s\n"
+			   " - label	  : %s",
+			   libhal_volume_get_udi (volume),
+			   libhal_volume_get_mount_point (volume),
+			   libhal_volume_get_device_file (volume),
+			   libhal_volume_get_uuid (volume),
+			   libhal_volume_is_mounted (volume) ? "yes" : "no",
+			   libhal_volume_get_fstype (volume),
+			   libhal_volume_get_label (volume));
+
+		hal_device_add (hal, volume);
+		libhal_volume_free (volume);
+	}
+
+	libhal_free_string_array (devices);
+
+	return TRUE;
+}
+
+static gboolean
+hal_setup_batteries (TrackerHal *hal)
+{
+	TrackerHalPriv	*priv;
+	DBusError	 error;
+	gchar	       **devices, **p;
+	gint		 num;
+
+	priv = GET_PRIV (hal);
+
+	dbus_error_init (&error);
+
+	devices = libhal_find_device_by_capability (priv->context,
+						    DEVICE_AC_ADAPTER,
+						    &num,
+						    &error);
+
+	if (dbus_error_is_set (&error)) {
+		g_critical ("Could not get AC adapter capable devices, %s",
+			    error.message);
+		dbus_error_free (&error);
+		return FALSE;
+	}
+
+	g_message ("HAL found %d AC adapter capable devices", num);
+
+	if (!devices || !devices[0]) {
+		libhal_free_string_array (devices);
+
+		priv->battery_in_use = FALSE;
+		g_object_notify (G_OBJECT (hal), "battery-in-use");
+
+		priv->battery_udi = NULL;
+		g_object_notify (G_OBJECT (hal), "battery-exists");
+
+		return TRUE;
+	}
+
+	for (p = devices; *p; p++) {
+		if (!priv->battery_udi) {
+			/* For now just use the first one we find */
+			priv->battery_udi = g_strdup (*p);
+			g_object_notify (G_OBJECT (hal), "battery-exists");
+
+			g_message (" - Device '%s' (default)", *p);
+		} else {
+			g_message (" - Device '%s'", *p);
+		}
+	}
+
+	libhal_free_string_array (devices);
+
+	/* Make sure we watch changes to the battery use */
+	libhal_device_add_property_watch (priv->context,
+					  priv->battery_udi,
+					  &error);
+
+	if (dbus_error_is_set (&error)) {
+		g_critical ("Could not add device:'%s' to property watch, %s",
+			       priv->battery_udi, error.message);
+		dbus_error_free (&error);
+		return FALSE;
+	}
+
+	/* Get current state, are we using the battery now? */
+	priv->battery_in_use = !libhal_device_get_property_bool (priv->context,
+								 priv->battery_udi,
+								 PROP_AC_ADAPTER_ON,
+								 NULL);
+
+	g_message ("HAL reports system is currently powered by %s",
+		   priv->battery_in_use ? "battery" : "AC adapter");
+
+	g_object_notify (G_OBJECT (hal), "battery-in-use");
+
+	return TRUE;
+}
+
+static void
+hal_mount_point_add (TrackerHal  *hal,
+		     const gchar *udi,
+		     const gchar *mount_point,
+		     gboolean	  removable_device)
+{
+	TrackerHalPriv *priv;
+
+	priv = GET_PRIV (hal);
+
+	g_message ("HAL device with mount point:'%s', removable:%s now being tracked",
+		     mount_point,
+		     removable_device ? "yes" : "no");
+
+	g_hash_table_insert (priv->mounted_devices,
+			     g_strdup (udi),
+			     g_strdup (mount_point));
+
+	if (removable_device) {
+		g_hash_table_insert (priv->removable_devices,
+				     g_strdup (udi),
+				     g_strdup (mount_point));
+	}
+
+	g_signal_emit (hal, signals[SIG_MOUNT_POINT_ADDED], 0, mount_point, NULL);
+}
+
+static void
+hal_mount_point_remove (TrackerHal  *hal,
+			const gchar *udi)
+{
+	TrackerHalPriv *priv;
+	const gchar    *mount_point;
+
+	priv = GET_PRIV (hal);
+
+	mount_point = g_hash_table_lookup (priv->mounted_devices, udi);
+	if (!mount_point) {
+		return;
+	}
+
+	g_message ("HAL device with mount point:'%s', removable:%s NO LONGER being tracked",
+		     mount_point,
+		     g_hash_table_remove (priv->removable_devices, udi) ? "yes" : "no");
+
+	g_signal_emit (hal, signals[SIG_MOUNT_POINT_REMOVED], 0, mount_point, NULL);
+
+	g_hash_table_remove (priv->mounted_devices, udi);
+	g_hash_table_remove (priv->removable_devices, udi);
+}
+
+static const gchar *
+hal_drive_type_to_string (LibHalDriveType type)
+{
+	switch (type) {
+	case LIBHAL_DRIVE_TYPE_REMOVABLE_DISK:
+		return "LIBHAL_DRIVE_TYPE_REMOVABLE_DISK";
+	case LIBHAL_DRIVE_TYPE_DISK:
+		return "LIBHAL_DRIVE_TYPE_DISK";
+	case LIBHAL_DRIVE_TYPE_CDROM:
+		return "LIBHAL_DRIVE_TYPE_CDROM";
+	case LIBHAL_DRIVE_TYPE_FLOPPY:
+		return "LIBHAL_DRIVE_TYPE_FLOPPY";
+	case LIBHAL_DRIVE_TYPE_TAPE:
+		return "LIBHAL_DRIVE_TYPE_TAPE";
+	case LIBHAL_DRIVE_TYPE_COMPACT_FLASH:
+		return "LIBHAL_DRIVE_TYPE_COMPACT_FLASH";
+	case LIBHAL_DRIVE_TYPE_MEMORY_STICK:
+		return "LIBHAL_DRIVE_TYPE_MEMORY_STICK";
+	case LIBHAL_DRIVE_TYPE_SMART_MEDIA:
+		return "LIBHAL_DRIVE_TYPE_SMART_MEDIA";
+	case LIBHAL_DRIVE_TYPE_SD_MMC:
+		return "LIBHAL_DRIVE_TYPE_SD_MMC";
+	case LIBHAL_DRIVE_TYPE_CAMERA:
+		return "LIBHAL_DRIVE_TYPE_CAMERA";
+	case LIBHAL_DRIVE_TYPE_PORTABLE_AUDIO_PLAYER:
+		return "LIBHAL_DRIVE_TYPE_PORTABLE_AUDIO_PLAYER";
+	case LIBHAL_DRIVE_TYPE_ZIP:
+		return "LIBHAL_DRIVE_TYPE_ZIP";
+	case LIBHAL_DRIVE_TYPE_JAZ:
+		return "LIBHAL_DRIVE_TYPE_JAZ";
+	case LIBHAL_DRIVE_TYPE_FLASHKEY:
+		return "LIBHAL_DRIVE_TYPE_FLASHKEY";
+	case LIBHAL_DRIVE_TYPE_MO:
+		return "LIBHAL_DRIVE_TYPE_MO";
+	}
+
+	return "";
+}
+
+static gboolean
+hal_device_is_removable (TrackerHal  *hal,
+			 const gchar *device_file)
+{
+	TrackerHalPriv	*priv;
+	LibHalDrive	*drive;
+	gboolean	 removable;
+
+	if (!device_file) {
+		return FALSE;
+	}
+
+	priv = GET_PRIV (hal);
+
+	drive = libhal_drive_from_device_file (priv->context, device_file);
+	if (!drive) {
+		return FALSE;
+	}
+
+	removable = libhal_drive_uses_removable_media (drive);
+	libhal_drive_free (drive);
+
+	return removable;
+}
+
+static gboolean
+hal_device_should_be_tracked (TrackerHal  *hal,
+			      const gchar *device_file)
+{
+	TrackerHalPriv	*priv;
+	LibHalDrive	*drive;
+	LibHalDriveType  drive_type;
+	gboolean	 eligible;
+
+	if (!device_file) {
+		return FALSE;
+	}
+
+	priv = GET_PRIV (hal);
+
+	drive = libhal_drive_from_device_file (priv->context, device_file);
+	if (!drive) {
+		return FALSE;
+	}
+
+	/* From the list, the first one below seems to be the ONLY one
+	 * to ignore:
+	 *
+	 * LIBHAL_DRIVE_TYPE_REMOVABLE_DISK	   = 0x00,
+	 * LIBHAL_DRIVE_TYPE_DISK		   = 0x01,
+	 * LIBHAL_DRIVE_TYPE_CDROM		   = 0x02,
+	 * LIBHAL_DRIVE_TYPE_FLOPPY		   = 0x03,
+	 * LIBHAL_DRIVE_TYPE_TAPE		   = 0x04,
+	 * LIBHAL_DRIVE_TYPE_COMPACT_FLASH	   = 0x05,
+	 * LIBHAL_DRIVE_TYPE_MEMORY_STICK	   = 0x06,
+	 * LIBHAL_DRIVE_TYPE_SMART_MEDIA	   = 0x07,
+	 * LIBHAL_DRIVE_TYPE_SD_MMC		   = 0x08,
+	 * LIBHAL_DRIVE_TYPE_CAMERA		   = 0x09,
+	 * LIBHAL_DRIVE_TYPE_PORTABLE_AUDIO_PLAYER = 0x0a,
+	 * LIBHAL_DRIVE_TYPE_ZIP		   = 0x0b,
+	 * LIBHAL_DRIVE_TYPE_JAZ		   = 0x0c,
+	 * LIBHAL_DRIVE_TYPE_FLASHKEY		   = 0x0d,
+	 * LIBHAL_DRIVE_TYPE_MO			   = 0x0e
+	 *
+	 */
+
+	drive_type = libhal_drive_get_type (drive);
+
+	/* So here we don't track CDROM devices or the hard disks in
+	 * the machine, we simply track devices which are added or
+	 * removed in real time which we are interested in and which
+	 * are viable for tracking. CDROMs are too slow.
+	 */
+	eligible = TRUE;
+	eligible &= drive_type != LIBHAL_DRIVE_TYPE_DISK;
+	eligible &= drive_type != LIBHAL_DRIVE_TYPE_CDROM;
+
+	libhal_drive_free (drive);
+
+	if (!eligible) {
+		g_message ("HAL device is not eligible, type is '%s'",
+			   hal_drive_type_to_string (drive_type));
+	} else {
+		g_message ("HAL device is eligible, type is '%s'",
+			   hal_drive_type_to_string (drive_type));
+	}
+
+	return eligible;
+}
+
+static gboolean
+hal_device_add (TrackerHal   *hal,
+		LibHalVolume *volume)
+{
+	TrackerHalPriv *priv;
+	DBusError	error;
+	const gchar    *udi;
+	const gchar    *mount_point;
+	const gchar    *device_file;
+
+	priv = GET_PRIV (hal);
+
+	dbus_error_init (&error);
+
+	udi = libhal_volume_get_udi (volume);
+	mount_point = libhal_volume_get_mount_point (volume);
+	device_file = libhal_volume_get_device_file (volume);
+
+	if (g_hash_table_lookup (priv->all_devices, udi)) {
+		return TRUE;
+	}
+
+	/* If there is no mount point, then there is nothing to track */
+	if (!hal_device_should_be_tracked (hal, device_file)) {
+		g_message ("HAL device should not be tracked (not eligible)");
+		return TRUE;
+	}
+
+	/* Make sure we watch changes to the mount/umount state */
+	libhal_device_add_property_watch (priv->context, udi, &error);
+
+	if (dbus_error_is_set (&error)) {
+		g_critical ("Could not add device property watch for udi:'%s', %s",
+			       udi, error.message);
+		dbus_error_free (&error);
+		return FALSE;
+	}
+
+	g_hash_table_insert (priv->all_devices,
+			     g_strdup (udi),
+			     g_strdup (device_file));
+
+	if (mount_point) {
+		hal_mount_point_add (hal,
+				     udi,
+				     mount_point,
+				     hal_device_is_removable (hal, device_file));
+	}
+
+	return TRUE;
+}
+
+static void
+hal_device_added_cb (LibHalContext *context,
+		     const gchar   *udi)
+{
+	TrackerHal   *hal;
+	DBusError     error;
+	LibHalVolume *volume;
+
+	dbus_error_init (&error);
+
+	volume = libhal_volume_from_udi (context, udi);
+	if (!volume) {
+		/* Not a device with a volume */
+		return;
+	}
+
+	g_message ("HAL device added:\n"
+		     " - udi	    : %s\n"
+		     " - mount point: %s\n"
+		     " - device file: %s\n"
+		     " - uuid	    : %s\n"
+		     " - mounted    : %s\n"
+		     " - file system: %s\n"
+		     " - label	    : %s",
+		     udi,
+		     libhal_volume_get_mount_point (volume),
+		     libhal_volume_get_device_file (volume),
+		     libhal_volume_get_uuid (volume),
+		     libhal_volume_is_mounted (volume) ? "yes" : "no",
+		     libhal_volume_get_fstype (volume),
+		     libhal_volume_get_label (volume));
+
+	hal = (TrackerHal*) libhal_ctx_get_user_data (context);
+	hal_device_add (hal, volume);
+	libhal_volume_free (volume);
+}
+
+static void
+hal_device_removed_cb (LibHalContext *context,
+		       const gchar   *udi)
+{
+	TrackerHal     *hal;
+	TrackerHalPriv *priv;
+	const gchar    *device_file;
+	const gchar    *mount_point;
+
+	hal = (TrackerHal*) libhal_ctx_get_user_data (context);
+	priv = GET_PRIV (hal);
+
+	device_file = g_hash_table_lookup (priv->all_devices, udi);
+
+	if (!device_file) {
+		/* Don't report about unknown devices */
+		return;
+	}
+
+	mount_point = g_hash_table_lookup (priv->mounted_devices, udi);
+
+	g_message ("HAL device removed:\n"
+		     " - udi	    : %s\n"
+		     " - mount point: %s\n"
+		     " - device_file: %s",
+		     udi,
+		     mount_point,
+		     device_file);
+
+	g_hash_table_remove (priv->all_devices, udi);
+
+	hal_mount_point_remove (hal, udi);
+}
+
+static void
+hal_device_property_modified_cb (LibHalContext *context,
+				 const char    *udi,
+				 const char    *key,
+				 dbus_bool_t	is_removed,
+				 dbus_bool_t	is_added)
+{
+	TrackerHal     *hal;
+	TrackerHalPriv *priv;
+	DBusError	error;
+	gboolean	device_is_battery;
+	gboolean	current_state;
+
+	hal = (TrackerHal*) libhal_ctx_get_user_data (context);
+	priv = GET_PRIV (hal);
+
+	current_state = priv->battery_in_use;
+	device_is_battery = priv->battery_udi && strcmp (priv->battery_udi, udi) == 0;
+
+	if (!device_is_battery &&
+	    !g_hash_table_lookup (priv->all_devices, udi)) {
+		g_message ("HAL device property change for another device, ignoring");
+		return;
+	}
+
+	dbus_error_init (&error);
+
+	/* We either get notifications about the battery state OR a
+	 * device being mounted/umounted.
+	 */
+	if (device_is_battery) {
+		priv->battery_in_use = !libhal_device_get_property_bool (priv->context,
+									 priv->battery_udi,
+									 PROP_AC_ADAPTER_ON,
+									 &error);
+		g_message ("HAL reports system is now powered by %s",
+			   priv->battery_in_use ? "battery" : "AC adapter");
+
+		g_object_notify (G_OBJECT (hal), "battery-in-use");
+
+		if (dbus_error_is_set (&error)) {
+			g_critical ("Could not get device property:'%s' for udi:'%s', %s",
+				    udi, PROP_AC_ADAPTER_ON, error.message);
+			dbus_error_free (&error);
+			return;
+		}
+	} else {
+		gboolean is_mounted;
+
+		g_message ("HAL device property change for udi:'%s' and key:'%s'",
+			   udi, key);
+
+		if (strcmp (key, PROP_IS_MOUNTED) != 0) {
+			return;
+		}
+
+		is_mounted = libhal_device_get_property_bool (context,
+							      udi,
+							      key,
+							      &error);
+
+		if (dbus_error_is_set (&error)) {
+			g_message ("Could not get device property:'%s' for udi:'%s', %s",
+				   udi, key, error.message);
+			dbus_error_free (&error);
+
+			g_message ("HAL device with udi:'%s' is now unmounted (due to error)",
+				   udi);
+			hal_mount_point_remove (hal, udi);
+			return;
+		}
+
+		if (is_mounted) {
+			LibHalVolume *volume;
+			const gchar  *mount_point;
+			const gchar  *device_file;
+
+			volume = libhal_volume_from_udi (context, udi);
+			mount_point = libhal_volume_get_mount_point (volume);
+			device_file = libhal_volume_get_device_file (volume);
+
+			g_message ("HAL device with udi:'%s' is now mounted",
+				   udi);
+
+			hal_mount_point_add (hal,
+					     udi,
+					     mount_point,
+					     hal_device_is_removable (hal, device_file));
+
+			libhal_volume_free (volume);
+		} else {
+			g_message ("HAL device with udi:'%s' is now unmounted",
+				   udi);
+
+			hal_mount_point_remove (hal, udi);
+		}
+	}
+}
+
+TrackerHal *
+tracker_hal_new (void)
+{
+	return g_object_new (TRACKER_TYPE_HAL, NULL);
+}
+
+gboolean
+tracker_hal_get_battery_in_use (TrackerHal *hal)
+{
+	TrackerHalPriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_HAL (hal), TRUE);
+
+	priv = GET_PRIV (hal);
+
+	return priv->battery_in_use;
+}
+
+gboolean
+tracker_hal_get_battery_exists (TrackerHal *hal)
+{
+	TrackerHalPriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_HAL (hal), TRUE);
+
+	priv = GET_PRIV (hal);
+
+	return priv->battery_udi != NULL;
+}
+
+static void
+hal_get_mount_point_by_udi_foreach (gpointer key,
+				    gpointer value,
+				    gpointer user_data)
+{
+	LibHalVolume  *volume;
+	GetRoots      *gr;
+	const gchar   *udi;
+	const gchar   *mount_point;
+	gboolean       is_mounted;
+
+	gr = (GetRoots*) user_data;
+	udi = (const gchar*) key;
+
+	volume = libhal_volume_from_udi (gr->context, udi);
+	if (!volume) {
+		g_message ("HAL device with udi:'%s' has no volume, "
+			   "should we delete?",
+			   udi);
+		return;
+	}
+
+	mount_point = libhal_volume_get_mount_point (volume);
+	is_mounted = libhal_volume_is_mounted (volume);
+
+	if (is_mounted && mount_point) {
+		gr->roots = g_slist_prepend (gr->roots, g_strdup (mount_point));
+	}
+
+	libhal_volume_free (volume);
+}
+
+GSList *
+tracker_hal_get_mounted_directory_roots (TrackerHal *hal)
+{
+	TrackerHalPriv *priv;
+	GetRoots	gr;
+
+	g_return_val_if_fail (TRACKER_IS_HAL (hal), NULL);
+
+	priv = GET_PRIV (hal);
+
+	gr.context = priv->context;
+	gr.roots = NULL;
+
+	g_hash_table_foreach (priv->mounted_devices,
+			      hal_get_mount_point_by_udi_foreach,
+			      &gr);
+
+	return gr.roots;
+}
+
+GSList *
+tracker_hal_get_removable_device_roots (TrackerHal *hal)
+{
+	TrackerHalPriv *priv;
+	GetRoots	gr;
+
+	g_return_val_if_fail (TRACKER_IS_HAL (hal), NULL);
+
+	priv = GET_PRIV (hal);
+
+	gr.context = priv->context;
+	gr.roots = NULL;
+
+	g_hash_table_foreach (priv->removable_devices,
+			      hal_get_mount_point_by_udi_foreach,
+			      &gr);
+
+	return gr.roots;
+}
+
+#endif /* HAVE_HAL */

Added: trunk/src/libtracker-common/tracker-hal.h
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-hal.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,65 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __LIBTRACKER_HAL_H__
+#define __LIBTRACKER_HAL_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#ifdef HAVE_HAL
+
+#define TRACKER_TYPE_HAL	 (tracker_hal_get_type ())
+#define TRACKER_HAL(o)		 (G_TYPE_CHECK_INSTANCE_CAST ((o), TRACKER_TYPE_HAL, TrackerHal))
+#define TRACKER_HAL_CLASS(k)	 (G_TYPE_CHECK_CLASS_CAST ((k), TRACKER_TYPE_HAL, TrackerHalClass))
+#define TRACKER_IS_HAL(o)	 (G_TYPE_CHECK_INSTANCE_TYPE ((o), TRACKER_TYPE_HAL))
+#define TRACKER_IS_HAL_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), TRACKER_TYPE_HAL))
+#define TRACKER_HAL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), TRACKER_TYPE_HAL, TrackerHalClass))
+
+typedef struct _TrackerHal	TrackerHal;
+typedef struct _TrackerHalClass TrackerHalClass;
+
+struct _TrackerHal {
+	GObject      parent;
+};
+
+struct _TrackerHalClass {
+	GObjectClass parent_class;
+};
+
+GType	    tracker_hal_get_type		    (void) G_GNUC_CONST;
+
+TrackerHal *tracker_hal_new			    (void);
+gboolean    tracker_hal_get_battery_in_use	    (TrackerHal *hal);
+gboolean    tracker_hal_get_battery_exists	    (TrackerHal *hal);
+GSList *    tracker_hal_get_mounted_directory_roots (TrackerHal *hal);
+GSList *    tracker_hal_get_removable_device_roots  (TrackerHal *hal);
+
+#else  /* HAVE_HAL */
+
+typedef void TrackerHal;
+
+#endif /* HAVE_HAL */
+
+G_END_DECLS
+
+#endif /* __LIBTRACKER_HAL_H__ */

Added: trunk/src/libtracker-common/tracker-ioprio.c
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-ioprio.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,149 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2005, Novell, Inc.
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2006, Anders Aagaard
+ *
+ * Based mostly on code by Robert Love <rml novell com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#ifdef HAVE_IOPRIO
+
+#include <stdio.h>
+#include <errno.h>
+
+#ifdef HAVE_LINUX_UNISTD_H
+#include <linux/unistd.h>
+#endif
+
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include <glib/gstdio.h>
+
+#include <libtracker-common/tracker-log.h>
+
+#include "tracker-ioprio.h"
+
+#ifndef __NR_ioprio_set
+
+#if defined(__i386__)
+#define __NR_ioprio_set		289
+#define __NR_ioprio_get		290
+#elif defined(__powerpc__) || defined(__powerpc64__)
+#define __NR_ioprio_set		273
+#define __NR_ioprio_get		274
+#elif defined(__x86_64__)
+#define __NR_ioprio_set		251
+#define __NR_ioprio_get		252
+#elif defined(__ia64__)
+#define __NR_ioprio_set		1274
+#define __NR_ioprio_get		1275
+#elif defined(__alpha__)
+#define __NR_ioprio_set		442
+#define __NR_ioprio_get		443
+#elif defined(__s390x__) || defined(__s390__)
+#define __NR_ioprio_set		282
+#define __NR_ioprio_get		283
+#elif defined(__SH4__)
+#define __NR_ioprio_set		288
+#define __NR_ioprio_get		289
+#elif defined(__SH5__)
+#define __NR_ioprio_set		316
+#define __NR_ioprio_get		317
+#elif defined(__sparc__) || defined(__sparc64__)
+#define __NR_ioprio_set		196
+#define __NR_ioprio_get		218
+#elif defined(__arm__)
+#define __NR_ioprio_set		314
+#define __NR_ioprio_get		315
+#else
+#error "Unsupported architecture!"
+#endif
+
+#endif
+
+enum {
+	IOPRIO_CLASS_NONE,
+	IOPRIO_CLASS_RT,
+	IOPRIO_CLASS_BE,
+	IOPRIO_CLASS_IDLE,
+};
+
+enum {
+	IOPRIO_WHO_PROCESS = 1,
+	IOPRIO_WHO_PGRP,
+	IOPRIO_WHO_USER,
+};
+
+#define IOPRIO_CLASS_SHIFT 13
+
+static inline int
+ioprio_set (int which, int who, int ioprio_val)
+{
+	return syscall (__NR_ioprio_set, which, who, ioprio_val);
+}
+
+static int
+set_io_priority_idle (void)
+{
+	int ioprio, ioclass;
+
+	ioprio = 7; /* priority is ignored with idle class */
+	ioclass = IOPRIO_CLASS_IDLE << IOPRIO_CLASS_SHIFT;
+
+	return ioprio_set (IOPRIO_WHO_PROCESS, 0, ioprio | ioclass);
+}
+
+static int
+set_io_priority_best_effort (int ioprio_val)
+{
+	int ioclass;
+
+	ioclass = IOPRIO_CLASS_BE << IOPRIO_CLASS_SHIFT;
+
+	return ioprio_set (IOPRIO_WHO_PROCESS, 0, ioprio_val | ioclass);
+}
+
+void
+tracker_ioprio_init (void)
+{
+	g_message ("Setting IO priority...");
+
+	if (set_io_priority_idle () == -1) {
+		g_message ("Could not set idle IO priority, attempting best effort of 7");
+
+		if (set_io_priority_best_effort (7) == -1) {
+			g_message ("Could not set best effort IO priority either, giving up");
+		}
+	}
+}
+
+#else  /* HAVE_IOPRIO */
+
+void
+tracker_ioprio_init (void)
+{
+}
+
+#endif /* HAVE_IOPRIO */

Added: trunk/src/libtracker-common/tracker-ioprio.h
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-ioprio.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,30 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Anders Aagaard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __LIBTRACKER_IOPRIO_H__
+#define __LIBTRACKER_IOPRIO_H__
+
+G_BEGIN_DECLS
+
+void tracker_ioprio_init (void);
+
+G_END_DECLS
+
+#endif /* __LIBTRACKER_IOPRIO_H__ */

Added: trunk/src/libtracker-common/tracker-language.c
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-language.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,489 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <glib.h>
+
+#include <libstemmer/libstemmer.h>
+
+#include "tracker-log.h"
+#include "tracker-language.h"
+
+#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TRACKER_TYPE_LANGUAGE, TrackerLanguagePriv))
+
+typedef struct _TrackerLanguagePriv TrackerLanguagePriv;
+typedef struct _Languages	    Languages;
+
+struct _TrackerLanguagePriv {
+	TrackerConfig *config;
+
+	GHashTable    *stop_words;
+
+	GMutex	      *stemmer_mutex;
+	gpointer       stemmer;
+};
+
+struct _Languages {
+	gchar *code;
+	gchar *name;
+};
+
+static Languages all_langs[] = {
+	{ "da", "danish" },
+	{ "nl", "dutch" },
+	{ "en", "english" },
+	{ "fi", "finnish" },
+	{ "fr", "french" },
+	{ "de", "german" },
+	{ "hu", "hungarian" },
+	{ "it", "italian" },
+	{ "nb", "norwegian" },
+	{ "pt", "portuguese" },
+	{ "ru", "russian" },
+	{ "es", "spanish" },
+	{ "sv", "swedish" },
+	{ NULL, NULL },
+};
+
+/* GObject properties */
+enum {
+	PROP_0,
+
+	PROP_CONFIG,
+	PROP_STOP_WORDS
+};
+
+static void	    language_finalize	       (GObject       *object);
+static void	    language_get_property      (GObject       *object,
+						guint	       param_id,
+						GValue	      *value,
+						GParamSpec    *pspec);
+static void	    language_set_property      (GObject       *object,
+						guint	       param_id,
+						const GValue  *value,
+						GParamSpec    *pspec);
+static const gchar *language_get_name_for_code (const gchar   *language_code);
+static void	    language_notify_cb	       (TrackerConfig *config,
+						GParamSpec    *param,
+						gpointer       user_data);
+
+G_DEFINE_TYPE (TrackerLanguage, tracker_language, G_TYPE_OBJECT);
+
+static void
+tracker_language_class_init (TrackerLanguageClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	object_class->finalize	   = language_finalize;
+	object_class->get_property = language_get_property;
+	object_class->set_property = language_set_property;
+
+	g_object_class_install_property (object_class,
+					 PROP_CONFIG,
+					 g_param_spec_object ("config",
+							      "Config",
+							      "Config",
+							      tracker_config_get_type (),
+							      G_PARAM_READWRITE));
+
+	g_object_class_install_property (object_class,
+					 PROP_STOP_WORDS,
+					 g_param_spec_boxed ("stop-words",
+							     "Stop words",
+							     "Stop words",
+							     g_hash_table_get_type (),
+							     G_PARAM_READABLE));
+
+	g_type_class_add_private (object_class, sizeof (TrackerLanguagePriv));
+}
+
+static void
+tracker_language_init (TrackerLanguage *language)
+{
+	TrackerLanguagePriv *priv;
+	const gchar	    *stem_language;
+
+	priv = GET_PRIV (language);
+
+	priv->stop_words = g_hash_table_new_full (g_str_hash,
+						  g_str_equal,
+						  g_free,
+						  NULL);
+
+	priv->stemmer_mutex = g_mutex_new ();
+
+	stem_language = language_get_name_for_code (NULL);
+	priv->stemmer = sb_stemmer_new (stem_language, NULL);
+}
+
+static void
+language_finalize (GObject *object)
+{
+	TrackerLanguagePriv *priv;
+
+	priv = GET_PRIV (object);
+
+	if (priv->config) {
+		g_signal_handlers_disconnect_by_func (priv->config,
+						      language_notify_cb,
+						      TRACKER_LANGUAGE (object));
+		g_object_unref (priv->config);
+	}
+
+	if (priv->stemmer) {
+		g_mutex_lock (priv->stemmer_mutex);
+		sb_stemmer_delete (priv->stemmer);
+		g_mutex_unlock (priv->stemmer_mutex);
+	}
+
+	g_mutex_free (priv->stemmer_mutex);
+
+	if (priv->stop_words) {
+		g_hash_table_unref (priv->stop_words);
+	}
+
+	(G_OBJECT_CLASS (tracker_language_parent_class)->finalize) (object);
+}
+
+static void
+language_get_property (GObject	  *object,
+		       guint	   param_id,
+		       GValue	  *value,
+		       GParamSpec *pspec)
+{
+	TrackerLanguagePriv *priv;
+
+	priv = GET_PRIV (object);
+
+	switch (param_id) {
+	case PROP_CONFIG:
+		g_value_set_object (value, priv->config);
+		break;
+	case PROP_STOP_WORDS:
+		g_value_set_boxed (value, priv->stop_words);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	};
+}
+
+static void
+language_set_property (GObject	    *object,
+		       guint	     param_id,
+		       const GValue *value,
+		       GParamSpec   *pspec)
+{
+	TrackerLanguagePriv *priv;
+
+	priv = GET_PRIV (object);
+
+	switch (param_id) {
+	case PROP_CONFIG:
+		tracker_language_set_config (TRACKER_LANGUAGE (object),
+					     g_value_get_object (value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	};
+}
+
+static gchar *
+language_get_stopword_filename (const gchar *language_code)
+{
+	gchar *str;
+	gchar *filename;
+
+	str = g_strconcat ("stopwords.", language_code, NULL);
+	filename = g_build_filename (SHAREDIR,
+				     "tracker",
+				     "languages",
+				     str,
+				     NULL);
+	g_free (str);
+
+	return filename;
+}
+
+static const gchar *
+language_get_name_for_code (const gchar *language_code)
+{
+	gint i;
+
+	if (!language_code || language_code[0] == '\0') {
+		return "english";
+	}
+
+	for (i = 0; all_langs[i].code; i++) {
+		if (g_str_has_prefix (language_code, all_langs[i].code)) {
+			return all_langs[i].name;
+		}
+	}
+
+	return "";
+}
+
+static void
+language_add_stopwords (TrackerLanguage *language,
+			const gchar	*filename)
+{
+	TrackerLanguagePriv  *priv;
+	GMappedFile	     *mapped_file;
+	GError		     *error = NULL;
+	gchar		     *content;
+	gchar		    **words, **p;
+
+	priv = GET_PRIV (language);
+
+	mapped_file = g_mapped_file_new (filename, FALSE, &error);
+	if (error) {
+		g_message ("Tracker couldn't read stopword file:'%s', %s",
+			     filename, error->message);
+		g_clear_error (&error);
+		return;
+	}
+
+	content = g_mapped_file_get_contents (mapped_file);
+	words = g_strsplit_set (content, "\n" , -1);
+
+	g_mapped_file_free (mapped_file);
+
+	/* FIXME: Shouldn't clear the hash table first? */
+	for (p = words; *p; p++) {
+		g_hash_table_insert (priv->stop_words,
+				     g_strdup (g_strstrip (*p)),
+				     GINT_TO_POINTER (1));
+	}
+
+	g_strfreev (words);
+}
+
+static void
+language_set_stopword_list (TrackerLanguage *language,
+			    const gchar     *language_code)
+{
+	TrackerLanguagePriv *priv;
+	gchar		    *stopword_filename;
+	const gchar	    *stem_language;
+
+	g_return_if_fail (TRACKER_IS_LANGUAGE (language));
+
+	priv = GET_PRIV (language);
+
+	/* Set up stopwords list */
+	g_message ("Setting up stopword list for language code:'%s'", language_code);
+
+	stopword_filename = language_get_stopword_filename (language_code);
+	language_add_stopwords (language, stopword_filename);
+	g_free (stopword_filename);
+
+	if (!language_code || strcmp (language_code, "en") != 0) {
+		stopword_filename = language_get_stopword_filename ("en");
+		language_add_stopwords (language, stopword_filename);
+		g_free (stopword_filename);
+	}
+
+	g_message ("Setting up stemmer for language code:'%s'", language_code);
+
+	stem_language = language_get_name_for_code (language_code);
+
+	g_mutex_lock (priv->stemmer_mutex);
+
+	if (priv->stemmer) {
+		sb_stemmer_delete (priv->stemmer);
+	}
+
+	priv->stemmer = sb_stemmer_new (stem_language, NULL);
+	if (!priv->stemmer) {
+		g_message ("No stemmer could be found for language:'%s'",
+			   stem_language);
+	}
+
+	g_mutex_unlock (priv->stemmer_mutex);
+}
+
+static void
+language_notify_cb (TrackerConfig *config,
+		    GParamSpec	  *param,
+		    gpointer	   user_data)
+{
+	TrackerLanguage *language;
+
+	language = (TrackerLanguage*) user_data;
+
+	language_set_stopword_list (language,
+				    tracker_config_get_language (config));
+}
+
+TrackerLanguage *
+tracker_language_new (TrackerConfig *config)
+{
+	TrackerLanguage *language;
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), NULL);
+
+	language = g_object_new (TRACKER_TYPE_LANGUAGE,
+				 "config", config,
+				 NULL);
+
+	language_set_stopword_list (language,
+				    tracker_config_get_language (config));
+
+	return language;
+
+}
+
+TrackerConfig *
+tracker_language_get_config (TrackerLanguage *language)
+{
+	TrackerLanguagePriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_LANGUAGE (language), NULL);
+
+	priv = GET_PRIV (language);
+
+	return priv->config;
+}
+
+GHashTable *
+tracker_language_get_stop_words (TrackerLanguage *language)
+{
+	TrackerLanguagePriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_LANGUAGE (language), NULL);
+
+	priv = GET_PRIV (language);
+
+	return priv->stop_words;
+}
+
+void
+tracker_language_set_config (TrackerLanguage *language,
+			     TrackerConfig   *config)
+{
+	TrackerLanguagePriv *priv;
+
+	g_return_if_fail (TRACKER_IS_LANGUAGE (language));
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	priv = GET_PRIV (language);
+
+	if (config) {
+		g_object_ref (config);
+	}
+
+	if (priv->config) {
+		g_signal_handlers_disconnect_by_func (priv->config,
+						      G_CALLBACK (language_notify_cb),
+						      language);
+		g_object_unref (priv->config);
+	}
+
+	priv->config = config;
+
+	if (priv->config) {
+		g_signal_connect (priv->config, "notify::language",
+				  G_CALLBACK (language_notify_cb),
+				  language);
+	}
+
+	g_object_notify (G_OBJECT (language), "config");
+}
+
+const gchar *
+tracker_language_stem_word (TrackerLanguage *language,
+			    const gchar     *word,
+			    gint	     word_length)
+{
+	TrackerLanguagePriv *priv;
+	const gchar	    *stem_word;
+
+	g_return_val_if_fail (TRACKER_IS_LANGUAGE (language), NULL);
+
+	priv = GET_PRIV (language);
+
+	if (!tracker_config_get_enable_stemmer (priv->config)) {
+		return g_strdup (word);
+	}
+
+	g_mutex_lock (priv->stemmer_mutex);
+
+	stem_word = (const gchar*) sb_stemmer_stem (priv->stemmer,
+						    (guchar*) word,
+						    word_length);
+
+	g_mutex_unlock (priv->stemmer_mutex);
+
+	return stem_word;
+}
+
+gboolean
+tracker_language_check_exists (const gchar *language_code)
+{
+	gint i;
+
+	if (!language_code || language_code[0] == '\0') {
+		return FALSE;
+	}
+
+	for (i = 0; all_langs[i].code; i++) {
+		if (g_str_has_prefix (language_code, all_langs[i].code)) {
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+gchar *
+tracker_language_get_default_code (void)
+{
+	const gchar **local_languages;
+	const gchar **p;
+
+	/* Get langauges for user's locale */
+	local_languages = (const gchar**) g_get_language_names ();
+
+	for (p = local_languages; *p; p++) {
+		const gchar *code;
+		gint	     i = 0;
+
+		if (!*p || *p[0] == '\0') {
+			continue;
+		}
+
+		code = all_langs[i].code;
+
+		while (code) {
+			if (g_str_has_prefix (*p, code)) {
+				return g_strndup (*p, strlen (code));
+			}
+
+			code = all_langs[i++].code;
+		}
+	}
+
+	return g_strdup ("en");
+}

Added: trunk/src/libtracker-common/tracker-language.h
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-language.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,66 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __LIBTRACKER_COMMON_LANGUAGE_H__
+#define __LIBTRACKER_COMMON_LANGUAGE_H__
+
+#include <glib-object.h>
+
+#include "tracker-config.h"
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_LANGUAGE	      (tracker_language_get_type ())
+#define TRACKER_LANGUAGE(o)	      (G_TYPE_CHECK_INSTANCE_CAST ((o), TRACKER_TYPE_LANGUAGE, TrackerLanguage))
+#define TRACKER_LANGUAGE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), TRACKER_TYPE_LANGUAGE, TrackerLanguageClass))
+#define TRACKER_IS_LANGUAGE(o)	      (G_TYPE_CHECK_INSTANCE_TYPE ((o), TRACKER_TYPE_LANGUAGE))
+#define TRACKER_IS_LANGUAGE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), TRACKER_TYPE_LANGUAGE))
+#define TRACKER_LANGUAGE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), TRACKER_TYPE_LANGUAGE, TrackerLanguageClass))
+
+typedef struct _TrackerLanguage      TrackerLanguage;
+typedef struct _TrackerLanguageClass TrackerLanguageClass;
+
+struct _TrackerLanguage {
+	GObject      parent;
+};
+
+struct _TrackerLanguageClass {
+	GObjectClass parent_class;
+};
+
+GType		 tracker_language_get_type	   (void) G_GNUC_CONST;
+
+TrackerLanguage *tracker_language_new		   (TrackerConfig   *language);
+TrackerConfig *  tracker_language_get_config	   (TrackerLanguage *language);
+GHashTable *	 tracker_language_get_stop_words   (TrackerLanguage *language);
+void		 tracker_language_set_config	   (TrackerLanguage *language,
+						    TrackerConfig   *config);
+const gchar *	 tracker_language_stem_word	   (TrackerLanguage *language,
+						    const gchar     *word,
+						    gint	     word_length);
+
+/* Utility functions */
+gboolean	 tracker_language_check_exists	   (const gchar     *language_code);
+gchar *		 tracker_language_get_default_code (void);
+
+G_END_DECLS
+
+#endif /* __LIBTRACKER_COMMON_LANGUAGE_H__ */

Added: trunk/src/libtracker-common/tracker-log.c
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-log.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,189 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <glib/gstdio.h>
+
+#include "tracker-log.h"
+
+static gboolean  initialized;
+static GMutex	*mutex;
+static FILE	*fd;
+static gint	 verbosity;
+static guint	 log_handler_id;
+
+static inline void
+log_output (const gchar    *domain,
+	    GLogLevelFlags  log_level,
+	    const gchar    *message)
+{
+	time_t	      now;
+	gchar	      time_str[64];
+	gchar	     *output;
+	struct tm    *local_time;
+	GTimeVal      current_time;
+	const gchar  *log_level_str;
+	static gsize  size = 0;
+
+	g_return_if_fail (initialized == TRUE);
+	g_return_if_fail (message != NULL && message[0] != '\0');
+
+	/* Ensure file logging is thread safe */
+	g_mutex_lock (mutex);
+
+	/* Check log size, 10MiB limit */
+	if (size > (10 << 20) && fd) {
+		rewind (fd);
+		ftruncate (fileno (fd), 0);
+		size = 0;
+	}
+
+	g_get_current_time (&current_time);
+
+	now = time ((time_t *) NULL);
+	local_time = localtime (&now);
+	strftime (time_str, 64, "%d %b %Y, %H:%M:%S:", local_time);
+
+	switch (log_level) {
+	case G_LOG_LEVEL_WARNING:
+		log_level_str = "-Warning **";
+		break;
+
+	case G_LOG_LEVEL_CRITICAL:
+		log_level_str = "-Critical **";
+		break;
+
+	case G_LOG_LEVEL_ERROR:
+		log_level_str = "-Error **";
+		break;
+
+	default:
+		log_level_str = NULL;
+		break;
+	}
+
+	output = g_strdup_printf ("%s%s %s%s: %s",
+				  log_level_str ? "\n" : "",
+				  time_str,
+				  domain,
+				  log_level_str ? log_level_str : "",
+				  message);
+
+	if (G_UNLIKELY (fd == NULL)) {
+		g_fprintf (stderr, "%s\n", output);
+		fflush (stderr);
+	} else {
+		size += g_fprintf (fd, "%s\n", output);
+		fflush (fd);
+	}
+
+	g_free (output);
+
+	g_mutex_unlock (mutex);
+}
+
+static void
+tracker_log_handler (const gchar    *domain,
+		     GLogLevelFlags  log_level,
+		     const gchar    *message,
+		     gpointer	     user_data)
+{
+	if (((log_level & G_LOG_LEVEL_DEBUG) && verbosity < 3) ||
+	    ((log_level & G_LOG_LEVEL_INFO) && verbosity < 2) ||
+	    ((log_level & G_LOG_LEVEL_MESSAGE) && verbosity < 1)) {
+		return;
+	}
+
+	log_output (domain, log_level, message);
+
+	/* Now show the message through stdout/stderr as usual */
+	g_log_default_handler (domain, log_level, message, user_data);
+}
+
+gboolean
+tracker_log_init (const gchar *filename,
+		  gint	       this_verbosity)
+{
+	g_return_val_if_fail (filename != NULL, FALSE);
+
+	if (initialized) {
+		return TRUE;
+	}
+
+	/* Open file */
+	fd = g_fopen (filename, "a");
+	if (!fd) {
+		const gchar *error_string;
+
+		error_string = g_strerror (errno);
+		g_fprintf (stderr,
+			   "Could not open log:'%s', %s\n",
+			   filename,
+			   error_string);
+		g_fprintf (stderr,
+			   "All logging will go to stderr\n");
+	}
+
+	verbosity = this_verbosity;
+	mutex = g_mutex_new ();
+
+	/* Add log handler function */
+	log_handler_id = g_log_set_handler (NULL,
+					    G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL,
+					    tracker_log_handler,
+					    NULL);
+
+	g_log_set_default_handler (tracker_log_handler, NULL);
+
+	initialized = TRUE;
+
+	return TRUE;
+}
+
+void
+tracker_log_shutdown (void)
+{
+	if (!initialized) {
+		return;
+	}
+
+	if (fd) {
+		fclose (fd);
+	}
+
+	g_log_remove_handler (NULL, log_handler_id);
+	log_handler_id = 0;
+
+	g_mutex_free (mutex);
+
+	initialized = FALSE;
+}

Added: trunk/src/libtracker-common/tracker-log.h
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-log.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,35 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __LIBTRACKER_COMMON_LOG_H__
+#define __LIBTRACKER_COMMON_LOG_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+gboolean tracker_log_init     (const char *filename,
+			       gint	   verbosity);
+void	 tracker_log_shutdown (void);
+
+G_END_DECLS
+
+#endif /* __LIBTRACKER_COMMON_LOG_H__ */

Added: trunk/src/libtracker-common/tracker-module-config.c
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-module-config.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,817 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include "tracker-module-config.h"
+#include "tracker-file-utils.h"
+#include "tracker-type-utils.h"
+
+#define GROUP_GENERAL  "General"
+#define GROUP_MONITORS "Monitors"
+#define GROUP_IGNORED  "Ignored"
+#define GROUP_INDEX    "Index"
+#define GROUP_SPECIFIC "Specific"
+
+typedef struct {
+	/* General */
+	gchar	   *description;
+	gboolean    enabled;
+
+	/* Monitors */
+	GHashTable *monitor_directories;
+	GHashTable *monitor_recurse_directories;
+
+	/* Ignored */
+	GHashTable *ignored_directories;
+	GHashTable *ignored_files;
+
+	GList	   *ignored_directory_patterns;
+	GList	   *ignored_file_patterns;
+
+	/* Index */
+	gchar	   *index_service;
+	GHashTable *index_mime_types;
+	GHashTable *index_files;
+	GList	   *index_file_patterns;
+
+	/* Specific Options, FIXME: Finish */
+
+} ModuleConfig;
+
+static gboolean      initiated;
+static GHashTable   *modules;
+static GFileMonitor *monitor;
+
+static void
+module_destroy_notify (gpointer data)
+{
+	ModuleConfig *mc;
+
+	mc = (ModuleConfig*) data;
+
+	g_list_foreach (mc->index_file_patterns,
+			(GFunc) g_pattern_spec_free,
+			NULL);
+	g_list_free (mc->index_file_patterns);
+
+	g_hash_table_unref (mc->index_files);
+	g_hash_table_unref (mc->index_mime_types);
+	g_free (mc->index_service);
+
+	g_list_foreach (mc->ignored_file_patterns,
+			(GFunc) g_pattern_spec_free,
+			NULL);
+	g_list_free (mc->ignored_file_patterns);
+
+	g_list_foreach (mc->ignored_directory_patterns,
+			(GFunc) g_pattern_spec_free,
+			 NULL);
+	g_list_free (mc->ignored_directory_patterns);
+
+	g_hash_table_unref (mc->ignored_files);
+	g_hash_table_unref (mc->ignored_directories);
+
+	g_hash_table_unref (mc->monitor_recurse_directories);
+	g_hash_table_unref (mc->monitor_directories);
+
+	g_free (mc->description);
+
+	g_slice_free (ModuleConfig, mc);
+}
+
+static void
+check_for_monitor_directory_conflicts (ModuleConfig *mc)
+{
+	GHashTableIter iter1, iter2;
+	gpointer       key;
+
+	/* Make sure we don't have duplicates from the monitor
+	 * directories in the monitor recurse directories hash table
+	 * and also, make sure there are no recurse directories higher
+	 * as parents of monitor directories, this would duplicate
+	 * monitors unnecesarily.
+	 */
+	g_hash_table_iter_init (&iter1, mc->monitor_directories);
+	while (g_hash_table_iter_next (&iter1, &key, NULL)) {
+		const gchar *path;
+
+		path = (const gchar*) key;
+
+		if (g_hash_table_lookup (mc->monitor_recurse_directories, path)) {
+			g_debug ("Removing path:'%s' from monitor directories, "
+				 "ALREADY in monitor recurse directories",
+				 path);
+
+			g_hash_table_iter_remove (&iter1);
+			g_hash_table_iter_init (&iter1, mc->monitor_directories);
+			continue;
+		}
+
+		g_hash_table_iter_init (&iter2, mc->monitor_recurse_directories);
+		while (g_hash_table_iter_next (&iter2, &key, NULL)) {
+			const gchar *in_path;
+
+			in_path = (const gchar*) key;
+
+			if (path == in_path) {
+				continue;
+			}
+
+			if (tracker_path_is_in_path (path, in_path)) {
+				g_debug ("Removing path:'%s' from monitor directories, "
+					 "ALREADY in monitor recurse directories HIERARCHY",
+					 path);
+
+				g_hash_table_iter_remove (&iter1);
+				g_hash_table_iter_init (&iter1, mc->monitor_directories);
+				break;
+			}
+		}
+	}
+}
+
+static gchar *
+get_directory (void)
+{
+	return g_build_path (G_DIR_SEPARATOR_S, SHAREDIR, "tracker", "modules", NULL);
+}
+
+static void
+set_ignored_file_patterns (ModuleConfig *mc)
+{
+	GPatternSpec *spec;
+	GList	     *ignored_files;
+	GList	     *l;
+	GList	     *patterns = NULL;
+
+	g_list_foreach (mc->ignored_file_patterns,
+			(GFunc) g_pattern_spec_free,
+			NULL);
+	g_list_free (mc->ignored_file_patterns);
+
+	ignored_files = g_hash_table_get_keys (mc->ignored_files);
+
+	for (l = ignored_files; l; l = l->next) {
+		g_message ("  Adding file ignore pattern:'%s'",
+			   (gchar *) l->data);
+		spec = g_pattern_spec_new (l->data);
+		patterns = g_list_prepend (patterns, spec);
+	}
+
+	g_list_free (ignored_files);
+
+	mc->ignored_file_patterns = g_list_reverse (patterns);
+}
+
+static void
+set_ignored_directory_patterns (ModuleConfig *mc)
+{
+	GPatternSpec *spec;
+	GList	     *ignored_directories;
+	GList	     *l;
+	GList	     *patterns = NULL;
+
+	g_list_foreach (mc->ignored_directory_patterns,
+			(GFunc) g_pattern_spec_free,
+			NULL);
+	g_list_free (mc->ignored_directory_patterns);
+
+	ignored_directories = g_hash_table_get_keys (mc->ignored_directories);
+
+	for (l = ignored_directories; l; l = l->next) {
+		g_message ("  Adding directory ignore pattern:'%s'",
+			   (gchar *) l->data);
+		spec = g_pattern_spec_new (l->data);
+		patterns = g_list_prepend (patterns, spec);
+	}
+
+	g_list_free (ignored_directories);
+
+	mc->ignored_directory_patterns = g_list_reverse (patterns);
+}
+
+static void
+set_index_file_patterns (ModuleConfig *mc)
+{
+	GPatternSpec *spec;
+	GList	     *index_files;
+	GList	     *l;
+	GList	     *patterns = NULL;
+
+	g_list_foreach (mc->index_file_patterns,
+			(GFunc) g_pattern_spec_free,
+			NULL);
+	g_list_free (mc->index_file_patterns);
+
+	index_files = g_hash_table_get_keys (mc->index_files);
+
+	for (l = index_files; l; l = l->next) {
+		g_message ("  Adding file index pattern:'%s'",
+			   (gchar *) l->data);
+		spec = g_pattern_spec_new (l->data);
+		patterns = g_list_prepend (patterns, spec);
+	}
+
+	g_list_free (index_files);
+
+	mc->index_file_patterns = g_list_reverse (patterns);
+}
+
+static gboolean
+load_boolean (GKeyFile	  *key_file,
+	      const gchar *group,
+	      const gchar *key)
+{
+	GError	 *error = NULL;
+	gboolean  boolean;
+
+	boolean = g_key_file_get_boolean (key_file, group, key, &error);
+
+	if (error) {
+		g_message ("Couldn't load module config boolean in "
+			   "group:'%s' with key:'%s', %s",
+			   group,
+			   key,
+			   error->message);
+
+		g_error_free (error);
+		g_key_file_free (key_file);
+
+		return FALSE;
+	}
+
+	return boolean;
+}
+
+static gchar *
+load_string (GKeyFile	 *key_file,
+	      const gchar *group,
+	      const gchar *key,
+	      gboolean	   expand_string_as_path)
+{
+	GError *error = NULL;
+	gchar  *str;
+
+	str = g_key_file_get_string (key_file, group, key, &error);
+
+	if (error) {
+		g_message ("Couldn't load module config string in "
+			   "group:'%s' with key:'%s', %s",
+			   group,
+			   key,
+			   error->message);
+
+		g_error_free (error);
+		g_key_file_free (key_file);
+
+		return NULL;
+	}
+
+	if (expand_string_as_path) {
+		gchar *real_path;
+
+		real_path = tracker_path_evaluate_name (str);
+		g_free (str);
+
+		return real_path;
+	}
+
+	return str;
+}
+
+static GHashTable *
+load_string_list (GKeyFile    *key_file,
+		  const gchar *group,
+		  const gchar *key,
+		  gboolean     expand_strings_as_paths,
+		  gboolean     remove_hierarchy_dups)
+{
+	GError	    *error = NULL;
+	GHashTable  *table;
+	gchar	   **str;
+	gchar	   **p;
+	gsize	     size;
+
+	table = g_hash_table_new_full (g_str_hash,
+				       g_str_equal,
+				       g_free,
+				       NULL);
+
+	str = g_key_file_get_string_list (key_file, group, key, &size, &error);
+
+	if (error) {
+		g_message ("Couldn't load module config string list in "
+			   "group:'%s' with key:'%s', %s",
+			   group,
+			   key,
+			   error->message);
+
+		g_error_free (error);
+		g_key_file_free (key_file);
+
+		return table;
+	}
+
+	for (p = str; *p; p++) {
+		gchar *real_path;
+
+		if (!expand_strings_as_paths) {
+			if (g_hash_table_lookup (table, *p)) {
+				continue;
+			}
+
+			g_hash_table_insert (table,
+					     g_strdup (*p),
+					     GINT_TO_POINTER (1));
+		} else {
+			if (g_hash_table_lookup (table, *p)) {
+				continue;
+			}
+
+			real_path = tracker_path_evaluate_name (*p);
+			if (g_hash_table_lookup (table, real_path)) {
+				g_free (real_path);
+				continue;
+			}
+
+			g_hash_table_insert (table,
+					     real_path,
+					     GINT_TO_POINTER (1));
+			g_debug ("Got real path:'%s' for '%s'", real_path, *p);
+		}
+	}
+
+	g_strfreev (str);
+
+	/* Go through again to make sure we don't have situations
+	 * where /foo and / exist, because of course /foo is
+	 * redundant here where 'remove_hierarchy_dups' is TRUE.
+	 */
+	if (remove_hierarchy_dups) {
+		tracker_path_hash_table_filter_duplicates (table);
+	}
+
+	return table;
+}
+
+static ModuleConfig *
+load_file (const gchar *filename)
+{
+	GKeyFile     *key_file;
+	GError	     *error = NULL;
+	ModuleConfig *mc;
+
+	key_file = g_key_file_new ();
+
+	/* Load options */
+	g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &error);
+
+	if (error) {
+		g_message ("Couldn't load module config for '%s', %s",
+			   filename,
+			   error->message);
+
+		g_error_free (error);
+		g_key_file_free (key_file);
+
+		return NULL;
+	}
+
+	g_message ("Loading module config:'%s'", filename);
+
+	mc = g_slice_new0 (ModuleConfig);
+
+	/* General */
+	mc->description = load_string (key_file,
+				       GROUP_GENERAL,
+				       "Description",
+				       FALSE);
+	mc->enabled = load_boolean (key_file,
+				    GROUP_GENERAL,
+				    "Enabled");
+
+	/* Monitors */
+	mc->monitor_directories = load_string_list (key_file,
+						    GROUP_MONITORS,
+						    "Directories",
+						    TRUE,
+						    FALSE);
+	mc->monitor_recurse_directories = load_string_list (key_file,
+							   GROUP_MONITORS,
+							   "RecurseDirectories",
+							   TRUE,
+							   TRUE);
+
+	/* Ignored */
+	mc->ignored_directories = load_string_list (key_file,
+						    GROUP_IGNORED,
+						    "Directories",
+						    TRUE,
+						    FALSE);
+	mc->ignored_files = load_string_list (key_file,
+					      GROUP_IGNORED,
+					      "Files",
+					      FALSE,
+					      FALSE);
+
+	/* Index */
+	mc->index_service = load_string (key_file,
+					 GROUP_INDEX,
+					 "Service",
+					 FALSE);
+	mc->index_mime_types = load_string_list (key_file,
+						 GROUP_INDEX,
+						 "MimeTypes",
+						 FALSE,
+						 FALSE);
+	mc->index_files = load_string_list (key_file,
+					    GROUP_INDEX,
+					    "Files",
+					    FALSE,
+					    FALSE);
+
+	check_for_monitor_directory_conflicts (mc);
+
+	/* FIXME: Specific options */
+
+	set_ignored_file_patterns (mc);
+	set_ignored_directory_patterns (mc);
+	set_index_file_patterns (mc);
+
+	g_key_file_free (key_file);
+
+	return mc;
+}
+
+static gboolean
+load_directory (void)
+{
+	GFile		*file;
+	GFileEnumerator *enumerator;
+	GFileInfo	*info;
+	GError		*error = NULL;
+	gchar		*path;
+	gchar		*filename;
+	const gchar	*name;
+	const gchar	*extension;
+	glong		 extension_len;
+
+	path = get_directory ();
+	file = g_file_new_for_path (path);
+
+	enumerator = g_file_enumerate_children (file,
+						G_FILE_ATTRIBUTE_STANDARD_NAME ","
+						G_FILE_ATTRIBUTE_STANDARD_TYPE,
+						G_PRIORITY_DEFAULT,
+						NULL,
+						&error);
+
+	if (error) {
+		g_warning ("Could not get module config from directory:'%s', %s",
+			   path,
+			   error->message);
+
+		g_free (path);
+		g_error_free (error);
+		g_object_unref (file);
+
+		return FALSE;
+	}
+
+	extension = ".module";
+	extension_len = g_utf8_strlen (extension, -1);
+
+	/* We should probably do this async */
+	for (info = g_file_enumerator_next_file (enumerator, NULL, &error);
+	     info && !error;
+	     info = g_file_enumerator_next_file (enumerator, NULL, &error)) {
+		GFile	     *child;
+		ModuleConfig *mc;
+
+		name = g_file_info_get_name (info);
+
+		if (!g_str_has_suffix (name, extension)) {
+			g_object_unref (info);
+			continue;
+		}
+
+		child = g_file_get_child (file, name);
+		filename = g_file_get_path (child);
+		mc = load_file (filename);
+		g_free (filename);
+
+		if (mc) {
+			gchar *name_stripped;
+
+			name_stripped = g_strndup (name, g_utf8_strlen (name, -1) - extension_len);
+
+			g_hash_table_insert (modules,
+					     name_stripped,
+					     mc);
+		}
+
+		g_object_unref (child);
+		g_object_unref (info);
+	}
+
+	if (error) {
+		g_warning ("Could not get module config information from directory:'%s', %s",
+			   path,
+			   error->message);
+		g_error_free (error);
+	}
+
+	g_message ("Loaded module config, %d found",
+		   g_hash_table_size (modules));
+
+	g_object_unref (enumerator);
+	g_object_unref (file);
+	g_free (path);
+
+	return TRUE;
+}
+
+static void
+changed_cb (GFileMonitor     *monitor,
+	    GFile	     *file,
+	    GFile	     *other_file,
+	    GFileMonitorEvent event_type,
+	    gpointer	      user_data)
+{
+	gchar *filename;
+
+	/* Do we recreate if the file is deleted? */
+
+	switch (event_type) {
+	case G_FILE_MONITOR_EVENT_CHANGED:
+	case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
+		filename = g_file_get_path (file);
+		g_message ("Config file changed:'%s', reloading settings...",
+			   filename);
+		g_free (filename);
+
+		load_directory ();
+		break;
+
+	default:
+		break;
+	}
+}
+
+gboolean
+tracker_module_config_init (void)
+{
+	GFile *file;
+	gchar *path;
+
+	if (initiated) {
+		return TRUE;
+	}
+
+	path = get_directory ();
+	if (!g_file_test (path, G_FILE_TEST_IS_DIR | G_FILE_TEST_EXISTS)) {
+		g_critical ("Module config directory:'%s' doesn't exist",
+			    path);
+		g_free (path);
+		return FALSE;
+	}
+
+	modules = g_hash_table_new_full (g_str_hash,
+					 g_str_equal,
+					 g_free,
+					 module_destroy_notify);
+
+	/* Get modules */
+	if (!load_directory ()) {
+		g_hash_table_unref (modules);
+		g_free (path);
+		return FALSE;
+	}
+
+	/* Add file monitoring for changes */
+	g_message ("Setting up monitor for changes to modules directory:'%s'",
+		   path);
+
+	file = g_file_new_for_path (path);
+	monitor = g_file_monitor_directory (file,
+					    G_FILE_MONITOR_NONE,
+					    NULL,
+					    NULL);
+
+	g_signal_connect (monitor, "changed",
+			  G_CALLBACK (changed_cb),
+			  NULL);
+
+	g_object_unref (file);
+	g_free (path);
+
+	initiated = TRUE;
+
+	return TRUE;
+}
+
+void
+tracker_module_config_shutdown (void)
+{
+	if (!initiated) {
+		return;
+	}
+
+	g_signal_handlers_disconnect_by_func (monitor, changed_cb, NULL);
+
+	g_object_unref (monitor);
+
+	g_hash_table_unref (modules);
+
+	initiated = FALSE;
+}
+
+GList *
+tracker_module_config_get_modules (void)
+{
+	return g_hash_table_get_keys (modules);
+}
+
+const gchar *
+tracker_module_config_get_description (const gchar *name)
+{
+	ModuleConfig *mc;
+
+	g_return_val_if_fail (name != NULL, NULL);
+
+	mc = g_hash_table_lookup (modules, name);
+	g_return_val_if_fail (mc, NULL);
+
+	return mc->description;
+}
+
+gboolean
+tracker_module_config_get_enabled (const gchar *name)
+{
+	ModuleConfig *mc;
+
+	g_return_val_if_fail (name != NULL, FALSE);
+
+	mc = g_hash_table_lookup (modules, name);
+	g_return_val_if_fail (mc, FALSE);
+
+	return mc->enabled;
+}
+
+GList *
+tracker_module_config_get_monitor_directories (const gchar *name)
+{
+	ModuleConfig *mc;
+
+	g_return_val_if_fail (name != NULL, FALSE);
+
+	mc = g_hash_table_lookup (modules, name);
+	g_return_val_if_fail (mc, NULL);
+
+	return g_hash_table_get_keys (mc->monitor_directories);
+}
+
+GList *
+tracker_module_config_get_monitor_recurse_directories (const gchar *name)
+{
+	ModuleConfig *mc;
+
+	g_return_val_if_fail (name != NULL, FALSE);
+
+	mc = g_hash_table_lookup (modules, name);
+	g_return_val_if_fail (mc, NULL);
+
+	return g_hash_table_get_keys (mc->monitor_recurse_directories);
+}
+
+GList *
+tracker_module_config_get_ignored_directories (const gchar *name)
+{
+	ModuleConfig *mc;
+
+	g_return_val_if_fail (name != NULL, FALSE);
+
+	mc = g_hash_table_lookup (modules, name);
+	g_return_val_if_fail (mc, NULL);
+
+	return g_hash_table_get_keys (mc->ignored_directories);
+}
+
+GList *
+tracker_module_config_get_ignored_files (const gchar *name)
+{
+	ModuleConfig *mc;
+
+	g_return_val_if_fail (name != NULL, FALSE);
+
+	mc = g_hash_table_lookup (modules, name);
+	g_return_val_if_fail (mc, NULL);
+
+	return g_hash_table_get_keys (mc->ignored_files);
+}
+
+const gchar *
+tracker_module_config_get_index_service (const gchar *name)
+{
+	ModuleConfig *mc;
+
+	g_return_val_if_fail (name != NULL, NULL);
+
+	mc = g_hash_table_lookup (modules, name);
+	g_return_val_if_fail (mc, NULL);
+
+	return mc->index_service;
+}
+
+GList *
+tracker_module_config_get_index_mime_types (const gchar *name)
+{
+	ModuleConfig *mc;
+
+	g_return_val_if_fail (name != NULL, FALSE);
+
+	mc = g_hash_table_lookup (modules, name);
+	g_return_val_if_fail (mc, NULL);
+
+	return g_hash_table_get_keys (mc->index_mime_types);
+}
+
+GList *
+tracker_module_config_get_index_files (const gchar *name)
+{
+	ModuleConfig *mc;
+
+	g_return_val_if_fail (name != NULL, FALSE);
+
+	mc = g_hash_table_lookup (modules, name);
+	g_return_val_if_fail (mc, NULL);
+
+	return g_hash_table_get_keys (mc->index_files);
+}
+
+/*
+ * Convenience functions
+ */
+
+GList *
+tracker_module_config_get_ignored_file_patterns (const gchar *name)
+{
+	ModuleConfig *mc;
+
+	g_return_val_if_fail (name != NULL, NULL);
+
+	mc = g_hash_table_lookup (modules, name);
+	g_return_val_if_fail (mc, NULL);
+
+	return g_list_copy (mc->ignored_file_patterns);
+}
+
+GList *
+tracker_module_config_get_ignored_directory_patterns (const gchar *name)
+{
+	ModuleConfig *mc;
+
+	g_return_val_if_fail (name != NULL, NULL);
+
+	mc = g_hash_table_lookup (modules, name);
+	g_return_val_if_fail (mc, NULL);
+
+	return g_list_copy (mc->ignored_directory_patterns);
+}
+
+GList *
+tracker_module_config_get_index_file_patterns (const gchar *name)
+{
+	ModuleConfig *mc;
+
+	g_return_val_if_fail (name != NULL, NULL);
+
+	mc = g_hash_table_lookup (modules, name);
+	g_return_val_if_fail (mc, NULL);
+
+	return g_list_copy (mc->index_file_patterns);
+}

Added: trunk/src/libtracker-common/tracker-module-config.h
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-module-config.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,54 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __LIBTRACKER_COMMON_MODULE_CONFIG_H__
+#define __LIBTRACKER_COMMON_MODULE_CONFIG_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+gboolean     tracker_module_config_init				   (void);
+void	     tracker_module_config_shutdown			   (void);
+
+GList *      tracker_module_config_get_modules			   (void);
+
+const gchar *tracker_module_config_get_description		   (const gchar *name);
+gboolean     tracker_module_config_get_enabled			   (const gchar *name);
+
+GList *      tracker_module_config_get_monitor_directories	   (const gchar *name);
+GList *      tracker_module_config_get_monitor_recurse_directories (const gchar *name);
+
+GList *      tracker_module_config_get_ignored_directories	   (const gchar *name);
+GList *      tracker_module_config_get_ignored_files		   (const gchar *name);
+
+const gchar *tracker_module_config_get_index_service		   (const gchar *name);
+GList *      tracker_module_config_get_index_mime_types		   (const gchar *name);
+GList *      tracker_module_config_get_index_files		   (const gchar *name);
+
+/* Convenience functions */
+GList *      tracker_module_config_get_ignored_directory_patterns  (const gchar *name);
+GList *      tracker_module_config_get_ignored_file_patterns	   (const gchar *name);
+GList *      tracker_module_config_get_index_file_patterns	   (const gchar *name);
+
+G_END_DECLS
+
+#endif /* __LIBTRACKER_COMMON_MODULE_CONFIG_H__ */
+

Added: trunk/src/libtracker-common/tracker-nfs-lock.c
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-nfs-lock.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,197 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <time.h>
+#include <glib/gstdio.h>
+
+#include "tracker-nfs-lock.h"
+#include "tracker-log.h"
+
+static gchar *lock_filename;
+static gchar *tmp_dir;
+
+static gboolean use_nfs_safe_locking;
+
+/* Get no of links to a file - used for safe NFS atomic file locking */
+static gint
+get_nlinks (const gchar *filename)
+{
+	struct stat st;
+
+	if (g_stat (filename, &st) == 0) {
+		return st.st_nlink;
+	} else {
+		return -1;
+	}
+}
+
+static gint
+get_mtime (const gchar *filename)
+{
+	struct stat st;
+
+	if (g_stat (filename, &st) == 0) {
+		return st.st_mtime;
+	} else {
+		return -1;
+	}
+}
+
+static gboolean
+is_initialized (void)
+{
+	return lock_filename != NULL || tmp_dir != NULL;
+}
+
+/* Serialises db access via a lock file for safe use on (lock broken)
+ * NFS mounts.
+ */
+gboolean
+tracker_nfs_lock_obtain (void)
+{
+	gchar *filename;
+	gint   attempt;
+	gint   fd;
+
+	if (!use_nfs_safe_locking) {
+		return TRUE;
+	}
+
+	if (!is_initialized()) {
+		g_critical ("Could not initialize NFS lock");
+		return FALSE;
+	}
+
+	filename = g_strdup_printf ("%s_%s.lock",
+				    tmp_dir,
+				    g_get_user_name ());
+
+	for (attempt = 0; attempt < 10000; ++attempt) {
+		/* Delete existing lock file if older than 5 mins */
+		if (g_file_test (lock_filename, G_FILE_TEST_EXISTS) &&
+		    time ((time_t *) - get_mtime (lock_filename)) > 300) {
+			g_unlink (lock_filename);
+		}
+
+		fd = g_open (lock_filename, O_CREAT | O_EXCL, 0644);
+
+		if (fd >= 0) {
+			/* Create host specific file and link to lock file */
+			if (link (lock_filename, filename) == -1) {
+				goto error;
+			}
+
+			/* For atomic NFS-safe locks, stat links = 2
+			 * if file locked. If greater than 2 then we
+			 * have a race condition.
+			 */
+			if (get_nlinks (lock_filename) == 2) {
+				close (fd);
+				g_free (filename);
+
+				return TRUE;
+			} else {
+				close (fd);
+				g_usleep (g_random_int_range (1000, 100000));
+			}
+		}
+	}
+
+error:
+	g_critical ("Could not get NFS lock state");
+	g_free (filename);
+
+	return FALSE;
+}
+
+void
+tracker_nfs_lock_release (void)
+{
+	gchar *filename;
+
+	if (!use_nfs_safe_locking) {
+		return;
+	}
+
+	if (!is_initialized ()) {
+		g_critical ("Could not initialize NFS lock");
+		return;
+	}
+
+	filename = g_strdup_printf ("%s_%s.lock",
+				    tmp_dir,
+				    g_get_user_name ());
+
+	g_unlink (filename);
+	g_unlink (lock_filename);
+
+	g_free (filename);
+}
+
+void
+tracker_nfs_lock_init (gboolean nfs)
+{
+	if (is_initialized ()) {
+		return;
+	}
+
+	use_nfs_safe_locking = nfs;
+
+	if (lock_filename == NULL) {
+		lock_filename = g_build_filename (g_get_user_data_dir (),
+						  "tracker",
+						  "tracker.lock",
+						  NULL);
+	}
+
+	if (tmp_dir == NULL) {
+		tmp_dir = g_build_filename (g_get_user_data_dir (),
+					    "tracker",
+					    g_get_host_name (),
+					    NULL);
+	}
+
+	g_message ("NFS lock initialized %s",
+		   use_nfs_safe_locking ? "" : "(safe locking not in use)");
+}
+
+void
+tracker_nfs_lock_shutdown (void)
+{
+	if (!is_initialized ()) {
+		return;
+	}
+
+	if (lock_filename) {
+		g_free (lock_filename);
+		lock_filename = NULL;
+	}
+
+	if (tmp_dir) {
+		g_free (tmp_dir);
+		tmp_dir = NULL;
+	}
+
+	g_message ("NFS lock finalized");
+}

Added: trunk/src/libtracker-common/tracker-nfs-lock.h
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-nfs-lock.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,36 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __LIBTRACKER_COMMON_NFS_LOCK_H__
+#define __LIBTRACKER_COMMON_NFS_LOCK_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+void	 tracker_nfs_lock_init	   (gboolean nfs);
+void	 tracker_nfs_lock_shutdown (void);
+gboolean tracker_nfs_lock_obtain   (void);
+void	 tracker_nfs_lock_release  (void);
+
+G_END_DECLS
+
+#endif /* __LIBTRACKER_COMMON_NFS_LOCK_H__ */

Added: trunk/src/libtracker-common/tracker-ontology.c
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-ontology.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,738 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <glib.h>
+
+#include "tracker-ontology.h"
+
+typedef struct {
+	gchar *prefix;
+	gint   service;
+} ServiceMimePrefixes;
+
+static gboolean    initialized;
+
+/* Hash (gint service_type_id, TrackerService *service) */
+static GHashTable *service_ids;
+
+/* Hash (gchar *service_name, TrackerService *service) */
+static GHashTable *service_names;
+
+/* Hash (gchar *mime, gint service_type_id) */
+static GHashTable *mimes_to_service_ids;
+
+/* List of ServiceMimePrefixes */
+static GSList	  *service_mime_prefixes;
+
+/* Field descriptions */
+static GHashTable *field_names;
+
+/* FieldType enum class */
+static gpointer    field_type_enum_class;
+
+static void
+ontology_mime_prefix_foreach (gpointer data,
+			      gpointer user_data)
+{
+	ServiceMimePrefixes *mime_prefix;
+
+	mime_prefix = (ServiceMimePrefixes*) data;
+
+	g_free (mime_prefix->prefix);
+	g_free (mime_prefix);
+}
+
+#if 0
+
+/* NOTE: This function *USED* to be for the service_names and
+ * field_names hash tables so they could collate strings before
+ * comparing them. But we have stopped using this because all service
+ * names and field names are quite specific and defined in code so
+ * they shouldn't ever need to be collated or case insensitively
+ * compared.
+ *
+ * If this breaks things, we can reinstate it.
+ */
+static gpointer
+ontology_hash_lookup_by_collated_str (GHashTable  *hash_table,
+				      const gchar *str)
+{
+	gpointer  data;
+	gchar	 *str_lower;
+
+	str_lower = g_utf8_collate_key (str, -1);
+	if (!str_lower) {
+		return NULL;
+	}
+
+	data = g_hash_table_lookup (hash_table, str_lower);
+	g_free (str_lower);
+
+	return data;
+}
+
+#endif
+
+static gpointer
+ontology_hash_lookup_by_id (GHashTable	*hash_table,
+			    gint	 id)
+{
+	gpointer  data;
+	gchar	 *str;
+
+	str = g_strdup_printf ("%d", id);
+	if (!str) {
+		return NULL;
+	}
+
+	data = g_hash_table_lookup (hash_table, str);
+	g_free (str);
+
+	return data;
+}
+
+void
+tracker_ontology_init (void)
+{
+	if (initialized) {
+		return;
+	}
+
+	service_ids = g_hash_table_new_full (g_str_hash,
+					     g_str_equal,
+					     g_free,
+					     g_object_unref);
+
+	service_names = g_hash_table_new_full (g_str_hash,
+					       g_str_equal,
+					       g_free,
+					       g_object_unref);
+
+	mimes_to_service_ids = g_hash_table_new_full (g_str_hash,
+						      g_str_equal,
+						      g_free,
+						      NULL);
+
+	field_names = g_hash_table_new_full (g_str_hash,
+					     g_str_equal,
+					     g_free,
+					     g_object_unref);
+
+	/* We will need the class later in order to match strings to enum values
+	 * when inserting metadata types in the DB, so the enum class needs to be
+	 * created beforehand.
+	 */
+	field_type_enum_class = g_type_class_ref (TRACKER_TYPE_FIELD_TYPE);
+
+	initialized = TRUE;
+}
+
+void
+tracker_ontology_shutdown (void)
+{
+	if (!initialized) {
+		return;
+	}
+
+	g_hash_table_unref (service_ids);
+	service_ids = NULL;
+
+	g_hash_table_unref (service_names);
+	service_names = NULL;
+
+	g_hash_table_unref (mimes_to_service_ids);
+	mimes_to_service_ids = NULL;
+
+	g_hash_table_unref (field_names);
+	field_names = NULL;
+
+	if (service_mime_prefixes) {
+		g_slist_foreach (service_mime_prefixes,
+				 ontology_mime_prefix_foreach,
+				 NULL);
+		g_slist_free (service_mime_prefixes);
+		service_mime_prefixes = NULL;
+	}
+
+	g_type_class_unref (field_type_enum_class);
+	field_type_enum_class = NULL;
+
+	initialized = FALSE;
+}
+
+void
+tracker_ontology_service_add (TrackerService *service,
+			      GSList	     *mimes,
+			      GSList	     *mime_prefixes)
+{
+
+	GSList		    *l;
+	ServiceMimePrefixes *service_mime_prefix;
+	gint		     id;
+	const gchar	    *name;
+
+	g_return_if_fail (TRACKER_IS_SERVICE (service));
+
+	id = tracker_service_get_id (service);
+	name = tracker_service_get_name (service);
+
+	g_hash_table_insert (service_names,
+			     /* g_utf8_collate_key (name, -1),	*/
+			     g_strdup (name),
+			     g_object_ref (service));
+	g_hash_table_insert (service_ids,
+			     g_strdup_printf ("%d", id),
+			     g_object_ref (service));
+
+	for (l = mimes; l && l->data; l = l->next) {
+		g_hash_table_insert (mimes_to_service_ids,
+				     l->data,
+				     GINT_TO_POINTER (id));
+	}
+
+	for (l = mime_prefixes; l; l = l->next) {
+		service_mime_prefix = g_new0 (ServiceMimePrefixes, 1);
+		service_mime_prefix->prefix = l->data;
+		service_mime_prefix->service = id;
+
+		service_mime_prefixes = g_slist_prepend (service_mime_prefixes,
+						       service_mime_prefix);
+	}
+}
+
+TrackerService *
+tracker_ontology_get_service_by_name (const gchar *service_str)
+{
+	g_return_val_if_fail (service_str != NULL, NULL);
+
+	return g_hash_table_lookup (service_names, service_str);
+}
+
+gchar *
+tracker_ontology_get_service_by_id (gint id)
+{
+	TrackerService *service;
+
+	service = ontology_hash_lookup_by_id (service_ids, id);
+
+	if (!service) {
+		return NULL;
+	}
+
+	return g_strdup (tracker_service_get_name (service));
+}
+
+gchar *
+tracker_ontology_get_service_by_mime (const gchar *mime)
+{
+	gpointer	     id;
+	ServiceMimePrefixes *item;
+	GSList		    *prefix_service;
+
+	g_return_val_if_fail (mime != NULL, g_strdup ("Other"));
+
+	/* Try a complete mime */
+	id = g_hash_table_lookup (mimes_to_service_ids, mime);
+	if (id) {
+		return tracker_ontology_get_service_by_id (GPOINTER_TO_INT (id));
+	}
+
+	/* Try in prefixes */
+	for (prefix_service = service_mime_prefixes;
+	     prefix_service != NULL;
+	     prefix_service = prefix_service->next) {
+		item = prefix_service->data;
+		if (g_str_has_prefix (mime, item->prefix)) {
+			return tracker_ontology_get_service_by_id (item->service);
+		}
+	}
+
+	/* Default option */
+	return g_strdup ("Other");
+}
+
+gint
+tracker_ontology_get_service_id_by_name (const char *service_str)
+{
+	TrackerService *service;
+
+	g_return_val_if_fail (service_str != NULL, -1);
+
+	service = g_hash_table_lookup (service_names, service_str);
+
+	if (!service) {
+		return -1;
+	}
+
+	return tracker_service_get_id (service);
+}
+
+gchar *
+tracker_ontology_get_service_parent (const gchar *service_str)
+{
+	TrackerService *service;
+	const gchar    *parent = NULL;
+
+	g_return_val_if_fail (service_str != NULL, NULL);
+
+	service = g_hash_table_lookup (service_names, service_str);
+
+	if (service) {
+		parent = tracker_service_get_parent (service);
+	}
+
+	return g_strdup (parent);
+}
+
+gchar *
+tracker_ontology_get_service_parent_by_id (gint id)
+{
+	TrackerService *service;
+
+	service = ontology_hash_lookup_by_id (service_ids, id);
+
+	if (!service) {
+		return NULL;
+	}
+
+	return g_strdup (tracker_service_get_parent (service));
+}
+
+gint
+tracker_ontology_get_service_parent_id_by_id (gint id)
+{
+	TrackerService *service;
+	const gchar    *parent = NULL;
+
+	service = ontology_hash_lookup_by_id (service_ids, id);
+
+	if (service) {
+		parent = tracker_service_get_parent (service);
+	}
+
+	if (!parent) {
+		return -1;
+	}
+
+	service = g_hash_table_lookup (service_names, parent);
+
+	if (!service) {
+		return -1;
+	}
+
+	return tracker_service_get_id (service);
+}
+
+TrackerDBType
+tracker_ontology_get_service_db_by_name (const gchar *service_str)
+{
+	TrackerDBType  type;
+	gchar	      *str;
+
+	g_return_val_if_fail (service_str != NULL, TRACKER_DB_TYPE_FILES);
+
+	str = g_utf8_strdown (service_str, -1);
+
+	if (g_str_has_suffix (str, "emails") ||
+	    g_str_has_suffix (str, "attachments")) {
+		type = TRACKER_DB_TYPE_EMAIL;
+	} else if (g_str_has_prefix (str, "files")) {
+		type = TRACKER_DB_TYPE_FILES;
+	} else if (g_str_has_prefix (str, "xesam")) {
+		type = TRACKER_DB_TYPE_XESAM;
+	} else {
+		type = TRACKER_DB_TYPE_FILES;
+	}
+
+	g_free (str);
+
+	return type;
+}
+
+GSList *
+tracker_ontology_get_service_names_registered (void)
+{
+	TrackerService *service;
+	GList	       *services, *l;
+	GSList	       *names = NULL;
+
+	services = g_hash_table_get_values (service_names);
+
+	for (l = services; l; l = l->next) {
+		service = l->data;
+		names = g_slist_prepend (names, g_strdup (tracker_service_get_name (service)));
+	}
+
+	return names;
+}
+
+GSList *
+tracker_ontology_get_field_names_registered (const gchar *service_str)
+{
+	GList	       *fields;
+	GList	       *l;
+	GSList	       *names;
+	const gchar    *prefix;
+	const gchar    *parent_prefix;
+
+	parent_prefix = NULL;
+
+	if (service_str) {
+		TrackerService *service;
+		TrackerService *parent;
+		const gchar    *parent_name;
+
+		service = tracker_ontology_get_service_by_name (service_str);
+		if (!service) {
+			return NULL;
+		}
+
+		/* Prefix for properties of the category */
+		prefix = tracker_service_get_property_prefix (service);
+
+		if (!prefix || g_strcmp0 (prefix, " ") == 0) {
+			prefix = service_str;
+		}
+
+		/* Prefix for properties of the parent */
+		parent_name = tracker_ontology_get_service_parent (service_str);
+
+		if (parent_name && g_strcmp0 (parent_name, " ") != 0) {
+			parent = tracker_ontology_get_service_by_name (parent_name);
+
+			if (parent) {
+				parent_prefix = tracker_service_get_property_prefix (parent);
+
+				if (!parent_prefix || g_strcmp0 (parent_prefix, " ") == 0) {
+					parent_prefix = parent_name;
+				}
+			}
+		}
+	}
+
+	names = NULL;
+	fields = g_hash_table_get_values (field_names);
+
+	for (l = fields; l; l = l->next) {
+		TrackerField *field;
+		const gchar  *name;
+
+		field = l->data;
+		name = tracker_field_get_name (field);
+
+		if (service_str == NULL ||
+		    (prefix && g_str_has_prefix (name, prefix)) ||
+		    (parent_prefix && g_str_has_prefix (name, parent_prefix))) {
+			names = g_slist_prepend (names, g_strdup (name));
+		}
+	}
+
+	g_list_free (fields);
+
+	return names;
+}
+
+/*
+ * Service data
+ */
+gboolean
+tracker_ontology_service_is_valid (const gchar *service_str)
+{
+	g_return_val_if_fail (service_str != NULL, FALSE);
+
+	return tracker_ontology_get_service_id_by_name (service_str) != -1;
+}
+
+gboolean
+tracker_ontology_service_has_embedded (const gchar *service_str)
+{
+	TrackerService *service;
+
+	g_return_val_if_fail (service_str != NULL, FALSE);
+
+	service = g_hash_table_lookup (service_names, service_str);
+
+	if (!service) {
+		return FALSE;
+	}
+
+	return tracker_service_get_embedded (service);
+}
+
+gboolean
+tracker_ontology_service_has_metadata (const gchar *service_str)
+{
+	TrackerService *service;
+
+	g_return_val_if_fail (service_str != NULL, FALSE);
+
+	service = g_hash_table_lookup (service_names, service_str);
+
+	if (!service) {
+		return FALSE;
+	}
+
+	return tracker_service_get_has_metadata (service);
+}
+
+gboolean
+tracker_ontology_service_has_thumbnails (const gchar *service_str)
+{
+	TrackerService *service;
+
+	g_return_val_if_fail (service_str != NULL, FALSE);
+
+	service = g_hash_table_lookup (service_names, service_str);
+
+	if (!service) {
+		return FALSE;
+	}
+
+	return tracker_service_get_has_thumbs (service);
+}
+
+gboolean
+tracker_ontology_service_has_text (const char *service_str)
+{
+	TrackerService *service;
+
+	g_return_val_if_fail (service_str != NULL, FALSE);
+
+	service = g_hash_table_lookup (service_names, service_str);
+
+	if (!service) {
+		return FALSE;
+	}
+
+	return tracker_service_get_has_full_text (service);
+}
+
+gint
+tracker_ontology_service_get_key_metadata (const gchar *service_str,
+					   const gchar *meta_name)
+{
+	TrackerService *service;
+	gint		i;
+	const GSList   *l;
+
+	g_return_val_if_fail (service_str != NULL, 0);
+	g_return_val_if_fail (meta_name != NULL, 0);
+
+	service = g_hash_table_lookup (service_names, service_str);
+
+	if (!service) {
+		return 0;
+	}
+
+	for (l = tracker_service_get_key_metadata (service), i = 1;
+	     l;
+	     l = l->next, i++) {
+		if (!l->data) {
+			continue;
+		}
+
+		if (strcasecmp (l->data, meta_name) == 0) {
+			return i;
+		}
+	}
+
+	return 0;
+}
+
+gboolean
+tracker_ontology_service_get_show_directories (const gchar *service_str)
+{
+	TrackerService *service;
+
+	service = g_hash_table_lookup (service_names, service_str);
+
+	if (!service) {
+		return FALSE;
+	}
+
+	return tracker_service_get_show_service_directories (service);
+}
+
+gboolean
+tracker_ontology_service_get_show_files (const gchar *service_str)
+{
+	TrackerService *service;
+
+	service = g_hash_table_lookup (service_names, service_str);
+
+	if (!service) {
+		return FALSE;
+	}
+
+	return tracker_service_get_show_service_files (service);
+}
+
+/* Field mechanics */
+void
+tracker_ontology_field_add (TrackerField *field)
+{
+	const gchar *name;
+
+	g_return_if_fail (TRACKER_IS_FIELD (field));
+
+	name = tracker_field_get_name (field);
+	g_return_if_fail (name != NULL);
+
+	g_hash_table_insert (field_names,
+			     /* g_utf8_collate_key (tracker_field_get_name (field), -1), */
+			     g_strdup (name),
+			     g_object_ref (field));
+}
+
+TrackerField *
+tracker_ontology_get_field_by_name (const gchar *name)
+{
+	g_return_val_if_fail (name != NULL, NULL);
+
+	return g_hash_table_lookup (field_names, name);
+}
+
+TrackerField *
+tracker_ontology_get_field_by_id (gint id)
+{
+	TrackerField *field = NULL;
+	GList	     *values;
+	GList	     *l;
+
+	/* TODO Create a hashtable with id -> field def. More efficient */
+
+	values = g_hash_table_get_values (field_names);
+
+	for (l = values; l && !field; l = l->next) {
+		if (atoi (tracker_field_get_id (l->data)) == id) {
+			field = l->data;
+		}
+	}
+
+	g_list_free (values);
+
+	return field;
+}
+
+gchar *
+tracker_ontology_get_field_name_by_service_name (TrackerField *field,
+						 const gchar  *service_str)
+{
+	const gchar *field_name;
+	const gchar *meta_name;
+	gint	     key_field;
+
+	g_return_val_if_fail (TRACKER_IS_FIELD (field), NULL);
+	g_return_val_if_fail (service_str != NULL, NULL);
+
+	meta_name = tracker_field_get_name (field);
+	key_field = tracker_ontology_service_get_key_metadata (service_str,
+							       meta_name);
+
+	if (key_field > 0) {
+		return g_strdup_printf ("KeyMetadata%d", key_field);
+
+	}
+
+	/* TODO do it using field_name in TrackerField! */
+	field_name = tracker_field_get_field_name (field);
+	if (field_name) {
+		return g_strdup (field_name);
+	} else {
+		return NULL;
+	}
+}
+
+/*
+ * Field data
+ */
+gchar *
+tracker_ontology_field_get_display_name (TrackerField *field)
+{
+	TrackerFieldType type;
+
+	g_return_val_if_fail (TRACKER_IS_FIELD (field), NULL);
+
+	type = tracker_field_get_data_type (field);
+
+	if (type == TRACKER_FIELD_TYPE_INDEX ||
+	    type == TRACKER_FIELD_TYPE_STRING ||
+	    type == TRACKER_FIELD_TYPE_DOUBLE) {
+		return g_strdup ("MetaDataDisplay");
+	}
+
+	return g_strdup ("MetaDataValue");
+}
+
+const gchar *
+tracker_ontology_field_get_id (const gchar *name)
+{
+	TrackerField *field;
+
+	g_return_val_if_fail (name != NULL, NULL);
+
+	field = tracker_ontology_get_field_by_name (name);
+
+	if (field) {
+		return tracker_field_get_id (field);
+	}
+
+	return NULL;
+}
+
+gboolean
+tracker_ontology_field_is_child_of (const gchar *field_str_child,
+				    const gchar *field_str_parent)
+{
+	TrackerField *field_child;
+	TrackerField *field_parent;
+	const GSList *l;
+
+	g_return_val_if_fail (field_str_child != NULL, FALSE);
+	g_return_val_if_fail (field_str_parent != NULL, FALSE);
+
+	field_child = tracker_ontology_get_field_by_name (field_str_child);
+
+	if (!field_child) {
+		return FALSE;
+	}
+
+	field_parent = tracker_ontology_get_field_by_name (field_str_parent);
+
+	if (!field_parent) {
+		return FALSE;
+	}
+
+	for (l = tracker_field_get_child_ids (field_parent); l; l = l->next) {
+		if (!l->data) {
+			return FALSE;
+		}
+
+		if (strcmp (tracker_field_get_id (field_child), l->data) == 0) {
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}

Added: trunk/src/libtracker-common/tracker-ontology.h
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-ontology.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,77 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKERD_ONTOLOGY_H__
+#define __TRACKERD_ONTOLOGY_H__
+
+#include <glib-object.h>
+
+#include "tracker-field.h"
+#include "tracker-service.h"
+
+G_BEGIN_DECLS
+
+void		tracker_ontology_init				(void);
+void		tracker_ontology_shutdown			(void);
+
+/* Service mechanics */
+void		tracker_ontology_service_add			(TrackerService *service,
+								 GSList		*mimes,
+								 GSList		*mime_prefixes);
+TrackerService *tracker_ontology_get_service_by_name		(const gchar	*service_str);
+gchar *		tracker_ontology_get_service_by_id		(gint		 id);
+gchar *		tracker_ontology_get_service_by_mime		(const gchar	*mime);
+gint		tracker_ontology_get_service_id_by_name		(const gchar	*service_str);
+TrackerDBType	tracker_ontology_get_service_db_by_name		(const gchar	*service_str);
+gchar *		tracker_ontology_get_service_parent		(const gchar	*service_str);
+gchar *		tracker_ontology_get_service_parent_by_id	(gint		 id);
+gint		tracker_ontology_get_service_parent_id_by_id	(gint		 id);
+GSList *	tracker_ontology_get_service_names_registered	(void);
+GSList *	tracker_ontology_get_field_names_registered	(const gchar	*service_str);
+
+/* Service data */
+gboolean	tracker_ontology_service_is_valid		(const gchar	*service_str);
+gboolean	tracker_ontology_service_has_embedded		(const gchar	*service_str);
+gboolean	tracker_ontology_service_has_metadata		(const gchar	*service_str);
+gboolean	tracker_ontology_service_has_thumbnails		(const gchar	*service_str);
+gboolean	tracker_ontology_service_has_text		(const gchar	*service_str);
+gint		tracker_ontology_service_get_key_metadata	(const gchar	*service_str,
+								 const gchar	*meta_name);
+gboolean	tracker_ontology_service_get_show_directories	(const gchar	*service_str);
+gboolean	tracker_ontology_service_get_show_files		(const gchar	*service_str);
+
+/* Field mechanics */
+void		tracker_ontology_field_add			(TrackerField	*field);
+TrackerField *	tracker_ontology_get_field_by_name		(const gchar	*name);
+TrackerField *	tracker_ontology_get_field_by_id		(gint		 id);
+gchar *		tracker_ontology_get_field_name_by_service_name (TrackerField	*field,
+								 const gchar	*service_str);
+
+/* Field data */
+gchar *		tracker_ontology_field_get_display_name		(TrackerField	*field);
+const gchar *	tracker_ontology_field_get_id			(const gchar	*name);
+gboolean	tracker_ontology_field_is_child_of		(const gchar	*child,
+								 const gchar	*parent);
+
+G_END_DECLS
+
+#endif /* __TRACKERD_ONTOLOGY_H__ */
+

Added: trunk/src/libtracker-common/tracker-os-dependant-unix.c
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-os-dependant-unix.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,241 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <unistd.h>
+#include <sys/resource.h>
+
+#include <glib.h>
+#include <glib/gspawn.h>
+#include <glib/gstring.h>
+
+#include "tracker-log.h"
+#include "tracker-os-dependant.h"
+
+#define MAX_MEM       128
+#define MAX_MEM_AMD64 512
+
+gboolean
+tracker_spawn (gchar **argv,
+	       gint    timeout,
+	       gchar **tmp_stdout,
+	       gint   *exit_status)
+{
+	GError	    *error = NULL;
+	GSpawnFlags  flags;
+	gboolean     result;
+
+	g_return_val_if_fail (argv != NULL, FALSE);
+	g_return_val_if_fail (argv[0] != NULL, FALSE);
+	g_return_val_if_fail (timeout > 0, FALSE);
+
+	flags = G_SPAWN_SEARCH_PATH |
+		G_SPAWN_STDERR_TO_DEV_NULL;
+
+	if (!tmp_stdout) {
+		flags = flags | G_SPAWN_STDOUT_TO_DEV_NULL;
+	}
+
+	result = g_spawn_sync (NULL,
+			       argv,
+			       NULL,
+			       flags,
+			       tracker_spawn_child_func,
+			       GINT_TO_POINTER (timeout),
+			       tmp_stdout,
+			       NULL,
+			       exit_status,
+			       &error);
+
+	if (error) {
+		g_warning ("Could not spawn command:'%s', %s",
+			   argv[0],
+			   error->message);
+		g_error_free (error);
+	}
+
+	return result;
+}
+
+gboolean
+tracker_spawn_async_with_channels (const gchar **argv,
+				   gint		 timeout,
+				   GPid		*pid,
+				   GIOChannel  **stdin_channel,
+				   GIOChannel  **stdout_channel,
+				   GIOChannel  **stderr_channel)
+{
+	GError	 *error = NULL;
+	gboolean  result;
+	gint	  stdin, stdout, stderr;
+
+	g_return_val_if_fail (argv != NULL, FALSE);
+	g_return_val_if_fail (argv[0] != NULL, FALSE);
+	g_return_val_if_fail (timeout > 0, FALSE);
+	g_return_val_if_fail (pid != NULL, FALSE);
+
+	result = g_spawn_async_with_pipes (NULL,
+					   (gchar **) argv,
+					   NULL,
+					   G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
+					   tracker_spawn_child_func,
+					   GINT_TO_POINTER (timeout),
+					   pid,
+					   stdin_channel ? &stdin : NULL,
+					   stdout_channel ? &stdout : NULL,
+					   stderr_channel ? &stderr : NULL,
+					   &error);
+
+	if (error) {
+		g_warning ("Could not spawn command:'%s', %s",
+			   argv[0],
+			   error->message);
+		g_error_free (error);
+	}
+
+	if (stdin_channel) {
+		*stdin_channel = result ? g_io_channel_unix_new (stdin) : NULL;
+	}
+
+	if (stdout_channel) {
+		*stdout_channel = result ? g_io_channel_unix_new (stdout) : NULL;
+	}
+
+	if (stderr_channel) {
+		*stderr_channel = result ? g_io_channel_unix_new (stderr) : NULL;
+	}
+
+	return result;
+}
+
+void
+tracker_spawn_child_func (gpointer user_data)
+{
+	struct rlimit cpu_limit;
+	gint	      timeout = GPOINTER_TO_INT (user_data);
+
+	/* set cpu limit */
+	getrlimit (RLIMIT_CPU, &cpu_limit);
+	cpu_limit.rlim_cur = timeout;
+	cpu_limit.rlim_max = timeout + 1;
+
+	if (setrlimit (RLIMIT_CPU, &cpu_limit) != 0) {
+		g_critical ("Failed to set resource limit for CPU");
+	}
+
+	tracker_memory_setrlimits ();
+
+	/* Set child's niceness to 19 */
+	errno = 0;
+
+	/* nice() uses attribute "warn_unused_result" and so complains
+	 * if we do not check its returned value. But it seems that
+	 * since glibc 2.2.4, nice() can return -1 on a successful call
+	 * so we have to check value of errno too. Stupid...
+	 */
+	if (nice (19) == -1 && errno) {
+		g_warning ("Failed to set nice value");
+	}
+
+	/* Have this as a precaution in cases where cpu limit has not
+	 * been reached due to spawned app sleeping.
+	 */
+	alarm (timeout + 2);
+}
+
+gchar *
+tracker_create_permission_string (struct stat finfo)
+{
+	gchar *str;
+	gint   n, bit;
+
+	/* Create permissions string */
+	str = g_strdup ("?rwxrwxrwx");
+
+	switch (finfo.st_mode & S_IFMT) {
+		case S_IFSOCK: str[0] = 's'; break;
+		case S_IFIFO:  str[0] = 'p'; break;
+		case S_IFLNK:  str[0] = 'l'; break;
+		case S_IFCHR:  str[0] = 'c'; break;
+		case S_IFBLK:  str[0] = 'b'; break;
+		case S_IFDIR:  str[0] = 'd'; break;
+		case S_IFREG:  str[0] = '-'; break;
+	}
+
+	for (bit = 0400, n = 1; bit; bit >>= 1, ++n) {
+		if (!(finfo.st_mode & bit)) {
+			str[n] = '-';
+		}
+	}
+
+	if (finfo.st_mode & S_ISUID) {
+		str[3] = (finfo.st_mode & S_IXUSR) ? 's' : 'S';
+	}
+
+	if (finfo.st_mode & S_ISGID) {
+		str[6] = (finfo.st_mode & S_IXGRP) ? 's' : 'S';
+	}
+
+	if (finfo.st_mode & S_ISVTX) {
+		str[9] = (finfo.st_mode & S_IXOTH) ? 't' : 'T';
+	}
+
+	return str;
+}
+
+gboolean
+tracker_memory_setrlimits (void)
+{
+	struct rlimit rl;
+	gboolean      fail = FALSE;
+
+	/* We want to limit the max virtual memory
+	 * most extractors use mmap() so only virtual memory can be
+	 * effectively limited.
+	 */
+#ifdef __x86_64__
+	/* Many extractors on AMD64 require 512M of virtual memory, so
+	 * we limit heap too.
+	 */
+	getrlimit (RLIMIT_AS, &rl);
+	rl.rlim_cur = MAX_MEM_AMD64 * 1024 * 1024;
+	fail |= setrlimit (RLIMIT_AS, &rl);
+
+	getrlimit (RLIMIT_DATA, &rl);
+	rl.rlim_cur = MAX_MEM * 1024 * 1024;
+	fail |= setrlimit (RLIMIT_DATA, &rl);
+#else  /* __x86_64__ */
+	/* On other architectures, 128M of virtual memory seems to be
+	 * enough.
+	 */
+	getrlimit (RLIMIT_AS, &rl);
+	rl.rlim_cur = MAX_MEM * 1024 * 1024;
+	fail |= setrlimit (RLIMIT_AS, &rl);
+#endif /* __x86_64__ */
+
+	if (fail) {
+		g_critical ("Error trying to set memory limit");
+	}
+
+	return !fail;
+}

Added: trunk/src/libtracker-common/tracker-os-dependant-win.c
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-os-dependant-win.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,180 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <glib/gspawn.h>
+#include <glib/gstring.h>
+
+#include "mingw-compat.h"
+
+#include "tracker-log.h"
+#include "tracker-os-dependant.h"
+
+gboolean
+tracker_spawn (gchar **argv,
+	       gint    timeout,
+	       gchar **tmp_stdout,
+	       gint   *exit_status)
+{
+	GSpawnFlags   flags;
+	GError	     *error = NULL;
+	gchar	    **new_argv;
+	gboolean      result;
+	gint	      length;
+	gint	      i;
+
+	g_return_val_if_fail (argv != NULL, FALSE);
+	g_return_val_if_fail (argv[0] != NULL, FALSE);
+	g_return_val_if_fail (timeout > 0, FALSE);
+
+	length = g_strv_length (argv);
+
+	new_argv = g_new0 (gchar*, length + 3);
+
+	new_argv[0] = "cmd.exe";
+	new_argv[1] = "/c";
+
+	for (i = 0; argv[i]; i++) {
+		new_argv[i + 2] = argv[i];
+	}
+
+	flags = G_SPAWN_SEARCH_PATH |
+		G_SPAWN_STDERR_TO_DEV_NULL;
+
+	if (!tmp_stdout) {
+		flags |= G_SPAWN_STDOUT_TO_DEV_NULL;
+	}
+
+	result = g_spawn_sync (NULL,
+			       new_argv,
+			       NULL,
+			       flags,
+			       NULL,
+			       GINT_TO_POINTER (timeout),
+			       tmp_stdout,
+			       NULL,
+			       exit_status,
+			       &error);
+
+	if (error) {
+		g_warning ("Could not spawn command:'%s', %s",
+			   argv[0],
+			   error->message);
+		g_error_free (error);
+	}
+
+	g_strfreev (new_argv);
+
+	return result;
+}
+
+gboolean
+tracker_spawn_async_with_channels (const gchar **argv,
+				   gint		 timeout,
+				   GPid		*pid,
+				   GIOChannel  **stdin_channel,
+				   GIOChannel  **stdout_channel,
+				   GIOChannel  **strerr_channel)
+{
+	GError	 *error = NULL;
+	gboolean  result;
+	gint	  stdin, stdout, stderr;
+
+	g_return_val_if_fail (argv != NULL, FALSE);
+	g_return_val_if_fail (argv[0] != NULL, FALSE);
+	g_return_val_if_fail (timeout > 0, FALSE);
+	g_return_val_if_fail (pid != NULL, FALSE);
+
+	result = g_spawn_async_with_pipes (NULL,
+					   (gchar **) argv,
+					   NULL,
+					   G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
+					   tracker_spawn_child_func,
+					   GINT_TO_POINTER (timeout),
+					   pid,
+					   stdin_channel ? &stdin : NULL,
+					   stdout_channel ? &stdout : NULL,
+					   stderr_channel ? &stderr : NULL,
+					   &error);
+
+	if (error) {
+		g_warning ("Could not spawn command:'%s', %s",
+			   argv[0],
+			   error->message);
+		g_error_free (error);
+	}
+
+	if (stdin_channel) {
+		*stdin_channel = result ? g_io_channel_win32_new_fd (stdin) : NULL;
+	}
+
+	if (stdout_channel) {
+		*stdout_channel = result ? g_io_channel_win32_new_fd (stdout) : NULL;
+	}
+
+	if (stderr_channel) {
+		*stderr_channel = result ? g_io_channel_win32_new_fd (stderr) : NULL;
+	}
+
+	return result;
+}
+
+void
+tracker_spawn_child_func (gpointer user_data)
+{
+}
+
+gchar *
+tracker_create_permission_string (struct stat finfo)
+{
+	gchar *str;
+	gint   n, bit;
+
+	/* Create permissions string */
+	str = g_strdup ("?rwxrwxrwx");
+
+	for (bit = 0400, n = 1; bit; bit >>= 1, ++n) {
+		if (!(finfo.st_mode & bit)) {
+			str[n] = '-';
+		}
+	}
+
+	if (finfo.st_mode & S_ISUID) {
+		str[3] = (finfo.st_mode & S_IXUSR) ? 's' : 'S';
+	}
+
+	if (finfo.st_mode & S_ISGID) {
+		str[6] = (finfo.st_mode & S_IXGRP) ? 's' : 'S';
+	}
+
+	if (finfo.st_mode & S_ISVTX) {
+		str[9] = (finfo.st_mode & S_IXOTH) ? 't' : 'T';
+	}
+
+	return str;
+}
+
+gboolean
+tracker_memory_setrlimits (void)
+{
+	return TRUE;
+}

Added: trunk/src/libtracker-common/tracker-os-dependant.h
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-os-dependant.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,47 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __LIBTRACKER_COMMON_OS_DEPENDANT_H__
+#define __LIBTRACKER_COMMON_OS_DEPENDANT_H__
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+/* Process spawning */
+gboolean tracker_spawn			   (gchar	**argv,
+					    gint	  timeout,
+					    gchar	**tmp_stdout,
+					    gint	 *exit_status);
+gboolean tracker_spawn_async_with_channels (const gchar **argv,
+					    gint	  timeout,
+					    GPid	 *pid,
+					    GIOChannel	**stdin_channel,
+					    GIOChannel	**stdout_channel,
+					    GIOChannel	**stderr_channel);
+void	 tracker_spawn_child_func	   (gpointer	  user_data);
+
+/* File permissions */
+gchar *  tracker_create_permission_string  (struct stat   finfo);
+
+/* Memory limits */
+gboolean tracker_memory_setrlimits (void);
+
+#endif /* __LIBTRACKER_COMMON_OS_DEPENDANT_H__ */

Added: trunk/src/libtracker-common/tracker-parser.c
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-parser.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,1203 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "tracker-parser.h"
+#include "tracker-log.h"
+#include "tracker-utils.h"
+
+#define INDEX_NUMBER_MIN_LENGTH 6
+
+/* Need pango for CJK ranges which are : 0x3400 - 0x4DB5, 0x4E00 -
+ * 0x9FA5, 0x20000 - <= 0x2A6D6
+ */
+#define NEED_PANGO(c)		 (((c) >= 0x3400 && (c) <= 0x4DB5)  ||	\
+				  ((c) >= 0x4E00 && (c) <= 0x9FA5)  ||	\
+				  ((c) >= 0x20000 && (c) <= 0x2A6D6))
+#define IS_LATIN(c)		 (((c) <= 0x02AF) ||			\
+				  ((c) >= 0x1E00 && (c) <= 0x1EFF))
+#define IS_ASCII(c)		 ((c) <= 0x007F)
+#define IS_ASCII_ALPHA_LOWER(c)  ((c) >= 0x0061 && (c) <= 0x007A)
+#define IS_ASCII_ALPHA_HIGHER(c) ((c) >= 0x0041 && (c) <= 0x005A)
+#define IS_ASCII_NUMERIC(c)	 ((c) >= 0x0030 && (c) <= 0x0039)
+#define IS_ASCII_IGNORE(c)	 ((c) <= 0x002C)
+#define IS_HYPHEN(c)		 ((c) == 0x002D)
+#define IS_UNDERSCORE(c)	 ((c) == 0x005F)
+#define IS_NEWLINE(c)		 ((c) == 0x000D)
+#define IS_O(c)			 ((c) == 0x006F)
+#define IS_R(c)			 ((c) == 0x0072)
+
+typedef enum {
+	TRACKER_PARSER_WORD_ASCII_HIGHER,
+	TRACKER_PARSER_WORD_ASCII_LOWER,
+	TRACKER_PARSER_WORD_HYPHEN,
+	TRACKER_PARSER_WORD_UNDERSCORE,
+	TRACKER_PARSER_WORD_NUM,
+	TRACKER_PARSER_WORD_ALPHA_HIGHER,
+	TRACKER_PARSER_WORD_ALPHA_LOWER,
+	TRACKER_PARSER_WORD_ALPHA,
+	TRACKER_PARSER_WORD_ALPHA_NUM,
+	TRACKER_PARSER_WORD_IGNORE,
+	TRACKER_PARSER_WORD_NEWLINE
+} TrackerParserWordType;
+
+static inline TrackerParserWordType
+get_word_type (gunichar c)
+{
+	/* Fast ascii handling */
+	if (IS_ASCII (c)) {
+		if (IS_ASCII_ALPHA_LOWER (c)) {
+			return TRACKER_PARSER_WORD_ASCII_LOWER;
+		}
+
+		if (IS_ASCII_ALPHA_HIGHER (c)) {
+			return TRACKER_PARSER_WORD_ASCII_HIGHER;
+		}
+
+		if (IS_ASCII_IGNORE (c)) {
+			return TRACKER_PARSER_WORD_IGNORE;
+		}
+
+		if (IS_ASCII_NUMERIC (c)) {
+			return TRACKER_PARSER_WORD_NUM;
+		}
+
+		if (IS_HYPHEN (c)) {
+			return TRACKER_PARSER_WORD_HYPHEN;
+		}
+
+		if (IS_UNDERSCORE (c)) {
+			return TRACKER_PARSER_WORD_UNDERSCORE;
+		}
+
+		if (IS_NEWLINE (c)) {
+			return TRACKER_PARSER_WORD_NEWLINE;
+		}
+	} else {
+		if (g_unichar_isalpha (c)) {
+			if (!g_unichar_isupper (c)) {
+				return TRACKER_PARSER_WORD_ALPHA_LOWER;
+			} else {
+				return TRACKER_PARSER_WORD_ALPHA_HIGHER;
+			}
+		} else if (g_unichar_isdigit (c)) {
+			return TRACKER_PARSER_WORD_NUM;
+		}
+	}
+
+	return TRACKER_PARSER_WORD_IGNORE;
+}
+
+static inline gchar *
+strip_word (const gchar *str,
+	    gint	 length,
+	    guint32	*len)
+{
+#ifdef HAVE_UNAC
+	gchar *s = NULL;
+
+	if (unac_string ("UTF-8", str, length, &s, &*len) != 0) {
+		g_warning ("UNAC failed to strip accents");
+	}
+
+	return s;
+#else
+	*len = length;
+	return NULL;
+#endif
+}
+
+static gboolean
+text_needs_pango (const gchar *text)
+{
+	const gchar *p;
+	gunichar     c;
+	gint	     i = 0;
+
+	/* Grab first 1024 non-whitespace chars and test */
+	for (p = text; *p && i < 1024; p = g_utf8_next_char (p)) {
+		c = g_utf8_get_char (p);
+
+		if (!g_unichar_isspace (c)) {
+			i++;
+		}
+
+		if (NEED_PANGO(c)) {
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+static TrackerParserEncoding
+get_encoding (const gchar *txt)
+{
+	const gchar *p;
+	gunichar     c;
+	gint	     i = 0;
+
+	/* Grab first 255 non-whitespace chars and test */
+	for (p = txt; *p && i < 255; p = g_utf8_next_char (p)) {
+		c = g_utf8_get_char (p);
+
+		if (!g_unichar_isspace (c)) {
+			i++;
+		}
+
+		if (IS_ASCII(c)) continue;
+
+		if (IS_LATIN(c)) return TRACKER_PARSER_ENCODING_LATIN;
+
+		if (NEED_PANGO(c)) return TRACKER_PARSER_ENCODING_CJK;
+
+		return TRACKER_PARSER_ENCODING_OTHER;
+	}
+
+	return TRACKER_PARSER_ENCODING_ASCII;
+
+}
+
+static gboolean
+is_stop_word (TrackerLanguage *language,
+	      const gchar     *word)
+{
+	GHashTable *stop_words;
+
+	if (!word) {
+		return FALSE;
+	}
+
+	stop_words = tracker_language_get_stop_words (language);
+
+	return g_hash_table_lookup (stop_words, word) != NULL;
+}
+
+static const gchar *
+analyze_text (const gchar      *text,
+	      TrackerLanguage  *language,
+	      gint		max_word_length,
+	      gint		min_word_length,
+	      gboolean		filter_words,
+	      gboolean		filter_numbers,
+	      gboolean		delimit_hyphen,
+	      gchar	      **index_word)
+{
+	TrackerParserWordType word_type;
+	gunichar	      word[64];
+	gboolean	      do_strip;
+	gboolean	      is_valid;
+	gint		      length;
+	glong		      bytes;
+	const char	     *p;
+	const char	     *start;
+
+	*index_word = NULL;
+
+	if (text == NULL || text[0] == '\0') {
+		return NULL;
+	}
+
+	word_type = TRACKER_PARSER_WORD_IGNORE;
+	do_strip = FALSE;
+	is_valid = TRUE;
+	length = 0;
+	bytes = 0;
+	start = NULL;
+
+	for (p = text; *p; p = g_utf8_next_char (p)) {
+		TrackerParserWordType type;
+		gunichar	      c;
+
+		c = g_utf8_get_char (p);
+		type = get_word_type (c);
+
+		if (type == TRACKER_PARSER_WORD_IGNORE ||
+		    type == TRACKER_PARSER_WORD_NEWLINE ||
+		    (delimit_hyphen &&
+		     (type == TRACKER_PARSER_WORD_HYPHEN ||
+		      type == TRACKER_PARSER_WORD_UNDERSCORE))) {
+			if (!start) {
+				continue;
+			} else {
+				break;
+			}
+		}
+
+		if (!is_valid) {
+			continue;
+		}
+
+		if (!start) {
+			start = p;
+
+			/* Valid words must start with an alpha or
+			 * underscore if we are filtering.
+			 */
+			if (filter_numbers) {
+				if (type == TRACKER_PARSER_WORD_NUM) {
+					is_valid = FALSE;
+					continue;
+				} else {
+					if (type == TRACKER_PARSER_WORD_HYPHEN) {
+						is_valid = FALSE;
+						continue;
+					}
+				}
+			}
+		}
+
+		if (length >= max_word_length) {
+			continue;
+		}
+
+		length++;
+
+		switch (type) {
+		case TRACKER_PARSER_WORD_ASCII_HIGHER:
+			c += 32;
+
+		case TRACKER_PARSER_WORD_ASCII_LOWER:
+		case TRACKER_PARSER_WORD_HYPHEN:
+		case TRACKER_PARSER_WORD_UNDERSCORE:
+			if (word_type == TRACKER_PARSER_WORD_NUM ||
+			    word_type == TRACKER_PARSER_WORD_ALPHA_NUM) {
+				word_type = TRACKER_PARSER_WORD_ALPHA_NUM;
+			} else {
+				word_type = TRACKER_PARSER_WORD_ALPHA;
+			}
+
+			break;
+
+		case TRACKER_PARSER_WORD_NUM:
+			if (word_type == TRACKER_PARSER_WORD_ALPHA ||
+			    word_type == TRACKER_PARSER_WORD_ALPHA_NUM) {
+				word_type = TRACKER_PARSER_WORD_ALPHA_NUM;
+			} else {
+				word_type = TRACKER_PARSER_WORD_NUM;
+			}
+			break;
+
+		case TRACKER_PARSER_WORD_ALPHA_HIGHER:
+			c = g_unichar_tolower (c);
+
+		case TRACKER_PARSER_WORD_ALPHA_LOWER:
+			if (!do_strip) {
+				do_strip = TRUE;
+			}
+
+			if (word_type == TRACKER_PARSER_WORD_NUM ||
+			    word_type == TRACKER_PARSER_WORD_ALPHA_NUM) {
+				word_type = TRACKER_PARSER_WORD_ALPHA_NUM;
+			} else {
+				word_type = TRACKER_PARSER_WORD_ALPHA;
+			}
+
+			break;
+
+		default:
+			break;
+		}
+
+		word[length -1] = c;
+	}
+
+	if (!is_valid) {
+		return p;
+	}
+
+	if (word_type == TRACKER_PARSER_WORD_NUM) {
+		if (!filter_numbers || length >= INDEX_NUMBER_MIN_LENGTH) {
+			*index_word = g_ucs4_to_utf8 (word, length, NULL, NULL, NULL);
+		}
+	} else if (length >= min_word_length) {
+		const gchar *stem_word;
+		gchar	    *stripped_word;
+		gchar	    *str;
+		gchar	    *utf8;
+		guint32      len;
+
+		utf8 = g_ucs4_to_utf8 (word, length, NULL, &bytes, NULL);
+
+		if (!utf8) {
+			return p;
+		}
+
+		if (do_strip && get_encoding (utf8) == TRACKER_PARSER_ENCODING_LATIN) {
+			stripped_word = strip_word (utf8, bytes, &len);
+		} else {
+			stripped_word = NULL;
+		}
+
+		if (!stripped_word) {
+			str = g_utf8_normalize (utf8,
+						bytes,
+						G_NORMALIZE_NFC);
+		} else {
+			str = g_utf8_normalize (stripped_word,
+						len,
+						G_NORMALIZE_NFC);
+			g_free (stripped_word);
+		}
+
+		g_free (utf8);
+
+		stem_word = tracker_language_stem_word (language,
+							str,
+							strlen (str));
+		g_free (str);
+
+		if (!filter_words || !is_stop_word (language, stem_word)) {
+			*index_word = g_strdup (stem_word);
+		}
+	}
+
+	return p;
+}
+
+static gboolean
+pango_next (TrackerParser *parser,
+	    gint	  *byte_offset_start,
+	    gint	  *byte_offset_end,
+	    gboolean	  *is_new_paragraph)
+
+{
+	/* CJK text does not need stemming or other treatment */
+	gint	word_start = -1;
+	gint	old_word_start = -1;
+	guint	i;
+
+	*is_new_paragraph = FALSE;
+
+	for (i = parser->attr_pos; i < parser->attr_length; i++) {
+		if (parser->attrs[i].is_word_start) {
+			word_start = i;
+			continue;
+		}
+
+		if (parser->attrs[i].is_word_end && word_start != old_word_start) {
+			gchar *start_word, *end_word;
+
+			old_word_start = word_start;
+
+			start_word = g_utf8_offset_to_pointer (parser->txt, word_start);
+			end_word = g_utf8_offset_to_pointer (parser->txt, i);
+
+			if (start_word != end_word) {
+				gchar *str;
+				gchar *index_word;
+
+				/* Normalize word */
+				str = g_utf8_casefold (start_word, end_word - start_word);
+				if (!str) {
+					continue;
+				}
+
+				index_word = g_utf8_normalize (str, -1, G_NORMALIZE_NFC);
+				g_free (str);
+
+				if (!index_word) {
+					continue;
+				}
+
+				if (word_start > 1 && parser->attrs[word_start -1].is_sentence_boundary) {
+					*is_new_paragraph = TRUE;
+				}
+
+				parser->word_length = strlen (index_word);
+				parser->word = index_word;
+
+				*byte_offset_start = (start_word - parser->txt);
+				*byte_offset_end = *byte_offset_start + (end_word - start_word);
+				parser->attr_pos = i;
+
+
+				return TRUE;
+
+			}
+
+			word_start = i;
+		}
+	}
+
+	parser->attr_pos = i;
+
+	return FALSE;
+}
+
+
+static gboolean
+parser_next (TrackerParser *parser,
+	     gint	   *byte_offset_start,
+	     gint	   *byte_offset_end,
+	     gboolean	   *is_new_paragraph)
+{
+	TrackerParserWordType word_type;
+	gunichar	      word[64];
+	gboolean	      is_valid;
+	guint		      length;
+	gint		      char_count = 0;
+	glong		      bytes;
+	const gchar	     *p;
+	const gchar	     *start;
+	const gchar	     *end;
+	gboolean	      do_strip = FALSE;
+
+	*byte_offset_start = 0;
+	*byte_offset_end = 0;
+	*is_new_paragraph = FALSE;
+
+	g_return_val_if_fail (parser, FALSE);
+
+	if (!parser->cursor) {
+		return FALSE;
+	}
+
+	word_type = TRACKER_PARSER_WORD_IGNORE;
+	is_valid = TRUE;
+	length = 0;
+	bytes = 0;
+
+	start = NULL;
+	end = NULL;
+
+	for (p = parser->cursor; *p; p = g_utf8_next_char (p)) {
+		TrackerParserWordType type;
+		gunichar	      c;
+
+		char_count++;
+		c = g_utf8_get_char (p);
+		type = get_word_type (c);
+
+		if (type == TRACKER_PARSER_WORD_NEWLINE) {
+			*is_new_paragraph = TRUE;
+		}
+
+		if (type == TRACKER_PARSER_WORD_IGNORE || type == TRACKER_PARSER_WORD_NEWLINE ||
+		    (parser->delimit_words &&
+		     (type == TRACKER_PARSER_WORD_HYPHEN ||
+		      type == TRACKER_PARSER_WORD_UNDERSCORE))) {
+			if (!start) {
+				continue;
+			} else {
+				/* word break */
+
+				/* check if word is reserved */
+				if (is_valid && parser->parse_reserved_words) {
+					if (length == 2 && word[0] == 'o' && word[1] == 'r') {
+						break;
+					}
+				}
+
+				if (!is_valid ||
+				    length < parser->min_word_length ||
+				    word_type == TRACKER_PARSER_WORD_NUM) {
+					*is_new_paragraph = FALSE;
+
+					word_type = TRACKER_PARSER_WORD_IGNORE;
+					is_valid = TRUE;
+					length = 0;
+					bytes = 0;
+					start = NULL;
+					end = NULL;
+					do_strip = FALSE;
+
+					continue;
+				}
+
+				break;
+			}
+		}
+
+		if (!is_valid) {
+			continue;
+		}
+
+		if (!start) {
+			start = g_utf8_offset_to_pointer (parser->cursor, char_count-1);
+
+			/* Valid words must start with an alpha or
+			 * underscore if we are filtering.
+			 */
+
+			if (type == TRACKER_PARSER_WORD_NUM) {
+				is_valid = FALSE;
+				start = NULL;
+				continue;
+			} else {
+				if (type == TRACKER_PARSER_WORD_HYPHEN) {
+					is_valid = parser->parse_reserved_words;
+					start = NULL;
+					continue;
+				}
+			}
+		}
+
+		if (length >= parser->max_word_length) {
+			continue;
+		}
+
+		length++;
+
+		switch (type) {
+		case TRACKER_PARSER_WORD_ASCII_HIGHER:
+			c += 32;
+
+		case TRACKER_PARSER_WORD_ASCII_LOWER:
+		case TRACKER_PARSER_WORD_HYPHEN:
+		case TRACKER_PARSER_WORD_UNDERSCORE:
+			if (word_type == TRACKER_PARSER_WORD_NUM ||
+			    word_type == TRACKER_PARSER_WORD_ALPHA_NUM) {
+				word_type = TRACKER_PARSER_WORD_ALPHA_NUM;
+			} else {
+				word_type = TRACKER_PARSER_WORD_ALPHA;
+			}
+
+			break;
+
+		case TRACKER_PARSER_WORD_NUM:
+			if (word_type == TRACKER_PARSER_WORD_ALPHA ||
+			    word_type == TRACKER_PARSER_WORD_ALPHA_NUM) {
+				word_type = TRACKER_PARSER_WORD_ALPHA_NUM;
+			} else {
+				word_type = TRACKER_PARSER_WORD_NUM;
+			}
+			break;
+
+		case TRACKER_PARSER_WORD_ALPHA_HIGHER:
+			c = g_unichar_tolower (c);
+
+		case TRACKER_PARSER_WORD_ALPHA_LOWER:
+			if (!do_strip) {
+				do_strip = TRUE;
+			}
+
+			if (word_type == TRACKER_PARSER_WORD_NUM ||
+			    word_type == TRACKER_PARSER_WORD_ALPHA_NUM) {
+				word_type = TRACKER_PARSER_WORD_ALPHA_NUM;
+			} else {
+				word_type = TRACKER_PARSER_WORD_ALPHA;
+			}
+
+			break;
+
+		default:
+			break;
+		}
+
+		word[length -1] = c;
+	}
+
+	parser->cursor = NULL;
+
+	if (!is_valid) {
+		return FALSE;
+	}
+
+	if (word_type == TRACKER_PARSER_WORD_ALPHA_NUM || word_type == TRACKER_PARSER_WORD_ALPHA) {
+		gchar	    *utf8;
+		gchar	    *processed_word;
+
+
+
+		utf8 = g_ucs4_to_utf8 (word, length, NULL, &bytes, NULL);
+
+		if (!utf8) {
+			return FALSE;
+		}
+
+		*byte_offset_start = start-parser->txt;
+		*byte_offset_end = *byte_offset_start + bytes;
+
+		parser->cursor = g_utf8_next_char (parser->txt + *byte_offset_end);
+
+		processed_word = tracker_parser_process_word (parser, utf8, bytes, do_strip);
+
+		g_free (utf8);
+
+		parser->word_length = strlen (processed_word);
+		parser->word = processed_word;
+
+		return TRUE;
+
+	}
+
+	return FALSE;
+
+}
+
+static gboolean
+word_table_increment (GHashTable *word_table,
+		      gchar	 *index_word,
+		      gint	  weight,
+		      gint	  total_words,
+		      gint	  max_words_to_index)
+{
+	gboolean update_count;
+
+	update_count = total_words <= max_words_to_index;
+
+	if (update_count) {
+		gpointer p;
+		gint	 count;
+
+		p = g_hash_table_lookup (word_table, index_word);
+		count = GPOINTER_TO_INT (p);
+
+		g_hash_table_replace (word_table,
+				      index_word,
+				      GINT_TO_POINTER (count + weight));
+	} else {
+		g_free (index_word);
+	}
+
+	return update_count;
+}
+
+TrackerParser *
+tracker_parser_new (TrackerLanguage *language,
+		    gint	     max_word_length,
+		    gint	     min_word_length)
+{
+	TrackerParser *parser;
+
+	g_return_val_if_fail (TRACKER_IS_LANGUAGE (language), NULL);
+	g_return_val_if_fail (min_word_length > 0, NULL);
+	g_return_val_if_fail (min_word_length < max_word_length, NULL);
+
+	parser = g_new0 (TrackerParser, 1);
+
+	parser->language = g_object_ref (language);
+
+	parser->max_word_length = max_word_length;
+	parser->min_word_length = min_word_length;
+	parser->word_length = 0;
+	parser->attrs = NULL;
+
+	return parser;
+}
+
+void
+tracker_parser_free (TrackerParser *parser)
+{
+	g_return_if_fail (parser != NULL);
+
+	if (parser->language) {
+		g_object_unref (parser->language);
+	}
+
+	g_free (parser->attrs);
+
+	g_free (parser->word);
+
+	g_free (parser);
+}
+
+void
+tracker_parser_reset (TrackerParser *parser,
+		      const gchar   *txt,
+		      gint	     txt_size,
+		      gboolean	     delimit_words,
+		      gboolean	     enable_stemmer,
+		      gboolean	     enable_stop_words,
+		      gboolean	     parse_reserved_words)
+{
+	g_return_if_fail (parser != NULL);
+	g_return_if_fail (txt != NULL);
+
+	g_free (parser->attrs);
+	parser->attrs = NULL;
+
+	parser->enable_stemmer = enable_stemmer;
+	parser->enable_stop_words = enable_stop_words;
+	parser->delimit_words = delimit_words;
+	parser->encoding = get_encoding (txt);
+	parser->txt_size = txt_size;
+	parser->txt = txt;
+	parser->parse_reserved_words = parse_reserved_words;
+
+	g_free (parser->word);
+	parser->word = NULL;
+
+	parser->word_position = 0;
+
+	parser->cursor = txt;
+
+	if (parser->encoding == TRACKER_PARSER_ENCODING_CJK) {
+		PangoLogAttr *attrs;
+
+		if (parser->txt_size == -1) {
+			parser->txt_size = strlen (parser->txt);
+		}
+
+		parser->attr_length = g_utf8_strlen (parser->txt, parser->txt_size) + 1;
+
+		attrs = g_new0 (PangoLogAttr, parser->attr_length);
+
+		pango_get_log_attrs (parser->txt,
+				     txt_size,
+				     0,
+				     pango_language_from_string ("C"),
+				     attrs,
+				     parser->attr_length);
+
+		parser->attrs = attrs;
+		parser->attr_pos = 0;
+	}
+}
+
+gchar *
+tracker_parser_process_word (TrackerParser *parser,
+			     const char    *word,
+			     gint	    length,
+			     gboolean	    do_strip)
+{
+	const gchar *stem_word;
+	gchar	    *str;
+	gchar	    *stripped_word;
+	guint	     bytes, len;
+
+	g_return_val_if_fail (parser != NULL, NULL);
+	g_return_val_if_fail (word != NULL, NULL);
+
+	str = NULL;
+	stripped_word = NULL;
+
+	if (word) {
+		if (length == -1) {
+			bytes = strlen (word);
+		} else {
+			bytes = length;
+		}
+
+		if (do_strip) {
+			stripped_word = strip_word (word, bytes, &len);
+		} else {
+			stripped_word = NULL;
+		}
+
+		if (!stripped_word) {
+			str = g_utf8_normalize (word,
+						bytes,
+						G_NORMALIZE_NFC);
+		} else {
+			str = g_utf8_normalize (stripped_word,
+						len,
+						G_NORMALIZE_NFC);
+			g_free (stripped_word);
+		}
+
+		if (!parser->enable_stemmer) {
+			return str;
+		}
+
+		len = strlen (str);
+
+		stem_word = tracker_language_stem_word (parser->language, str, len);
+
+		if (stem_word) {
+			gchar *result;
+
+			result = g_strdup (stem_word);
+			g_free (str);
+
+			return result;
+		}
+	}
+
+	return str;
+}
+
+gboolean
+tracker_parser_is_stop_word (TrackerParser *parser, const gchar *word)
+{
+	if (get_encoding (word) == TRACKER_PARSER_ENCODING_CJK) return FALSE;
+
+
+	char *processed_word = tracker_parser_process_word (parser, word, -1, TRUE);
+	gboolean result = is_stop_word (parser->language, processed_word);
+	g_free (processed_word);
+	return result;
+}
+
+const gchar *
+tracker_parser_next (TrackerParser *parser,
+		     gint	   *position,
+		     gint	   *byte_offset_start,
+		     gint	   *byte_offset_end,
+		     gboolean	   *new_paragraph,
+		     gboolean	   *stop_word,
+		     gint	   *word_length)
+{
+	const gchar  *str;
+	gint	 byte_start = 0, byte_end = 0;
+	gboolean  new_para;
+
+	str = NULL;
+
+	g_free (parser->word);
+	parser->word = NULL;
+
+	if (parser->encoding == TRACKER_PARSER_ENCODING_CJK) {
+		if (pango_next (parser, &byte_start, &byte_end, &new_para)) {
+			str = parser->word;
+		}
+		parser->word_position++;
+
+		*stop_word = FALSE;
+	} else {
+		if (parser_next (parser, &byte_start, &byte_end, &new_para)) {
+			str = parser->word;
+		}
+
+		if (parser->enable_stop_words && is_stop_word (parser->language, str)) {
+			*stop_word = TRUE;
+		} else {
+			parser->word_position++;
+			*stop_word = FALSE;
+		}
+	}
+
+	*word_length = parser->word_length;
+	*position = parser->word_position;
+	*byte_offset_start = byte_start;
+	*byte_offset_end = byte_end;
+	*new_paragraph = new_para;
+
+	return str;
+}
+
+
+gchar *
+tracker_parser_text_to_string (const gchar     *text,
+			       TrackerLanguage *language,
+			       gint		max_word_length,
+			       gint		min_word_length,
+			       gboolean		filter_words,
+			       gboolean		filter_numbers,
+			       gboolean		delimit)
+{
+	const gchar *p;
+	gchar	    *parsed_text;
+	guint32      i = 0;
+	gint	     len;
+
+	g_return_val_if_fail (TRACKER_IS_LANGUAGE (language), NULL);
+
+	if (text == NULL || text[0] == '\0') {
+		return NULL;
+	}
+
+	p = text;
+	len = strlen (text);
+	len = MIN (len, 500);
+
+	if (!g_utf8_validate (text, len, NULL)) {
+		return NULL;
+	}
+
+	if (text_needs_pango (text)) {
+		/* CJK text does not need stemming or other
+		 * treatment.
+		 */
+		PangoLogAttr *attrs;
+		guint	      str_len, word_start;
+		GString	     *strs;
+
+		str_len = g_utf8_strlen (text, -1);
+
+		strs = g_string_new (" ");
+
+		attrs = g_new0 (PangoLogAttr, str_len + 1);
+
+		pango_get_log_attrs (text,
+				     len,
+				     0,
+				     pango_language_from_string ("C"),
+				     attrs,
+				     str_len + 1);
+
+		word_start = 0;
+
+		for (i = 0; i < str_len + 1; i++) {
+			if (attrs[i].is_word_end) {
+				gchar *start_word, *end_word;
+
+				start_word = g_utf8_offset_to_pointer (text, word_start);
+				end_word = g_utf8_offset_to_pointer (text, i);
+
+				if (start_word != end_word) {
+					/* Normalize word */
+					gchar *s;
+					gchar *index_word;
+
+					s = g_utf8_casefold (start_word, end_word - start_word);
+					index_word  = g_utf8_normalize (s, -1, G_NORMALIZE_NFC);
+					g_free (s);
+
+					strs = g_string_append (strs, index_word);
+					strs = g_string_append_c (strs, ' ');
+					g_free (index_word);
+				}
+
+				word_start = i;
+			}
+
+			if (attrs[i].is_word_start) {
+				word_start = i;
+			}
+		}
+
+		g_free (attrs);
+
+		parsed_text = g_string_free (strs, FALSE);
+		return g_strstrip (parsed_text);
+	} else {
+		GString *str;
+		gchar	*word;
+
+		str = g_string_new (" ");
+
+		while (TRUE) {
+			i++;
+			p = analyze_text (p,
+					  language,
+					  max_word_length,
+					  min_word_length,
+					  filter_words,
+					  filter_numbers,
+					  delimit,
+					  &word);
+
+			if (word) {
+				g_string_append (str, word);
+				g_string_append_c (str, ' ');
+				g_free (word);
+			}
+
+			if (!p || !*p) {
+				parsed_text = g_string_free (str, FALSE);
+				return g_strstrip (parsed_text);
+			}
+		}
+
+		g_string_free (str, TRUE);
+	}
+
+	return NULL;
+}
+
+gchar **
+tracker_parser_text_into_array (const gchar	*text,
+				TrackerLanguage *language,
+				gint		 max_word_length,
+				gint		 min_word_length)
+{
+	gchar  *s;
+	gchar **strv;
+
+	g_return_val_if_fail (TRACKER_IS_LANGUAGE (language), NULL);
+
+	s = tracker_parser_text_to_string (text,
+					   language,
+					   max_word_length,
+					   min_word_length,
+					   TRUE,
+					   FALSE,
+					   FALSE);
+	strv = g_strsplit (g_strstrip (s), " ", -1);
+	g_free (s);
+
+	return strv;
+}
+
+GHashTable *
+tracker_parser_text_fast (GHashTable  *word_table,
+			  const gchar *txt,
+			  gint	       weight)
+{
+	gchar **array;
+	gchar **p;
+
+	/* Use this for already processed text only */
+	if (!word_table) {
+		word_table = g_hash_table_new_full (g_str_hash,
+						    g_str_equal,
+						    g_free,
+						    NULL);
+	}
+
+	if (!txt || weight == 0) {
+		return word_table;
+	}
+
+	array = g_strsplit (txt, " ", -1);
+	if (!array) {
+		return word_table;
+	}
+
+	for (p = array; *p; p++) {
+		word_table_increment (word_table, *p, weight, 0, 0);
+	}
+
+	g_free (array);
+
+	return word_table;
+}
+
+GHashTable *
+tracker_parser_text (GHashTable      *word_table,
+		     const gchar     *text,
+		     gint	      weight,
+		     TrackerLanguage *language,
+		     gint	      max_words_to_index,
+		     gint	      max_word_length,
+		     gint	      min_word_length,
+		     gboolean	      filter_words,
+		     gboolean	      delimit_words)
+{
+	const gchar *p;
+	guint32      i;
+
+	/* Use this for unprocessed raw text */
+	gint	     total_words;
+
+	g_return_val_if_fail (TRACKER_IS_LANGUAGE (language), NULL);
+
+	if (!word_table) {
+		word_table = g_hash_table_new_full (g_str_hash,
+						    g_str_equal,
+						    g_free,
+						    NULL);
+		total_words = 0;
+	} else {
+		total_words = g_hash_table_size (word_table);
+	}
+
+	if (text == NULL || text[0] == '\0' || weight == 0) {
+		return word_table;
+	}
+
+	p = text;
+	i = 0;
+
+	if (text_needs_pango (text)) {
+		/* CJK text does not need stemming or other treatment */
+		PangoLogAttr *attrs;
+		guint	      len, str_len, word_start;
+
+		len = strlen (text);
+		str_len = g_utf8_strlen (text, -1);
+
+		attrs = g_new0 (PangoLogAttr, str_len + 1);
+
+		pango_get_log_attrs (text,
+				     len,
+				     0,
+				     pango_language_from_string ("C"),
+				     attrs,
+				     str_len + 1);
+
+		word_start = 0;
+
+		for (i = 0; i < str_len + 1; i++) {
+			if (attrs[i].is_word_end) {
+				gchar *start_word, *end_word;
+
+				start_word = g_utf8_offset_to_pointer (text, word_start);
+				end_word = g_utf8_offset_to_pointer (text, i);
+
+				if (start_word != end_word) {
+					gchar	 *str;
+					gchar	 *index_word;
+					gboolean  was_updated;
+
+					/* Normalize word */
+					str = g_utf8_casefold (start_word, end_word - start_word);
+					if (!str) {
+						continue;
+					}
+
+					index_word = g_utf8_normalize (str, -1, G_NORMALIZE_NFC);
+					g_free (str);
+
+					if (!index_word) {
+						continue;
+					}
+
+					total_words++;
+					was_updated = word_table_increment (word_table,
+									    index_word,
+									    weight,
+									    total_words,
+									    max_words_to_index);
+
+					if (!was_updated) {
+						break;
+					}
+				}
+
+				word_start = i;
+			}
+
+			if (attrs[i].is_word_start) {
+				word_start = i;
+			}
+		}
+
+		g_free (attrs);
+	} else {
+		gchar *word;
+
+		while (TRUE) {
+			i++;
+			p = analyze_text (p,
+					  language,
+					  max_word_length,
+					  min_word_length,
+					  filter_words,
+					  filter_words,
+					  delimit_words,
+					  &word);
+
+			if (word) {
+				total_words++;
+
+				if (!word_table_increment (word_table,
+							   word,
+							   weight,
+							   total_words,
+							   max_words_to_index)) {
+					break;
+				}
+			}
+
+			if (!p || !*p) {
+				break;
+			}
+		}
+	}
+
+	return word_table;
+}

Added: trunk/src/libtracker-common/tracker-parser.h
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-parser.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,135 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKERD_PARSER_H__
+#define __TRACKERD_PARSER_H__
+
+#include <glib.h>
+#include <pango/pango.h>
+
+#include "tracker-language.h"
+
+G_BEGIN_DECLS
+
+typedef enum {
+	TRACKER_PARSER_ENCODING_ASCII,
+	TRACKER_PARSER_ENCODING_LATIN,
+	TRACKER_PARSER_ENCODING_CJK,
+	TRACKER_PARSER_ENCODING_OTHER
+} TrackerParserEncoding;
+
+typedef struct {
+	const gchar	      *txt;
+	gint		       txt_size;
+
+	TrackerLanguage       *language;
+	gboolean	       enable_stemmer;
+	gboolean	       enable_stop_words;
+	guint		       max_words_to_index;
+	guint		       max_word_length;
+	guint		       min_word_length;
+	gboolean	       delimit_words;
+	gboolean	       parse_reserved_words;
+
+	/* Private members */
+	gchar			*word;
+	gint			word_length;
+	guint			word_position;
+	TrackerParserEncoding	encoding;
+	const gchar		*cursor;
+
+	/* Pango members for CJK text parsing */
+	PangoLogAttr	      *attrs;
+	guint		       attr_length;
+	guint		       attr_pos;
+} TrackerParser;
+
+TrackerParser *tracker_parser_new	      (TrackerLanguage *language,
+					       gint		max_word_length,
+					       gint		min_word_length);
+void	       tracker_parser_reset	      (TrackerParser   *parser,
+					       const gchar     *txt,
+					       gint		txt_size,
+					       gboolean		delimit_words,
+					       gboolean		enable_stemmer,
+					       gboolean		enable_stop_words,
+					       gboolean		parse_reserved_words);
+const gchar *  tracker_parser_next	      (TrackerParser   *parser,
+					       gint	       *position,
+					       gint	       *byte_offset_start,
+					       gint	       *byte_offset_end,
+					       gboolean        *new_paragraph,
+					       gboolean        *stop_word,
+					       gint	       *word_length);
+void	       tracker_parser_set_posititon   (TrackerParser   *parser,
+					       gint		position);
+gboolean       tracker_parser_is_stop_word    (TrackerParser   *parser,
+					       const gchar     *word);
+gchar *        tracker_parser_process_word    (TrackerParser   *parser,
+					       const char      *word,
+					       gint		length,
+					       gboolean		do_strip);
+void	       tracker_parser_free	      (TrackerParser   *parser);
+
+
+/*
+ * Functions to parse supplied text and break into individual words and
+ * maintain a count of no of occurences of the word multiplied by a
+ * "weight" factor.
+ *
+ * The word_table - can be NULL. It contains the accumulated parsed words
+ * with weighted word counts for the text (useful for indexing stuff
+ * line by line)
+ *
+ *   text   - the text to be parsed
+ *   weight - used to multiply the count of a word's occurance to create
+ *	      a weighted rank score
+ *
+ * Returns the word_table.
+ */
+GHashTable *   tracker_parser_text	      (GHashTable      *word_table,
+					       const gchar     *txt,
+					       gint		weight,
+					       TrackerLanguage *language,
+					       gint		max_words_to_index,
+					       gint		max_word_length,
+					       gint		min_word_length,
+					       gboolean		filter_words,
+					       gboolean		delimit_words);
+GHashTable *   tracker_parser_text_fast       (GHashTable      *word_table,
+					       const char      *txt,
+					       gint		weight);
+gchar *        tracker_parser_text_to_string  (const gchar     *txt,
+					       TrackerLanguage *language,
+					       gint		max_word_length,
+					       gint		min_word_length,
+					       gboolean		filter_words,
+					       gboolean		filter_numbers,
+					       gboolean		delimit);
+gchar **       tracker_parser_text_into_array (const gchar     *text,
+					       TrackerLanguage *language,
+					       gint		max_word_length,
+					       gint		min_word_length);
+
+
+G_END_DECLS
+
+#endif /* __TRACKERD_PARSER_H__ */

Added: trunk/src/libtracker-common/tracker-service.c
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-service.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,847 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <glib.h>
+
+#include "tracker-service.h"
+
+#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TRACKER_TYPE_SERVICE, TrackerServicePriv))
+
+typedef struct _TrackerServicePriv TrackerServicePriv;
+
+struct _TrackerServicePriv {
+	gint	       id;
+
+	gchar	      *name;
+	gchar	      *parent;
+
+	gchar	      *property_prefix;
+	gchar	      *content_metadata;
+	GSList	      *key_metadata;
+
+	TrackerDBType  db_type;
+
+	gboolean       enabled;
+	gboolean       embedded;
+
+	gboolean       has_metadata;
+	gboolean       has_full_text;
+	gboolean       has_thumbs;
+
+	gboolean       show_service_files;
+	gboolean       show_service_directories;
+};
+
+static void service_finalize	 (GObject      *object);
+static void service_get_property (GObject      *object,
+				  guint		param_id,
+				  GValue       *value,
+				  GParamSpec   *pspec);
+static void service_set_property (GObject      *object,
+				  guint		param_id,
+				  const GValue *value,
+				  GParamSpec   *pspec);
+
+enum {
+	PROP_0,
+	PROP_ID,
+	PROP_NAME,
+	PROP_PARENT,
+	PROP_PROPERTY_PREFIX,
+	PROP_CONTENT_METADATA,
+	PROP_KEY_METADATA,
+	PROP_DB_TYPE,
+	PROP_ENABLED,
+	PROP_EMBEDDED,
+	PROP_HAS_METADATA,
+	PROP_HAS_FULL_TEXT,
+	PROP_HAS_THUMBS,
+	PROP_SHOW_SERVICE_FILES,
+	PROP_SHOW_SERVICE_DIRECTORIES
+};
+
+GType
+tracker_db_type_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			{ TRACKER_DB_TYPE_UNKNOWN,
+			  "TRACKER_DB_TYPE_UNKNOWN",
+			  "unknown" },
+			{ TRACKER_DB_TYPE_DATA,
+			  "TRACKER_DB_TYPE_DATA",
+			  "data" },
+			{ TRACKER_DB_TYPE_INDEX,
+			  "TRACKER_DB_TYPE_INDEX",
+			  "index" },
+			{ TRACKER_DB_TYPE_COMMON,
+			  "TRACKER_DB_TYPE_COMMON",
+			  "common" },
+			{ TRACKER_DB_TYPE_CONTENT,
+			  "TRACKER_DB_TYPE_CONTENT",
+			  "content" },
+			{ TRACKER_DB_TYPE_EMAIL,
+			  "TRACKER_DB_TYPE_EMAIL",
+			  "email" },
+			{ TRACKER_DB_TYPE_FILES,
+			  "TRACKER_DB_TYPE_FILES",
+			  "files" },
+			{ TRACKER_DB_TYPE_XESAM,
+			  "TRACKER_DB_TYPE_XESAM",
+			  "xesam" },
+			{ TRACKER_DB_TYPE_CACHE,
+			  "TRACKER_DB_TYPE_CACHE",
+			  "cache" },
+			{ TRACKER_DB_TYPE_USER,
+			  "TRACKER_DB_TYPE_USER",
+			  "user" },
+			{ 0, NULL, NULL }
+		};
+
+		etype = g_enum_register_static ("TrackerDBType", values);
+	}
+
+	return etype;
+}
+
+G_DEFINE_TYPE (TrackerService, tracker_service, G_TYPE_OBJECT);
+
+static void
+tracker_service_class_init (TrackerServiceClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	object_class->finalize	   = service_finalize;
+	object_class->get_property = service_get_property;
+	object_class->set_property = service_set_property;
+
+	g_object_class_install_property (object_class,
+					 PROP_ID,
+					 g_param_spec_int ("id",
+							   "id",
+							   "Unique identifier for this service",
+							   0,
+							   G_MAXINT,
+							   0,
+							   G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_NAME,
+					 g_param_spec_string ("name",
+							      "name",
+							      "Service name",
+							      NULL,
+							      G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_PARENT,
+					 g_param_spec_string ("parent",
+							      "parent",
+							      "Service name of parent",
+							      NULL,
+							      G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_PROPERTY_PREFIX,
+					 g_param_spec_string ("property-prefix",
+							      "property-prefix",
+							      "The properties of this category are prefix:name",
+							      NULL,
+							      G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_CONTENT_METADATA,
+					 g_param_spec_string ("content-metadata",
+							      "content-metadata",
+							      "Content metadata",
+							      NULL,
+							      G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_KEY_METADATA,
+					 g_param_spec_pointer ("key-metadata",
+							       "key-metadata",
+							       "Key metadata",
+							       G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_DB_TYPE,
+					 g_param_spec_enum ("db-type",
+							    "db-type",
+							    "Database type",
+							    tracker_db_type_get_type (),
+							    TRACKER_DB_TYPE_DATA,
+							    G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_ENABLED,
+					 g_param_spec_boolean ("enabled",
+							       "enabled",
+							       "Enabled",
+							       TRUE,
+							       G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_EMBEDDED,
+					 g_param_spec_boolean ("embedded",
+							       "embedded",
+							       "Embedded",
+							       FALSE,
+							       G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_HAS_METADATA,
+					 g_param_spec_boolean ("has-metadata",
+							       "has-metadata",
+							       "Has metadata",
+							       FALSE,
+							       G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_HAS_FULL_TEXT,
+					 g_param_spec_boolean ("has-full-text",
+							       "has-full-text",
+							       "Has full text",
+							       FALSE,
+							       G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_HAS_THUMBS,
+					 g_param_spec_boolean ("has-thumbs",
+							       "has-thumbs",
+							       "Has thumbnails",
+							       FALSE,
+							       G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_SHOW_SERVICE_FILES,
+					 g_param_spec_boolean ("show-service-files",
+							       "show-service-files",
+							       "Show service files",
+							       FALSE,
+							       G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_SHOW_SERVICE_DIRECTORIES,
+					 g_param_spec_boolean ("show-service-directories",
+							       "show-service-directories",
+							       "Show service directories",
+							       FALSE,
+							       G_PARAM_READWRITE));
+
+	g_type_class_add_private (object_class, sizeof (TrackerServicePriv));
+}
+
+static void
+tracker_service_init (TrackerService *service)
+{
+}
+
+static void
+service_finalize (GObject *object)
+{
+	TrackerServicePriv *priv;
+
+	priv = GET_PRIV (object);
+
+	g_free (priv->name);
+	g_free (priv->parent);
+	g_free (priv->content_metadata);
+	g_free (priv->property_prefix);
+
+	g_slist_foreach (priv->key_metadata, (GFunc) g_free, NULL);
+	g_slist_free (priv->key_metadata);
+
+	(G_OBJECT_CLASS (tracker_service_parent_class)->finalize) (object);
+}
+
+static void
+service_get_property (GObject	 *object,
+		      guint	  param_id,
+		      GValue	 *value,
+		      GParamSpec *pspec)
+{
+	TrackerServicePriv *priv;
+
+	priv = GET_PRIV (object);
+
+	switch (param_id) {
+	case PROP_ID:
+		g_value_set_int (value, priv->id);
+		break;
+	case PROP_NAME:
+		g_value_set_string (value, priv->name);
+		break;
+	case PROP_PARENT:
+		g_value_set_string (value, priv->parent);
+		break;
+	case PROP_PROPERTY_PREFIX:
+		g_value_set_string (value, priv->property_prefix);
+		break;
+	case PROP_CONTENT_METADATA:
+		g_value_set_string (value, priv->content_metadata);
+		break;
+	case PROP_KEY_METADATA:
+		g_value_set_pointer (value, priv->key_metadata);
+		break;
+	case PROP_DB_TYPE:
+		g_value_set_enum (value, priv->db_type);
+		break;
+	case PROP_ENABLED:
+		g_value_set_boolean (value, priv->enabled);
+		break;
+	case PROP_EMBEDDED:
+		g_value_set_boolean (value, priv->embedded);
+		break;
+	case PROP_HAS_METADATA:
+		g_value_set_boolean (value, priv->has_metadata);
+		break;
+	case PROP_HAS_FULL_TEXT:
+		g_value_set_boolean (value, priv->has_full_text);
+		break;
+	case PROP_HAS_THUMBS:
+		g_value_set_boolean (value, priv->has_thumbs);
+		break;
+	case PROP_SHOW_SERVICE_FILES:
+		g_value_set_boolean (value, priv->show_service_files);
+		break;
+	case PROP_SHOW_SERVICE_DIRECTORIES:
+		g_value_set_boolean (value, priv->show_service_directories);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	};
+}
+
+static void
+service_set_property (GObject	   *object,
+		      guint	    param_id,
+		      const GValue *value,
+		      GParamSpec   *pspec)
+{
+	TrackerServicePriv *priv;
+
+	priv = GET_PRIV (object);
+
+	switch (param_id) {
+	case PROP_ID:
+		tracker_service_set_id (TRACKER_SERVICE (object),
+					g_value_get_int (value));
+		break;
+	case PROP_NAME:
+		tracker_service_set_name (TRACKER_SERVICE (object),
+					  g_value_get_string (value));
+		break;
+	case PROP_PROPERTY_PREFIX:
+		tracker_service_set_property_prefix (TRACKER_SERVICE (object),
+						     g_value_get_string (value));
+		break;
+	case PROP_PARENT:
+		tracker_service_set_parent (TRACKER_SERVICE (object),
+					    g_value_get_string (value));
+		break;
+	case PROP_CONTENT_METADATA:
+		tracker_service_set_content_metadata (TRACKER_SERVICE (object),
+						      g_value_get_string (value));
+		break;
+	case PROP_KEY_METADATA:
+		tracker_service_set_key_metadata (TRACKER_SERVICE (object),
+						  g_value_get_pointer (value));
+		break;
+	case PROP_DB_TYPE:
+		tracker_service_set_db_type (TRACKER_SERVICE (object),
+					     g_value_get_enum (value));
+		break;
+	case PROP_ENABLED:
+		tracker_service_set_enabled (TRACKER_SERVICE (object),
+					     g_value_get_boolean (value));
+		break;
+	case PROP_EMBEDDED:
+		tracker_service_set_embedded (TRACKER_SERVICE (object),
+					      g_value_get_boolean (value));
+		break;
+	case PROP_HAS_METADATA:
+		tracker_service_set_has_metadata (TRACKER_SERVICE (object),
+						  g_value_get_boolean (value));
+		break;
+	case PROP_HAS_FULL_TEXT:
+		tracker_service_set_has_full_text (TRACKER_SERVICE (object),
+						   g_value_get_boolean (value));
+		break;
+	case PROP_HAS_THUMBS:
+		tracker_service_set_has_thumbs (TRACKER_SERVICE (object),
+						g_value_get_boolean (value));
+		break;
+	case PROP_SHOW_SERVICE_FILES:
+		tracker_service_set_show_service_files (TRACKER_SERVICE (object),
+							g_value_get_boolean (value));
+		break;
+	case PROP_SHOW_SERVICE_DIRECTORIES:
+		tracker_service_set_show_service_directories (TRACKER_SERVICE (object),
+							      g_value_get_boolean (value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	};
+}
+
+static gboolean
+service_int_validate (TrackerService *service,
+		      const gchar   *property,
+		      gint	    value)
+{
+#ifdef G_DISABLE_CHECKS
+	GParamSpec *spec;
+	GValue	    value = { 0 };
+	gboolean    valid;
+
+	spec = g_object_class_find_property (G_OBJECT_CLASS (service), property);
+	g_return_val_if_fail (spec != NULL, FALSE);
+
+	g_value_init (&value, spec->value_type);
+	g_value_set_int (&value, verbosity);
+	valid = g_param_value_validate (spec, &value);
+	g_value_unset (&value);
+
+	g_return_val_if_fail (valid != TRUE, FALSE);
+#endif
+
+	return TRUE;
+}
+
+TrackerService *
+tracker_service_new (void)
+{
+	TrackerService *service;
+
+	service = g_object_new (TRACKER_TYPE_SERVICE, NULL);
+
+	return service;
+}
+
+gint
+tracker_service_get_id (TrackerService *service)
+{
+	TrackerServicePriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_SERVICE (service), -1);
+
+	priv = GET_PRIV (service);
+
+	return priv->id;
+}
+
+const gchar *
+tracker_service_get_name (TrackerService *service)
+{
+	TrackerServicePriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_SERVICE (service), NULL);
+
+	priv = GET_PRIV (service);
+
+	return priv->name;
+}
+
+const gchar *
+tracker_service_get_parent (TrackerService *service)
+{
+	TrackerServicePriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_SERVICE (service), NULL);
+
+	priv = GET_PRIV (service);
+
+	return priv->parent;
+}
+
+const gchar *
+tracker_service_get_property_prefix (TrackerService *service)
+{
+	TrackerServicePriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_SERVICE (service), NULL);
+
+	priv = GET_PRIV (service);
+
+	return priv->property_prefix;
+}
+
+const gchar *
+tracker_service_get_content_metadata (TrackerService *service)
+{
+	TrackerServicePriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_SERVICE (service), NULL);
+
+	priv = GET_PRIV (service);
+
+	return priv->content_metadata;
+}
+
+const GSList *
+tracker_service_get_key_metadata (TrackerService *service)
+{
+	TrackerServicePriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_SERVICE (service), NULL);
+
+	priv = GET_PRIV (service);
+
+	return priv->key_metadata;
+}
+
+TrackerDBType
+tracker_service_get_db_type (TrackerService *service)
+{
+	TrackerServicePriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_SERVICE (service), TRACKER_DB_TYPE_DATA);
+
+	priv = GET_PRIV (service);
+
+	return priv->db_type;
+}
+
+gboolean
+tracker_service_get_enabled (TrackerService *service)
+{
+	TrackerServicePriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_SERVICE (service), FALSE);
+
+	priv = GET_PRIV (service);
+
+	return priv->enabled;
+}
+
+gboolean
+tracker_service_get_embedded (TrackerService *service)
+{
+	TrackerServicePriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_SERVICE (service), FALSE);
+
+	priv = GET_PRIV (service);
+
+	return priv->embedded;
+}
+
+gboolean
+tracker_service_get_has_metadata (TrackerService *service)
+{
+	TrackerServicePriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_SERVICE (service), FALSE);
+
+	priv = GET_PRIV (service);
+
+	return priv->has_metadata;
+}
+
+gboolean
+tracker_service_get_has_full_text (TrackerService *service)
+{
+	TrackerServicePriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_SERVICE (service), FALSE);
+
+	priv = GET_PRIV (service);
+
+	return priv->has_full_text;
+}
+
+gboolean
+tracker_service_get_has_thumbs (TrackerService *service)
+{
+	TrackerServicePriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_SERVICE (service), FALSE);
+
+	priv = GET_PRIV (service);
+
+	return priv->has_thumbs;
+}
+
+gboolean
+tracker_service_get_show_service_files (TrackerService *service)
+{
+	TrackerServicePriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_SERVICE (service), FALSE);
+
+	priv = GET_PRIV (service);
+
+	return priv->show_service_files;
+}
+
+gboolean
+tracker_service_get_show_service_directories (TrackerService *service)
+{
+	TrackerServicePriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_SERVICE (service), FALSE);
+
+	priv = GET_PRIV (service);
+
+	return priv->show_service_directories;
+}
+
+void
+tracker_service_set_id (TrackerService *service,
+			gint		value)
+{
+	TrackerServicePriv *priv;
+
+	g_return_if_fail (TRACKER_IS_SERVICE (service));
+
+	if (!service_int_validate (service, "id", value)) {
+		return;
+	}
+
+	priv = GET_PRIV (service);
+
+	priv->id = value;
+	g_object_notify (G_OBJECT (service), "id");
+}
+
+void
+tracker_service_set_name (TrackerService *service,
+			  const gchar	 *value)
+{
+	TrackerServicePriv *priv;
+
+	g_return_if_fail (TRACKER_IS_SERVICE (service));
+
+	priv = GET_PRIV (service);
+
+	g_free (priv->name);
+
+	if (value) {
+		priv->name = g_strdup (value);
+	} else {
+		priv->name = NULL;
+	}
+
+	g_object_notify (G_OBJECT (service), "name");
+}
+
+void
+tracker_service_set_parent (TrackerService *service,
+			    const gchar    *value)
+{
+	TrackerServicePriv *priv;
+
+	g_return_if_fail (TRACKER_IS_SERVICE (service));
+
+	priv = GET_PRIV (service);
+
+	g_free (priv->parent);
+
+	if (value) {
+		priv->parent = g_strdup (value);
+	} else {
+		priv->parent = NULL;
+	}
+
+	g_object_notify (G_OBJECT (service), "parent");
+}
+
+void
+tracker_service_set_property_prefix (TrackerService *service,
+				     const gchar    *value)
+{
+	TrackerServicePriv *priv;
+
+	g_return_if_fail (TRACKER_IS_SERVICE (service));
+
+	priv = GET_PRIV (service);
+
+	g_free (priv->property_prefix);
+
+	if (value) {
+		priv->property_prefix = g_strdup (value);
+	} else {
+		priv->property_prefix = NULL;
+	}
+
+	g_object_notify (G_OBJECT (service), "property-prefix");
+}
+
+void
+tracker_service_set_content_metadata (TrackerService *service,
+				      const gchar    *value)
+{
+	TrackerServicePriv *priv;
+
+	g_return_if_fail (TRACKER_IS_SERVICE (service));
+
+	priv = GET_PRIV (service);
+
+	g_free (priv->content_metadata);
+
+	if (value) {
+		priv->content_metadata = g_strdup (value);
+	} else {
+		priv->content_metadata = NULL;
+	}
+
+	g_object_notify (G_OBJECT (service), "content-metadata");
+}
+
+void
+tracker_service_set_key_metadata (TrackerService *service,
+				  const GSList	 *value)
+{
+	TrackerServicePriv *priv;
+
+	g_return_if_fail (TRACKER_IS_SERVICE (service));
+
+	priv = GET_PRIV (service);
+
+	g_slist_foreach (priv->key_metadata, (GFunc) g_free, NULL);
+	g_slist_free (priv->key_metadata);
+
+	if (value) {
+		GSList	     *new_list;
+		const GSList *l;
+
+		new_list = NULL;
+
+		for (l = value; l; l = l->next) {
+			new_list = g_slist_prepend (new_list, g_strdup (l->data));
+		}
+
+		new_list = g_slist_reverse (new_list);
+		priv->key_metadata = new_list;
+	} else {
+		priv->key_metadata = NULL;
+	}
+
+	g_object_notify (G_OBJECT (service), "key-metadata");
+}
+
+void
+tracker_service_set_db_type (TrackerService *service,
+			     TrackerDBType   value)
+{
+	TrackerServicePriv *priv;
+
+	g_return_if_fail (TRACKER_IS_SERVICE (service));
+
+	priv = GET_PRIV (service);
+
+	priv->db_type = value;
+	g_object_notify (G_OBJECT (service), "db-type");
+}
+
+void
+tracker_service_set_enabled (TrackerService *service,
+			     gboolean	     value)
+{
+	TrackerServicePriv *priv;
+
+	g_return_if_fail (TRACKER_IS_SERVICE (service));
+
+	priv = GET_PRIV (service);
+
+	priv->enabled = value;
+	g_object_notify (G_OBJECT (service), "enabled");
+}
+
+void
+tracker_service_set_embedded (TrackerService *service,
+			      gboolean	      value)
+{
+	TrackerServicePriv *priv;
+
+	g_return_if_fail (TRACKER_IS_SERVICE (service));
+
+	priv = GET_PRIV (service);
+
+	priv->embedded = value;
+	g_object_notify (G_OBJECT (service), "embedded");
+}
+
+void
+tracker_service_set_has_metadata (TrackerService *service,
+				  gboolean	  value)
+{
+	TrackerServicePriv *priv;
+
+	g_return_if_fail (TRACKER_IS_SERVICE (service));
+
+	priv = GET_PRIV (service);
+
+	priv->has_metadata = value;
+	g_object_notify (G_OBJECT (service), "has-metadata");
+}
+
+void
+tracker_service_set_has_full_text (TrackerService *service,
+				   gboolean	   value)
+{
+	TrackerServicePriv *priv;
+
+	g_return_if_fail (TRACKER_IS_SERVICE (service));
+
+	priv = GET_PRIV (service);
+
+	priv->has_full_text = value;
+	g_object_notify (G_OBJECT (service), "has-full-text");
+}
+
+void
+tracker_service_set_has_thumbs (TrackerService *service,
+				gboolean	value)
+{
+	TrackerServicePriv *priv;
+
+	g_return_if_fail (TRACKER_IS_SERVICE (service));
+
+	priv = GET_PRIV (service);
+
+	priv->has_thumbs = value;
+	g_object_notify (G_OBJECT (service), "has-thumbs");
+}
+
+void
+tracker_service_set_show_service_files (TrackerService *service,
+					gboolean	value)
+{
+	TrackerServicePriv *priv;
+
+	g_return_if_fail (TRACKER_IS_SERVICE (service));
+
+	priv = GET_PRIV (service);
+
+	priv->show_service_files = value;
+	g_object_notify (G_OBJECT (service), "show-service-files");
+}
+
+void
+tracker_service_set_show_service_directories (TrackerService *service,
+					      gboolean	      value)
+{
+	TrackerServicePriv *priv;
+
+	g_return_if_fail (TRACKER_IS_SERVICE (service));
+
+	priv = GET_PRIV (service);
+
+	priv->show_service_directories = value;
+	g_object_notify (G_OBJECT (service), "show-service-directories");
+}
+

Added: trunk/src/libtracker-common/tracker-service.h
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-service.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,116 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKERD_SERVICES_H__
+#define __TRACKERD_SERVICES_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_DB_TYPE (tracker_db_type_get_type ())
+
+typedef enum {
+	TRACKER_DB_TYPE_UNKNOWN,
+	TRACKER_DB_TYPE_DATA,
+	TRACKER_DB_TYPE_INDEX,
+	TRACKER_DB_TYPE_COMMON,
+	TRACKER_DB_TYPE_CONTENT,
+	TRACKER_DB_TYPE_EMAIL,
+	TRACKER_DB_TYPE_FILES,
+	TRACKER_DB_TYPE_XESAM,
+	TRACKER_DB_TYPE_CACHE,
+	TRACKER_DB_TYPE_USER
+} TrackerDBType;
+
+GType tracker_db_type_get_type (void) G_GNUC_CONST;
+
+
+#define TRACKER_TYPE_SERVICE	     (tracker_service_get_type ())
+#define TRACKER_SERVICE(o)	     (G_TYPE_CHECK_INSTANCE_CAST ((o), TRACKER_TYPE_SERVICE, TrackerService))
+#define TRACKER_SERVICE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), TRACKER_TYPE_SERVICE, TrackerServiceClass))
+#define TRACKER_IS_SERVICE(o)	     (G_TYPE_CHECK_INSTANCE_TYPE ((o), TRACKER_TYPE_SERVICE))
+#define TRACKER_IS_SERVICE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), TRACKER_TYPE_SERVICE))
+#define TRACKER_SERVICE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), TRACKER_TYPE_SERVICE, TrackerServiceClass))
+
+typedef struct _TrackerService	    TrackerService;
+typedef struct _TrackerServiceClass TrackerServiceClass;
+
+struct _TrackerService {
+	GObject      parent;
+};
+
+struct _TrackerServiceClass {
+	GObjectClass parent_class;
+};
+
+GType		tracker_service_get_type		     (void) G_GNUC_CONST;
+
+TrackerService *tracker_service_new			     (void);
+
+gint		tracker_service_get_id			     (TrackerService *service);
+const gchar *	tracker_service_get_name		     (TrackerService *service);
+const gchar *	tracker_service_get_parent		     (TrackerService *service);
+const gchar *	tracker_service_get_property_prefix	     (TrackerService *service);
+const gchar *	tracker_service_get_content_metadata	     (TrackerService *service);
+const GSList *	tracker_service_get_key_metadata	     (TrackerService *service);
+TrackerDBType	tracker_service_get_db_type		     (TrackerService *service);
+gboolean	tracker_service_get_enabled		     (TrackerService *service);
+gboolean	tracker_service_get_embedded		     (TrackerService *service);
+gboolean	tracker_service_get_has_metadata	     (TrackerService *service);
+gboolean	tracker_service_get_has_full_text	     (TrackerService *service);
+gboolean	tracker_service_get_has_thumbs		     (TrackerService *service);
+gboolean	tracker_service_get_show_service_files	     (TrackerService *service);
+gboolean	tracker_service_get_show_service_directories (TrackerService *service);
+
+void		tracker_service_set_id			     (TrackerService *service,
+							      gint	      value);
+void		tracker_service_set_name		     (TrackerService *service,
+							      const gchar    *value);
+void		tracker_service_set_parent		     (TrackerService *service,
+							      const gchar    *value);
+void		tracker_service_set_property_prefix	     (TrackerService *service,
+							      const gchar    *value);
+void		tracker_service_set_content_metadata	     (TrackerService *service,
+							      const gchar    *value);
+void		tracker_service_set_key_metadata	     (TrackerService *service,
+							      const GSList   *value);
+void		tracker_service_set_db_type		     (TrackerService *service,
+							      TrackerDBType   value);
+void		tracker_service_set_enabled		     (TrackerService *service,
+							      gboolean	      value);
+void		tracker_service_set_embedded		     (TrackerService *service,
+							      gboolean	      value);
+void		tracker_service_set_has_metadata	     (TrackerService *service,
+							      gboolean	      value);
+void		tracker_service_set_has_full_text	     (TrackerService *service,
+							      gboolean	      value);
+void		tracker_service_set_has_thumbs		     (TrackerService *service,
+							      gboolean	      value);
+void		tracker_service_set_show_service_files	     (TrackerService *service,
+							      gboolean	      value);
+void		tracker_service_set_show_service_directories (TrackerService *service,
+							      gboolean	      value);
+
+G_END_DECLS
+
+#endif /* __TRACKERD_SERVICE_H__ */
+

Added: trunk/src/libtracker-common/tracker-type-utils.c
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-type-utils.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,754 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <glib.h>
+
+#include "tracker-log.h"
+#include "tracker-utils.h"
+#include "tracker-type-utils.h"
+
+static const char *months[] = {
+	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+static const char imonths[] = {
+	'1', '2', '3', '4', '5',
+	'6', '7', '8', '9', '0', '1', '2'
+};
+
+static gboolean
+is_int (const gchar *str)
+{
+	gint	 i, len;
+
+	if (!str || str[0] == '\0') {
+		return FALSE;
+	}
+
+	len = strlen (str);
+
+	for (i = 0; i < len; i++) {
+		if (!g_ascii_isdigit(str[i])) {
+			return FALSE;
+		}
+	}
+
+	return TRUE ;
+}
+
+static gint
+parse_month (const gchar *month)
+{
+	gint i;
+
+	for (i = 0; i < 12; i++) {
+		if (!strncmp (month, months[i], 3)) {
+			return i;
+		}
+	}
+
+	return -1;
+}
+
+/* Determine date format and convert to ISO 8601 format */
+gchar *
+tracker_date_format (const gchar *timestamp)
+{
+	gchar buf[30];
+	gint  len;
+
+	if (!timestamp) {
+		return NULL;
+	}
+
+	len = strlen (timestamp);
+
+	/* We cannot format a date without at least a four digit
+	 * year.
+	 */
+	if (len < 4) {
+		return NULL;
+	}
+
+	/* Check for year only dates (EG ID3 music tags might have
+	 * Audio.ReleaseDate as 4 digit year)
+	 */
+	if (len == 4) {
+		if (is_int (timestamp)) {
+			buf[0] = timestamp[0];
+			buf[1] = timestamp[1];
+			buf[2] = timestamp[2];
+			buf[3] = timestamp[3];
+			buf[4] = '-';
+			buf[5] = '0';
+			buf[6] = '1';
+			buf[7] = '-';
+			buf[8] = '0';
+			buf[9] = '1';
+			buf[10] = 'T';
+			buf[11] = '0';
+			buf[12] = '0';
+			buf[13] = ':';
+			buf[14] = '0';
+			buf[15] = '0';
+			buf[16] = ':';
+			buf[17] = '0';
+			buf[18] = '0';
+			buf[19] = '\0';
+
+			return g_strdup (buf);
+		} else {
+			return NULL;
+		}
+	} else if (len == 10)  {
+		/* Check for date part only YYYY-MM-DD*/
+		buf[0] = timestamp[0];
+		buf[1] = timestamp[1];
+		buf[2] = timestamp[2];
+		buf[3] = timestamp[3];
+		buf[4] = '-';
+		buf[5] = timestamp[5];
+		buf[6] = timestamp[6];
+		buf[7] = '-';
+		buf[8] = timestamp[8];
+		buf[9] = timestamp[9];
+		buf[10] = 'T';
+		buf[11] = '0';
+		buf[12] = '0';
+		buf[13] = ':';
+		buf[14] = '0';
+		buf[15] = '0';
+		buf[16] = ':';
+		buf[17] = '0';
+		buf[18] = '0';
+		buf[19] = '\0';
+
+		return g_strdup (buf);
+	} else if (len == 14) {
+		/* Check for pdf format EG 20050315113224-08'00' or
+		 * 20050216111533Z
+		 */
+		buf[0] = timestamp[0];
+		buf[1] = timestamp[1];
+		buf[2] = timestamp[2];
+		buf[3] = timestamp[3];
+		buf[4] = '-';
+		buf[5] = timestamp[4];
+		buf[6] = timestamp[5];
+		buf[7] = '-';
+		buf[8] = timestamp[6];
+		buf[9] = timestamp[7];
+		buf[10] = 'T';
+		buf[11] = timestamp[8];
+		buf[12] = timestamp[9];
+		buf[13] = ':';
+		buf[14] = timestamp[10];
+		buf[15] = timestamp[11];
+		buf[16] = ':';
+		buf[17] = timestamp[12];
+		buf[18] = timestamp[13];
+		buf[19] = '\0';
+
+		return g_strdup (buf);
+	} else if (len == 15 && timestamp[14] == 'Z') {
+		buf[0] = timestamp[0];
+		buf[1] = timestamp[1];
+		buf[2] = timestamp[2];
+		buf[3] = timestamp[3];
+		buf[4] = '-';
+		buf[5] = timestamp[4];
+		buf[6] = timestamp[5];
+		buf[7] = '-';
+		buf[8] = timestamp[6];
+		buf[9] = timestamp[7];
+		buf[10] = 'T';
+		buf[11] = timestamp[8];
+		buf[12] = timestamp[9];
+		buf[13] = ':';
+		buf[14] = timestamp[10];
+		buf[15] = timestamp[11];
+		buf[16] = ':';
+		buf[17] = timestamp[12];
+		buf[18] = timestamp[13];
+		buf[19] = 'Z';
+		buf[20] = '\0';
+
+		return g_strdup (buf);
+	} else if (len == 21 && (timestamp[14] == '-' || timestamp[14] == '+' )) {
+		buf[0] = timestamp[0];
+		buf[1] = timestamp[1];
+		buf[2] = timestamp[2];
+		buf[3] = timestamp[3];
+		buf[4] = '-';
+		buf[5] = timestamp[4];
+		buf[6] = timestamp[5];
+		buf[7] = '-';
+		buf[8] = timestamp[6];
+		buf[9] = timestamp[7];
+		buf[10] = 'T';
+		buf[11] = timestamp[8];
+		buf[12] = timestamp[9];
+		buf[13] = ':';
+		buf[14] = timestamp[10];
+		buf[15] = timestamp[11];
+		buf[16] = ':';
+		buf[17] = timestamp[12];
+		buf[18] = timestamp[13];
+		buf[19] = timestamp[14];
+		buf[20] = timestamp[15];
+		buf[21] =  timestamp[16];
+		buf[22] =  ':';
+		buf[23] =  timestamp[18];
+		buf[24] = timestamp[19];
+		buf[25] = '\0';
+
+		return g_strdup (buf);
+	} else if ((len == 24) && (timestamp[3] == ' ')) {
+		/* Check for msoffice date format "Mon Feb  9 10:10:00 2004" */
+		gint  num_month;
+		gchar mon1;
+		gchar day1;
+
+		num_month = parse_month (timestamp + 4);
+
+		mon1 = imonths[num_month];
+
+		if (timestamp[8] == ' ') {
+			day1 = '0';
+		} else {
+			day1 = timestamp[8];
+		}
+
+		buf[0] = timestamp[20];
+		buf[1] = timestamp[21];
+		buf[2] = timestamp[22];
+		buf[3] = timestamp[23];
+		buf[4] = '-';
+
+		if (num_month < 10) {
+			buf[5] = '0';
+			buf[6] = mon1;
+		} else {
+			buf[5] = '1';
+			buf[6] = mon1;
+		}
+
+		buf[7] = '-';
+		buf[8] = day1;
+		buf[9] = timestamp[9];
+		buf[10] = 'T';
+		buf[11] = timestamp[11];
+		buf[12] = timestamp[12];
+		buf[13] = ':';
+		buf[14] = timestamp[14];
+		buf[15] = timestamp[15];
+		buf[16] = ':';
+		buf[17] = timestamp[17];
+		buf[18] = timestamp[18];
+		buf[19] = '\0';
+
+		return g_strdup (buf);
+	} else if ((len == 19) && (timestamp[4] == ':') && (timestamp[7] == ':')) {
+		/* Check for Exif date format "2005:04:29 14:56:54" */
+		buf[0] = timestamp[0];
+		buf[1] = timestamp[1];
+		buf[2] = timestamp[2];
+		buf[3] = timestamp[3];
+		buf[4] = '-';
+		buf[5] = timestamp[5];
+		buf[6] = timestamp[6];
+		buf[7] = '-';
+		buf[8] = timestamp[8];
+		buf[9] = timestamp[9];
+		buf[10] = 'T';
+		buf[11] = timestamp[11];
+		buf[12] = timestamp[12];
+		buf[13] = ':';
+		buf[14] = timestamp[14];
+		buf[15] = timestamp[15];
+		buf[16] = ':';
+		buf[17] = timestamp[17];
+		buf[18] = timestamp[18];
+		buf[19] = '\0';
+
+		return g_strdup (buf);
+	}
+
+	return g_strdup (timestamp);
+}
+
+gchar *
+tracker_date_to_time_string (const gchar  *time_string)
+{
+	gchar *dvalue;
+
+	dvalue = tracker_date_format (time_string);
+
+	if (dvalue) {
+		time_t time;
+
+		time = tracker_string_to_date (dvalue);
+		g_free (dvalue);
+
+		if (time != -1) {
+			return tracker_gint_to_string (time);
+		}
+	}
+
+	return NULL;
+}
+
+static gboolean
+is_valid_8601_datetime (const gchar *timestamp)
+{
+	gint len;
+
+	len = strlen (timestamp);
+
+	if (len < 19) {
+		return FALSE;
+	}
+
+	if (!g_ascii_isdigit (timestamp[0]) ||
+	    !g_ascii_isdigit (timestamp[1]) ||
+	    !g_ascii_isdigit (timestamp[2]) ||
+	    !g_ascii_isdigit (timestamp[3])) {
+		return FALSE;
+	}
+
+	if (timestamp[4] != '-') {
+		return FALSE;
+	}
+
+	if (!g_ascii_isdigit (timestamp[5]) ||
+	    !g_ascii_isdigit (timestamp[6])) {
+		return FALSE;
+	}
+
+	if (timestamp[7] != '-') {
+		return FALSE;
+	}
+
+	if (!g_ascii_isdigit (timestamp[8]) ||
+	    !g_ascii_isdigit (timestamp[9])) {
+		return FALSE;
+	}
+
+	if ((timestamp[10] != 'T')) {
+		return FALSE;
+	}
+
+	if (!g_ascii_isdigit (timestamp[11]) ||
+	    !g_ascii_isdigit (timestamp[12])) {
+		return FALSE;
+	}
+
+	if (timestamp[13] != ':') {
+		return FALSE;
+	}
+
+	if (!g_ascii_isdigit (timestamp[14]) ||
+	    !g_ascii_isdigit (timestamp[15])) {
+		return FALSE;
+	}
+
+	if (timestamp[16] != ':'){
+		return FALSE;
+	}
+
+	if (!g_ascii_isdigit (timestamp[17]) ||
+	    !g_ascii_isdigit (timestamp[18])) {
+		return FALSE;
+	}
+
+	if (len == 20) {
+		if (timestamp[19] != 'Z') {
+			return FALSE;
+		}
+	} else {
+		if (len > 20) {
+			/* Format must be YYYY-MM-DDThh:mm:ss+xx  or
+			 * YYYY-MM-DDThh:mm:ss+xx:yy
+			 */
+			if (len < 22 || len > 25) {
+				return FALSE;
+			}
+
+			if (timestamp[19] != '+' &&
+			    timestamp[19] != '-') {
+				return FALSE;
+			}
+
+			if (!g_ascii_isdigit (timestamp[20]) ||
+			    !g_ascii_isdigit (timestamp[21])) {
+				return FALSE;
+			}
+		}
+	}
+
+	return TRUE;
+}
+
+time_t
+tracker_string_to_date (const gchar *timestamp)
+{
+	struct tm tm;
+	long	  val;
+	time_t	  t;
+
+	g_return_val_if_fail (timestamp, -1);
+
+	/* We should have a valid iso 8601 date in format
+	 * YYYY-MM-DDThh:mm:ss with optional TZ
+	 */
+	if (!is_valid_8601_datetime (timestamp)) {
+		return -1;
+	}
+
+	memset (&tm, 0, sizeof (struct tm));
+	val = strtoul (timestamp, (gchar**) &timestamp, 10);
+
+	if (*timestamp == '-') {
+		/* YYYY-MM-DD */
+		tm.tm_year = val - 1900;
+		timestamp++;
+		tm.tm_mon = strtoul (timestamp, (gchar **) &timestamp, 10) - 1;
+
+		if (*timestamp++ != '-') {
+			return -1;
+		}
+
+		tm.tm_mday = strtoul (timestamp, (gchar **) &timestamp, 10);
+	}
+
+	if (*timestamp++ != 'T') {
+		g_critical ("Date validation failed for '%s' st '%c'",
+			    timestamp,
+			    *timestamp);
+		return -1;
+	}
+
+	val = strtoul (timestamp, (gchar**) &timestamp, 10);
+
+	if (*timestamp == ':') {
+		/* hh:mm:ss */
+		tm.tm_hour = val;
+		timestamp++;
+		tm.tm_min = strtoul (timestamp, (gchar**) &timestamp, 10);
+
+		if (*timestamp++ != ':') {
+			return -1;
+		}
+
+		tm.tm_sec = strtoul (timestamp, (gchar**) &timestamp, 10);
+	}
+
+	/* mktime() always assumes that "tm" is in locale time but we
+	 * want to keep control on time, so we go to UTC
+	 */
+	t  = mktime (&tm);
+	t -= timezone;
+
+	if (*timestamp == '+' ||
+	    *timestamp == '-') {
+		gint sign;
+
+		sign = *timestamp++ == '+' ? -1 : 1;
+
+		/* We have format hh:mm or hhmm */
+		/* Now, we are reading hours */
+		if (timestamp[0] &&
+		    timestamp[1]) {
+			if (g_ascii_isdigit (timestamp[0]) &&
+			    g_ascii_isdigit (timestamp[1])) {
+				gchar buff[3];
+
+				buff[0] = timestamp[0];
+				buff[1] = timestamp[1];
+				buff[2] = '\0';
+
+				val = strtoul (buff, NULL, 10);
+				t += sign * (3600 * val);
+				timestamp += 2;
+			}
+
+			if (*timestamp == ':' || *timestamp == '\'') {
+				timestamp++;
+			}
+		}
+
+		/* Now, we are reading minutes */
+		if (timestamp[0] &&
+		    timestamp[1]) {
+			if (g_ascii_isdigit (timestamp[0]) &&
+			    g_ascii_isdigit (timestamp[1])) {
+				gchar buff[3];
+
+				buff[0] = timestamp[0];
+				buff[1] = timestamp[1];
+				buff[2] = '\0';
+
+				val = strtoul (buff, NULL, 10);
+				t += sign * (60 * val);
+				timestamp += 2;
+			}
+		}
+	}
+
+	return t;
+}
+
+gchar *
+tracker_date_to_string (time_t date_time)
+{
+	gchar	  buffer[30];
+	struct tm local_time;
+	size_t	  count;
+
+	memset (buffer, '\0', sizeof (buffer));
+	memset (&local_time, 0, sizeof (struct tm));
+
+	localtime_r (&date_time, &local_time);
+
+	/* Output is ISO 8160 format : "YYYY-MM-DDThh:mm:ss+zz:zz" */
+	count = strftime (buffer, sizeof (buffer), "%FT%T%z", &local_time);
+
+	return count > 0 ? g_strdup (buffer) : NULL;
+}
+
+gchar *
+tracker_glong_to_string (glong i)
+{
+	return g_strdup_printf ("%ld", i);
+}
+
+gchar *
+tracker_gint_to_string (gint i)
+{
+	return g_strdup_printf ("%d", i);
+}
+
+gchar *
+tracker_guint_to_string (guint i)
+{
+	return g_strdup_printf ("%u", i);
+}
+
+gchar *
+tracker_gint32_to_string (gint32 i)
+{
+	return g_strdup_printf ("%" G_GINT32_FORMAT, i);
+}
+
+gchar *
+tracker_guint32_to_string (guint32 i)
+{
+	return g_strdup_printf ("%" G_GUINT32_FORMAT, i);
+}
+
+gboolean
+tracker_string_to_uint (const gchar *s,
+			guint	    *value)
+{
+	unsigned long int  n;
+	gchar		  *end;
+
+	g_return_val_if_fail (s != NULL, FALSE);
+	g_return_val_if_fail (value != NULL, FALSE);
+
+	n = (guint) strtoul (s, &end, 10);
+
+	if (end == s) {
+		*value = 0;
+		return FALSE;
+	}
+
+	if (n > G_MAXUINT) {
+		*value = 0;
+		return FALSE;
+
+	} else {
+		*value = (guint) n;
+		return TRUE;
+	}
+}
+
+gint
+tracker_string_in_string_list (const gchar  *str,
+			       gchar	   **strv)
+{
+	gchar **p;
+	gint	i;
+
+	g_return_val_if_fail (str != NULL, -1);
+
+	if (!strv) {
+		return -1;
+	}
+
+	for (p = strv, i = 0; *p; p++, i++) {
+		if (strcasecmp (*p, str) == 0) {
+			return i;
+		}
+	}
+
+	return -1;
+}
+
+GSList *
+tracker_string_list_to_gslist (gchar **strv,
+			       gsize   size)
+{
+	GSList *list;
+	gsize	i;
+	gsize	size_used;
+
+	g_return_val_if_fail (strv != NULL, NULL);
+
+	if (size < 1) {
+		size_used = g_strv_length (strv);
+	} else {
+		size_used = size;
+	}
+
+	list = NULL;
+
+	for (i = 0; i < size; i++) {
+		if (strv[i]) {
+			list = g_slist_prepend (list, g_strdup (strv[i]));
+		} else {
+			break;
+		}
+	}
+
+	return g_slist_reverse (list);
+}
+
+gchar *
+tracker_string_list_to_string (gchar **strv,
+			       gsize   size,
+			       gchar   sep)
+{
+	GString *string;
+	gsize	 i;
+	gsize	 size_used;
+
+	g_return_val_if_fail (strv != NULL, NULL);
+
+	if (size < 1) {
+		size_used = g_strv_length (strv);
+	} else {
+		size_used = size;
+	}
+
+	string = g_string_new ("");
+
+	for (i = 0; i < size; i++) {
+		if (strv[i]) {
+			if (i > 0) {
+				g_string_append_c (string, sep);
+			}
+
+			string = g_string_append (string, strv[i]);
+		} else {
+			break;
+		}
+	}
+
+	return g_string_free (string, FALSE);
+}
+
+gchar **
+tracker_string_to_string_list (const gchar *str)
+{
+	gchar **result;
+
+	result = g_new0 (gchar *, 2);
+
+	result [0] = g_strdup (str);
+	result [1] = NULL;
+
+	return result;
+}
+
+gchar **
+tracker_gslist_to_string_list (GSList *list)
+{
+	GSList	*l;
+	gchar  **strv;
+	gint	 i;
+
+	strv = g_new0 (gchar*, g_slist_length (list) + 1);
+
+	for (l = list, i = 0; l; l = l->next) {
+		if (!l->data) {
+			continue;
+		}
+
+		strv[i++] = g_strdup (l->data);
+	}
+
+	strv[i] = NULL;
+
+	return strv;
+}
+
+GSList *
+tracker_gslist_copy_with_string_data (GSList *list)
+{
+	GSList *l;
+	GSList *new_list;
+
+	if (!list) {
+		return NULL;
+	}
+
+	new_list = NULL;
+
+	for (l = list; l; l = l->next) {
+		if (!l->data) {
+			continue;
+		}
+
+		new_list = g_slist_prepend (new_list, g_strdup (l->data));
+	}
+
+	new_list = g_slist_reverse (new_list);
+
+	return new_list;
+}
+gchar *
+tracker_string_boolean_to_string_gint (const gchar *value)
+{
+	g_return_val_if_fail (value != NULL, NULL);
+
+	if (g_ascii_strcasecmp (value, "true") == 0) {
+		return g_strdup ("1");
+	} else if (g_ascii_strcasecmp (value, "false") == 0) {
+		return g_strdup ("0");
+	} else {
+		return g_strdup (value);
+	}
+}

Added: trunk/src/libtracker-common/tracker-type-utils.h
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-type-utils.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,54 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __LIBTRACKER_COMMON_TYPE_UTILS_H__
+#define __LIBTRACKER_COMMON_TYPE_UTILS_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+gchar *  tracker_date_format		       (const gchar  *time_string);
+gchar *  tracker_date_to_time_string	       (const gchar  *date_string);
+time_t	 tracker_string_to_date		       (const gchar  *time_string);
+gchar *  tracker_date_to_string		       (time_t	      date_time);
+gchar *  tracker_glong_to_string	       (glong	      i);
+gchar *  tracker_gint_to_string		       (gint	      i);
+gchar *  tracker_guint_to_string	       (guint	      i);
+gchar *  tracker_gint32_to_string	       (gint32	      i);
+gchar *  tracker_guint32_to_string	       (guint32       i);
+gboolean tracker_string_to_uint		       (const gchar  *s,
+						guint	     *ret);
+gint	 tracker_string_in_string_list	       (const gchar  *str,
+						gchar	    **strv);
+GSList * tracker_string_list_to_gslist	       (gchar	    **strv,
+						gsize	      length);
+gchar *  tracker_string_list_to_string	       (gchar	    **strv,
+						gsize	      length,
+						gchar	      sep);
+gchar ** tracker_string_to_string_list	       (const gchar  *str);
+gchar ** tracker_gslist_to_string_list	       (GSList	     *list);
+GSList * tracker_gslist_copy_with_string_data  (GSList	     *list);
+gchar *  tracker_string_boolean_to_string_gint (const gchar  *value);
+
+G_END_DECLS
+
+#endif /* __LIBTRACKER_COMMON_TYPE_UTILS_H__ */

Added: trunk/src/libtracker-common/tracker-utils.c
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-utils.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,240 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "tracker-utils.h"
+
+inline gboolean
+tracker_is_empty_string (const char *str)
+{
+	return str == NULL || str[0] == '\0';
+}
+
+/* Removes a substring modifing haystack in place */
+gchar *
+tracker_string_remove (gchar	   *haystack,
+		       const gchar *needle)
+{
+	gchar *current, *pos, *next, *end;
+	gint len;
+
+	len = strlen (needle);
+	end = haystack + strlen (haystack);
+	current = pos = strstr (haystack, needle);
+
+	if (!current) {
+		return haystack;
+	}
+
+	while (*current != '\0') {
+		pos = strstr (pos, needle) + len;
+		next = strstr (pos, needle);
+
+		if (!next) {
+			next = end;
+		}
+
+		while (pos < next) {
+			*current = *pos;
+			current++;
+			pos++;
+		}
+
+		if (*pos == '\0') {
+			*current = *pos;
+		}
+	}
+
+	return haystack;
+}
+
+gchar *
+tracker_string_replace (const gchar *haystack,
+			const gchar *needle,
+			const gchar *replacement)
+{
+	GString *str;
+	gint	 pos, needle_len;
+
+	g_return_val_if_fail (haystack != NULL, NULL);
+	g_return_val_if_fail (needle != NULL, NULL);
+
+	needle_len = strlen (needle);
+
+	str = g_string_new ("");
+
+	/* FIXME: should use strstr */
+	for (pos = 0; haystack[pos]; pos++) {
+		if (strncmp (&haystack[pos], needle, needle_len) == 0) {
+			if (replacement) {
+				str = g_string_append (str, replacement);
+			}
+
+			pos += needle_len - 1;
+		} else {
+			str = g_string_append_c (str, haystack[pos]);
+		}
+	}
+
+	return g_string_free (str, FALSE);
+}
+
+gchar *
+tracker_escape_string (const gchar *in)
+{
+	gchar **array, *out;
+
+	if (strchr (in, '\'')) {
+		return g_strdup (in);
+	}
+
+	/* double single quotes */
+	array = g_strsplit (in, "'", -1);
+	out = g_strjoinv ("''", array);
+	g_strfreev (array);
+
+	return out;
+}
+
+gchar *
+tracker_seconds_estimate_to_string (gdouble  seconds_elapsed,
+				    gboolean short_string,
+				    guint    items_done,
+				    guint    items_remaining)
+{
+	gdouble per_item;
+	gdouble total;
+
+	g_return_val_if_fail (seconds_elapsed >= 0.0, g_strdup (_("unknown time")));
+
+	/* We don't want division by 0 or if total is 0 because items
+	 * remaining is 0 then, equally pointless.
+	 */
+	if (items_done < 1 ||
+	    items_remaining < 1) {
+		return g_strdup (_("unknown time"));
+	}
+
+	per_item = seconds_elapsed / items_done;
+	total = per_item * items_remaining;
+
+	return tracker_seconds_to_string (total, short_string);
+}
+
+gchar *
+tracker_seconds_to_string (gdouble  seconds_elapsed,
+			   gboolean short_string)
+{
+	GString *s;
+	gchar	*str;
+	gdouble  total;
+	gint	 days, hours, minutes, seconds;
+
+	g_return_val_if_fail (seconds_elapsed >= 0.0, g_strdup (_("unknown time")));
+
+	total	 = seconds_elapsed;
+
+	seconds  = (gint) total % 60;
+	total	/= 60;
+	minutes  = (gint) total % 60;
+	total	/= 60;
+	hours	 = (gint) total % 24;
+	days	 = (gint) total / 24;
+
+	s = g_string_new ("");
+
+	if (short_string) {
+		if (days) {
+			g_string_append_printf (s, " %dd", days);
+		}
+
+		if (hours) {
+			g_string_append_printf (s, " %2.2dh", hours);
+		}
+
+		if (minutes) {
+			g_string_append_printf (s, " %2.2dm", minutes);
+		}
+
+		if (seconds) {
+			g_string_append_printf (s, " %2.2ds", seconds);
+		}
+	} else {
+		if (days) {
+			g_string_append_printf (s, " %d day%s",
+						days,
+						days == 1 ? "" : "s");
+		}
+
+		if (hours) {
+			g_string_append_printf (s, " %2.2d hour%s",
+						hours,
+						hours == 1 ? "" : "s");
+		}
+
+		if (minutes) {
+			g_string_append_printf (s, " %2.2d minute%s",
+						minutes,
+						minutes == 1 ? "" : "s");
+		}
+
+		if (seconds) {
+			g_string_append_printf (s, " %2.2d second%s",
+						seconds,
+						seconds == 1 ? "" : "s");
+		}
+	}
+
+	str = g_string_free (s, FALSE);
+
+	if (str[0] == '\0') {
+		g_free (str);
+		str = g_strdup (_("unknown time"));
+	} else {
+		g_strchug (str);
+	}
+
+	return str;
+}
+
+void
+tracker_throttle (TrackerConfig *config,
+		  gint		 multiplier)
+{
+	gint throttle;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	/* Get the throttle, add 5 (minimum value) so we don't do
+	 * nothing and then multiply it by the factor given
+	 */
+	throttle  = tracker_config_get_throttle (config);
+	throttle += 5;
+	throttle *= multiplier;
+
+	if (throttle > 0) {
+		g_usleep (throttle);
+	}
+}

Added: trunk/src/libtracker-common/tracker-utils.h
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-common/tracker-utils.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,45 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __LIBTRACKER_COMMON_UTILS_H__
+#define __LIBTRACKER_COMMON_UTILS_H__
+
+#include <glib.h>
+
+#include "tracker-config.h"
+
+gboolean tracker_is_empty_string	    (const char    *str);
+gchar *  tracker_string_replace		    (const gchar   *haystack,
+					     const gchar   *needle,
+					     const gchar   *replacement);
+gchar *  tracker_string_remove		    (gchar	   *haystack,
+					     const gchar   *needle);
+gchar *  tracker_escape_string		    (const gchar   *in);
+gchar *  tracker_seconds_estimate_to_string (gdouble	    seconds_elapsed,
+					     gboolean	    short_string,
+					     guint	    items_done,
+					     guint	    items_remaining);
+gchar *  tracker_seconds_to_string	    (gdouble	    seconds_elapsed,
+					     gboolean	    short_string);
+void	 tracker_throttle		    (TrackerConfig *config,
+					     gint	    multiplier);
+
+#endif /* __LIBTRACKER_COMMON_UTILS_H__ */

Added: trunk/src/libtracker-db/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-db/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,44 @@
+include $(top_srcdir)/Makefile.decl
+
+INCLUDES =								\
+	-DSHAREDIR=\""$(datadir)"\"					\
+	-DG_LOG_DOMAIN=\"Tracker\"					\
+	-I$(top_srcdir)/src						\
+	$(SQLITE3_CFLAGS)						\
+	$(QDBM_CFLAGS)							\
+	$(DBUS_CFLAGS)							\
+	$(GLIB2_CFLAGS)
+
+libtracker_dbdir = $(libdir)/tracker
+libtracker_db_LTLIBRARIES = libtracker-db.la
+
+libtracker_db_la_SOURCES = 						\
+	tracker-db-action.c						\
+	tracker-db-dbus.c						\
+	tracker-db-file-info.c						\
+	tracker-db-index.c						\
+	tracker-db-index-item.c						\
+	tracker-db-index-manager.c					\
+	tracker-db-interface.c						\
+	tracker-db-interface-sqlite.c					\
+	tracker-db-manager.c
+
+noinst_HEADERS =							\
+	tracker-db-action.h						\
+	tracker-db-dbus.h						\
+	tracker-db-file-info.h						\
+	tracker-db-index.h						\
+	tracker-db-index-item.h						\
+	tracker-db-index-manager.h					\
+	tracker-db-interface.h						\
+	tracker-db-interface-sqlite.h					\
+	tracker-db-manager.h 
+
+libtracker_db_la_LDFLAGS = -version-info 0:0:0
+libtracker_db_la_LIBADD = 						\
+	$(top_builddir)/src/libtracker-common/libtracker-common.la	\
+	$(SQLITE3_LIBS)							\
+	$(QDBM_LIBS)							\
+	$(DBUS_LIBS)							\
+	$(GLIB2_LIBS)							\
+	-lz
\ No newline at end of file

Added: trunk/src/libtracker-db/tracker-db-action.c
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-db/tracker-db-action.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,149 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "tracker-db-action.h"
+
+GType
+tracker_db_action_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			{ TRACKER_DB_ACTION_IGNORE,
+			  "TRACKER_DB_ACTION_IGNORE",
+			  "Ignoring" },
+			{ TRACKER_DB_ACTION_CHECK,
+			  "TRACKER_DB_ACTION_CHECK",
+			  "Checking" },
+			{ TRACKER_DB_ACTION_DELETE,
+			  "TRACKER_DB_ACTION_DELETE",
+			  "Deleting" },
+			{ TRACKER_DB_ACTION_DELETE_SELF,
+			  "TRACKER_DB_ACTION_DELETE_SELF",
+			  "Deleting Self" },
+			{ TRACKER_DB_ACTION_CREATE,
+			  "TRACKER_DB_ACTION_CREATE",
+			  "Creating" },
+			{ TRACKER_DB_ACTION_MOVED_FROM,
+			  "TRACKER_DB_ACTION_MOVED_FROM",
+			  "Moved From" },
+			{ TRACKER_DB_ACTION_MOVED_TO,
+			  "TRACKER_DB_ACTION_MOVED_TO",
+			  "Moved To" },
+			{ TRACKER_DB_ACTION_FILE_CHECK,
+			  "TRACKER_DB_ACTION_FILE_CHECK",
+			  "File Check" },
+			{ TRACKER_DB_ACTION_FILE_CHANGED,
+			  "TRACKER_DB_ACTION_FILE_CHANGED",
+			  "File Changed" },
+			{ TRACKER_DB_ACTION_FILE_DELETED,
+			  "TRACKER_DB_ACTION_FILE_DELETED",
+			  "File Deleted" },
+			{ TRACKER_DB_ACTION_FILE_CREATED,
+			  "TRACKER_DB_ACTION_FILE_CREATED",
+			  "File Created" },
+			{ TRACKER_DB_ACTION_FILE_MOVED_FROM,
+			  "TRACKER_DB_ACTION_FILE_MOVED_FROM",
+			  "File Moved From" },
+			{ TRACKER_DB_ACTION_FILE_MOVED_TO,
+			  "TRACKER_DB_ACTION_FILE_MOVED_TO",
+			  "File Moved To" },
+			{ TRACKER_DB_ACTION_WRITABLE_FILE_CLOSED,
+			  "TRACKER_DB_ACTION_WRITABLE_FILE_CLOSED",
+			  "Writable File Closed" },
+			{ TRACKER_DB_ACTION_DIRECTORY_CHECK,
+			  "TRACKER_DB_ACTION_DIRECTORY_CHECK",
+			  "Directory Check" },
+			{ TRACKER_DB_ACTION_DIRECTORY_CREATED,
+			  "TRACKER_DB_ACTION_DIRECTORY_CREATED",
+			  "Directory Created" },
+			{ TRACKER_DB_ACTION_DIRECTORY_UNMOUNTED,
+			  "TRACKER_DB_ACTION_DIRECTORY_UNMOUNTED",
+			  "Directory Unmounted" },
+			{ TRACKER_DB_ACTION_DIRECTORY_DELETED,
+			  "TRACKER_DB_ACTION_DIRECTORY_DELETED",
+			  "Directory Deleted" },
+			{ TRACKER_DB_ACTION_DIRECTORY_MOVED_FROM,
+			  "TRACKER_DB_ACTION_DIRECTORY_MOVED_FROM",
+			  "Directory Moved From" },
+			{ TRACKER_DB_ACTION_DIRECTORY_MOVED_TO,
+			  "TRACKER_DB_ACTION_DIRECTORY_MOVED_TO",
+			  "Directory Moved To" },
+			{ TRACKER_DB_ACTION_DIRECTORY_REFRESH,
+			  "TRACKER_DB_ACTION_DIRECTORY_REFRESH",
+			  "Directory Refresh" },
+			{ TRACKER_DB_ACTION_EXTRACT_METADATA,
+			  "TRACKER_DB_ACTION_EXTRACT_METADATA",
+			  "Extract Metadata" },
+			{ TRACKER_DB_ACTION_FORCE_REFRESH,
+			  "TRACKER_DB_ACTION_FORCE_REFRESH",
+			  "Forcing Refresh" },
+			{ 0, NULL, NULL }
+		};
+
+		etype = g_enum_register_static ("TrackerDBAction", values);
+
+		/* Since we don't reference this enum anywhere, we do
+		 * it here to make sure it exists when we call
+		 * g_type_class_peek(). This wouldn't be necessary if
+		 * it was a param in a GObject for example.
+		 *
+		 * This does mean that we are leaking by 1 reference
+		 * here and should clean it up, but it doesn't grow so
+		 * this is acceptable.
+		 */
+
+		g_type_class_ref (etype);
+	}
+
+	return etype;
+}
+
+const gchar *
+tracker_db_action_to_string (TrackerDBAction action)
+{
+	GType	    type;
+	GEnumClass *enum_class;
+	GEnumValue *enum_value;
+
+	type = tracker_db_action_get_type ();
+	enum_class = G_ENUM_CLASS (g_type_class_peek (type));
+	enum_value = g_enum_get_value (enum_class, action);
+
+	if (!enum_value) {
+		enum_value = g_enum_get_value (enum_class, TRACKER_DB_ACTION_IGNORE);
+	}
+
+	return enum_value->value_nick;
+}
+
+gboolean
+tracker_db_action_is_delete (TrackerDBAction action)
+{
+	return
+		action == TRACKER_DB_ACTION_DELETE ||
+		action == TRACKER_DB_ACTION_DELETE_SELF ||
+		action == TRACKER_DB_ACTION_FILE_DELETED ||
+		action == TRACKER_DB_ACTION_DIRECTORY_DELETED;
+}

Added: trunk/src/libtracker-db/tracker-db-action.h
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-db/tracker-db-action.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,63 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKERD_DB_ACTION_H__
+#define __TRACKERD_DB_ACTION_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_ACTION (tracker_db_action_get_type ())
+
+typedef enum {
+	TRACKER_DB_ACTION_IGNORE,
+	TRACKER_DB_ACTION_CHECK,
+	TRACKER_DB_ACTION_DELETE,
+	TRACKER_DB_ACTION_DELETE_SELF,
+	TRACKER_DB_ACTION_CREATE,
+	TRACKER_DB_ACTION_MOVED_FROM,
+	TRACKER_DB_ACTION_MOVED_TO,
+	TRACKER_DB_ACTION_FILE_CHECK,
+	TRACKER_DB_ACTION_FILE_CHANGED,
+	TRACKER_DB_ACTION_FILE_DELETED,
+	TRACKER_DB_ACTION_FILE_CREATED,
+	TRACKER_DB_ACTION_FILE_MOVED_FROM,
+	TRACKER_DB_ACTION_FILE_MOVED_TO,
+	TRACKER_DB_ACTION_WRITABLE_FILE_CLOSED,
+	TRACKER_DB_ACTION_DIRECTORY_CHECK,
+	TRACKER_DB_ACTION_DIRECTORY_CREATED,
+	TRACKER_DB_ACTION_DIRECTORY_UNMOUNTED,
+	TRACKER_DB_ACTION_DIRECTORY_DELETED,
+	TRACKER_DB_ACTION_DIRECTORY_MOVED_FROM,
+	TRACKER_DB_ACTION_DIRECTORY_MOVED_TO,
+	TRACKER_DB_ACTION_DIRECTORY_REFRESH,
+	TRACKER_DB_ACTION_EXTRACT_METADATA,
+	TRACKER_DB_ACTION_FORCE_REFRESH
+} TrackerDBAction;
+
+GType	     tracker_db_action_get_type  (void) G_GNUC_CONST;
+const gchar *tracker_db_action_to_string (TrackerDBAction action);
+gboolean     tracker_db_action_is_delete (TrackerDBAction action);
+
+G_END_DECLS
+
+#endif /* __TRACKERD_DB_ACTION_H__ */

Added: trunk/src/libtracker-db/tracker-db-dbus.c
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-db/tracker-db-dbus.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,283 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <libtracker-common/tracker-dbus.h>
+
+#include "tracker-db-dbus.h"
+
+static gchar **
+dbus_query_result_to_strv (TrackerDBResultSet *result_set,
+			   gint		       column,
+			   gint		      *count,
+			   gboolean	       numeric)
+
+{
+	gchar **strv = NULL;
+	gint	rows = 0;
+	gint	i = 0;
+
+	if (result_set) {
+		gchar	 *str;
+		gboolean  valid = TRUE;
+		gint	  value;
+
+		/* Make sure we rewind before iterating the result set */
+		tracker_db_result_set_rewind (result_set);
+
+		rows = tracker_db_result_set_get_n_rows (result_set);
+		strv = g_new (gchar*, rows + 1);
+
+		while (valid) {
+			if (numeric) {
+				tracker_db_result_set_get (result_set, column, &value, -1);
+				str = g_strdup_printf ("%d", value);
+			} else {
+				tracker_db_result_set_get (result_set, column, &str, -1);
+			}
+
+			if (!str) {
+				valid = tracker_db_result_set_iter_next (result_set);
+				continue;
+			}
+
+			if (!g_utf8_validate (str, -1, NULL)) {
+				g_warning ("Could not add string:'%s' to GStrv, invalid UTF-8", str);
+				g_free (str);
+				str = g_strdup ("");
+			}
+
+			strv[i++] = str;
+			valid = tracker_db_result_set_iter_next (result_set);
+		}
+
+		strv[i] = NULL;
+	}
+
+	if (count) {
+		*count = i;
+	}
+
+	return strv;
+}
+
+gchar **
+tracker_dbus_query_result_to_strv (TrackerDBResultSet *result_set,
+				   gint		       column,
+				   gint		      *count)
+{
+	return dbus_query_result_to_strv (result_set, column, count, FALSE);
+}
+
+gchar **
+tracker_dbus_query_result_numeric_to_strv (TrackerDBResultSet *result_set,
+					   gint		       column,
+					   gint		      *count)
+{
+	return dbus_query_result_to_strv (result_set, column, count, TRUE);
+}
+
+gchar **
+tracker_dbus_query_result_columns_to_strv (TrackerDBResultSet *result_set,
+					   gint offset_column,
+					   gint until_column,
+					   gboolean rewind)
+{
+	gchar **strv = NULL;
+	gint	i = 0;
+	gint	columns;
+
+	if (result_set) {
+		columns = tracker_db_result_set_get_n_columns (result_set);
+		if (rewind) {
+			 /* Make sure we rewind before iterating the result set */
+			tracker_db_result_set_rewind (result_set);
+		}
+	}
+
+	if (!result_set || offset_column > columns) {
+		strv = g_new (gchar*, 1);
+		strv[0] = NULL;
+		return strv;
+	} else if (offset_column == -1)
+		offset_column = 0;
+
+	if (until_column == -1)
+		until_column = columns;
+
+	strv = g_new (gchar*, until_column + 1);
+
+
+	for (i = offset_column ; i < until_column; i++) {
+		GValue value = {0, };
+		GValue	transform = {0, };
+
+		g_value_init (&transform, G_TYPE_STRING);
+
+		_tracker_db_result_set_get_value (result_set, i, &value);
+		if (g_value_transform (&value, &transform)) {
+			strv[i] = g_value_dup_string (&transform);
+		}
+		g_value_unset (&value);
+		g_value_unset (&transform);
+	}
+
+	strv[i] = NULL;
+
+	return strv;
+}
+
+
+GHashTable *
+tracker_dbus_query_result_to_hash_table (TrackerDBResultSet *result_set)
+{
+	GHashTable *hash_table;
+	gint	    field_count;
+	gboolean    valid = FALSE;
+
+	hash_table = g_hash_table_new_full (g_str_hash,
+					    g_str_equal,
+					    (GDestroyNotify) g_free,
+					    (GDestroyNotify) tracker_dbus_gvalue_slice_free);
+
+	if (result_set) {
+		valid = TRUE;
+
+		/* Make sure we rewind before iterating the result set */
+		tracker_db_result_set_rewind (result_set);
+
+		/* Find out how many columns to iterate */
+		field_count = tracker_db_result_set_get_n_columns (result_set);
+	}
+
+	while (valid) {
+		GValue	 transform = { 0, };
+		GValue	*values;
+		gchar  **p;
+		gint	 i = 0;
+		gchar	*key;
+		GSList	*list = NULL;
+
+		g_value_init (&transform, G_TYPE_STRING);
+
+		tracker_db_result_set_get (result_set, 0, &key, -1);
+		values = tracker_dbus_gvalue_slice_new (G_TYPE_STRV);
+
+		for (i = 1; i < field_count; i++) {
+			GValue	value = { 0, };
+			gchar  *str;
+
+			_tracker_db_result_set_get_value (result_set, i, &value);
+
+			if (g_value_transform (&value, &transform)) {
+				str = g_value_dup_string (&transform);
+
+				if (!g_utf8_validate (str, -1, NULL)) {
+					g_warning ("Could not add string:'%s' to GStrv, invalid UTF-8", str);
+					g_free (str);
+					str = g_strdup ("");
+				}
+			} else {
+				str = g_strdup ("");
+			}
+
+			list = g_slist_prepend (list, (gchar*) str);
+		}
+
+		list = g_slist_reverse (list);
+		p = tracker_dbus_slist_to_strv (list);
+		g_slist_free (list);
+		g_value_take_boxed (values, p);
+		g_hash_table_insert (hash_table, key, values);
+
+		valid = tracker_db_result_set_iter_next (result_set);
+	}
+
+	return hash_table;
+}
+
+GPtrArray *
+tracker_dbus_query_result_to_ptr_array (TrackerDBResultSet *result_set)
+{
+	GPtrArray *ptr_array;
+	gboolean   valid = FALSE;
+	gint	   columns;
+	gint	   i;
+
+	ptr_array = g_ptr_array_new ();
+
+	if (result_set) {
+		valid = TRUE;
+
+		/* Make sure we rewind before iterating the result set */
+		tracker_db_result_set_rewind (result_set);
+
+		/* Find out how many columns to iterate */
+		columns = tracker_db_result_set_get_n_columns (result_set);
+	}
+
+	while (valid) {
+		GSList	*list = NULL;
+		gchar  **p;
+
+		/* Append fields to the array */
+		for (i = 0; i < columns; i++) {
+			GValue	 transform = { 0, };
+			GValue	value = { 0, };
+			gchar  *str;
+
+			g_value_init (&transform, G_TYPE_STRING);
+
+			_tracker_db_result_set_get_value (result_set, i, &value);
+
+			if (g_value_transform (&value, &transform)) {
+				str = g_value_dup_string (&transform);
+
+				if (!str) {
+					str = g_strdup ("");
+				} else if (!g_utf8_validate (str, -1, NULL)) {
+					g_warning ("Could not add string:'%s' to GStrv, invalid UTF-8", str);
+					g_free (str);
+					str = g_strdup ("");
+				}
+			} else {
+				str = g_strdup ("");
+			}
+
+			list = g_slist_prepend (list, (gchar*) str);
+
+			g_value_unset (&value);
+			g_value_unset (&transform);
+		}
+
+		list = g_slist_reverse (list);
+		p = tracker_dbus_slist_to_strv (list);
+
+		g_slist_foreach (list, (GFunc)g_free, NULL);
+		g_slist_free (list);
+
+		g_ptr_array_add (ptr_array, p);
+
+		valid = tracker_db_result_set_iter_next (result_set);
+	}
+
+	return ptr_array;
+}
+

Added: trunk/src/libtracker-db/tracker-db-dbus.h
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-db/tracker-db-dbus.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,46 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __LIBTRACKER_DB_DBUS_H__
+#define __LIBTRACKER_DB_DBUS_H__
+
+G_BEGIN_DECLS
+
+#include <glib.h>
+
+#include "tracker-db-interface.h"
+
+gchar **    tracker_dbus_query_result_to_strv	     (TrackerDBResultSet *result_set,
+						      gint		  column,
+						      gint		 *count);
+gchar **    tracker_dbus_query_result_numeric_to_strv (TrackerDBResultSet *result_set,
+						       gint		   column,
+						       gint		  *count);
+gchar **    tracker_dbus_query_result_columns_to_strv (TrackerDBResultSet *result_set,
+						       gint offset_column,
+						       gint until_column,
+						       gboolean rewind);
+GHashTable *tracker_dbus_query_result_to_hash_table (TrackerDBResultSet *result_set);
+GPtrArray * tracker_dbus_query_result_to_ptr_array  (TrackerDBResultSet *result_set);
+
+G_END_DECLS
+
+#endif /* __LIBTRACKER_DB_DBUS_H__ */

Added: trunk/src/libtracker-db/tracker-db-file-info.c
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-db/tracker-db-file-info.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,346 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include <libtracker-common/tracker-log.h>
+#include <libtracker-common/tracker-os-dependant.h>
+
+#include "tracker-db-file-info.h"
+
+static gint allocated;
+static gint deallocated;
+
+/*
+ * TrackerDBWatch
+ */
+GType
+tracker_db_watch_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			{ TRACKER_DB_WATCH_ROOT,
+			  "TRACKER_DB_WATCH_ROOT",
+			  "Watching Root" },
+			{ TRACKER_DB_WATCH_SUBFOLDER,
+			  "TRACKER_DB_WATCH_SUBFOLDER",
+			  "Watching Subfolder" },
+			{ TRACKER_DB_WATCH_SPECIAL_FOLDER,
+			  "TRACKER_DB_WATCH_SPECIAL_FOLDER",
+			  "Watching Special Folder" },
+			{ TRACKER_DB_WATCH_SPECIAL_FILE,
+			  "TRACKER_DB_WATCH_SPECIAL_FILE",
+			  "Watching Special File" },
+			{ TRACKER_DB_WATCH_NO_INDEX,
+			  "TRACKER_DB_WATCH_NO_INDEX",
+			  "Watching No Index" },
+			{ TRACKER_DB_WATCH_OTHER,
+			  "TRACKER_DB_WATCH_OTHER",
+			  "Watching Other" },
+			{ 0, NULL, NULL }
+		};
+
+		etype = g_enum_register_static ("TrackerDBWatch", values);
+
+		/* Since we don't reference this enum anywhere, we do
+		 * it here to make sure it exists when we call
+		 * g_type_class_peek(). This wouldn't be necessary if
+		 * it was a param in a GObject for example.
+		 *
+		 * This does mean that we are leaking by 1 reference
+		 * here and should clean it up, but it doesn't grow so
+		 * this is acceptable.
+		 */
+
+		g_type_class_ref (etype);
+	}
+
+	return etype;
+}
+
+const gchar *
+tracker_db_watch_to_string (TrackerDBWatch watch)
+{
+	GType	    type;
+	GEnumClass *enum_class;
+	GEnumValue *enum_value;
+
+	type = tracker_db_action_get_type ();
+	enum_class = G_ENUM_CLASS (g_type_class_peek (type));
+	enum_value = g_enum_get_value (enum_class, watch);
+
+	if (!enum_value) {
+		enum_value = g_enum_get_value (enum_class, TRACKER_DB_WATCH_OTHER);
+	}
+
+	return enum_value->value_nick;
+}
+
+/*
+ * TrackerDBFileInfo
+ */
+TrackerDBFileInfo *
+tracker_db_file_info_new (const char	  *uri,
+			  TrackerDBAction  action,
+			  gint		   counter,
+			  TrackerDBWatch   watch)
+{
+	TrackerDBFileInfo *info;
+
+	info = g_slice_new0 (TrackerDBFileInfo);
+
+	info->action = action;
+	info->uri = g_strdup (uri);
+
+	info->counter = counter;
+	info->file_id = 0;
+
+	info->watch_type = watch;
+	info->is_directory = FALSE;
+
+	info->is_link = FALSE;
+	info->link_id = 0;
+	info->link_path = NULL;
+	info->link_name = NULL;
+
+	info->mime = NULL;
+	info->file_size = 0;
+	info->permissions = g_strdup ("-r--r--r--");
+	info->mtime = 0;
+	info->atime = 0;
+	info->indextime = 0;
+	info->offset = 0;
+	info->aux_id = -1;
+
+	info->is_hidden = FALSE;
+
+	info->is_new = TRUE;
+	info->service_type_id = -1;
+
+	info->ref_count = 1;
+
+	/* Keep a tally of how many we have created */
+	allocated++;
+
+	return info;
+}
+
+void
+tracker_db_file_info_free (TrackerDBFileInfo *info)
+{
+	if (!info) {
+		return;
+	}
+
+	if (info->uri) {
+		g_free (info->uri);
+	}
+
+	if (info->moved_to_uri) {
+		g_free (info->moved_to_uri);
+	}
+
+	if (info->link_path) {
+		g_free (info->link_path);
+	}
+
+	if (info->link_name) {
+		g_free (info->link_name);
+	}
+
+	if (info->mime) {
+		g_free (info->mime);
+	}
+
+	if (info->permissions) {
+		g_free (info->permissions);
+	}
+
+	g_slice_free (TrackerDBFileInfo, info);
+
+	/* Keep a tally of how many we have removed */
+	deallocated++;
+}
+
+/* Ref count TrackerDBFileInfo instances */
+TrackerDBFileInfo *
+tracker_db_file_info_ref (TrackerDBFileInfo *info)
+{
+	if (info) {
+		g_atomic_int_inc (&info->ref_count);
+	}
+
+	return info;
+}
+
+TrackerDBFileInfo *
+tracker_db_file_info_unref (TrackerDBFileInfo *info)
+{
+	if (!info) {
+		return NULL;
+	}
+
+	if g_atomic_int_dec_and_test (&info->ref_count) {
+		tracker_db_file_info_free (info);
+		return NULL;
+	}
+
+	return info;
+}
+
+#if 0
+static TrackerDBFileInfo *
+db_file_info_get_pending (guint32	   file_id,
+			  const gchar	  *uri,
+			  const gchar	  *mime,
+			  gint		   counter,
+			  TrackerDBAction  action,
+			  gboolean	   is_directory)
+{
+	TrackerDBFileInfo *info;
+
+	info = g_slice_new0 (TrackerDBFileInfo);
+
+	info->action = action;
+	info->uri = g_strdup (uri);
+
+	info->counter = counter;
+	info->file_id = file_id;
+
+	info->is_directory = is_directory;
+
+	info->is_link = FALSE;
+	info->link_id = 0;
+	info->link_path = NULL;
+	info->link_name = NULL;
+
+	if (mime) {
+		info->mime = g_strdup (mime);
+	} else {
+		info->mime = NULL;
+	}
+
+	info->file_size = 0;
+	info->permissions = g_strdup ("-r--r--r--");
+	info->mtime = 0;
+	info->atime = 0;
+	info->indextime = 0;
+	info->offset = 0;
+
+	info->service_type_id = -1;
+	info->is_new = TRUE;
+
+	info->ref_count = 1;
+
+	allocated++;
+
+	return info;
+}
+#endif
+
+TrackerDBFileInfo *
+tracker_db_file_info_get (TrackerDBFileInfo *info)
+{
+	struct stat  finfo;
+	gchar	    *str, *uri_in_locale;
+
+	if (!info || !info->uri) {
+		return info;
+	}
+
+	uri_in_locale = g_filename_from_utf8 (info->uri, -1, NULL, NULL, NULL);
+
+	if (uri_in_locale) {
+		if (g_lstat (uri_in_locale, &finfo) == -1) {
+			g_free (uri_in_locale);
+
+			return info;
+		}
+
+	} else {
+		g_warning ("URI:'%s' could not be converted to locale format",
+			   info->uri);
+		return NULL;
+	}
+
+	info->is_directory = S_ISDIR (finfo.st_mode);
+	info->is_link = S_ISLNK (finfo.st_mode);
+
+	if (info->is_link && !info->link_name) {
+		str = g_file_read_link (uri_in_locale, NULL);
+
+		if (str) {
+			char *link_uri;
+
+			link_uri = g_filename_to_utf8 (str, -1, NULL, NULL, NULL);
+			info->link_name = g_path_get_basename (link_uri);
+			info->link_path = g_path_get_dirname (link_uri);
+			g_free (link_uri);
+			g_free (str);
+		}
+	}
+
+	g_free (uri_in_locale);
+
+	if (!info->is_directory) {
+		info->file_size = (guint32) finfo.st_size;
+	} else {
+		if (info->watch_type == TRACKER_DB_WATCH_OTHER) {
+			info->watch_type = TRACKER_DB_WATCH_SUBFOLDER;
+		}
+	}
+
+	g_free (info->permissions);
+	info->permissions = tracker_create_permission_string (finfo);
+
+	info->mtime =  finfo.st_mtime;
+	info->atime =  finfo.st_atime;
+
+	return info;
+}
+
+gboolean
+tracker_db_file_info_is_valid (TrackerDBFileInfo *info)
+{
+	g_return_val_if_fail (info != NULL, FALSE);
+	g_return_val_if_fail (info->uri != NULL, FALSE);
+
+	if (!g_utf8_validate (info->uri, -1, NULL)) {
+		g_warning ("Expected UTF-8 validation of TrackerDBFileInfo URI");
+		return FALSE;
+	}
+
+	if (info->action == TRACKER_DB_ACTION_IGNORE) {
+		return FALSE;
+	}
+
+	return TRUE;
+}
+

Added: trunk/src/libtracker-db/tracker-db-file-info.h
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-db/tracker-db-file-info.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,108 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKER_DB_FILE_INFO_H__
+#define __TRACKER_DB_FILE_INFO_H__
+
+#include <glib-object.h>
+
+#include "tracker-db-action.h"
+
+/*
+ * Watch types
+ */
+#define TRACKER_TYPE_DB_WATCH (tracker_db_watch_get_type ())
+
+typedef enum {
+	TRACKER_DB_WATCH_ROOT,
+	TRACKER_DB_WATCH_SUBFOLDER,
+	TRACKER_DB_WATCH_SPECIAL_FOLDER,
+	TRACKER_DB_WATCH_SPECIAL_FILE,
+	TRACKER_DB_WATCH_NO_INDEX,
+	TRACKER_DB_WATCH_OTHER
+} TrackerDBWatch;
+
+GType		   tracker_db_watch_get_type  (void) G_GNUC_CONST;
+const gchar *	   tracker_db_watch_to_string (TrackerDBWatch	  watch);
+
+/*
+ * File Information
+ */
+typedef struct _TrackerDBFileInfo TrackerDBFileInfo;
+
+struct _TrackerDBFileInfo {
+	/* File name/path related info */
+	gchar		*uri;
+	guint32		 file_id;
+
+	TrackerDBAction  action;
+	guint32		 cookie;
+	gint		 counter;
+	TrackerDBWatch	 watch_type;
+
+	/* Symlink info - File ID of link might not be in DB so need
+	 * to store path/filename too.
+	 */
+	gint32		 link_id;
+	gchar		*link_path;
+	gchar		*link_name;
+
+	gchar		*mime;
+	gint		 service_type_id;
+	guint32		 file_size;
+	gchar		*permissions;
+	gint32		 mtime;
+	gint32		 atime;
+	gint32		 indextime;
+	gint32		 offset;
+
+	/* Options */
+	gchar		*moved_to_uri;
+
+	gint		 aux_id;
+
+	guint		 is_new : 1;
+	guint		 is_directory : 1;
+	guint		 is_link : 1;
+	guint		 extract_embedded : 1;
+	guint		 extract_contents : 1;
+	guint		 extract_thumbs : 1;
+	guint		 is_hidden : 1;
+
+	/* We ref count FileInfo as it has a complex lifespan and is
+	 * tossed between various threads, lists, queues and hash
+	 * tables.
+	 */
+	gint		 ref_count;
+};
+
+TrackerDBFileInfo *tracker_db_file_info_new	 (const gchar	    *uri,
+						  TrackerDBAction    action,
+						  gint		     counter,
+						  TrackerDBWatch     watch);
+void		   tracker_db_file_info_free	 (TrackerDBFileInfo *info);
+TrackerDBFileInfo *tracker_db_file_info_ref	 (TrackerDBFileInfo *info);
+TrackerDBFileInfo *tracker_db_file_info_unref	 (TrackerDBFileInfo *info);
+TrackerDBFileInfo *tracker_db_file_info_get	 (TrackerDBFileInfo *info);
+gboolean	   tracker_db_file_info_is_valid (TrackerDBFileInfo *info);
+
+
+#endif /* __TRACKER_DB_FILE_INFO_H__ */

Added: trunk/src/libtracker-db/tracker-db-index-item.c
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-db/tracker-db-index-item.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,77 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia
+
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "tracker-db-index-item.h"
+
+guint32
+tracker_db_index_item_calc_amalgamated (gint service_type,
+					gint score)
+{
+	unsigned char a[4];
+	gint16	      score16;
+	guint8	      service_type_8;
+
+	if (score > 30000) {
+		score16 = 30000;
+	} else {
+		score16 = (gint16) score;
+	}
+
+	service_type_8 = (guint8) service_type;
+
+	/* Amalgamate and combine score and service_type into a single
+	 * 32-bit int for compact storage.
+	 */
+	a[0] = service_type_8;
+	a[1] = (score16 >> 8) & 0xFF;
+	a[2] = score16 & 0xFF;
+	a[3] = 0;
+
+	return (a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3];
+}
+
+guint8
+tracker_db_index_item_get_service_type (TrackerDBIndexItem *item)
+{
+	g_return_val_if_fail (item != NULL, 0);
+
+	return (item->amalgamated >> 24) & 0xFF;
+}
+
+gint16
+tracker_db_index_item_get_score (TrackerDBIndexItem *item)
+{
+	unsigned char a[2];
+
+	g_return_val_if_fail (item != NULL, 0);
+
+	a[0] = (item->amalgamated >> 16) & 0xFF;
+	a[1] = (item->amalgamated >> 8) & 0xFF;
+
+	return (gint16) (a[0] << 8) | (a[1]);
+}
+
+guint32
+tracker_db_index_item_get_id (TrackerDBIndexItem *item)
+{
+	g_return_val_if_fail (item != NULL, 0);
+
+	return item->id;
+}

Added: trunk/src/libtracker-db/tracker-db-index-item.h
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-db/tracker-db-index-item.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,52 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia
+
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKER_DB_INDEX_ITEM_H__
+#define __TRACKER_DB_INDEX_ITEM_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct {
+	/* Service ID number of the document */
+	guint32 id;
+
+	/* Amalgamation of service_type and score of the word in the
+	 * document's metadata.
+	 */
+	gint	amalgamated;
+} TrackerDBIndexItem;
+
+typedef struct {
+	guint32 service_id;	 /* Service ID of the document */
+	guint32 service_type_id; /* Service type ID of the document */
+	guint32 score;		 /* Ranking score */
+} TrackerDBIndexItemRank;
+
+guint32 tracker_db_index_item_calc_amalgamated (gint		    service_type,
+						gint		    score);
+guint8	tracker_db_index_item_get_service_type (TrackerDBIndexItem *details);
+gint16	tracker_db_index_item_get_score        (TrackerDBIndexItem *details);
+guint32 tracker_db_index_item_get_id	       (TrackerDBIndexItem *details);
+
+G_END_DECLS
+
+#endif /* __TRACKER_DB_INDEX_ITEM_H__ */

Added: trunk/src/libtracker-db/tracker-db-index-manager.c
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-db/tracker-db-index-manager.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,363 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <stdio.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <gio/gio.h>
+
+#include <libtracker-common/tracker-file-utils.h>
+#include <libtracker-common/tracker-ontology.h>
+
+#include "tracker-db-index-manager.h"
+
+#define MIN_BUCKET_DEFAULT  10
+#define MAX_BUCKET_DEFAULT  20
+
+#define MAX_INDEX_FILE_SIZE 2000000000
+
+typedef struct {
+	TrackerDBIndexType  type;
+	TrackerDBIndex	   *index;
+	const gchar	   *file;
+	const gchar	   *name;
+	gchar		   *abs_filename;
+} TrackerDBIndexDefinition;
+
+static gboolean			 initialized;
+static gchar			*data_dir;
+
+static TrackerDBIndexDefinition  indexes[] = {
+	{ TRACKER_DB_INDEX_UNKNOWN,
+	  NULL,
+	  NULL,
+	  NULL,
+	  NULL },
+	{ TRACKER_DB_INDEX_FILE,
+	  NULL,
+	  "file-index.db",
+	  "file-index",
+	  NULL },
+	{ TRACKER_DB_INDEX_FILE_UPDATE,
+	  NULL,
+	  "file-index-update.db",
+	  "file-index-update",
+	  NULL },
+	{ TRACKER_DB_INDEX_EMAIL,
+	  NULL,
+	  "email-index.db",
+	  "email-index",
+	  NULL }
+};
+
+static gboolean
+has_tmp_merge_files (TrackerDBIndexType type)
+{
+	GFile		*file;
+	GFileEnumerator *enumerator;
+	GFileInfo	*info;
+	GError		*error = NULL;
+	gchar		*prefix;
+	gchar		*dirname;
+	gboolean	 found;
+
+	if (type == TRACKER_DB_INDEX_UNKNOWN) {
+		return FALSE;
+	}
+
+	prefix = g_strconcat (indexes[type].name, ".tmp", NULL);
+
+	dirname = g_path_get_dirname (indexes[type].abs_filename);
+	file = g_file_new_for_path (dirname);
+
+	enumerator = g_file_enumerate_children (file,
+						G_FILE_ATTRIBUTE_STANDARD_NAME ","
+						G_FILE_ATTRIBUTE_STANDARD_TYPE,
+						G_PRIORITY_DEFAULT,
+						NULL,
+						&error);
+
+	if (error) {
+		g_warning ("Could not check for temporary index files in "
+			   "directory:'%s', %s",
+			   dirname,
+			   error->message);
+
+		g_error_free (error);
+		g_object_unref (file);
+		g_free (dirname);
+		g_free (prefix);
+
+		return FALSE;
+	}
+
+	found = FALSE;
+
+	for (info = g_file_enumerator_next_file (enumerator, NULL, &error);
+	     info && !error && !found;
+	     info = g_file_enumerator_next_file (enumerator, NULL, &error)) {
+		/* Check each file has or hasn't got the prefix */
+		if (g_str_has_prefix (g_file_info_get_name (info), prefix)) {
+			found = TRUE;
+		}
+
+		g_object_unref (info);
+	}
+
+	if (error) {
+		g_warning ("Could not get file information for temporary "
+			   "index files, %s",
+			   error->message);
+		g_error_free (error);
+	}
+
+	g_object_unref (enumerator);
+	g_object_unref (file);
+	g_free (dirname);
+	g_free (prefix);
+
+	return found;
+}
+
+gboolean
+tracker_db_index_manager_init (TrackerDBIndexManagerFlags  flags,
+			       gint			   min_bucket,
+			       gint			   max_bucket)
+{
+	gchar	 *final_index_filename;
+	gchar	 *name;
+	gboolean  need_reindex = FALSE;
+	gboolean  force_reindex;
+	gboolean  readonly;
+	guint	  i;
+
+	g_return_val_if_fail (min_bucket >= 0, FALSE);
+	g_return_val_if_fail (max_bucket >= min_bucket, FALSE);
+
+	if (initialized) {
+		return TRUE;
+	}
+
+	g_message ("Setting index database locations");
+
+	data_dir = g_build_filename (g_get_user_cache_dir (),
+				     "tracker",
+				     NULL);
+
+	g_message ("Checking index directories exist");
+
+	g_mkdir_with_parents (data_dir, 00755);
+
+	g_message ("Checking index files exist");
+
+	for (i = 1; i < G_N_ELEMENTS (indexes); i++) {
+		indexes[i].abs_filename = g_build_filename (data_dir, indexes[i].file, NULL);
+
+		if (need_reindex) {
+			continue;
+		}
+
+		if (!g_file_test (indexes[i].abs_filename, G_FILE_TEST_EXISTS)) {
+			g_message ("Could not find index file:'%s'", indexes[i].abs_filename);
+		}
+	}
+
+	g_message ("Merging old temporary indexes");
+
+	i = TRACKER_DB_INDEX_FILE;
+	name = g_strconcat (indexes[i].name, "-final", NULL);
+	final_index_filename = g_build_filename (data_dir, name, NULL);
+	g_free (name);
+
+	if (g_file_test (final_index_filename, G_FILE_TEST_EXISTS) &&
+	    !has_tmp_merge_files (i)) {
+		g_message ("  Overwriting '%s' with '%s'",
+			   indexes[i].abs_filename,
+			   final_index_filename);
+
+		g_rename (final_index_filename, indexes[i].abs_filename);
+	}
+
+	g_free (final_index_filename);
+
+	i = TRACKER_DB_INDEX_EMAIL;
+	name = g_strconcat (indexes[i].name, "-final", NULL);
+	final_index_filename = g_build_filename (data_dir, name, NULL);
+	g_free (name);
+
+	if (g_file_test (final_index_filename, G_FILE_TEST_EXISTS) &&
+	    !has_tmp_merge_files (i)) {
+		g_message ("  Overwriting '%s' with '%s'",
+			   indexes[i].abs_filename,
+			   final_index_filename);
+
+		g_rename (final_index_filename, indexes[i].abs_filename);
+	}
+
+	g_free (final_index_filename);
+
+	/* Now we have cleaned up merge files, see if we are supposed
+	 * to be reindexing.
+	 */
+
+	force_reindex = (flags & TRACKER_DB_INDEX_MANAGER_FORCE_REINDEX) != 0;
+
+	if (force_reindex || need_reindex) {
+		g_message ("Cleaning up index files for reindex");
+
+		for (i = 1; i < G_N_ELEMENTS (indexes); i++) {
+			g_unlink (indexes[i].abs_filename);
+		}
+	}
+
+	g_message ("Creating index files, this may take a few moments...");
+
+	readonly = (flags & TRACKER_DB_INDEX_MANAGER_READONLY) != 0;
+
+	for (i = 1; i < G_N_ELEMENTS (indexes); i++) {
+		indexes[i].index = tracker_db_index_new (indexes[i].abs_filename,
+							 min_bucket,
+							 max_bucket,
+							 readonly);
+	}
+
+	initialized = TRUE;
+
+	return TRUE;
+}
+
+void
+tracker_db_index_manager_shutdown (void)
+{
+	guint i;
+
+	if (!initialized) {
+		return;
+	}
+
+	for (i = 1; i < G_N_ELEMENTS (indexes); i++) {
+		g_object_unref (indexes[i].index);
+		indexes[i].index = NULL;
+
+		g_free (indexes[i].abs_filename);
+		indexes[i].abs_filename = NULL;
+	}
+
+	g_free (data_dir);
+
+	initialized = FALSE;
+}
+
+TrackerDBIndex *
+tracker_db_index_manager_get_index (TrackerDBIndexType type)
+{
+	return indexes[type].index;
+}
+
+TrackerDBIndex *
+tracker_db_index_manager_get_index_by_service (const gchar *service)
+{
+	TrackerDBType	   type;
+	TrackerDBIndexType index_type;
+
+	g_return_val_if_fail (initialized == TRUE, NULL);
+	g_return_val_if_fail (service != NULL, NULL);
+
+	type = tracker_ontology_get_service_db_by_name (service);
+
+	switch (type) {
+	case TRACKER_DB_TYPE_FILES:
+		index_type = TRACKER_DB_INDEX_FILE;
+		break;
+	case TRACKER_DB_TYPE_EMAIL:
+		index_type = TRACKER_DB_INDEX_EMAIL;
+		break;
+	default:
+		/* How do we handle XESAM? and others? default to files? */
+		index_type = TRACKER_DB_INDEX_UNKNOWN;
+		break;
+	}
+
+	return indexes[index_type].index;
+}
+
+TrackerDBIndex *
+tracker_db_index_manager_get_index_by_service_id (gint id)
+{
+	TrackerDBType	    type;
+	TrackerDBIndexType  index_type;
+	gchar		   *service;
+
+	g_return_val_if_fail (initialized == TRUE, NULL);
+
+	service = tracker_ontology_get_service_by_id (id);
+	if (!service) {
+		return NULL;
+	}
+
+	type = tracker_ontology_get_service_db_by_name (service);
+	g_free (service);
+
+	switch (type) {
+	case TRACKER_DB_TYPE_FILES:
+		index_type = TRACKER_DB_INDEX_FILE;
+		break;
+	case TRACKER_DB_TYPE_EMAIL:
+		index_type = TRACKER_DB_INDEX_EMAIL;
+		break;
+	default:
+		/* How do we handle XESAM? and others? default to files? */
+		index_type = TRACKER_DB_INDEX_UNKNOWN;
+		break;
+	}
+
+	if (index_type == TRACKER_DB_INDEX_UNKNOWN) {
+		return NULL;
+	}
+
+	return indexes[index_type].index;
+}
+
+const gchar *
+tracker_db_index_manager_get_filename (TrackerDBIndexType type)
+{
+	return indexes[type].abs_filename;
+}
+
+gboolean
+tracker_db_index_manager_are_indexes_too_big (void)
+{
+	gboolean too_big;
+	guint	 i;
+
+	g_return_val_if_fail (initialized == TRUE, FALSE);
+
+	for (i = 1, too_big = FALSE; i < G_N_ELEMENTS (indexes) && !too_big; i++) {
+		too_big = tracker_file_get_size (indexes[i].abs_filename) > MAX_INDEX_FILE_SIZE;
+	}
+
+	if (too_big) {
+		g_critical ("One or more index files are too big, indexing disabled");
+		return TRUE;
+	}
+
+	return FALSE;
+}

Added: trunk/src/libtracker-db/tracker-db-index-manager.h
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-db/tracker-db-index-manager.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKERD_INDEX_MANAGER_H__
+#define __TRACKERD_INDEX_MANAGER_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#include "tracker-db-index.h"
+
+typedef enum {
+	TRACKER_DB_INDEX_UNKNOWN,
+	TRACKER_DB_INDEX_FILE,
+	TRACKER_DB_INDEX_FILE_UPDATE,
+	TRACKER_DB_INDEX_EMAIL
+} TrackerDBIndexType;
+
+typedef enum {
+	TRACKER_DB_INDEX_MANAGER_FORCE_REINDEX = 1 << 1,
+	TRACKER_DB_INDEX_MANAGER_READONLY      = 1 << 2
+} TrackerDBIndexManagerFlags;
+
+gboolean	tracker_db_index_manager_init			 (TrackerDBIndexManagerFlags  flags,
+								  gint			      min_bucket,
+								  gint			      max_bucket);
+void		tracker_db_index_manager_shutdown		 (void);
+TrackerDBIndex *tracker_db_index_manager_get_index		 (TrackerDBIndexType	      index);
+TrackerDBIndex *tracker_db_index_manager_get_index_by_service	 (const gchar		     *service);
+TrackerDBIndex *tracker_db_index_manager_get_index_by_service_id (gint			      id);
+const gchar *	tracker_db_index_manager_get_filename		 (TrackerDBIndexType	      index);
+gboolean	tracker_db_index_manager_are_indexes_too_big	 (void);
+
+G_END_DECLS
+
+#endif /* __TRACKERD_INDEX_MANAGER_H__ */

Added: trunk/src/libtracker-db/tracker-db-index.c
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-db/tracker-db-index.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,1183 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <depot.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include <libtracker-common/tracker-log.h>
+#include <libtracker-common/tracker-file-utils.h>
+
+#include <libtracker-db/tracker-db-manager.h>
+
+#include "tracker-db-index.h"
+
+/* Size of free block pool of inverted index */
+#define MAX_HIT_BUFFER 480000
+
+#define TRACKER_DB_INDEX_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRACKER_TYPE_DB_INDEX, TrackerDBIndexPrivate))
+
+typedef struct TrackerDBIndexPrivate TrackerDBIndexPrivate;
+
+struct TrackerDBIndexPrivate {
+	/* From the daemon */
+	DEPOT	   *index;
+
+	guint	    min_bucket;
+	guint	    max_bucket;
+
+	guint	    reload : 1;
+	guint	    readonly : 1;
+	guint	    in_pause : 1;
+	guint	    in_flush : 1;
+
+	/* From the indexer */
+	GHashTable *cache;
+	gchar	   *filename;
+	gint	    bucket_count;
+};
+
+static void tracker_db_index_class_init   (TrackerDBIndexClass *class);
+static void tracker_db_index_init	  (TrackerDBIndex      *tree);
+static void tracker_db_index_finalize	  (GObject	       *object);
+static void tracker_db_index_set_property (GObject	       *object,
+					   guint		prop_id,
+					   const GValue        *value,
+					   GParamSpec	       *pspec);
+static void tracker_db_index_get_property (GObject	       *object,
+					   guint		prop_id,
+					   GValue	       *value,
+					   GParamSpec	       *pspec);
+static void free_cache_values		  (GArray	       *array);
+
+enum {
+	PROP_0,
+	PROP_FILENAME,
+	PROP_MIN_BUCKET,
+	PROP_MAX_BUCKET,
+	PROP_RELOAD,
+	PROP_READONLY
+};
+
+G_DEFINE_TYPE (TrackerDBIndex, tracker_db_index, G_TYPE_OBJECT)
+
+static void
+tracker_db_index_class_init (TrackerDBIndexClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	object_class->finalize = tracker_db_index_finalize;
+	object_class->set_property = tracker_db_index_set_property;
+	object_class->get_property = tracker_db_index_get_property;
+
+	g_object_class_install_property (object_class,
+					 PROP_FILENAME,
+					 g_param_spec_string ("filename",
+							      "Filename",
+							      "Filename",
+							      NULL,
+							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_class_install_property (object_class,
+					 PROP_MIN_BUCKET,
+					 g_param_spec_int ("min-bucket",
+							   "Minimum bucket",
+							   "Minimum bucket",
+							   0,
+							   1000000, /* FIXME MAX_GUINT ?? */
+							   0,
+							   G_PARAM_READWRITE));
+
+	g_object_class_install_property (object_class,
+					 PROP_MAX_BUCKET,
+					 g_param_spec_int ("max-bucket",
+							   "Maximum bucket",
+							   "Maximum bucket",
+							   0,
+							   1000000, /* FIXME MAX_GUINT ?? */
+							   0,
+							   G_PARAM_READWRITE));
+
+	g_object_class_install_property (object_class,
+					 PROP_RELOAD,
+					 g_param_spec_boolean ("reload",
+							       "Reload the index file before read",
+							       "Reload the index file before read",
+							       TRUE,
+							       G_PARAM_READWRITE |
+							       G_PARAM_CONSTRUCT));
+
+	g_object_class_install_property (object_class,
+					 PROP_READONLY,
+					 g_param_spec_boolean ("readonly",
+							       "Open the index for readonly purposes",
+							       "Open the index for readonly purposes",
+							       TRUE,
+							       G_PARAM_READWRITE |
+							       G_PARAM_CONSTRUCT));
+
+	g_type_class_add_private (object_class, sizeof (TrackerDBIndexPrivate));
+}
+
+static void
+tracker_db_index_init (TrackerDBIndex *index)
+{
+	TrackerDBIndexPrivate *priv;
+
+	priv = TRACKER_DB_INDEX_GET_PRIVATE (index);
+
+	priv->reload = TRUE;
+
+	priv->cache = g_hash_table_new_full (g_str_hash,
+					     g_str_equal,
+					     (GDestroyNotify) g_free,
+					     (GDestroyNotify) free_cache_values);
+}
+
+static void
+tracker_db_index_finalize (GObject *object)
+{
+	TrackerDBIndex	      *index;
+	TrackerDBIndexPrivate *priv;
+
+	index = TRACKER_DB_INDEX (object);
+	priv = TRACKER_DB_INDEX_GET_PRIVATE (index);
+
+	tracker_db_index_flush (index);
+	tracker_db_index_close (index);
+
+	g_hash_table_destroy (priv->cache);
+
+	g_free (priv->filename);
+
+
+	G_OBJECT_CLASS (tracker_db_index_parent_class)->finalize (object);
+}
+
+static void
+tracker_db_index_set_property (GObject	    *object,
+			       guint	     prop_id,
+			       const GValue *value,
+			       GParamSpec   *pspec)
+{
+	switch (prop_id) {
+	case PROP_FILENAME:
+		tracker_db_index_set_filename (TRACKER_DB_INDEX (object),
+					    g_value_get_string (value));
+		break;
+	case PROP_MIN_BUCKET:
+		tracker_db_index_set_min_bucket (TRACKER_DB_INDEX (object),
+					      g_value_get_int (value));
+		break;
+	case PROP_MAX_BUCKET:
+		tracker_db_index_set_max_bucket (TRACKER_DB_INDEX (object),
+					      g_value_get_int (value));
+		break;
+	case PROP_RELOAD:
+		tracker_db_index_set_reload (TRACKER_DB_INDEX (object),
+					  g_value_get_boolean (value));
+		break;
+	case PROP_READONLY:
+		tracker_db_index_set_readonly (TRACKER_DB_INDEX (object),
+					       g_value_get_boolean (value));
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+	}
+}
+
+static void
+tracker_db_index_get_property (GObject	  *object,
+			       guint	   prop_id,
+			       GValue	  *value,
+			       GParamSpec *pspec)
+{
+	TrackerDBIndexPrivate *priv;
+
+	priv = TRACKER_DB_INDEX_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_FILENAME:
+		g_value_set_string (value, priv->filename);
+		break;
+	case PROP_MIN_BUCKET:
+		g_value_set_int (value, priv->min_bucket);
+		break;
+	case PROP_MAX_BUCKET:
+		g_value_set_int (value, priv->max_bucket);
+		break;
+	case PROP_RELOAD:
+		g_value_set_boolean (value, priv->reload);
+		break;
+	case PROP_READONLY:
+		g_value_set_boolean (value, priv->readonly);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+	}
+}
+
+static void
+free_cache_values (GArray *array)
+{
+	g_array_free (array, TRUE);
+}
+
+TrackerDBIndex *
+tracker_db_index_new (const gchar *filename,
+		      gint	   min_bucket,
+		      gint	   max_bucket,
+		      gboolean	   readonly)
+{
+	TrackerDBIndex *index;
+
+	g_return_val_if_fail (filename != NULL, NULL);
+	g_return_val_if_fail (min_bucket > 0, NULL);
+	g_return_val_if_fail (min_bucket < max_bucket, NULL);
+
+	index = g_object_new (TRACKER_TYPE_DB_INDEX,
+			      "filename", filename,
+			      "min-bucket", min_bucket,
+			      "max-bucket", max_bucket,
+			      "readonly", readonly,
+			      NULL);
+
+	tracker_db_index_open (index);
+
+	return index;
+}
+
+void
+tracker_db_index_set_filename (TrackerDBIndex *index,
+			       const gchar    *filename)
+{
+	TrackerDBIndexPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_DB_INDEX (index));
+
+	priv = TRACKER_DB_INDEX_GET_PRIVATE (index);
+
+	if (priv->filename) {
+		g_free (priv->filename);
+	}
+
+	priv->filename = g_strdup (filename);
+
+	g_object_notify (G_OBJECT (index), "filename");
+}
+
+void
+tracker_db_index_set_min_bucket (TrackerDBIndex *index,
+				 gint		 min_bucket)
+{
+	TrackerDBIndexPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_DB_INDEX (index));
+
+	priv = TRACKER_DB_INDEX_GET_PRIVATE (index);
+
+	priv->min_bucket = min_bucket;
+
+	g_object_notify (G_OBJECT (index), "min-bucket");
+}
+
+void
+tracker_db_index_set_max_bucket (TrackerDBIndex *index,
+				 gint		 max_bucket)
+{
+	TrackerDBIndexPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_DB_INDEX (index));
+
+	priv = TRACKER_DB_INDEX_GET_PRIVATE (index);
+
+	priv->max_bucket = max_bucket;
+
+	g_object_notify (G_OBJECT (index), "max-bucket");
+}
+
+void
+tracker_db_index_set_reload (TrackerDBIndex *index,
+			     gboolean	     reload)
+{
+	TrackerDBIndexPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_DB_INDEX (index));
+
+	priv = TRACKER_DB_INDEX_GET_PRIVATE (index);
+
+	priv->reload = reload;
+
+	g_object_notify (G_OBJECT (index), "reload");
+}
+
+void
+tracker_db_index_set_readonly (TrackerDBIndex *index,
+			       gboolean        readonly)
+{
+	TrackerDBIndexPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_DB_INDEX (index));
+
+	priv = TRACKER_DB_INDEX_GET_PRIVATE (index);
+
+	priv->readonly = readonly;
+
+	g_object_notify (G_OBJECT (index), "readonly");
+}
+
+gboolean
+tracker_db_index_get_reload (TrackerDBIndex *index)
+{
+	TrackerDBIndexPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INDEX (index), FALSE);
+
+	priv = TRACKER_DB_INDEX_GET_PRIVATE (index);
+
+	return priv->reload;
+}
+
+gboolean
+tracker_db_index_get_readonly (TrackerDBIndex *index)
+{
+	TrackerDBIndexPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INDEX (index), TRUE);
+
+	priv = TRACKER_DB_INDEX_GET_PRIVATE (index);
+
+	return priv->readonly;
+}
+
+static inline gboolean
+has_word (TrackerDBIndex *index,
+	  const gchar	 *word)
+{
+	TrackerDBIndexPrivate *priv;
+	gchar		       buffer[32];
+	gint		       count;
+
+	priv = TRACKER_DB_INDEX_GET_PRIVATE (index);
+
+	count = dpgetwb (priv->index, word, -1, 0, 32, buffer);
+
+	return count > 7;
+}
+
+static inline gint
+count_hit_size_for_word (TrackerDBIndex *index,
+			 const gchar	*word)
+{
+	TrackerDBIndexPrivate *priv;
+	gint		     tsiz;
+
+	priv = TRACKER_DB_INDEX_GET_PRIVATE (index);
+
+	tsiz = dpvsiz (priv->index, word, -1);
+
+	return tsiz;
+}
+
+/* int levenshtein ()
+ * Original license: GNU Lesser Public License
+ * from the Dixit project, (http://dixit.sourceforge.net/)
+ * Author: Octavian Procopiuc <oprocopiuc gmail com>
+ * Created: July 25, 2004
+ * Copied into tracker, by Edward Duffy
+ */
+static gint
+levenshtein (const gchar *source,
+	     gchar	 *target,
+	     gint	  maxdist)
+{
+	gchar n, m;
+	gint  l;
+	gchar mincolval;
+	gchar matrix[51][51];
+	gchar j;
+	gchar i;
+	gchar cell;
+
+	l = strlen (source);
+	if (l > 50)
+		return -1;
+	n = l;
+
+	l = strlen (target);
+	if (l > 50)
+		return -1;
+	m = l;
+
+	if (maxdist == 0)
+		maxdist = MAX(m, n);
+	if (n == 0)
+		return MIN(m, maxdist);
+	if (m == 0)
+		return MIN(n, maxdist);
+
+	/* Store the min. value on each column, so that, if it
+	 * reaches. maxdist, we break early.
+	 */
+	for (j = 0; j <= m; j++)
+		matrix[0][(gint)j] = j;
+
+	for (i = 1; i <= n; i++) {
+		gchar s_i;
+
+		mincolval = MAX(m, i);
+		matrix[(gint)i][0] = i;
+
+		s_i = source[i-1];
+
+		for (j = 1; j <= m; j++) {
+			gchar t_j = target[j-1];
+			gchar cost = (s_i == t_j ? 0 : 1);
+			gchar above = matrix[i-1][(gint)j];
+			gchar left = matrix[(gint)i][j-1];
+			gchar diag = matrix[i-1][j-1];
+
+			cell = MIN(above + 1, MIN(left + 1, diag + cost));
+
+			/* Cover transposition, in addition to deletion,
+			 * insertion and substitution. This step is taken from:
+			 * Berghel, Hal ; Roach, David : "An Extension of Ukkonen's
+			 * Enhanced Dynamic Programming ASM Algorithm"
+			 * (http://www.acm.org/~hlb/publications/asm/asm.html)
+			 */
+			if (i > 2 && j > 2) {
+				gchar trans = matrix[i-2][j-2] + 1;
+
+				if (source[i-2] != t_j)
+					trans++;
+				if (s_i != target[j-2])
+					trans++;
+				if (cell > trans)
+					cell = trans;
+			}
+
+			mincolval = MIN(mincolval, cell);
+			matrix[(gint)i][(gint)j] = cell;
+		}
+
+		if (mincolval >= maxdist)
+			break;
+	}
+
+	if (i == n + 1) {
+		return (gint) matrix[(gint)n][(gint)m];
+	} else {
+		return maxdist;
+	}
+}
+
+static gint
+count_hits_for_word (TrackerDBIndex *index,
+		     const gchar    *str)
+{
+
+	gint tsiz;
+	gint hits = 0;
+
+	tsiz = count_hit_size_for_word (index, str);
+
+	if (tsiz == -1 ||
+	    tsiz % sizeof (TrackerDBIndexItem) != 0) {
+		return -1;
+	}
+
+	hits = tsiz / sizeof (TrackerDBIndexItem);
+
+	return hits;
+}
+
+static gboolean
+check_index_is_up_to_date (TrackerDBIndex *index)
+{
+	TrackerDBIndexPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INDEX (index), FALSE);
+
+	priv = TRACKER_DB_INDEX_GET_PRIVATE (index);
+
+	if (priv->reload) {
+		g_message ("Reloading index:'%s'", priv->filename);
+		tracker_db_index_close (index);
+	}
+
+	if (!priv->index) {
+		tracker_db_index_open (index);
+	}
+
+	return !priv->reload;
+}
+
+/* Use for deletes or updates of multiple entities when they are not
+ * new.
+ */
+static gboolean
+indexer_update_word (DEPOT	 *index,
+		     const gchar *word,
+		     GArray	 *new_hits)
+{
+	TrackerDBIndexItem *new_hit;
+	TrackerDBIndexItem *previous_hits;
+	GArray		   *pending_hits;
+	gboolean	    write_back;
+	gboolean	    edited;
+	gboolean	    result;
+	gint		    old_hit_count;
+	gint		    score;
+	gint		    tsiz;
+	guint		    j;
+
+	g_return_val_if_fail (index != NULL, FALSE);
+	g_return_val_if_fail (word != NULL, FALSE);
+	g_return_val_if_fail (new_hits != NULL, FALSE);
+
+	write_back = FALSE;
+	edited = FALSE;
+	old_hit_count = 0;
+	pending_hits = NULL;
+
+	previous_hits = (TrackerDBIndexItem *) dpget (index,
+						      word,
+						      -1,
+						      0,
+						      MAX_HIT_BUFFER,
+						      &tsiz);
+
+	/* New word in the index */
+	if (previous_hits == NULL) {
+		result = dpput (index,
+				word, -1,
+				(char *) new_hits->data,
+				new_hits->len * sizeof (TrackerDBIndexItem),
+				DP_DOVER);
+
+		if (!result) {
+			g_warning ("Could not store word '%s': %s", word, dperrmsg (dpecode));
+			return FALSE;
+		}
+
+		return TRUE;
+	}
+
+	/* Word already exists */
+	old_hit_count = tsiz / sizeof (TrackerDBIndexItem);
+
+	for (j = 0; j < new_hits->len; j++) {
+		guint left, right, center;
+
+		new_hit = &g_array_index (new_hits, TrackerDBIndexItem, j);
+		edited = FALSE;
+
+		left = 0;
+		right = old_hit_count;
+		center = (right - left) / 2;
+
+		/* New items are going to have always a higher service ID,
+		 * so the insertion is sorted, perform a binary search.
+		 */
+
+		do {
+			center += left;
+
+			if (new_hit->id > previous_hits[center].id) {
+				left = center;
+			} else if (new_hit->id < previous_hits[center].id) {
+				right = center;
+			} else if (new_hit->id == previous_hits[center].id) {
+				write_back = TRUE;
+
+				/* NB the paramter score can be negative */
+				score =  tracker_db_index_item_get_score (&previous_hits[center]);
+				score += tracker_db_index_item_get_score (new_hit);
+
+
+				/* Check for deletion */
+				if (score < 1) {
+					/* Shift all subsequent records in array down one place */
+					g_memmove (&previous_hits[center], &previous_hits[center + 1],
+						   (old_hit_count - center) * sizeof (TrackerDBIndexItem));
+					old_hit_count--;
+				} else {
+					guint32 service_type;
+
+					service_type =
+						tracker_db_index_item_get_service_type (&previous_hits[center]);
+					previous_hits[center].amalgamated =
+						tracker_db_index_item_calc_amalgamated (service_type,
+											score);
+				}
+
+				edited = TRUE;
+				break;
+			}
+
+			center = (right - left) / 2;
+		} while (center > 0);
+
+		/* Add hits that could not be updated directly here so
+		 * they can be appended later
+		 */
+		if (!edited) {
+			if (!pending_hits) {
+				pending_hits = g_array_new (FALSE,
+							    TRUE,
+							    sizeof (TrackerDBIndexItem));
+			}
+
+			g_array_append_val (pending_hits, *new_hit);
+		}
+	}
+
+	/* Write back if we have modded anything */
+	if (write_back) {
+		/* If the word has no hits, remove it! Otherwise
+		 * overwrite the value with the new hits array
+		 */
+		if (old_hit_count < 1) {
+			result = dpout (index, word, -1);
+		} else {
+			result = dpput (index,
+					word, -1,
+					(char *) previous_hits,
+					old_hit_count * sizeof (TrackerDBIndexItem),
+					DP_DOVER);
+		}
+
+		if (!result) {
+			g_warning ("Could not modify word '%s': %s", word, dperrmsg (dpecode));
+		}
+	}
+
+	/*  Append new occurences */
+	if (pending_hits) {
+		result = dpput (index,
+				word, -1,
+				(char*) pending_hits->data,
+				pending_hits->len * sizeof (TrackerDBIndexItem),
+				DP_DCAT);
+		g_array_free (pending_hits, TRUE);
+
+		if (!result) {
+			g_warning ("Could not insert pending word '%s': %s", word, dperrmsg (dpecode));
+		}
+	}
+
+	g_free (previous_hits);
+
+	return TRUE;
+}
+
+static gboolean
+cache_flush_item (gpointer key,
+		  gpointer value,
+		  gpointer user_data)
+{
+	GArray *array;
+	DEPOT  *index;
+	gchar  *word;
+
+	word = (gchar *) key;
+	array = (GArray *) value;
+	index = (DEPOT *) user_data;
+
+	/* Mark element for removal if succesfull insertion */
+
+	/**
+	 * FIXME:
+	 *
+	 * Not removing the word from the memory-queue is not a good solution.
+	 * That's because the only thing we'll achieve is letting this queue
+	 * grow until it starts succeeding again. Which might end up being
+	 * never. Making tracker-indexer both becoming increasingly slow and
+	 * start consuming increasing amounts of memory.
+	 **/
+
+	return indexer_update_word (index, word, array);
+}
+
+gboolean
+tracker_db_index_open (TrackerDBIndex *index)
+{
+	TrackerDBIndexPrivate *priv;
+	gint		       flags;
+	gint		       bucket_count;
+	gint		       rec_count;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INDEX (index), FALSE);
+
+	priv = TRACKER_DB_INDEX_GET_PRIVATE (index);
+
+	g_return_val_if_fail (priv->filename != NULL, FALSE);
+
+	if (priv->index) {
+		return TRUE;
+	}
+
+	g_debug ("Opening index:'%s' (%s)",
+		 priv->filename,
+		 priv->readonly ? "readonly" : "read/write");
+
+	if (priv->readonly) {
+		flags = DP_OREADER | DP_ONOLCK;
+	} else {
+		flags = DP_OWRITER | DP_OCREAT | DP_ONOLCK;
+	}
+
+	priv->index = dpopen (priv->filename,
+			      flags,
+			      priv->max_bucket);
+
+	if (!priv->index) {
+		if (!g_file_test (priv->filename, G_FILE_TEST_EXISTS)) {
+			g_debug ("Index doesnt exists yet:'%s'",
+				 priv->filename);
+		} else {
+			g_debug ("Index was not closed properly:'%s', %s",
+				 priv->filename,
+				 dperrmsg (dpecode));
+
+			if (dprepair (priv->filename)) {
+				priv->index = dpopen (priv->filename,
+						      flags,
+						      priv->max_bucket);
+			} else {
+				g_critical ("Corrupted index file %s.",
+					    priv->filename);
+			}
+		}
+	}
+
+	if (priv->index) {
+		dpsetalign (priv->index, 8);
+
+		/* Reoptimize database if bucket count < rec count */
+		bucket_count = dpbnum (priv->index);
+		rec_count = dprnum (priv->index);
+
+		g_debug ("Bucket count (max is %d) is %d and record count is %d",
+			 priv->max_bucket,
+			 bucket_count,
+			 rec_count);
+
+		priv->reload = FALSE;
+	} else {
+		priv->reload = TRUE;
+	}
+
+	return !priv->reload;
+}
+
+gboolean
+tracker_db_index_close (TrackerDBIndex *index)
+{
+	TrackerDBIndexPrivate *priv;
+	gboolean	       retval;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INDEX (index), FALSE);
+
+	priv = TRACKER_DB_INDEX_GET_PRIVATE (index);
+
+	retval = TRUE;
+
+	if (priv->index) {
+		g_debug ("Closing index:'%s'", priv->filename);
+
+		if (!dpclose (priv->index)) {
+			g_message ("Could not close index, %s",
+				   dperrmsg (dpecode));
+			retval = FALSE;
+		}
+
+		priv->index = NULL;
+	}
+
+	return retval;
+}
+
+void
+tracker_db_index_set_paused (TrackerDBIndex *index,
+			     gboolean	     paused)
+{
+	TrackerDBIndexPrivate *priv;
+
+	priv = TRACKER_DB_INDEX_GET_PRIVATE (index);
+
+	if (!priv->in_pause && paused) {
+		priv->in_pause = paused;
+		tracker_db_index_close (index);
+	} else if (priv->in_pause && !paused) {
+		priv->in_pause = paused;
+		tracker_db_index_open (index);
+	}
+}
+
+guint
+tracker_db_index_flush (TrackerDBIndex *index)
+{
+	TrackerDBIndexPrivate *priv;
+	guint		       size, removed_items;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INDEX (index), 0);
+
+	priv = TRACKER_DB_INDEX_GET_PRIVATE (index);
+
+	if (priv->in_flush) {
+		g_debug ("Index was already in the middle of a flush");
+		return 0;
+	}
+
+	if (!priv->index) {
+		g_debug ("Index was not open for flush, waiting...");
+		return 0;
+	}
+
+	priv->in_flush = TRUE;
+	size = g_hash_table_size (priv->cache);
+	removed_items = 0;
+
+	if (size > 0) {
+		GList *keys, *k;
+		gpointer value;
+
+		g_debug ("Flushing index with %d items in cache", size);
+
+		keys = g_hash_table_get_keys (priv->cache);
+
+		for (k = keys; k; k = k->next) {
+			value = g_hash_table_lookup (priv->cache, k->data);
+
+			if (cache_flush_item (k->data, value, priv->index)) {
+				g_hash_table_remove (priv->cache, k->data);
+				removed_items++;
+			}
+
+			g_main_context_iteration (NULL, FALSE);
+		}
+
+		g_list_free (keys);
+	}
+
+	priv->in_flush = FALSE;
+
+	return removed_items;
+}
+
+guint32
+tracker_db_index_get_size (TrackerDBIndex *index)
+{
+	TrackerDBIndexPrivate *priv;
+	guint32		       size;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INDEX (index), 0);
+
+	if (!check_index_is_up_to_date (index)) {
+		return 0;
+	}
+
+	priv = TRACKER_DB_INDEX_GET_PRIVATE (index);
+
+	size = dpfsiz (priv->index);
+
+	return size;
+}
+
+gchar *
+tracker_db_index_get_suggestion (TrackerDBIndex *index,
+				 const gchar	*term,
+				 gint		 maxdist)
+{
+	TrackerDBIndexPrivate *priv;
+	gchar		    *str;
+	gint		     dist;
+	gchar		    *winner_str;
+	gint		     winner_dist;
+	gint		     hits;
+	GTimeVal	     start, current;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INDEX (index), NULL);
+	g_return_val_if_fail (term != NULL, NULL);
+	g_return_val_if_fail (maxdist >= 0, NULL);
+
+	if (!check_index_is_up_to_date (index)) {
+		return NULL;
+	}
+
+	priv = TRACKER_DB_INDEX_GET_PRIVATE (index);
+
+	winner_str = g_strdup (term);
+	winner_dist = G_MAXINT;  /* Initialize to the worst case */
+
+	dpiterinit (priv->index);
+
+	g_get_current_time (&start);
+
+	str = dpiternext (priv->index, NULL);
+
+	while (str != NULL) {
+		dist = levenshtein (term, str, 0);
+
+		if (dist != -1 &&
+		    dist < maxdist &&
+		    dist < winner_dist) {
+			hits = count_hits_for_word (index, str);
+
+			if (hits < 0) {
+				g_free (winner_str);
+				g_free (str);
+
+				return NULL;
+			} else if (hits > 0) {
+				g_free (winner_str);
+				winner_str = g_strdup (str);
+				winner_dist = dist;
+			} else {
+				g_message ("No hits for:'%s'!", str);
+			}
+		}
+
+		g_free (str);
+
+		g_get_current_time (&current);
+
+		/* 2 second time out */
+		if (current.tv_sec - start.tv_sec >= 2) {
+			g_message ("Timed out in %s, not collecting more suggestions.",
+				   __FUNCTION__);
+			break;
+		}
+
+		str = dpiternext (priv->index, NULL);
+	}
+
+	return winner_str;
+}
+
+TrackerDBIndexItem *
+tracker_db_index_get_word_hits (TrackerDBIndex *index,
+				const gchar    *word,
+				guint	       *count)
+{
+	TrackerDBIndexPrivate *priv;
+	TrackerDBIndexItem    *details;
+	gint		       tsiz;
+	gchar		      *tmp;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INDEX (index), NULL);
+	g_return_val_if_fail (word != NULL, NULL);
+
+	priv = TRACKER_DB_INDEX_GET_PRIVATE (index);
+
+	if (!check_index_is_up_to_date (index)) {
+		return NULL;
+	}
+
+
+	details = NULL;
+
+	if (count) {
+		*count = 0;
+	}
+
+	if ((tmp = dpget (priv->index, word, -1, 0, MAX_HIT_BUFFER, &tsiz)) != NULL) {
+		if (tsiz >= (gint) sizeof (TrackerDBIndexItem)) {
+			details = (TrackerDBIndexItem *) tmp;
+
+			if (count) {
+				*count = tsiz / sizeof (TrackerDBIndexItem);
+			}
+		}
+	}
+
+
+	return details;
+}
+
+void
+tracker_db_index_add_word (TrackerDBIndex *index,
+			   const gchar	  *word,
+			   guint32	   service_id,
+			   gint		   service_type,
+			   gint		   weight)
+{
+	TrackerDBIndexPrivate *priv;
+	TrackerDBIndexItem     elem;
+	TrackerDBIndexItem    *current;
+	GArray		      *array;
+	guint		       i, new_score;
+
+	g_return_if_fail (TRACKER_IS_DB_INDEX (index));
+	g_return_if_fail (word != NULL);
+
+	priv = TRACKER_DB_INDEX_GET_PRIVATE (index);
+
+	g_return_if_fail (priv->in_flush == FALSE);
+
+	elem.id = service_id;
+	elem.amalgamated = tracker_db_index_item_calc_amalgamated (service_type, weight);
+
+	array = g_hash_table_lookup (priv->cache, word);
+
+	if (!array) {
+		/* Create the array if it didn't exist (first time we
+		 * find the word)
+		 */
+		array = g_array_new (FALSE, TRUE, sizeof (TrackerDBIndexItem));
+		g_hash_table_insert (priv->cache, g_strdup (word), array);
+		g_array_append_val (array, elem);
+
+		return;
+	}
+
+	/* It is not the first time we find the word */
+	for (i = 0; i < array->len; i++) {
+		current = &g_array_index (array, TrackerDBIndexItem, i);
+
+		if (current->id == service_id) {
+			/* The word was already found in the same
+			 * service_id (file), increase score
+			 */
+			new_score = tracker_db_index_item_get_score (current) + weight;
+			if (new_score < 1) {
+				array = g_array_remove_index (array, i);
+				if (array->len == 0) {
+					g_hash_table_remove (priv->cache, word);
+				}
+			} else {
+				guint32 service_type;
+
+				service_type =
+					tracker_db_index_item_get_service_type (current);
+				current->amalgamated =
+					tracker_db_index_item_calc_amalgamated (service_type,
+										new_score);
+			}
+
+
+			return;
+		}
+	}
+
+	/* First time in the file */
+	g_array_append_val (array, elem);
+
+}
+
+/*
+ * UNUSED
+ *
+ *  Use to delete dud hits for a word - dud_list is a list of
+ * TrackerSearchHit structs.
+ */
+gboolean
+tracker_db_index_remove_dud_hits (TrackerDBIndex *index,
+				  const gchar	 *word,
+				  GSList	 *dud_list)
+{
+	TrackerDBIndexPrivate *priv;
+	gchar		      *tmp;
+	gint		       tsiz;
+	gboolean	       retval = FALSE;
+
+	g_return_val_if_fail (index, FALSE);
+	g_return_val_if_fail (word, FALSE);
+	g_return_val_if_fail (dud_list, FALSE);
+
+	if (!check_index_is_up_to_date (index)) {
+		return TRUE;
+	}
+
+	priv = TRACKER_DB_INDEX_GET_PRIVATE (index);
+
+	g_return_val_if_fail (priv->index, FALSE);
+
+
+	/* Check if existing record is there  */
+	tmp = dpget (priv->index,
+		     word,
+		     -1,
+		     0,
+		     MAX_HIT_BUFFER,
+		     &tsiz);
+
+	if (!tmp) {
+		return FALSE;
+	}
+
+	if (tsiz >= (int) sizeof (TrackerDBIndexItem)) {
+		TrackerDBIndexItem *details;
+		gint		    wi, i, pnum;
+
+		details = (TrackerDBIndexItem *) tmp;
+		pnum = tsiz / sizeof (TrackerDBIndexItem);
+		wi = 0;
+
+		for (i = 0; i < pnum; i++) {
+			GSList *lst;
+
+			for (lst = dud_list; lst; lst = lst->next) {
+				TrackerDBIndexItemRank *rank = lst->data;
+
+				if (!rank) {
+					continue;
+				}
+
+				if (details[i].id == rank->service_id) {
+					gint k;
+
+					/* Shift all subsequent
+					 * records in array down one
+					 * place.
+					 */
+					for (k = i + 1; k < pnum; k++) {
+						details[k - 1] = details[k];
+					}
+
+					/* Make size of array one size
+					 * smaller.
+					 */
+					tsiz -= sizeof (TrackerDBIndexItem);
+					pnum--;
+
+					break;
+				}
+			}
+		}
+
+		dpput (priv->index, word, -1, (gchar *) details, tsiz, DP_DOVER);
+
+		retval = TRUE;
+	}
+
+	g_free (tmp);
+
+
+	return retval;
+}

Added: trunk/src/libtracker-db/tracker-db-index.h
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-db/tracker-db-index.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,96 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKER_DB_INDEX_H__
+#define __TRACKER_DB_INDEX_H__
+
+G_BEGIN_DECLS
+
+#include <glib-object.h>
+
+#include <libtracker-db/tracker-db-index-item.h>
+
+
+#define TRACKER_TYPE_DB_INDEX	      (tracker_db_index_get_type())
+#define TRACKER_DB_INDEX(o)	      (G_TYPE_CHECK_INSTANCE_CAST ((o), TRACKER_TYPE_DB_INDEX, TrackerDBIndex))
+#define TRACKER_DB_INDEX_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c), TRACKER_TYPE_DB_INDEX, TrackerDBIndexClass))
+#define TRACKER_IS_DB_INDEX(o)	      (G_TYPE_CHECK_INSTANCE_TYPE ((o), TRACKER_TYPE_DB_INDEX))
+#define TRACKER_IS_DB_INDEX_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c), TRACKER_TYPE_DB_INDEX))
+#define TRACKER_DB_INDEX_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), TRACKER_TYPE_DB_INDEX, TrackerDBIndexClass))
+
+typedef struct TrackerDBIndex		 TrackerDBIndex;
+typedef struct TrackerDBIndexClass	 TrackerDBIndexClass;
+typedef struct TrackerDBIndexWordDetails TrackerDBIndexWordDetails;
+
+struct TrackerDBIndex {
+	GObject parent;
+};
+
+struct TrackerDBIndexClass {
+	GObjectClass parent_class;
+};
+
+GType		    tracker_db_index_get_type	     (void);
+TrackerDBIndex *    tracker_db_index_new	     (const gchar    *filename,
+						      gint	      min_bucket,
+						      gint	      max_bucket,
+						      gboolean	      readonly);
+void		    tracker_db_index_set_filename    (TrackerDBIndex *index,
+						      const gchar    *filename);
+void		    tracker_db_index_set_min_bucket  (TrackerDBIndex *index,
+						      gint	      min_bucket);
+void		    tracker_db_index_set_max_bucket  (TrackerDBIndex *index,
+						      gint	      max_bucket);
+void		    tracker_db_index_set_reload      (TrackerDBIndex *index,
+						      gboolean	      reload);
+void		    tracker_db_index_set_readonly    (TrackerDBIndex *index,
+						      gboolean	      readonly);
+gboolean	    tracker_db_index_get_reload      (TrackerDBIndex *index);
+gboolean	    tracker_db_index_get_readonly    (TrackerDBIndex *index);
+
+void		    tracker_db_index_set_paused      (TrackerDBIndex *index,
+						      gboolean paused);
+
+/* Open/Close/Flush */
+gboolean	    tracker_db_index_open	     (TrackerDBIndex *index);
+gboolean	    tracker_db_index_close	     (TrackerDBIndex *index);
+guint		    tracker_db_index_flush	     (TrackerDBIndex *index);
+
+/* Using the index */
+guint32		    tracker_db_index_get_size	     (TrackerDBIndex *index);
+char *		    tracker_db_index_get_suggestion  (TrackerDBIndex *index,
+						      const gchar    *term,
+						      gint	      maxdist);
+TrackerDBIndexItem *tracker_db_index_get_word_hits   (TrackerDBIndex *index,
+						      const gchar    *word,
+						      guint	     *count);
+void		    tracker_db_index_add_word	     (TrackerDBIndex *index,
+						      const gchar    *word,
+						      guint32	      service_id,
+						      gint	      service_type,
+						      gint	      weight);
+gboolean	    tracker_db_index_remove_dud_hits (TrackerDBIndex *index,
+						      const gchar    *word,
+						      GSList	     *dud_list);
+
+G_END_DECLS
+
+#endif /* __TRACKER_DB_INDEX_H__ */

Added: trunk/src/libtracker-db/tracker-db-interface-sqlite.c
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-db/tracker-db-interface-sqlite.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,626 @@
+/* Tracker - Sqlite implementation
+ * Copyright (C) 2008 Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <sqlite3.h>
+#include "tracker-db-interface-sqlite.h"
+
+#define TRACKER_DB_INTERFACE_SQLITE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRACKER_TYPE_DB_INTERFACE_SQLITE, TrackerDBInterfaceSqlitePrivate))
+
+typedef struct TrackerDBInterfaceSqlitePrivate TrackerDBInterfaceSqlitePrivate;
+typedef struct SqliteFunctionData SqliteFunctionData;
+
+struct TrackerDBInterfaceSqlitePrivate {
+	gchar *filename;
+	sqlite3 *db;
+
+	GHashTable *statements;
+	GHashTable *procedures;
+
+	GSList *function_data;
+
+	guint in_transaction : 1;
+};
+
+struct SqliteFunctionData {
+	TrackerDBInterface *interface;
+	TrackerDBFunc func;
+};
+
+static void tracker_db_interface_sqlite_iface_init (TrackerDBInterfaceIface *iface);
+
+enum {
+	PROP_0,
+	PROP_FILENAME,
+	PROP_IN_TRANSACTION
+};
+
+G_DEFINE_TYPE_WITH_CODE (TrackerDBInterfaceSqlite, tracker_db_interface_sqlite, G_TYPE_OBJECT,
+			 G_IMPLEMENT_INTERFACE (TRACKER_TYPE_DB_INTERFACE,
+						tracker_db_interface_sqlite_iface_init))
+
+static GObject *
+tracker_db_interface_sqlite_constructor (GType			type,
+					 guint			n_construct_properties,
+					 GObjectConstructParam *construct_params)
+{
+	GObject *object;
+	TrackerDBInterfaceSqlitePrivate *priv;
+
+	object = (* G_OBJECT_CLASS (tracker_db_interface_sqlite_parent_class)->constructor) (type,
+											     n_construct_properties,
+											     construct_params);
+	priv = TRACKER_DB_INTERFACE_SQLITE_GET_PRIVATE (object);
+	g_assert (priv->filename != NULL);
+
+	if (sqlite3_open (priv->filename, &priv->db) != SQLITE_OK) {
+		g_critical ("Could not open sqlite3 database:'%s'", priv->filename);
+	} else {
+		g_message ("Opened sqlite3 database:'%s'", priv->filename);
+	}
+
+	sqlite3_extended_result_codes (priv->db, 0);
+	sqlite3_busy_timeout (priv->db, 10000000);
+
+	return object;
+}
+
+static void
+tracker_db_interface_sqlite_set_property (GObject	*object,
+					  guint		 prop_id,
+					  const GValue	*value,
+					  GParamSpec	*pspec)
+{
+	TrackerDBInterfaceSqlitePrivate *priv;
+
+	priv = TRACKER_DB_INTERFACE_SQLITE_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_FILENAME:
+		priv->filename = g_value_dup_string (value);
+		break;
+	case PROP_IN_TRANSACTION:
+		priv->in_transaction = g_value_get_boolean (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+	}
+}
+
+static void
+tracker_db_interface_sqlite_get_property (GObject    *object,
+					  guint       prop_id,
+					  GValue     *value,
+					  GParamSpec *pspec)
+{
+	TrackerDBInterfaceSqlitePrivate *priv;
+
+	priv = TRACKER_DB_INTERFACE_SQLITE_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_FILENAME:
+		g_value_set_string (value, priv->filename);
+		break;
+	case PROP_IN_TRANSACTION:
+		g_value_set_boolean (value, priv->in_transaction);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+	}
+}
+
+static void
+tracker_db_interface_sqlite_finalize (GObject *object)
+{
+	TrackerDBInterfaceSqlitePrivate *priv;
+
+	priv = TRACKER_DB_INTERFACE_SQLITE_GET_PRIVATE (object);
+
+	g_hash_table_destroy (priv->statements);
+
+	if (priv->procedures) {
+		g_hash_table_unref (priv->procedures);
+	}
+
+	g_slist_foreach (priv->function_data, (GFunc) g_free, NULL);
+	g_slist_free (priv->function_data);
+
+	sqlite3_close (priv->db);
+	g_message ("Closed sqlite3 database:'%s'", priv->filename);
+
+	g_free (priv->filename);
+
+	G_OBJECT_CLASS (tracker_db_interface_sqlite_parent_class)->finalize (object);
+}
+
+static void
+tracker_db_interface_sqlite_class_init (TrackerDBInterfaceSqliteClass *class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+	object_class->constructor = tracker_db_interface_sqlite_constructor;
+	object_class->set_property = tracker_db_interface_sqlite_set_property;
+	object_class->get_property = tracker_db_interface_sqlite_get_property;
+	object_class->finalize = tracker_db_interface_sqlite_finalize;
+
+	g_object_class_install_property (object_class,
+					 PROP_FILENAME,
+					 g_param_spec_string ("filename",
+							      "DB filename",
+							      "DB filename",
+							      NULL,
+							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+	/* Override properties from interface */
+	g_object_class_override_property (object_class,
+					  PROP_IN_TRANSACTION,
+					  "in-transaction");
+
+	g_type_class_add_private (object_class,
+				  sizeof (TrackerDBInterfaceSqlitePrivate));
+}
+
+static void
+tracker_db_interface_sqlite_init (TrackerDBInterfaceSqlite *db_interface)
+{
+	TrackerDBInterfaceSqlitePrivate *priv;
+
+	priv = TRACKER_DB_INTERFACE_SQLITE_GET_PRIVATE (db_interface);
+
+	priv->statements = g_hash_table_new_full (g_str_hash, g_str_equal,
+						  (GDestroyNotify) g_free,
+						  (GDestroyNotify) sqlite3_finalize);
+}
+
+static void
+add_row (TrackerDBResultSet *result_set,
+	 sqlite3_stmt	    *stmt)
+{
+	gint cols, i;
+
+	cols = sqlite3_column_count (stmt);
+	_tracker_db_result_set_append (result_set);
+
+	for (i = 0; i < cols; i++) {
+		GValue value = { 0, };
+		gint col_type;
+
+		col_type = sqlite3_column_type (stmt, i);
+
+		switch (col_type) {
+		case SQLITE_TEXT:
+			g_value_init (&value, G_TYPE_STRING);
+			g_value_set_string (&value, (gchar *) sqlite3_column_text (stmt, i));
+			break;
+		case SQLITE_INTEGER:
+			g_value_init (&value, G_TYPE_INT);
+			g_value_set_int (&value, sqlite3_column_int (stmt, i));
+			break;
+		case SQLITE_FLOAT:
+			g_value_init (&value, G_TYPE_DOUBLE);
+			g_value_set_double (&value, sqlite3_column_double (stmt, i));
+			break;
+		case SQLITE_NULL:
+			/* just ignore NULLs */
+			break;
+		default:
+			g_critical ("Unknown sqlite3 database column type:%d", col_type);
+		}
+
+		if (G_VALUE_TYPE (&value) != G_TYPE_INVALID) {
+			_tracker_db_result_set_set_value (result_set, i, &value);
+			g_value_unset (&value);
+		}
+	}
+}
+
+static void
+internal_sqlite3_function (sqlite3_context *context,
+			   int		    argc,
+			   sqlite3_value   *argv[])
+{
+	SqliteFunctionData *data;
+	GValue *values, result;
+	GByteArray *blob_array;
+	gint i;
+
+	data = (SqliteFunctionData *) sqlite3_user_data (context);
+	values = g_new0 (GValue, argc);
+
+	/* Transform the arguments */
+	for (i = 0; i < argc; i++) {
+		switch (sqlite3_value_type (argv[i])) {
+		case SQLITE_TEXT:
+			g_value_init (&values[i], G_TYPE_STRING);
+			g_value_set_string (&values[i], (gchar *) sqlite3_value_text (argv[i]));
+			break;
+		case SQLITE_INTEGER:
+			g_value_init (&values[i], G_TYPE_INT);
+			g_value_set_int (&values[i], sqlite3_value_int (argv[i]));
+			break;
+		case SQLITE_FLOAT:
+			g_value_init (&values[i], G_TYPE_DOUBLE);
+			g_value_set_double (&values[i], sqlite3_value_double (argv[i]));
+			break;
+		case SQLITE_BLOB: {
+			gconstpointer blob;
+			gint size;
+
+			blob = sqlite3_value_blob (argv[i]);
+			size = sqlite3_value_bytes (argv[i]);
+
+			blob_array = g_byte_array_sized_new (size);
+			g_byte_array_append (blob_array, blob, size);
+
+			g_value_init (&values[i], TRACKER_TYPE_DB_BLOB);
+			g_value_take_boxed (&values[i], blob_array);
+
+			break;
+		}
+		default:
+			g_critical ("Unknown sqlite3 database value type:%d",
+				    sqlite3_value_type (argv[i]));
+		}
+	}
+
+	/* Call the function */
+	result = data->func (data->interface, argc, values);
+
+	/* And return something appropriate to the context */
+	if (G_VALUE_HOLDS_INT (&result)) {
+		sqlite3_result_int (context, g_value_get_int (&result));
+	} else if (G_VALUE_HOLDS_DOUBLE (&result)) {
+		sqlite3_result_double (context, g_value_get_double (&result));
+	} else if (G_VALUE_HOLDS_STRING (&result)) {
+		sqlite3_result_text (context,
+				     g_value_dup_string (&result),
+				     -1, g_free);
+	} else if (G_VALUE_HOLDS (&result, TRACKER_TYPE_DB_BLOB)) {
+		blob_array = g_value_get_boxed (&result);
+		sqlite3_result_blob (context,
+				     g_memdup (blob_array->data, blob_array->len),
+				     blob_array->len,
+				     g_free);
+	} else if (G_VALUE_HOLDS (&result, G_TYPE_INVALID)) {
+		sqlite3_result_null (context);
+	} else {
+		g_critical ("Sqlite3 returned type not managed:'%s'",
+			    G_VALUE_TYPE_NAME (&result));
+		sqlite3_result_null (context);
+	}
+
+	/* Now free all this mess */
+	for (i = 0; i < argc; i++) {
+		g_value_unset (&values[i]);
+	}
+
+	if (! G_VALUE_HOLDS (&result, G_TYPE_INVALID)) {
+		g_value_unset (&result);
+	}
+
+	g_free (values);
+}
+
+static void
+tracker_db_interface_sqlite_set_procedure_table (TrackerDBInterface *db_interface,
+						 GHashTable	    *procedure_table)
+{
+	TrackerDBInterfaceSqlitePrivate *priv;
+
+	priv = TRACKER_DB_INTERFACE_SQLITE_GET_PRIVATE (db_interface);
+
+	if (priv->procedures) {
+		g_hash_table_unref (priv->procedures);
+		priv->procedures = NULL;
+	}
+
+	if (procedure_table) {
+		priv->procedures = g_hash_table_ref (procedure_table);
+	}
+}
+
+static void
+foreach_print_error (gpointer key, gpointer value, gpointer stmt)
+{
+	if (value == stmt)
+		g_print ("In %s\n", (char*) key);
+}
+
+static TrackerDBResultSet *
+create_result_set_from_stmt (TrackerDBInterfaceSqlite  *interface,
+			     sqlite3_stmt	       *stmt,
+			     GError		      **error)
+{
+	TrackerDBInterfaceSqlitePrivate *priv;
+	TrackerDBResultSet *result_set = NULL;
+	gint columns, result, busy_count;
+
+	priv = TRACKER_DB_INTERFACE_SQLITE_GET_PRIVATE (interface);
+	columns = sqlite3_column_count (stmt);
+	result = SQLITE_OK;
+	busy_count = 0;
+
+	while (result == SQLITE_OK  ||
+	       result == SQLITE_ROW ||
+	       result == SQLITE_BUSY) {
+
+		result = sqlite3_step (stmt);
+
+		switch (result) {
+		case SQLITE_ERROR:
+			sqlite3_reset (stmt);
+			break;
+		case SQLITE_BUSY:
+			busy_count++;
+
+			if (busy_count > 100000) {
+				/* tracker_error ("ERROR: excessive busy count in query %s", query); */
+				busy_count = 0;
+			}
+
+			if (busy_count > 50) {
+				g_usleep (g_random_int_range (1000, busy_count * 200));
+			} else {
+				g_usleep (100);
+			}
+
+			break;
+		case SQLITE_ROW:
+			if (G_UNLIKELY (!result_set)) {
+				result_set = _tracker_db_result_set_new (columns);
+			}
+
+			add_row (result_set, stmt);
+			break;
+		}
+	}
+
+	if (result != SQLITE_DONE) {
+
+		g_hash_table_foreach (priv->statements, foreach_print_error, stmt);
+
+		if (result == SQLITE_CORRUPT) {
+			g_critical ("Sqlite3 database:'%s' is corrupt, can not live without it",
+				    priv->filename);
+			g_assert_not_reached ();
+		}
+
+		if (!error) {
+			g_warning (sqlite3_errmsg (priv->db));
+		} else {
+			g_set_error (error,
+				     TRACKER_DB_INTERFACE_ERROR,
+				     TRACKER_DB_QUERY_ERROR,
+				     sqlite3_errmsg (priv->db));
+		}
+
+		/* If there was an error, result set may be invalid or incomplete */
+		if (result_set) {
+			g_object_unref (result_set);
+		}
+
+		return NULL;
+	}
+
+	return result_set;
+}
+
+static sqlite3_stmt *
+get_stored_stmt (TrackerDBInterfaceSqlite *db_interface,
+		 const gchar		  *procedure_name)
+{
+	TrackerDBInterfaceSqlitePrivate *priv;
+	sqlite3_stmt *stmt;
+	gint result;
+
+	priv = TRACKER_DB_INTERFACE_SQLITE_GET_PRIVATE (db_interface);
+	stmt = g_hash_table_lookup (priv->statements, procedure_name);
+
+	if (!stmt || sqlite3_expired (stmt) != 0) {
+		const gchar *procedure;
+
+		procedure = g_hash_table_lookup (priv->procedures, procedure_name);
+
+		if (!procedure) {
+			g_critical ("Sqlite3 prepared query:'%s' was not found",
+				    procedure_name);
+			return NULL;
+		}
+
+		result = sqlite3_prepare_v2 (priv->db, procedure, -1, &stmt, NULL);
+
+		if (result == SQLITE_OK && stmt) {
+			g_hash_table_insert (priv->statements,
+					     g_strdup (procedure_name),
+					     stmt);
+		}
+	} else {
+		sqlite3_reset (stmt);
+	}
+
+	return stmt;
+}
+
+static TrackerDBResultSet *
+tracker_db_interface_sqlite_execute_procedure (TrackerDBInterface  *db_interface,
+					       GError		  **error,
+					       const gchar	   *procedure_name,
+					       va_list		    args)
+{
+	TrackerDBInterfaceSqlitePrivate *priv;
+	sqlite3_stmt *stmt;
+	gint stmt_args, n_args;
+	gchar *str;
+
+	priv = TRACKER_DB_INTERFACE_SQLITE_GET_PRIVATE (db_interface);
+	stmt = get_stored_stmt (TRACKER_DB_INTERFACE_SQLITE (db_interface), procedure_name);
+	stmt_args = sqlite3_bind_parameter_count (stmt);
+
+	for (n_args = 1; n_args <= stmt_args; n_args++) {
+		str = va_arg (args, gchar *);
+		sqlite3_bind_text (stmt, n_args, str, -1, SQLITE_STATIC);
+	}
+
+	return create_result_set_from_stmt (TRACKER_DB_INTERFACE_SQLITE (db_interface), stmt, error);
+}
+
+static TrackerDBResultSet *
+tracker_db_interface_sqlite_execute_procedure_len (TrackerDBInterface  *db_interface,
+						   GError	      **error,
+						   const gchar	       *procedure_name,
+						   va_list		args)
+{
+	TrackerDBInterfaceSqlitePrivate *priv;
+	sqlite3_stmt *stmt;
+	gint stmt_args, n_args, len;
+	gchar *str;
+
+	priv = TRACKER_DB_INTERFACE_SQLITE_GET_PRIVATE (db_interface);
+	stmt = get_stored_stmt (TRACKER_DB_INTERFACE_SQLITE (db_interface), procedure_name);
+	stmt_args = sqlite3_bind_parameter_count (stmt);
+
+	for (n_args = 1; n_args <= stmt_args; n_args++) {
+		str = va_arg (args, gchar *);
+		len = va_arg (args, gint);
+
+		if (len == -1) {
+			/* Assume we're dealing with strings */
+			sqlite3_bind_text (stmt, n_args, str, len, SQLITE_STATIC);
+		} else {
+			/* Deal with it as a blob */
+			sqlite3_bind_blob (stmt, n_args, str, len, SQLITE_STATIC);
+		}
+	}
+
+	return create_result_set_from_stmt (TRACKER_DB_INTERFACE_SQLITE (db_interface), stmt, error);
+}
+
+static TrackerDBResultSet *
+tracker_db_interface_sqlite_execute_query (TrackerDBInterface  *db_interface,
+					   GError	      **error,
+					   const gchar	       *query)
+{
+	TrackerDBInterfaceSqlitePrivate *priv;
+	TrackerDBResultSet *result_set;
+	sqlite3_stmt *stmt;
+	int retval;
+
+	priv = TRACKER_DB_INTERFACE_SQLITE_GET_PRIVATE (db_interface);
+
+	retval = sqlite3_prepare_v2 (priv->db, query, -1, &stmt, NULL);
+
+	if (retval != SQLITE_OK) {
+		g_set_error (error,
+			     TRACKER_DB_INTERFACE_ERROR,
+			     TRACKER_DB_QUERY_ERROR,
+			     sqlite3_errmsg (priv->db));
+		return NULL;
+	} else if (stmt == NULL) {
+		g_set_error (error,
+			     TRACKER_DB_INTERFACE_ERROR,
+			     TRACKER_DB_QUERY_ERROR,
+			     "Could not prepare SQL statement:'%s'",
+			     query);
+
+		return NULL;
+	}
+
+	result_set = create_result_set_from_stmt (TRACKER_DB_INTERFACE_SQLITE (db_interface), stmt, error);
+	sqlite3_finalize (stmt);
+
+	return result_set;
+}
+
+static void
+tracker_db_interface_sqlite_iface_init (TrackerDBInterfaceIface *iface)
+{
+	iface->set_procedure_table = tracker_db_interface_sqlite_set_procedure_table;
+	iface->execute_procedure = tracker_db_interface_sqlite_execute_procedure;
+	iface->execute_procedure_len = tracker_db_interface_sqlite_execute_procedure_len;
+	iface->execute_query = tracker_db_interface_sqlite_execute_query;
+}
+
+TrackerDBInterface *
+tracker_db_interface_sqlite_new (const gchar *filename)
+{
+	return g_object_new (TRACKER_TYPE_DB_INTERFACE_SQLITE,
+			     "filename", filename,
+			     NULL);
+}
+
+static gint
+collation_function (gpointer	  data,
+		    int		  len1,
+		    gconstpointer str1,
+		    int		  len2,
+		    gconstpointer str2)
+{
+	TrackerDBCollationFunc func;
+
+	func = (TrackerDBCollationFunc) data;
+
+	return (func) ((gchar *) str1, len1, (gchar *) str2, len2);
+}
+
+void
+tracker_db_interface_sqlite_create_function (TrackerDBInterface *interface,
+					     const gchar	*name,
+					     TrackerDBFunc	 func,
+					     gint		 n_args)
+{
+	SqliteFunctionData *data;
+	TrackerDBInterfaceSqlitePrivate *priv;
+
+	priv = TRACKER_DB_INTERFACE_SQLITE_GET_PRIVATE (interface);
+
+	data = g_new0 (SqliteFunctionData, 1);
+	data->interface = interface;
+	data->func = func;
+
+	priv->function_data = g_slist_prepend (priv->function_data, data);
+
+	sqlite3_create_function (priv->db, name, n_args, SQLITE_ANY, data, &internal_sqlite3_function, NULL, NULL);
+}
+
+gboolean
+tracker_db_interface_sqlite_set_collation_function (TrackerDBInterfaceSqlite *interface,
+						    const gchar		     *name,
+						    TrackerDBCollationFunc    func)
+{
+	TrackerDBInterfaceSqlitePrivate *priv;
+	gint result;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE_SQLITE (interface), FALSE);
+
+	priv = TRACKER_DB_INTERFACE_SQLITE_GET_PRIVATE (interface);
+
+	result = sqlite3_create_collation (priv->db, name, SQLITE_UTF8, func, &collation_function);
+
+	return (result == SQLITE_OK);
+}
+
+gint64
+tracker_db_interface_sqlite_get_last_insert_id (TrackerDBInterfaceSqlite *interface)
+{
+	TrackerDBInterfaceSqlitePrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE_SQLITE (interface), 0);
+
+	priv = TRACKER_DB_INTERFACE_SQLITE_GET_PRIVATE (interface);
+
+	return (gint64) sqlite3_last_insert_rowid (priv->db);
+}

Added: trunk/src/libtracker-db/tracker-db-interface-sqlite.h
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-db/tracker-db-interface-sqlite.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,70 @@
+/* Tracker - Sqlite implementation
+ * Copyright (C) 2008 Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKER_DB_INTERFACE_SQLITE_H__
+#define __TRACKER_DB_INTERFACE_SQLITE_H__
+
+#include "tracker-db-interface.h"
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_DB_INTERFACE_SQLITE	 (tracker_db_interface_sqlite_get_type ())
+#define TRACKER_DB_INTERFACE_SQLITE(o)		 (G_TYPE_CHECK_INSTANCE_CAST ((o), TRACKER_TYPE_DB_INTERFACE_SQLITE, TrackerDBInterfaceSqlite))
+#define TRACKER_DB_INTERFACE_SQLITE_CLASS(c)	 (G_TYPE_CHECK_CLASS_CAST ((c),    TRACKER_TYPE_DB_INTERFACE_SQLITE, TrackerDBInterfaceSqliteClass))
+#define TRACKER_IS_DB_INTERFACE_SQLITE(o)	 (G_TYPE_CHECK_INSTANCE_TYPE ((o), TRACKER_TYPE_DB_INTERFACE_SQLITE))
+#define TRACKER_IS_DB_INTERFACE_SQLITE_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((o),    TRACKER_TYPE_DB_INTERFACE_SQLITE))
+#define TRACKER_DB_INTERFACE_SQLITE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o),  TRACKER_TYPE_DB_INTERFACE_SQLITE, TrackerDBInterfaceSqliteClass))
+
+typedef struct TrackerDBInterfaceSqlite      TrackerDBInterfaceSqlite;
+typedef struct TrackerDBInterfaceSqliteClass TrackerDBInterfaceSqliteClass;
+
+typedef gint (* TrackerDBCollationFunc) (gchar *str1,
+					 gint	len1,
+					 gchar *str2,
+					 gint	len2);
+typedef GValue (* TrackerDBFunc) (TrackerDBInterface *interface,
+				  gint		      argc,
+				  GValue	      argv[]);
+
+struct TrackerDBInterfaceSqlite {
+	GObject parent_instance;
+};
+
+struct TrackerDBInterfaceSqliteClass {
+	GObjectClass parent_class;
+};
+
+GType tracker_db_interface_sqlite_get_type (void);
+
+TrackerDBInterface * tracker_db_interface_sqlite_new (const gchar *filename);
+
+void		     tracker_db_interface_sqlite_create_function	(TrackerDBInterface	  *interface,
+									 const gchar		  *name,
+									 TrackerDBFunc		   func,
+									 gint			   n_args);
+gboolean	     tracker_db_interface_sqlite_set_collation_function (TrackerDBInterfaceSqlite *interface,
+									 const gchar		  *name,
+									 TrackerDBCollationFunc    func);
+
+gint64		     tracker_db_interface_sqlite_get_last_insert_id	(TrackerDBInterfaceSqlite *interface);
+
+
+G_END_DECLS
+
+#endif /* __TRACKER_DB_INTERFACE_SQLITE_H__ */

Added: trunk/src/libtracker-db/tracker-db-interface.c
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-db/tracker-db-interface.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,680 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <string.h>
+
+#include <gobject/gvaluecollector.h>
+
+#include "tracker-db-interface.h"
+
+#define TRACKER_DB_RESULT_SET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRACKER_TYPE_DB_RESULT_SET, TrackerDBResultSetPrivate))
+
+typedef struct TrackerDBResultSetPrivate TrackerDBResultSetPrivate;
+
+struct TrackerDBResultSetPrivate {
+	GType *col_types;
+	GPtrArray *array;
+	guint columns;
+	guint current_row;
+};
+
+enum {
+	PROP_0,
+	PROP_COLUMNS
+};
+
+G_DEFINE_TYPE (TrackerDBResultSet, tracker_db_result_set, G_TYPE_OBJECT)
+
+GQuark
+tracker_db_interface_error_quark (void)
+{
+	return g_quark_from_static_string ("tracker-db-interface-error-quark");
+}
+
+static void
+tracker_db_interface_class_init (gpointer iface)
+{
+	g_object_interface_install_property (iface,
+					     g_param_spec_boolean ("in-transaction",
+								   "In transaction",
+								   "Whether the connection has a transaction opened",
+								   FALSE,
+								   G_PARAM_READWRITE));
+}
+
+GType
+tracker_db_interface_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		type = g_type_register_static_simple (G_TYPE_INTERFACE,
+						      "TrackerDBInterface",
+						      sizeof (TrackerDBInterfaceIface),
+						      (GClassInitFunc) tracker_db_interface_class_init,
+						      0, NULL, 0);
+
+		g_type_interface_add_prerequisite (type, G_TYPE_OBJECT);
+	}
+
+	return type;
+}
+
+/* Boxed type for blobs */
+static gpointer
+blob_copy (gpointer boxed)
+{
+	GByteArray *array, *copy;
+
+	array = (GByteArray *) boxed;
+	copy = g_byte_array_sized_new (array->len);
+	g_byte_array_append (copy, array->data, array->len);
+
+	return copy;
+}
+
+static void
+blob_free (gpointer boxed)
+{
+	GByteArray *array;
+
+	array = (GByteArray *) boxed;
+	g_byte_array_free (array, TRUE);
+}
+
+GType
+tracker_db_blob_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		type = g_boxed_type_register_static ("TrackerDBBlob",
+						     blob_copy,
+						     blob_free);
+	}
+
+	return type;
+}
+
+/* TrackerDBResultSet */
+static void
+tracker_db_result_set_set_property (GObject	  *object,
+				    guint	   prop_id,
+				    const GValue  *value,
+				    GParamSpec	  *pspec)
+{
+	TrackerDBResultSetPrivate *priv;
+
+	priv = TRACKER_DB_RESULT_SET_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_COLUMNS:
+		priv->columns = g_value_get_uint (value);
+		priv->col_types = g_new0 (GType, priv->columns);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+	}
+}
+
+static void
+tracker_db_result_set_get_property (GObject    *object,
+				    guint	prop_id,
+				    GValue     *value,
+				    GParamSpec *pspec)
+{
+	TrackerDBResultSetPrivate *priv;
+
+	priv = TRACKER_DB_RESULT_SET_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_COLUMNS:
+		g_value_set_uint (value, priv->columns);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+	}
+}
+
+static void
+free_row (gpointer *row,
+	  gpointer  data)
+{
+	guint columns = GPOINTER_TO_UINT (data);
+	guint i;
+
+	if (!row)
+		return;
+
+	for (i = 0; i < columns; i++) {
+		g_free (row[i]);
+	}
+
+	g_free (row);
+}
+
+static void
+tracker_db_result_set_finalize (GObject *object)
+{
+	TrackerDBResultSetPrivate *priv;
+
+	priv = TRACKER_DB_RESULT_SET_GET_PRIVATE (object);
+
+	if (priv->array) {
+		g_ptr_array_foreach (priv->array, (GFunc) free_row,
+				     GUINT_TO_POINTER (priv->columns));
+		g_ptr_array_free (priv->array, TRUE);
+	}
+
+	g_free (priv->col_types);
+
+	G_OBJECT_CLASS (tracker_db_result_set_parent_class)->finalize (object);
+}
+
+static void
+tracker_db_result_set_class_init (TrackerDBResultSetClass *class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+	object_class->set_property = tracker_db_result_set_set_property;
+	object_class->get_property = tracker_db_result_set_get_property;
+	object_class->finalize = tracker_db_result_set_finalize;
+
+	g_object_class_install_property (object_class,
+					 PROP_COLUMNS,
+					 g_param_spec_uint ("columns",
+							    "Columns",
+							    "Resultset columns",
+							    0, G_MAXUINT, 0,
+							    G_PARAM_READWRITE |
+							    G_PARAM_CONSTRUCT_ONLY));
+
+
+	g_type_class_add_private (object_class,
+				  sizeof (TrackerDBResultSetPrivate));
+}
+
+static void
+tracker_db_result_set_init (TrackerDBResultSet *result_set)
+{
+}
+
+static TrackerDBResultSet *
+ensure_result_set_state (TrackerDBResultSet *result_set)
+{
+	if (!result_set)
+		return NULL;
+
+	if (tracker_db_result_set_get_n_rows (result_set) == 0) {
+		g_object_unref (result_set);
+		return NULL;
+	}
+
+	/* ensure that it's at the first item */
+	tracker_db_result_set_rewind (result_set);
+
+	return result_set;
+}
+
+TrackerDBResultSet *
+tracker_db_interface_execute_vquery (TrackerDBInterface  *interface,
+				     GError		**error,
+				     const gchar	 *query,
+				     va_list		  args)
+{
+	TrackerDBResultSet *result_set = NULL;
+	gchar *str;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (interface), NULL);
+	g_return_val_if_fail (query != NULL, NULL);
+
+	if (!TRACKER_DB_INTERFACE_GET_IFACE (interface)->execute_query) {
+		g_critical ("Database abstraction %s doesn't implement "
+			    "the method execute_vquery()",
+			    G_OBJECT_TYPE_NAME (interface));
+		return NULL;
+	}
+
+	str = g_strdup_vprintf (query, args);
+	result_set = TRACKER_DB_INTERFACE_GET_IFACE (interface)->execute_query (interface,
+										error,
+										str);
+	g_free (str);
+
+	return ensure_result_set_state (result_set);
+}
+
+
+
+TrackerDBResultSet *
+tracker_db_interface_execute_query (TrackerDBInterface	*interface,
+				    GError	       **error,
+				    const gchar		*query,
+				    ...)
+{
+	TrackerDBResultSet *result_set;
+	va_list args;
+
+	va_start (args, query);
+	result_set = tracker_db_interface_execute_vquery (interface,
+							  error,
+							  query,
+							  args);
+	va_end (args);
+
+	return result_set;
+}
+
+void
+tracker_db_interface_set_procedure_table (TrackerDBInterface *interface,
+					  GHashTable	     *procedure_table)
+{
+	g_return_if_fail (TRACKER_IS_DB_INTERFACE (interface));
+	g_return_if_fail (procedure_table != NULL);
+
+	if (!TRACKER_DB_INTERFACE_GET_IFACE (interface)->set_procedure_table) {
+		g_critical ("Database abstraction %s doesn't implement "
+			    "the method set_procedure_table()",
+			    G_OBJECT_TYPE_NAME (interface));
+		return;
+	}
+
+	TRACKER_DB_INTERFACE_GET_IFACE (interface)->set_procedure_table (interface,
+									 procedure_table);
+}
+
+TrackerDBResultSet *
+tracker_db_interface_execute_vprocedure (TrackerDBInterface  *interface,
+					 GError		    **error,
+					 const gchar	     *procedure,
+					 va_list	      args)
+{
+	TrackerDBResultSet *result_set;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (interface), NULL);
+	g_return_val_if_fail (procedure != NULL, NULL);
+
+	if (!TRACKER_DB_INTERFACE_GET_IFACE (interface)->execute_procedure) {
+		g_critical ("Database abstraction %s doesn't implement "
+			    "the method execute_procedure()",
+			    G_OBJECT_TYPE_NAME (interface));
+		return NULL;
+	}
+
+	result_set = TRACKER_DB_INTERFACE_GET_IFACE (interface)->execute_procedure (interface,
+										    error,
+										    procedure,
+										    args);
+
+	return ensure_result_set_state (result_set);
+}
+
+
+
+TrackerDBResultSet *
+tracker_db_interface_execute_vprocedure_len (TrackerDBInterface  *interface,
+					     GError		**error,
+					     const gchar	 *procedure,
+					     va_list		  args)
+{
+	TrackerDBResultSet *result_set;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (interface), NULL);
+	g_return_val_if_fail (procedure != NULL, NULL);
+
+	if (!TRACKER_DB_INTERFACE_GET_IFACE (interface)->execute_procedure_len) {
+		g_critical ("Database abstraction %s doesn't implement "
+			    "the method execute_procedure_len()",
+			    G_OBJECT_TYPE_NAME (interface));
+		return NULL;
+	}
+
+	result_set = TRACKER_DB_INTERFACE_GET_IFACE (interface)->execute_procedure_len (interface,
+											error,
+											procedure,
+											args);
+
+	return ensure_result_set_state (result_set);
+}
+
+TrackerDBResultSet *
+tracker_db_interface_execute_procedure (TrackerDBInterface  *interface,
+					GError		   **error,
+					const gchar	    *procedure,
+					...)
+{
+	TrackerDBResultSet *result_set;
+	va_list args;
+
+	va_start (args, procedure);
+	result_set = tracker_db_interface_execute_vprocedure (interface,
+							      error,
+							      procedure,
+							      args);
+	va_end (args);
+
+	return result_set;
+}
+
+TrackerDBResultSet *
+tracker_db_interface_execute_procedure_len (TrackerDBInterface	*interface,
+					    GError	       **error,
+					    const gchar		*procedure,
+					    ...)
+{
+	TrackerDBResultSet *result_set;
+	va_list args;
+
+	va_start (args, procedure);
+	result_set = tracker_db_interface_execute_vprocedure_len (interface,
+								  error,
+								  procedure,
+								  args);
+	va_end (args);
+
+	return result_set;
+}
+
+gboolean
+tracker_db_interface_start_transaction (TrackerDBInterface *interface)
+{
+	GError *error = NULL;
+
+	tracker_db_interface_execute_query (interface,
+					    &error,
+					    "BEGIN TRANSACTION");
+
+	if (error) {
+		g_warning (error->message);
+		g_error_free (error);
+		return FALSE;
+	}
+
+	g_object_set (interface, "in-transaction", TRUE, NULL);
+
+	return TRUE;
+}
+
+gboolean
+tracker_db_interface_end_transaction (TrackerDBInterface *interface)
+{
+	gboolean in_transaction;
+	GError *error = NULL;
+
+	g_object_get (interface, "in-transaction", &in_transaction, NULL);
+
+	if (!in_transaction) {
+		return FALSE;
+	}
+
+	g_object_set (interface, "in-transaction", FALSE, NULL);
+	tracker_db_interface_execute_query (interface, &error, "COMMIT");
+
+	if (error) {
+		g_warning (error->message);
+		g_error_free (error);
+
+		tracker_db_interface_execute_query (interface, NULL, "ROLLBACK");
+
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+/* TrackerDBResultSet semiprivate API */
+TrackerDBResultSet *
+_tracker_db_result_set_new (guint columns)
+{
+	return g_object_new (TRACKER_TYPE_DB_RESULT_SET,
+			     "columns", columns,
+			     NULL);
+}
+
+void
+_tracker_db_result_set_append (TrackerDBResultSet *result_set)
+{
+	TrackerDBResultSetPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_DB_RESULT_SET (result_set));
+
+	priv = TRACKER_DB_RESULT_SET_GET_PRIVATE (result_set);
+
+	if (G_UNLIKELY (!priv->array)) {
+		priv->array = g_ptr_array_sized_new (100);
+	}
+
+	g_ptr_array_add (priv->array, NULL);
+	priv->current_row = priv->array->len - 1;
+}
+
+void
+_tracker_db_result_set_set_value (TrackerDBResultSet *result_set,
+				  guint		      column,
+				  const GValue	     *value)
+{
+	TrackerDBResultSetPrivate *priv;
+	gpointer *row = NULL;
+
+	g_return_if_fail (TRACKER_IS_DB_RESULT_SET (result_set));
+
+	/* just return if the value doesn't contain anything */
+	if (G_VALUE_TYPE (value) == 0)
+		return;
+
+	priv = TRACKER_DB_RESULT_SET_GET_PRIVATE (result_set);
+
+	g_return_if_fail (column < priv->columns);
+
+	/* Assign a GType if it didn't have any */
+	/* if (G_UNLIKELY (priv->col_types[column] == 0)) */
+	priv->col_types[column] = G_VALUE_TYPE (value);
+
+	row = g_ptr_array_index (priv->array, priv->current_row);
+
+	/* Allocate space for the row, if it wasn't allocated previously */
+	if (G_UNLIKELY (!row)) {
+		row = g_new0 (gpointer, priv->columns);
+		g_ptr_array_index (priv->array, priv->current_row) = row;
+	}
+
+	switch (priv->col_types [column]) {
+	case G_TYPE_INT: {
+		gint *val;
+
+		val = g_new (gint, 1);
+		*val = g_value_get_int (value);
+		row[column] = val;
+		break;
+	}
+	case G_TYPE_DOUBLE: {
+		gdouble *val;
+
+		val = g_new (gdouble, 1);
+		*val = g_value_get_double (value);
+		row[column] = val;
+		break;
+	}
+	case G_TYPE_STRING:
+		row[column] = (gpointer) g_value_dup_string (value);
+		break;
+	default:
+		g_warning ("Unknown type for resultset: %s\n", G_VALUE_TYPE_NAME (value));
+	}
+}
+
+static void
+fill_in_value (GValue	*value,
+	       gpointer  data)
+{
+	switch (G_VALUE_TYPE (value)) {
+	case G_TYPE_INT:
+		g_value_set_int (value, *((gint*) data));
+		break;
+	case G_TYPE_DOUBLE:
+		g_value_set_double (value, *((gdouble *) data));
+		break;
+	case G_TYPE_STRING:
+		g_value_set_string (value, data);
+		break;
+	}
+}
+
+void
+_tracker_db_result_set_get_value (TrackerDBResultSet *result_set,
+				  guint		      column,
+				  GValue	     *value)
+{
+	TrackerDBResultSetPrivate *priv;
+	gpointer *row;
+
+	g_return_if_fail (TRACKER_IS_DB_RESULT_SET (result_set));
+
+	priv = TRACKER_DB_RESULT_SET_GET_PRIVATE (result_set);
+	row = g_ptr_array_index (priv->array, priv->current_row);
+
+	if (priv->col_types[column] != G_TYPE_INVALID) {
+		g_value_init (value, priv->col_types[column]);
+		if (row && row[column]) {
+			fill_in_value (value, row[column]);
+		} else {
+			/* Make up some empty value. */
+			switch (G_VALUE_TYPE (value)) {
+			case G_TYPE_INT:
+				g_value_set_int (value, 0);
+				break;
+			case G_TYPE_DOUBLE:
+				g_value_set_double (value, 0.0);
+				break;
+			case G_TYPE_STRING:
+				g_value_set_string (value, "");
+				break;
+			}
+		}
+	} else {
+		/* Make up some empty value */
+		g_value_init (value, G_TYPE_STRING);
+		g_value_set_string (value, "");
+	}
+}
+
+/* TrackerDBResultSet API */
+void
+tracker_db_result_set_get (TrackerDBResultSet *result_set,
+			   ...)
+{
+	TrackerDBResultSetPrivate *priv;
+	va_list args;
+	gint n_col;
+	GValue value = { 0, };
+	gpointer *row;
+	gchar *error = NULL;
+
+	g_return_if_fail (TRACKER_IS_DB_RESULT_SET (result_set));
+
+	priv = TRACKER_DB_RESULT_SET_GET_PRIVATE (result_set);
+	g_return_if_fail (priv->array != NULL);
+
+	row = g_ptr_array_index (priv->array, priv->current_row);
+	va_start (args, result_set);
+
+	while ((n_col = va_arg (args, gint)) >= 0) {
+		if ((guint) n_col >= priv->columns) {
+			g_critical ("Result set has %d columns, trying to access column %d, "
+				    "maybe -1 is missing at the end of the arguments?",
+				    priv->columns, n_col);
+			break;
+		}
+
+		if (priv->col_types[n_col] != G_TYPE_INVALID) {
+			g_value_init (&value, priv->col_types[n_col]);
+			fill_in_value (&value, row[n_col]);
+			G_VALUE_LCOPY (&value, args, 0, &error);
+			g_value_unset (&value);
+		} else {
+			gpointer *pointer;
+
+			/* No valid type, set to NULL/0 */
+			pointer = va_arg (args, gpointer *);
+			*pointer = NULL;
+		}
+
+		if (error) {
+			g_warning (error);
+			g_free (error);
+		}
+	}
+
+	va_end (args);
+}
+
+void
+tracker_db_result_set_rewind (TrackerDBResultSet *result_set)
+{
+	TrackerDBResultSetPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_DB_RESULT_SET (result_set));
+
+	priv = TRACKER_DB_RESULT_SET_GET_PRIVATE (result_set);
+	priv->current_row = 0;
+}
+
+gboolean
+tracker_db_result_set_iter_next (TrackerDBResultSet *result_set)
+{
+	TrackerDBResultSetPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_DB_RESULT_SET (result_set), FALSE);
+
+	priv = TRACKER_DB_RESULT_SET_GET_PRIVATE (result_set);
+
+	if (priv->current_row + 1 >= priv->array->len)
+		return FALSE;
+
+	priv->current_row++;
+	return TRUE;
+}
+
+guint
+tracker_db_result_set_get_n_columns (TrackerDBResultSet *result_set)
+{
+	TrackerDBResultSetPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_DB_RESULT_SET (result_set), 0);
+
+	priv = TRACKER_DB_RESULT_SET_GET_PRIVATE (result_set);
+
+	return priv->columns;
+}
+
+guint
+tracker_db_result_set_get_n_rows (TrackerDBResultSet *result_set)
+{
+	TrackerDBResultSetPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_DB_RESULT_SET (result_set), 0);
+
+	priv = TRACKER_DB_RESULT_SET_GET_PRIVATE (result_set);
+
+	if (!priv->array)
+		return 0;
+
+	return priv->array->len;
+}

Added: trunk/src/libtracker-db/tracker-db-interface.h
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-db/tracker-db-interface.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,143 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKER_DB_INTERFACE_H__
+#define __TRACKER_DB_INTERFACE_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_DB_INTERFACE	    (tracker_db_interface_get_type ())
+#define TRACKER_DB_INTERFACE(obj)	    (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRACKER_TYPE_DB_INTERFACE, TrackerDBInterface))
+#define TRACKER_IS_DB_INTERFACE(obj)	    (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRACKER_TYPE_DB_INTERFACE))
+#define TRACKER_DB_INTERFACE_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), TRACKER_TYPE_DB_INTERFACE, TrackerDBInterfaceIface))
+
+#define TRACKER_TYPE_DB_RESULT_SET	    (tracker_db_result_set_get_type ())
+#define TRACKER_DB_RESULT_SET(o)	    (G_TYPE_CHECK_INSTANCE_CAST ((o), TRACKER_TYPE_DB_RESULT_SET, TrackerDbResultSet))
+#define TRACKER_DB_RESULT_SET_CLASS(c)	    (G_TYPE_CHECK_CLASS_CAST ((c),    TRACKER_TYPE_DB_RESULT_SET, TrackerDbResultSetClass))
+#define TRACKER_IS_DB_RESULT_SET(o)	    (G_TYPE_CHECK_INSTANCE_TYPE ((o), TRACKER_TYPE_DB_RESULT_SET))
+#define TRACKER_IS_DB_RESULT_SET_CLASS(c)   (G_TYPE_CHECK_CLASS_TYPE ((o),    TRACKER_TYPE_DB_RESULT_SET))
+#define TRACKER_DB_RESULT_SET_GET_CLASS(o)  (G_TYPE_INSTANCE_GET_CLASS ((o),  TRACKER_TYPE_DB_RESULT_SET, TrackerDbResultSetClass))
+
+#define TRACKER_TYPE_DB_BLOB		    (tracker_db_blob_get_type ())
+
+#define TRACKER_DB_INTERFACE_ERROR	    (tracker_db_interface_error_quark ())
+
+typedef enum {
+	TRACKER_DB_QUERY_ERROR,
+	TRACKER_DB_CORRUPT
+} TrackerDBInterfaceError;
+
+typedef struct TrackerDBInterface TrackerDBInterface;
+typedef struct TrackerDBInterfaceIface TrackerDBInterfaceIface;
+typedef struct TrackerDBResultSet TrackerDBResultSet;
+typedef struct TrackerDBResultSetClass TrackerDBResultSetClass;
+
+struct TrackerDBInterfaceIface {
+	GTypeInterface iface;
+
+	void		     (* set_procedure_table)   (TrackerDBInterface  *interface,
+							GHashTable	    *procedure_table);
+	TrackerDBResultSet * (* execute_procedure)     (TrackerDBInterface  *interface,
+							GError		   **error,
+							const gchar	    *procedure,
+							va_list		     args);
+	TrackerDBResultSet * (* execute_procedure_len) (TrackerDBInterface  *interface,
+							GError		   **error,
+							const gchar	    *procedure,
+							va_list		     args);
+	TrackerDBResultSet * (* execute_query)	       (TrackerDBInterface  *interface,
+							GError		   **error,
+							const gchar	    *query);
+
+};
+
+struct TrackerDBResultSet {
+	GObject parent_class;
+};
+
+struct TrackerDBResultSetClass {
+	GObjectClass parent_class;
+};
+
+
+GQuark tracker_db_interface_error_quark (void);
+
+GType tracker_db_interface_get_type (void);
+GType tracker_db_result_set_get_type (void);
+GType tracker_db_blob_get_type (void);
+
+
+/* Functions to create queries/procedures */
+TrackerDBResultSet *	tracker_db_interface_execute_vquery	 (TrackerDBInterface   *interface,
+								  GError	     **error,
+								  const gchar	       *query,
+								  va_list		args);
+TrackerDBResultSet *	tracker_db_interface_execute_query	 (TrackerDBInterface   *interface,
+								  GError	     **error,
+								  const gchar	       *query,
+								  ...) G_GNUC_PRINTF (3, 4);
+void			tracker_db_interface_set_procedure_table (TrackerDBInterface   *interface,
+								  GHashTable	       *procedure_table);
+TrackerDBResultSet *	tracker_db_interface_execute_vprocedure  (TrackerDBInterface   *interface,
+								  GError	     **error,
+								  const gchar	       *procedure,
+								  va_list		args);
+TrackerDBResultSet *	tracker_db_interface_execute_procedure	 (TrackerDBInterface   *interface,
+								  GError	     **error,
+								  const gchar	       *procedure,
+								  ...) G_GNUC_NULL_TERMINATED;
+
+TrackerDBResultSet *	tracker_db_interface_execute_vprocedure_len (TrackerDBInterface   *interface,
+								     GError		**error,
+								     const gchar	  *procedure,
+								     va_list		   args);
+TrackerDBResultSet *	tracker_db_interface_execute_procedure_len  (TrackerDBInterface   *interface,
+								     GError		**error,
+								     const gchar	  *procedure,
+								     ...) G_GNUC_NULL_TERMINATED;
+
+gboolean		tracker_db_interface_start_transaction	    (TrackerDBInterface   *interface);
+gboolean		tracker_db_interface_end_transaction	    (TrackerDBInterface   *interface);
+
+
+/* Semi private TrackerDBResultSet functions */
+TrackerDBResultSet *	  _tracker_db_result_set_new	       (guint		    cols);
+void			  _tracker_db_result_set_append        (TrackerDBResultSet *result_set);
+void			  _tracker_db_result_set_set_value     (TrackerDBResultSet *result_set,
+								guint		    column,
+								const GValue	   *value);
+void			  _tracker_db_result_set_get_value     (TrackerDBResultSet *result_set,
+								guint		    column,
+								GValue		   *value);
+
+/* Functions to deal with the resultset */
+void			  tracker_db_result_set_get	       (TrackerDBResultSet *result_set,
+								...);
+void			  tracker_db_result_set_rewind	       (TrackerDBResultSet *result_set);
+gboolean		  tracker_db_result_set_iter_next      (TrackerDBResultSet *result_set);
+guint			  tracker_db_result_set_get_n_columns  (TrackerDBResultSet *result_set);
+guint			  tracker_db_result_set_get_n_rows     (TrackerDBResultSet *result_set);
+
+
+G_END_DECLS
+
+#endif /* __TRACKER_DB_INTERFACE_H__ */

Added: trunk/src/libtracker-db/tracker-db-manager.c
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-db/tracker-db-manager.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,2788 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <regex.h>
+#include <zlib.h>
+
+#include <glib/gstdio.h>
+
+#include <libtracker-common/tracker-field.h>
+#include <libtracker-common/tracker-file-utils.h>
+#include <libtracker-common/tracker-nfs-lock.h>
+#include <libtracker-common/tracker-ontology.h>
+#include <libtracker-common/tracker-type-utils.h>
+#include <libtracker-common/tracker-utils.h>
+
+#include "tracker-db-manager.h"
+#include "tracker-db-interface-sqlite.h"
+
+/* ZLib buffer settings */
+#define ZLIB_BUF_SIZE		      8192
+
+/* Default memory settings for databases */
+#define TRACKER_DB_PAGE_SIZE_DEFAULT  4096
+#define TRACKER_DB_PAGE_SIZE_DONT_SET -1
+
+/* Size is in bytes and is currently 2Gb */
+#define TRACKER_DB_MAX_FILE_SIZE      2000000000 
+
+/* Set current database version we are working with */
+#define TRACKER_DB_VERSION_NOW        TRACKER_DB_VERSION_2
+#define TRACKER_DB_VERSION_FILE       "db-version.txt"
+
+typedef enum {
+	TRACKER_DB_LOCATION_DATA_DIR,
+	TRACKER_DB_LOCATION_USER_DATA_DIR,
+	TRACKER_DB_LOCATION_SYS_TMP_DIR,
+} TrackerDBLocation;
+
+typedef enum {
+	TRACKER_DB_VERSION_UNKNOWN, /* Unknown */
+	TRACKER_DB_VERSION_1,       /* TRUNK before indexer-split */
+	TRACKER_DB_VERSION_2        /* The indexer-split branch */
+} TrackerDBVersion;
+
+typedef struct {
+	TrackerDB	    db;
+	TrackerDBLocation   location;
+	TrackerDBInterface *iface;
+	const gchar	   *file;
+	const gchar	   *name;
+	gchar		   *abs_filename;
+	gint		    cache_size;
+	gint		    page_size;
+	gboolean	    add_functions;
+	gboolean	    attached;
+} TrackerDBDefinition;
+
+static TrackerDBDefinition dbs[] = {
+	{ TRACKER_DB_UNKNOWN,
+	  TRACKER_DB_LOCATION_USER_DATA_DIR,
+	  NULL,
+	  NULL,
+	  NULL,
+	  NULL,
+	  32,
+	  TRACKER_DB_PAGE_SIZE_DEFAULT,
+	  FALSE,
+	  FALSE },
+	{ TRACKER_DB_COMMON,
+	  TRACKER_DB_LOCATION_USER_DATA_DIR,
+	  NULL,
+	  "common.db",
+	  "common",
+	  NULL,
+	  32,
+	  TRACKER_DB_PAGE_SIZE_DEFAULT,
+	  FALSE,
+	  FALSE },
+	{ TRACKER_DB_CACHE,
+	  TRACKER_DB_LOCATION_SYS_TMP_DIR,
+	  NULL,
+	  "cache.db",
+	  "cache",
+	  NULL,
+	  128,
+	  TRACKER_DB_PAGE_SIZE_DONT_SET,
+	  FALSE,
+	  FALSE },
+	{ TRACKER_DB_FILE_METADATA,
+	  TRACKER_DB_LOCATION_DATA_DIR,
+	  NULL,
+	  "file-meta.db",
+	  "file-meta",
+	  NULL,
+	  512,
+	  TRACKER_DB_PAGE_SIZE_DEFAULT,
+	  TRUE,
+	  FALSE },
+	{ TRACKER_DB_FILE_CONTENTS,
+	  TRACKER_DB_LOCATION_DATA_DIR,
+	  NULL,
+	  "file-contents.db",
+	  "file-contents",
+	  NULL,
+	  1024,
+	  TRACKER_DB_PAGE_SIZE_DEFAULT,
+	  FALSE,
+	  FALSE },
+	{ TRACKER_DB_EMAIL_METADATA,
+	  TRACKER_DB_LOCATION_DATA_DIR,
+	  NULL,
+	  "email-meta.db",
+	  "email-meta",
+	  NULL,
+	  512,
+	  TRACKER_DB_PAGE_SIZE_DEFAULT,
+	  TRUE,
+	  FALSE},
+	{ TRACKER_DB_EMAIL_CONTENTS,
+	  TRACKER_DB_LOCATION_DATA_DIR,
+	  NULL,
+	  "email-contents.db",
+	  "email-contents",
+	  NULL,
+	  512,
+	  TRACKER_DB_PAGE_SIZE_DEFAULT,
+	  FALSE,
+	  FALSE },
+	{ TRACKER_DB_XESAM,
+	  TRACKER_DB_LOCATION_DATA_DIR,
+	  NULL,
+	  "xesam.db",
+	  "xesam",
+	  NULL,
+	  512,
+	  TRACKER_DB_PAGE_SIZE_DEFAULT,
+	  TRUE,
+	  FALSE },
+};
+
+static gboolean		   db_exec_no_reply    (TrackerDBInterface *iface,
+						const gchar	   *query,
+						...);
+static TrackerDBInterface *db_interface_create (TrackerDB	    db);
+
+static gboolean		   initialized;
+static GHashTable	  *prepared_queries;
+static gchar		  *services_dir;
+static gchar		  *sql_dir;
+static gchar		  *data_dir;
+static gchar		  *user_data_dir;
+static gchar		  *sys_tmp_dir;
+static gpointer		   db_type_enum_class_pointer;
+static TrackerDBInterface *file_iface;
+static TrackerDBInterface *email_iface;
+static TrackerDBInterface *xesam_iface;
+
+static const gchar *
+location_to_directory (TrackerDBLocation location)
+{
+	switch (location) {
+	case TRACKER_DB_LOCATION_DATA_DIR:
+		return data_dir;
+	case TRACKER_DB_LOCATION_USER_DATA_DIR:
+		return user_data_dir;
+	case TRACKER_DB_LOCATION_SYS_TMP_DIR:
+		return sys_tmp_dir;
+	};
+
+	return NULL;
+}
+
+static void
+load_sql_file (TrackerDBInterface *iface,
+	       const gchar	  *file,
+	       const gchar	  *delimiter)
+{
+	gchar *path, *content, **queries;
+	gint   count;
+	gint   i;
+
+	path = g_build_filename (sql_dir, file, NULL);
+
+	if (!delimiter) {
+		delimiter = ";";
+	}
+
+	if (!g_file_get_contents (path, &content, NULL, NULL)) {
+		g_critical ("Cannot read SQL file:'%s', please reinstall tracker"
+			    " or check read permissions on the file if it exists", file);
+		g_assert_not_reached ();
+	}
+
+	queries = g_strsplit (content, delimiter, -1);
+
+	for (i = 0, count = 0; queries[i]; i++) {
+		GError *error = NULL;
+		gchar  *sql;
+
+		/* Skip white space, including control characters */
+		for (sql = queries[i]; sql && g_ascii_isspace (sql[0]); sql++);
+
+		if (!sql || sql[0] == '\0') {
+			continue;
+		}
+
+		tracker_db_interface_execute_query (iface, &error, sql);
+
+		if (error) {
+			g_warning ("Error loading query:'%s' #%d, %s", file, i, error->message);
+			g_error_free (error);
+			continue;
+		}
+
+		count++;
+	}
+
+	g_message ("  Loaded SQL file:'%s' (%d queries)", file, count);
+
+	g_strfreev (queries);
+	g_free (content);
+	g_free (path);
+}
+
+static void
+load_metadata_file (TrackerDBInterface *iface,
+		    const gchar        *filename)
+{
+	GKeyFile      *key_file = NULL;
+	gchar	      *service_file, *str_id;
+	gchar	     **groups, **keys;
+	TrackerField  *def;
+	gint	       id, i, j;
+
+	key_file = g_key_file_new ();
+	service_file = g_build_filename (services_dir, filename, NULL);
+
+	if (!g_key_file_load_from_file (key_file, service_file, G_KEY_FILE_NONE, NULL)) {
+		g_free (service_file);
+		g_key_file_free (key_file);
+		return;
+	}
+
+	groups = g_key_file_get_groups (key_file, NULL);
+
+	for (i = 0; groups[i]; i++) {
+		def = tracker_ontology_get_field_by_name (groups[i]);
+
+		if (!def) {
+			tracker_db_interface_execute_procedure (iface,
+								NULL,
+								"InsertMetadataType",
+								groups[i],
+								NULL);
+			id = tracker_db_interface_sqlite_get_last_insert_id (TRACKER_DB_INTERFACE_SQLITE (iface));
+		} else {
+			id = atoi (tracker_field_get_id (def));
+			g_error ("Duplicated metadata description %s", groups[i]);
+		}
+
+		str_id = tracker_guint_to_string (id);
+		keys = g_key_file_get_keys (key_file, groups[i], NULL, NULL);
+
+		for (j = 0; keys[j]; j++) {
+			gchar *value, *new_value;
+
+			value = g_key_file_get_locale_string (key_file, groups[i], keys[j], NULL, NULL);
+
+			if (!value) {
+				continue;
+			}
+
+			new_value = tracker_string_boolean_to_string_gint (value);
+			g_free (value);
+
+			if (strcasecmp (keys[j], "Parent") == 0) {
+				tracker_db_interface_execute_procedure (iface,
+									NULL,
+									"InsertMetaDataChildren",
+									str_id,
+									new_value,
+									NULL);
+			} else if (strcasecmp (keys[j], "DataType") == 0) {
+				GEnumValue *enum_value;
+
+				enum_value = g_enum_get_value_by_nick (g_type_class_peek (TRACKER_TYPE_FIELD_TYPE), new_value);
+
+				if (enum_value) {
+					tracker_db_interface_execute_query (iface, NULL,
+									    "update MetaDataTypes set DataTypeID = %d where ID = %d",
+									    enum_value->value, id);
+				}
+			} else {
+				gchar *esc_value;
+
+				esc_value = tracker_escape_string (new_value);
+
+				tracker_db_interface_execute_query (iface, NULL,
+								    "update MetaDataTypes set  %s = '%s' where ID = %d",
+								    keys[j], esc_value, id);
+
+				g_free (esc_value);
+			}
+
+			g_free (new_value);
+		}
+
+		g_free (str_id);
+		g_strfreev (keys);
+	}
+
+	g_strfreev (groups);
+	g_free (service_file);
+	g_key_file_free (key_file);
+}
+
+static void
+load_service_file (TrackerDBInterface *iface,
+		   const gchar	      *filename)
+{
+	TrackerService	*service;
+	GKeyFile	*key_file = NULL;
+	gchar		*service_file, *str_id;
+	gchar	       **groups, **keys;
+	gint		 i, j, id;
+
+	service_file = g_build_filename (services_dir, filename, NULL);
+
+	key_file = g_key_file_new ();
+
+	if (!g_key_file_load_from_file (key_file, service_file, G_KEY_FILE_NONE, NULL)) {
+		g_free (service_file);
+		g_key_file_free (key_file);
+		return;
+	}
+
+	groups = g_key_file_get_groups (key_file, NULL);
+
+	for (i = 0; groups[i]; i++) {
+		g_message ("Trying to obtain service:'%s' in cache", groups[i]);
+		service = tracker_ontology_get_service_by_name (groups[i]);
+
+		if (!service) {
+			tracker_db_interface_execute_procedure (iface,
+								NULL,
+								"InsertServiceType",
+								groups[i],
+								NULL);
+			id = tracker_db_interface_sqlite_get_last_insert_id (TRACKER_DB_INTERFACE_SQLITE (iface));
+		} else {
+			id = tracker_service_get_id (service);
+		}
+
+		str_id = tracker_guint_to_string (id);
+
+		keys = g_key_file_get_keys (key_file, groups[i], NULL, NULL);
+
+		for (j = 0; keys[j]; j++) {
+			if (strcasecmp (keys[j], "TabularMetadata") == 0) {
+				gchar **tab_array;
+				gint	k;
+
+				tab_array = g_key_file_get_string_list (key_file,
+									groups[i],
+									keys[j],
+									NULL,
+									NULL);
+
+				for (k = 0; tab_array[k]; k++) {
+					tracker_db_interface_execute_procedure (iface,
+										NULL,
+										"InsertServiceTabularMetadata",
+										str_id,
+										tab_array[k],
+										NULL);
+				}
+
+				g_strfreev (tab_array);
+			} else if (strcasecmp (keys[j], "TileMetadata") == 0) {
+				gchar **tab_array;
+				gint	k;
+
+				tab_array = g_key_file_get_string_list (key_file,
+									groups[i],
+									keys[j],
+									NULL,
+									NULL);
+
+				for (k = 0; tab_array[k]; k++) {
+					tracker_db_interface_execute_procedure (iface,
+										NULL,
+										"InsertServiceTileMetadata",
+										str_id,
+										tab_array[k],
+										NULL);
+				}
+
+				g_strfreev (tab_array);
+			} else if (strcasecmp (keys[j], "Mimes") == 0) {
+				gchar **tab_array;
+				gint	k;
+
+				tab_array = g_key_file_get_string_list (key_file,
+									groups[i],
+									keys[j],
+									NULL,
+									NULL);
+
+				for (k = 0; tab_array[k]; k++) {
+					tracker_db_interface_execute_procedure (iface, NULL,
+										"InsertMimes",
+										tab_array[k],
+										NULL);
+					tracker_db_interface_execute_query (iface,
+									    NULL,
+									    "update FileMimes set ServiceTypeID = %s where Mime = '%s'",
+									    str_id,
+									    tab_array[k]);
+				}
+
+				g_strfreev (tab_array);
+			} else if (strcasecmp (keys[j], "MimePrefixes") == 0) {
+				gchar **tab_array;
+				gint	k;
+
+				tab_array = g_key_file_get_string_list (key_file,
+									groups[i],
+									keys[j],
+									NULL,
+									NULL);
+
+				for (k = 0; tab_array[k]; k++) {
+					tracker_db_interface_execute_procedure (iface,
+										NULL,
+										"InsertMimePrefixes",
+										tab_array[k],
+										NULL);
+					tracker_db_interface_execute_query (iface,
+									    NULL,
+									    "update FileMimePrefixes set ServiceTypeID = %s where MimePrefix = '%s'",
+									    str_id,
+									    tab_array[k]);
+				}
+
+				g_strfreev (tab_array);
+			} else {
+				gchar *value, *new_value, *esc_value;
+
+				value = g_key_file_get_string (key_file, groups[i], keys[j], NULL);
+				new_value = tracker_string_boolean_to_string_gint (value);
+				esc_value = tracker_escape_string (new_value);
+
+				tracker_db_interface_execute_query (iface,
+								    NULL,
+								    "update ServiceTypes set  %s = '%s' where TypeID = %s",
+								    keys[j],
+								    esc_value,
+								    str_id);
+
+				g_free (esc_value);
+				g_free (value);
+				g_free (new_value);
+			}
+		}
+
+		g_free (str_id);
+		g_strfreev (keys);
+	}
+
+	g_key_file_free (key_file);
+	g_strfreev (groups);
+	g_free (service_file);
+}
+
+static TrackerDBResultSet *
+db_exec_proc (TrackerDBInterface *iface,
+	      const gchar	 *procedure,
+	      ...)
+{
+	TrackerDBResultSet *result_set;
+	va_list		    args;
+
+	va_start (args, procedure);
+	result_set = tracker_db_interface_execute_vprocedure (iface,
+							      NULL,
+							      procedure,
+							      args);
+	va_end (args);
+
+	return result_set;
+}
+
+static gboolean
+db_exec_no_reply (TrackerDBInterface *iface,
+		  const gchar	     *query,
+		  ...)
+{
+	TrackerDBResultSet *result_set;
+	va_list		    args;
+
+	tracker_nfs_lock_obtain ();
+
+	va_start (args, query);
+	result_set = tracker_db_interface_execute_vquery (iface, NULL, query, args);
+	va_end (args);
+
+	if (result_set) {
+		g_object_unref (result_set);
+	}
+
+	tracker_nfs_lock_release ();
+
+	return TRUE;
+}
+
+static void
+load_service_file_xesam_map (TrackerDBInterface *iface,
+			     const gchar	*db_proc,
+			     const gchar	*data_to_split,
+			     const gchar	*data_to_insert)
+{
+	gchar **mappings;
+	gchar **mapping;
+
+	mappings = g_strsplit_set (data_to_split, ";", -1);
+
+	if (!mappings) {
+		return;
+	}
+
+	for (mapping = mappings; *mapping; mapping++) {
+		gchar *esc_value;
+
+		esc_value = tracker_escape_string (*mapping);
+		db_exec_proc (iface,
+			      db_proc,
+			      data_to_insert,
+			      esc_value,
+			      NULL);
+		g_free (esc_value);
+	}
+
+	g_strfreev (mappings);
+}
+
+static void
+load_service_file_xesam_insert (TrackerDBInterface *iface,
+				const gchar	   *sql_format,
+				const gchar	   *data_to_split,
+				const gchar	   *data_to_insert)
+{
+	gchar **parents;
+	gchar **parent;
+
+	parents = g_strsplit_set (data_to_split, ";", -1);
+
+	if (!parents) {
+		return;
+	}
+
+	for (parent = parents; *parent; parent++) {
+		gchar *sql;
+
+		sql = g_strdup_printf (sql_format, *parent, data_to_insert);
+		db_exec_no_reply (iface, sql);
+		g_free (sql);
+	}
+
+	g_strfreev (parents);
+}
+
+static void
+load_service_file_xesam_update (TrackerDBInterface *iface,
+				const gchar	   *sql_format,
+				const gchar	   *data_to_update,
+				const gchar	   *data_key,
+				const gchar	   *data_value)
+{
+	gchar *str;
+	gchar *sql;
+
+	str = tracker_escape_string (data_key);
+	sql = g_strdup_printf (sql_format,
+			       data_to_update,
+			       str,
+			       data_value);
+	db_exec_no_reply (iface, sql);
+	g_free (sql);
+	g_free (str);
+}
+
+static gboolean
+load_service_file_xesam (TrackerDBInterface *iface,
+			 const gchar	    *filename)
+{
+	GKeyFile	     *key_file;
+	GError		     *error = NULL;
+	const gchar * const  *language_names;
+	gchar		    **groups;
+	gchar		     *service_file;
+	gchar		     *sql;
+	gboolean	      is_metadata;
+	gboolean	      is_service;
+	gboolean	      is_metadata_mapping;
+	gboolean	      is_service_mapping;
+	gint		      i, j;
+
+	const gchar	     *data_types[] = {
+		"string",
+		"float",
+		"integer",
+		"boolean",
+		"dateTime",
+		"List of strings",
+		"List of Uris",
+		"List of Urls",
+		NULL
+	};
+
+	key_file = g_key_file_new ();
+	service_file = g_build_filename (services_dir, filename, NULL);
+
+	if (!g_key_file_load_from_file (key_file, service_file, G_KEY_FILE_NONE, &error)) {
+		g_critical ("Couldn't load XESAM service file:'%s', %s",
+			    filename,
+			    error->message);
+		g_clear_error (&error);
+		g_free (service_file);
+		g_key_file_free (key_file);
+
+		return FALSE;
+	}
+
+	g_free (service_file);
+
+	is_metadata = FALSE;
+	is_service = FALSE;
+	is_metadata_mapping = FALSE;
+	is_service_mapping = FALSE;
+
+	if (g_str_has_suffix (filename, ".metadata")) {
+		is_metadata = TRUE;
+	} else if (g_str_has_suffix (filename, ".service")) {
+		is_service = TRUE;
+	} else if (g_str_has_suffix (filename, ".mmapping")) {
+		is_metadata_mapping = TRUE;
+	} else if (g_str_has_suffix (filename, ".smapping")) {
+		is_service_mapping = TRUE;
+	} else {
+		g_warning ("XESAM Service file:'%s' does not a recognised suffix "
+			   "('.service', '.metadata', '.mmapping' or '.smapping')",
+			   filename);
+		g_key_file_free (key_file);
+		return FALSE;
+	}
+
+	language_names = g_get_language_names ();
+
+	groups = g_key_file_get_groups (key_file, NULL);
+
+	for (i = 0; groups[i]; i++) {
+		gchar  *str_id;
+		gchar **keys;
+		gint	id = -1;
+
+		if (is_metadata) {
+			db_exec_proc (iface,
+				      "InsertXesamMetadataType",
+				      groups[i],
+				      NULL);
+			id = tracker_db_interface_sqlite_get_last_insert_id (TRACKER_DB_INTERFACE_SQLITE (iface));
+		} else if (is_service) {
+			db_exec_proc (iface,
+				      "InsertXesamServiceType",
+				      groups[i],
+				      NULL);
+			id = tracker_db_interface_sqlite_get_last_insert_id (TRACKER_DB_INTERFACE_SQLITE (iface));
+		}
+
+		/* Get inserted ID */
+		str_id = tracker_guint_to_string (id);
+		keys = g_key_file_get_keys (key_file, groups[i], NULL, NULL);
+
+		for (j = 0; keys[j]; j++) {
+			gchar *value;
+
+			value = g_key_file_get_locale_string (key_file,
+							      groups[i],
+							      keys[j],
+							      language_names[0],
+							      NULL);
+
+			if (!value) {
+				continue;
+			}
+
+			if (strcasecmp (value, "true") == 0) {
+				g_free (value);
+				value = g_strdup ("1");
+			} else if  (strcasecmp (value, "false") == 0) {
+				g_free (value);
+				value = g_strdup ("0");
+			}
+
+			if (is_metadata) {
+				if (strcasecmp (keys[j], "Parents") == 0) {
+					load_service_file_xesam_insert (iface,
+									"INSERT INTO XesamMetadataChildren (Parent, Child) VALUES ('%s', '%s')",
+									value,
+									groups[i]);
+				} else if (strcasecmp (keys[j], "ValueType") == 0) {
+					gint data_id;
+
+					data_id = tracker_string_in_string_list (value, (gchar **) data_types);
+
+					if (data_id != -1) {
+						gint mapped_data_id;
+						gboolean list = FALSE;
+
+						/* We map these values
+						 * to existing field
+						 * types. FIXME
+						 * Eventually we
+						 * should change the
+						 * config file
+						 * instead.
+						 */
+
+						switch (data_id) {
+						case 0:
+							mapped_data_id = TRACKER_FIELD_TYPE_STRING;
+							break;
+						case 1:
+							mapped_data_id = TRACKER_FIELD_TYPE_DOUBLE;
+							break;
+						case 2:
+							mapped_data_id = TRACKER_FIELD_TYPE_INTEGER;
+							break;
+						case 3:
+							mapped_data_id = TRACKER_FIELD_TYPE_INTEGER;
+							break;
+						case 4:
+							mapped_data_id = TRACKER_FIELD_TYPE_DATE;
+							break;
+						case 5:
+						case 6:
+						case 7:
+							list = TRUE;
+							mapped_data_id = TRACKER_FIELD_TYPE_STRING;
+							break;
+						default:
+							g_warning ("Couldn't map data id %d to TrackerFieldType",
+								   data_id);
+							mapped_data_id = -1;
+						}
+
+						sql = g_strdup_printf ("update XesamMetadataTypes set DataTypeID = %d where ID = %s",
+								       mapped_data_id,
+								       str_id);
+						db_exec_no_reply (iface, sql);
+						g_free (sql);
+
+						if (list) {
+							sql = g_strdup_printf ("update XesamMetadataTypes set MultipleValues = 1 where ID = %s",
+									       str_id);
+							db_exec_no_reply (iface, sql);
+							g_free (sql);
+						}
+					}
+				} else {
+					load_service_file_xesam_update (iface,
+									"update XesamMetadataTypes set	%s = '%s' where ID = %s",
+									keys[j],
+									value,
+									str_id);
+				}
+			} else	if (is_service) {
+				if (strcasecmp (keys[j], "Parents") == 0) {
+					load_service_file_xesam_insert (iface,
+									"INSERT INTO XesamServiceChildren (Parent, Child) VALUES ('%s', '%s')",
+									value,
+									groups[i]);
+				} else if (strcasecmp (keys[j], "Mimes") == 0) {
+					gchar **tab_array;
+					gint	k;
+
+					tab_array = g_key_file_get_string_list (key_file,
+										groups[i],
+										keys[j],
+										NULL,
+										NULL);
+
+					for (k = 0; tab_array[k]; k++) {
+						tracker_db_interface_execute_procedure (iface, NULL,
+											"InsertXesamMimes",
+											tab_array[k],
+											NULL);
+						tracker_db_interface_execute_query (iface,
+										    NULL,
+										    "update XesamFileMimes set ServiceTypeID = %s where Mime = '%s'",
+										    str_id,
+										    tab_array[k]);
+					}
+
+					g_strfreev (tab_array);
+				} else if (strcasecmp (keys[j], "MimePrefixes") == 0) {
+					gchar **tab_array;
+					gint	k;
+
+					tab_array = g_key_file_get_string_list (key_file,
+										groups[i],
+										keys[j],
+										NULL,
+										NULL);
+
+					for (k = 0; tab_array[k]; k++) {
+						tracker_db_interface_execute_procedure (iface,
+											NULL,
+											"InsertXesamMimePrefixes",
+											tab_array[k],
+											NULL);
+						tracker_db_interface_execute_query (iface,
+										    NULL,
+										    "update XesamFileMimePrefixes set ServiceTypeID = %s where MimePrefix = '%s'",
+										    str_id,
+										    tab_array[k]);
+					}
+
+					g_strfreev (tab_array);
+				} else {
+					load_service_file_xesam_update (iface,
+									"update XesamServiceTypes set  %s = '%s' where typeID = %s",
+									keys[j],
+									value,
+									str_id);
+				}
+			} else	if (is_metadata_mapping) {
+				load_service_file_xesam_map (iface,
+							     "InsertXesamMetaDataMapping",
+							     value,
+							     groups[i]);
+			} else {
+				load_service_file_xesam_map (iface,
+							     "InsertXesamServiceMapping",
+							     value,
+							     groups[i]);
+			}
+
+			g_free (value);
+		}
+
+		g_strfreev (keys);
+		g_free (str_id);
+	}
+
+	g_strfreev (groups);
+	g_key_file_free (key_file);
+
+	return TRUE;
+}
+
+static gboolean
+load_prepared_queries (void)
+{
+	GTimer	    *t;
+	GError	    *error = NULL;
+	GMappedFile *mapped_file;
+	GStrv	     queries;
+	gchar	    *filename;
+	gdouble      secs;
+
+	g_message ("Loading prepared queries...");
+
+	filename = g_build_filename (sql_dir, "sqlite-stored-procs.sql", NULL);
+
+	t = g_timer_new ();
+
+	mapped_file = g_mapped_file_new (filename, FALSE, &error);
+
+	if (error || !mapped_file) {
+		g_warning ("Could not get contents of SQL file:'%s', %s",
+			   filename,
+			   error ? error->message : "no error given");
+
+		if (mapped_file) {
+			g_mapped_file_free (mapped_file);
+		}
+
+		g_timer_destroy (t);
+		g_free (filename);
+
+		return FALSE;
+	}
+
+	g_message ("Loaded prepared queries file:'%s' size:%" G_GSIZE_FORMAT " bytes",
+		   filename,
+		   g_mapped_file_get_length (mapped_file));
+
+	queries = g_strsplit (g_mapped_file_get_contents (mapped_file), "\n", -1);
+	g_free (filename);
+
+	if (queries) {
+		GStrv p;
+
+		for (p = queries; *p; p++) {
+			GStrv details;
+
+			if (**p == '#') {
+				continue;
+			}
+
+			details = g_strsplit (*p, " ", 2);
+
+			if (!details) {
+				continue;
+			}
+
+			if (!details[0] || !details[1]) {
+				g_strfreev (details);
+				continue;
+			}
+
+			g_message ("  Adding query:'%s'", details[0]);
+
+			g_hash_table_insert (prepared_queries,
+					     g_strdup (details[0]),
+					     g_strdup (details[1]));
+			g_strfreev (details);
+		}
+
+		g_strfreev (queries);
+	}
+
+	secs = g_timer_elapsed (t, NULL);
+	g_timer_destroy (t);
+	g_mapped_file_free (mapped_file);
+
+	g_message ("Found %d prepared queries in %4.4f seconds",
+		   g_hash_table_size (prepared_queries),
+		   secs);
+
+	return TRUE;
+}
+
+static TrackerField *
+db_row_to_field_def (TrackerDBResultSet *result_set)
+{
+	TrackerField	 *field_def;
+	TrackerFieldType  field_type;
+	gchar		 *id_str, *field_name, *name;
+	gint		  weight, id;
+	gboolean	  embedded, multiple_values, delimited, filtered, store_metadata;
+
+	field_def = tracker_field_new ();
+
+	tracker_db_result_set_get (result_set,
+				   0, &id,
+				   1, &name,
+				   2, &field_type,
+				   3, &field_name,
+				   4, &weight,
+				   5, &embedded,
+				   6, &multiple_values,
+				   7, &delimited,
+				   8, &filtered,
+				   9, &store_metadata,
+				   -1);
+
+	id_str = tracker_gint_to_string (id);
+
+	tracker_field_set_id (field_def, id_str);
+	tracker_field_set_name (field_def, name);
+	tracker_field_set_data_type (field_def, field_type);
+	tracker_field_set_field_name (field_def, field_name);
+	tracker_field_set_weight (field_def, weight);
+	tracker_field_set_embedded (field_def, embedded);
+	tracker_field_set_multiple_values (field_def, multiple_values);
+	tracker_field_set_delimited (field_def, delimited);
+	tracker_field_set_filtered (field_def, filtered);
+	tracker_field_set_store_metadata (field_def, store_metadata);
+
+	g_free (id_str);
+	g_free (field_name);
+	g_free (name);
+
+	return field_def;
+}
+
+static TrackerService *
+db_row_to_service (TrackerDBResultSet *result_set)
+{
+	TrackerService *service;
+	GSList	       *new_list = NULL;
+	gint		id, i;
+	gchar	       *name, *parent, *content_metadata, *property_prefix = NULL;
+	gboolean	enabled, embedded, has_metadata, has_fulltext;
+	gboolean	has_thumbs, show_service_files, show_service_directories;
+
+	service = tracker_service_new ();
+
+	tracker_db_result_set_get (result_set,
+				   0, &id,
+				   1, &name,
+				   2, &parent,
+				   3, &property_prefix,
+				   4, &enabled,
+				   5, &embedded,
+				   6, &has_metadata,
+				   7, &has_fulltext,
+				   8, &has_thumbs,
+				   9, &content_metadata,
+				   11, &show_service_files,
+				   12, &show_service_directories,
+				   -1);
+
+	tracker_service_set_id (service, id);
+	tracker_service_set_name (service, name);
+	tracker_service_set_parent (service, parent);
+	tracker_service_set_property_prefix (service, property_prefix);
+	tracker_service_set_enabled (service, enabled);
+	tracker_service_set_embedded (service, embedded);
+	tracker_service_set_has_metadata (service, has_metadata);
+	tracker_service_set_has_full_text (service, has_fulltext);
+	tracker_service_set_has_thumbs (service, has_thumbs);
+	tracker_service_set_content_metadata (service, content_metadata);
+
+	tracker_service_set_show_service_files (service, show_service_files);
+	tracker_service_set_show_service_directories (service, show_service_directories);
+
+	for (i = 13; i < 24; i++) {
+		gchar *metadata;
+
+		tracker_db_result_set_get (result_set, i, &metadata, -1);
+
+		if (metadata) {
+			new_list = g_slist_prepend (new_list, metadata);
+		}
+	}
+
+	/* FIXME: is this necessary?
+	 * This values are set as key metadata in default.service already
+	 */
+#if 0
+	/* Hack to prevent db change late in the cycle, check the
+	 * service name matches "Applications", then add some voodoo.
+	 */
+	if (strcmp (name, "Applications") == 0) {
+		/* These strings should be definitions at the top of
+		 * this file somewhere really.
+		 */
+		new_list = g_slist_prepend (new_list, g_strdup ("App:DisplayName"));
+		new_list = g_slist_prepend (new_list, g_strdup ("App:Exec"));
+		new_list = g_slist_prepend (new_list, g_strdup ("App:Icon"));
+	}
+#endif
+
+	new_list = g_slist_reverse (new_list);
+
+	tracker_service_set_key_metadata (service, new_list);
+	g_slist_foreach (new_list, (GFunc) g_free, NULL);
+	g_slist_free (new_list);
+
+	g_free (name);
+	g_free (parent);
+	g_free (property_prefix);
+	g_free (content_metadata);
+
+	return service;
+}
+
+static GSList *
+db_mime_query (TrackerDBInterface *iface,
+	       const gchar	  *stored_proc,
+	       gint		   service_id)
+{
+	TrackerDBResultSet *result_set;
+	GSList		   *result = NULL;
+	gchar		   *service_id_str;
+
+	service_id_str = g_strdup_printf ("%d", service_id);
+
+	result_set = tracker_db_interface_execute_procedure (iface,
+							     NULL,
+							     stored_proc,
+							     service_id_str,
+							     NULL);
+	g_free (service_id_str);
+
+	if (result_set) {
+		gchar	 *str;
+		gboolean  valid = TRUE;
+
+		while (valid) {
+			tracker_db_result_set_get (result_set, 0, &str, -1);
+			result = g_slist_prepend (result, str);
+			valid = tracker_db_result_set_iter_next (result_set);
+		}
+
+		g_object_unref (result_set);
+	}
+
+	return result;
+}
+
+static GSList *
+db_get_mimes_for_service_id (TrackerDBInterface *iface,
+			     gint		 service_id)
+{
+	return db_mime_query (iface, "GetMimeForServiceId", service_id);
+}
+
+static GSList *
+db_get_mime_prefixes_for_service_id (TrackerDBInterface *iface,
+				     gint		 service_id)
+{
+	return db_mime_query (iface, "GetMimePrefixForServiceId", service_id);
+}
+
+static GSList *
+db_get_xesam_mimes_for_service_id (TrackerDBInterface *iface,
+				   gint		       service_id)
+{
+	return db_mime_query (iface, "GetXesamMimeForServiceId", service_id);
+}
+
+static GSList *
+db_get_xesam_mime_prefixes_for_service_id (TrackerDBInterface *iface,
+					   gint		       service_id)
+{
+	return db_mime_query (iface, "GetXesamMimePrefixForServiceId", service_id);
+}
+
+/* Sqlite utf-8 user defined collation sequence */
+static gint
+utf8_collation_func (gchar *str1,
+		     gint   len1,
+		     gchar *str2,
+		     int    len2)
+{
+	gchar *word1, *word2;
+	gint   result;
+
+	/* Collate words */
+	word1 = g_utf8_collate_key_for_filename (str1, len1);
+	word2 = g_utf8_collate_key_for_filename (str2, len2);
+
+	result = strcmp (word1, word2);
+
+	g_free (word1);
+	g_free (word2);
+
+	return result;
+}
+
+/* Converts date/time in UTC format to ISO 8160 standardised format for display */
+static GValue
+function_date_to_str (TrackerDBInterface *interface,
+		      gint		  argc,
+		      GValue		  values[])
+{
+	GValue	result = { 0, };
+	gchar  *str;
+
+	str = tracker_date_to_string (g_value_get_double (&values[0]));
+	g_value_init (&result, G_TYPE_STRING);
+	g_value_take_string (&result, str);
+
+	return result;
+}
+
+static GValue
+function_regexp (TrackerDBInterface *interface,
+		 gint		     argc,
+		 GValue		     values[])
+{
+	GValue	result = { 0, };
+	regex_t	regex;
+	int	ret;
+
+	if (argc != 2) {
+		g_critical ("Invalid argument count");
+		return result;
+	}
+
+	ret = regcomp (&regex,
+		       g_value_get_string (&values[0]),
+		       REG_EXTENDED | REG_NOSUB);
+
+	if (ret != 0) {
+		g_critical ("Error compiling regular expression");
+		return result;
+	}
+
+	ret = regexec (&regex,
+		       g_value_get_string (&values[1]),
+		       0, NULL, 0);
+
+	g_value_init (&result, G_TYPE_INT);
+	g_value_set_int (&result, (ret == REG_NOMATCH) ? 0 : 1);
+	regfree (&regex);
+
+	return result;
+}
+
+static GValue
+function_get_service_name (TrackerDBInterface *interface,
+			   gint		       argc,
+			   GValue	       values[])
+{
+	GValue	result = { 0, };
+	gchar  *str;
+
+	str = tracker_ontology_get_service_by_id (g_value_get_int (&values[0]));
+	g_value_init (&result, G_TYPE_STRING);
+	g_value_take_string (&result, str);
+
+	return result;
+}
+
+static GValue
+function_get_service_type (TrackerDBInterface *interface,
+			   gint		       argc,
+			   GValue	       values[])
+{
+	GValue result = { 0, };
+	gint   id;
+
+	id = tracker_ontology_get_service_id_by_name (g_value_get_string (&values[0]));
+	g_value_init (&result, G_TYPE_INT);
+	g_value_set_int (&result, id);
+
+	return result;
+}
+
+static GValue
+function_get_max_service_type (TrackerDBInterface *interface,
+			       gint		   argc,
+			       GValue		   values[])
+{
+	GValue result = { 0, };
+	gint   id;
+
+	id = tracker_ontology_get_service_id_by_name (g_value_get_string (&values[0]));
+	g_value_init (&result, G_TYPE_INT);
+	g_value_set_int (&result, id);
+
+	return result;
+}
+
+static gchar *
+function_uncompress_string (const gchar *ptr,
+			    gint	 size,
+			    gint	*uncompressed_size)
+{
+	z_stream       zs;
+	gchar	      *buf, *swap;
+	unsigned char  obuf[ZLIB_BUF_SIZE];
+	gint	       rv, asiz, bsiz, osiz;
+
+	zs.zalloc = Z_NULL;
+	zs.zfree = Z_NULL;
+	zs.opaque = Z_NULL;
+
+	if (inflateInit2 (&zs, 15) != Z_OK) {
+		return NULL;
+	}
+
+	asiz = size * 2 + 16;
+
+	if (asiz < ZLIB_BUF_SIZE) {
+		asiz = ZLIB_BUF_SIZE;
+	}
+
+	if (!(buf = malloc (asiz))) {
+		inflateEnd (&zs);
+		return NULL;
+	}
+
+	bsiz = 0;
+	zs.next_in = (unsigned char *)ptr;
+	zs.avail_in = size;
+	zs.next_out = obuf;
+	zs.avail_out = ZLIB_BUF_SIZE;
+
+	while ((rv = inflate (&zs, Z_NO_FLUSH)) == Z_OK) {
+		osiz = ZLIB_BUF_SIZE - zs.avail_out;
+
+		if (bsiz + osiz >= asiz) {
+			asiz = asiz * 2 + osiz;
+
+			if (!(swap = realloc (buf, asiz))) {
+				free (buf);
+				inflateEnd (&zs);
+				return NULL;
+			}
+
+			buf = swap;
+		}
+
+		memcpy (buf + bsiz, obuf, osiz);
+		bsiz += osiz;
+		zs.next_out = obuf;
+		zs.avail_out = ZLIB_BUF_SIZE;
+	}
+
+	if (rv != Z_STREAM_END) {
+		free (buf);
+		inflateEnd (&zs);
+		return NULL;
+	}
+	osiz = ZLIB_BUF_SIZE - zs.avail_out;
+
+	if (bsiz + osiz >= asiz) {
+		asiz = asiz * 2 + osiz;
+
+		if (!(swap = realloc (buf, asiz))) {
+			free (buf);
+			inflateEnd (&zs);
+			return NULL;
+		}
+
+		buf = swap;
+	}
+
+	memcpy (buf + bsiz, obuf, osiz);
+	bsiz += osiz;
+	buf[bsiz] = '\0';
+	*uncompressed_size = bsiz;
+	inflateEnd (&zs);
+
+	return buf;
+}
+
+static GByteArray *
+function_compress_string (const gchar *text)
+{
+	GByteArray *array;
+	z_stream zs;
+	gchar *buf, *swap;
+	guchar obuf[ZLIB_BUF_SIZE];
+	gint rv, asiz, bsiz, osiz, size;
+
+	size = strlen (text);
+
+	zs.zalloc = Z_NULL;
+	zs.zfree = Z_NULL;
+	zs.opaque = Z_NULL;
+
+	if (deflateInit2 (&zs, 6, Z_DEFLATED, 15, 6, Z_DEFAULT_STRATEGY) != Z_OK) {
+		return NULL;
+	}
+
+	asiz = size + 16;
+
+	if (asiz < ZLIB_BUF_SIZE) {
+		asiz = ZLIB_BUF_SIZE;
+	}
+
+	if (!(buf = malloc (asiz))) {
+		deflateEnd (&zs);
+		return NULL;
+	}
+
+	bsiz = 0;
+	zs.next_in = (unsigned char *) text;
+	zs.avail_in = size;
+	zs.next_out = obuf;
+	zs.avail_out = ZLIB_BUF_SIZE;
+
+	while ((rv = deflate (&zs, Z_FINISH)) == Z_OK) {
+		osiz = ZLIB_BUF_SIZE - zs.avail_out;
+
+		if (bsiz + osiz > asiz) {
+			asiz = asiz * 2 + osiz;
+
+			if (!(swap = realloc (buf, asiz))) {
+				free (buf);
+				deflateEnd (&zs);
+				return NULL;
+			}
+
+			buf = swap;
+		}
+
+		memcpy (buf + bsiz, obuf, osiz);
+		bsiz += osiz;
+		zs.next_out = obuf;
+		zs.avail_out = ZLIB_BUF_SIZE;
+	}
+
+	if (rv != Z_STREAM_END) {
+		free (buf);
+		deflateEnd (&zs);
+		return NULL;
+	}
+
+	osiz = ZLIB_BUF_SIZE - zs.avail_out;
+
+	if (bsiz + osiz + 1 > asiz) {
+		asiz = asiz * 2 + osiz;
+
+		if (!(swap = realloc (buf, asiz))) {
+			free (buf);
+			deflateEnd (&zs);
+			return NULL;
+		}
+
+		buf = swap;
+	}
+
+	memcpy (buf + bsiz, obuf, osiz);
+	bsiz += osiz;
+	buf[bsiz] = '\0';
+
+	array = g_byte_array_new ();
+	g_byte_array_append (array, (const guint8 *) buf, bsiz);
+
+	g_free (buf);
+
+	deflateEnd (&zs);
+
+	return array;
+}
+
+static GValue
+function_uncompress (TrackerDBInterface *interface,
+		     gint		 argc,
+		     GValue		 values[])
+{
+	GByteArray *array;
+	GValue	    result = { 0, };
+	gchar	   *output;
+	gint	    len;
+
+	array = g_value_get_boxed (&values[0]);
+
+	if (!array) {
+		return result;
+	}
+
+	output = function_uncompress_string ((const gchar *) array->data,
+					     array->len,
+					     &len);
+
+	if (!output) {
+		g_warning ("Uncompress failed");
+		return result;
+	}
+
+	g_value_init (&result, G_TYPE_STRING);
+	g_value_take_string (&result, output);
+
+	return result;
+}
+
+static GValue
+function_compress (TrackerDBInterface *interface,
+		   gint		       argc,
+		   GValue	       values[])
+{
+	GByteArray *array;
+	GValue result = { 0, };
+	const gchar *text;
+
+	text = g_value_get_string (&values[0]);
+
+	array = function_compress_string (text);
+
+	if (!array) {
+		g_warning ("Compress failed");
+		return result;
+	}
+
+	g_value_init (&result, TRACKER_TYPE_DB_BLOB);
+	g_value_take_boxed (&result, array);
+
+	return result;
+}
+
+static GValue
+function_replace (TrackerDBInterface *interface,
+		  gint		      argc,
+		  GValue	      values[])
+{
+	GValue result = { 0, };
+	gchar *str;
+
+	str = tracker_string_replace (g_value_get_string (&values[0]),
+				      g_value_get_string (&values[1]),
+				      g_value_get_string (&values[2]));
+
+	g_value_init (&result, G_TYPE_STRING);
+	g_value_take_string (&result, str);
+
+	return result;
+}
+
+static void
+db_set_params (TrackerDBInterface *iface,
+	       gint		   cache_size,
+	       gint		   page_size,
+	       gboolean		   add_functions)
+{
+	tracker_db_interface_execute_query (iface, NULL, "PRAGMA synchronous = NORMAL;");
+	tracker_db_interface_execute_query (iface, NULL, "PRAGMA count_changes = 0;");
+	tracker_db_interface_execute_query (iface, NULL, "PRAGMA temp_store = FILE;");
+	tracker_db_interface_execute_query (iface, NULL, "PRAGMA encoding = \"UTF-8\"");
+	tracker_db_interface_execute_query (iface, NULL, "PRAGMA auto_vacuum = 0;");
+
+	if (page_size != TRACKER_DB_PAGE_SIZE_DONT_SET) {
+		g_message ("  Setting page size to %d", page_size);
+		tracker_db_interface_execute_query (iface, NULL, "PRAGMA page_size = %d", page_size);
+	}
+
+	tracker_db_interface_execute_query (iface, NULL, "PRAGMA cache_size = %d", cache_size);
+	g_message ("  Setting cache size to %d", cache_size);
+
+	if (add_functions) {
+		g_message ("  Adding functions (FormatDate, etc)");
+
+		if (!tracker_db_interface_sqlite_set_collation_function (TRACKER_DB_INTERFACE_SQLITE (iface),
+									 "UTF8",
+									 utf8_collation_func)) {
+			g_critical ("Collation sequence failed");
+		}
+
+		/* Create user defined functions that can be used in sql */
+		tracker_db_interface_sqlite_create_function (iface,
+							     "FormatDate",
+							     function_date_to_str,
+							     1);
+		tracker_db_interface_sqlite_create_function (iface,
+							     "GetServiceName",
+							     function_get_service_name,
+							     1);
+		tracker_db_interface_sqlite_create_function (iface,
+							     "GetServiceTypeID",
+							     function_get_service_type,
+							     1);
+		tracker_db_interface_sqlite_create_function (iface,
+							     "GetMaxServiceTypeID",
+							     function_get_max_service_type,
+							     1);
+		tracker_db_interface_sqlite_create_function (iface,
+							     "REGEXP",
+							     function_regexp,
+							     2);
+
+		tracker_db_interface_sqlite_create_function (iface,
+							     "uncompress",
+							     function_uncompress,
+							     1);
+		tracker_db_interface_sqlite_create_function (iface,
+							     "compress",
+							     function_compress,
+							     1);
+		tracker_db_interface_sqlite_create_function (iface,
+							     "replace",
+							     function_replace,
+							     3);
+	}
+}
+
+
+static void
+db_get_static_data (TrackerDBInterface *iface)
+{
+	TrackerDBResultSet *result_set;
+
+	/* Get static metadata info */
+	result_set = tracker_db_interface_execute_procedure (iface,
+							     NULL,
+							     "GetMetadataTypes",
+							     NULL);
+
+	if (result_set) {
+		gboolean valid = TRUE;
+		gint	 id;
+
+		while (valid) {
+			TrackerDBResultSet *result_set2;
+			TrackerField	   *def;
+			GSList		   *child_ids = NULL;
+
+			def = db_row_to_field_def (result_set);
+
+			result_set2 = tracker_db_interface_execute_procedure (iface,
+									      NULL,
+									      "GetMetadataAliases",
+									      tracker_field_get_id (def),
+									      NULL);
+
+			if (result_set2) {
+				valid = TRUE;
+
+				while (valid) {
+					tracker_db_result_set_get (result_set2, 1, &id, -1);
+					child_ids = g_slist_prepend (child_ids,
+								     tracker_gint_to_string (id));
+
+					valid = tracker_db_result_set_iter_next (result_set2);
+				}
+
+				tracker_field_set_child_ids (def, child_ids);
+				g_object_unref (result_set2);
+
+				g_slist_foreach (child_ids, (GFunc) g_free, NULL);
+				g_slist_free (child_ids);
+			}
+
+			g_message ("Loading metadata def:'%s' with weight:%d",
+				   tracker_field_get_name (def),
+				   tracker_field_get_weight (def));
+
+			tracker_ontology_field_add (def);
+			g_object_unref (def);
+
+			valid = tracker_db_result_set_iter_next (result_set);
+		}
+
+		g_object_unref (result_set);
+	}
+
+	/* Get static service info */
+	result_set = tracker_db_interface_execute_procedure (iface,
+							     NULL,
+							     "GetAllServices",
+							     NULL);
+
+	if (result_set) {
+		gboolean valid = TRUE;
+
+		while (valid) {
+			TrackerService *service;
+			GSList	       *mimes, *mime_prefixes;
+			const gchar    *name;
+			gint		id;
+
+			service = db_row_to_service (result_set);
+
+			if (!service) {
+				continue;
+			}
+
+			id = tracker_service_get_id (service);
+			name = tracker_service_get_name (service);
+
+			mimes = db_get_mimes_for_service_id (iface, id);
+			mime_prefixes = db_get_mime_prefixes_for_service_id (iface, id);
+
+			g_message ("Adding service:'%s' with id:%d and mimes:%d",
+				   name,
+				   id,
+				   g_slist_length (mimes));
+
+			tracker_ontology_service_add (service,
+							   mimes,
+							   mime_prefixes);
+
+			g_slist_free (mimes);
+			g_slist_free (mime_prefixes);
+			g_object_unref (service);
+
+			valid = tracker_db_result_set_iter_next (result_set);
+		}
+
+		g_object_unref (result_set);
+	}
+}
+
+static void
+db_get_static_xesam_data (TrackerDBInterface *iface)
+{
+	TrackerDBResultSet *result_set;
+
+	/* Get static xesam metadata info */
+	result_set = tracker_db_interface_execute_procedure (iface,
+							     NULL,
+							     "GetXesamMetaDataTypes",
+							     NULL);
+
+	if (result_set) {
+		gboolean valid = TRUE;
+
+		while (valid) {
+			TrackerField  *def;
+
+			def = db_row_to_field_def (result_set);
+			/*
+			 * The ids in xesam db overwritte the IDs in common db! It means that all the
+			 * files are assigned to a wrong category
+			 *
+			 * g_message ("Loading xesam metadata def:'%s' with type:%d",
+			 *		   tracker_field_get_name (def),
+			 *		   tracker_field_get_data_type (def));
+			 *
+			 * tracker_ontology_field_add (def);
+			 */
+			valid = tracker_db_result_set_iter_next (result_set);
+			g_object_unref (def);
+		}
+
+		g_object_unref (result_set);
+	}
+
+	/* Get static xesam service info */
+	result_set = tracker_db_interface_execute_procedure (iface,
+							     NULL,
+							     "GetXesamServiceTypes",
+							     NULL);
+
+	if (result_set) {
+		gboolean valid = TRUE;
+
+		while (valid) {
+			TrackerService *service;
+			GSList	       *mimes, *mime_prefixes;
+			const gchar    *name;
+			gint		id;
+
+			service = db_row_to_service (result_set);
+
+			if (!service) {
+				continue;
+			}
+
+			id = tracker_service_get_id (service);
+			name = tracker_service_get_name (service);
+
+			mimes = db_get_xesam_mimes_for_service_id (iface, id);
+			mime_prefixes = db_get_xesam_mime_prefixes_for_service_id (iface, id);
+
+			/*
+			 * Same as above
+			 *
+			 * g_message ("Adding xesam service:'%s' with id:%d and mimes:%d",
+			 *   name,
+			 *   id,
+			 *   g_slist_length (mimes));
+			 *
+			 * tracker_ontology_service_add (service,
+			 *				   mimes,
+			 *				   mime_prefixes);
+			 */
+			g_slist_free (mimes);
+			g_slist_free (mime_prefixes);
+			g_object_unref (service);
+
+			valid = tracker_db_result_set_iter_next (result_set);
+		}
+
+		g_object_unref (result_set);
+	}
+}
+
+static const gchar *
+db_type_to_string (TrackerDB db)
+{
+	GType	    type;
+	GEnumClass *enum_class;
+	GEnumValue *enum_value;
+
+	type = tracker_db_get_type ();
+	enum_class = G_ENUM_CLASS (g_type_class_peek (type));
+	enum_value = g_enum_get_value (enum_class, db);
+
+	if (!enum_value) {
+		return "unknown";
+	}
+
+	return enum_value->value_nick;
+}
+
+static TrackerDBInterface *
+db_interface_get (TrackerDB  type,
+		  gboolean  *create)
+{
+	TrackerDBInterface *iface;
+	const gchar	   *path;
+
+	path = dbs[type].abs_filename;
+
+	if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
+		*create = TRUE;
+	} else {
+		*create = FALSE;
+	}
+
+	g_message ("%s database... '%s' (%s)",
+		   *create ? "Creating" : "Loading",
+		   path,
+		   db_type_to_string (type));
+
+	iface = tracker_db_interface_sqlite_new (path);
+
+	tracker_db_interface_set_procedure_table (iface,
+						  prepared_queries);
+
+	db_set_params (iface,
+		       dbs[type].cache_size,
+		       dbs[type].page_size,
+		       dbs[type].add_functions);
+
+	return iface;
+}
+
+static TrackerDBInterface *
+db_interface_get_common (void)
+{
+	TrackerDBInterface *iface;
+	gboolean	    create;
+
+	iface = db_interface_get (TRACKER_DB_COMMON, &create);
+
+	if (create) {
+		tracker_db_interface_start_transaction (iface);
+		/* Create tables */
+		load_sql_file (iface, "sqlite-tracker.sql", NULL);
+		load_sql_file (iface, "sqlite-metadata.sql", NULL);
+		load_sql_file (iface, "sqlite-service-types.sql", NULL);
+
+		/* Load services info */
+		load_service_file (iface, "default.service");
+
+		/* Load metadata info */
+		load_metadata_file (iface, "default.metadata");
+		load_metadata_file (iface, "file.metadata");
+		load_metadata_file (iface, "audio.metadata");
+		load_metadata_file (iface, "application.metadata");
+		load_metadata_file (iface, "document.metadata");
+		load_metadata_file (iface, "email.metadata");
+		load_metadata_file (iface, "image.metadata");
+		load_metadata_file (iface, "video.metadata");
+		tracker_db_interface_end_transaction (iface);
+	}
+
+	/* Load static data into tracker ontology */
+	db_get_static_data (iface);
+
+	return iface;
+}
+
+static TrackerDBInterface *
+db_interface_get_cache (void)
+{
+	TrackerDBInterface *iface;
+	gboolean	    create;
+
+	iface = db_interface_get (TRACKER_DB_CACHE, &create);
+
+	if (create) {
+		tracker_db_interface_start_transaction (iface);
+		load_sql_file (iface, "sqlite-cache.sql", NULL);
+		tracker_db_interface_end_transaction (iface);
+	}
+
+	return iface;
+}
+
+static TrackerDBInterface *
+db_interface_get_file_metadata (void)
+{
+	TrackerDBInterface *iface;
+	gboolean	    create;
+
+	iface = db_interface_get (TRACKER_DB_FILE_METADATA, &create);
+
+	if (create) {
+		tracker_db_interface_start_transaction (iface);
+		load_sql_file (iface, "sqlite-service.sql", NULL);
+		load_sql_file (iface, "sqlite-service-triggers.sql", "!");
+		tracker_db_interface_end_transaction (iface);
+	}
+
+	return iface;
+}
+
+static TrackerDBInterface *
+db_interface_get_file_contents (void)
+{
+	TrackerDBInterface *iface;
+	gboolean	    create;
+
+	iface = db_interface_get (TRACKER_DB_FILE_CONTENTS, &create);
+
+	if (create) {
+		tracker_db_interface_start_transaction (iface);
+		load_sql_file (iface, "sqlite-contents.sql", NULL);
+		tracker_db_interface_end_transaction (iface);
+	}
+
+	tracker_db_interface_sqlite_create_function (iface,
+						     "uncompress",
+						     function_uncompress,
+						     1);
+	tracker_db_interface_sqlite_create_function (iface,
+						     "compress",
+						     function_compress,
+						     1);
+
+	return iface;
+}
+
+static TrackerDBInterface *
+db_interface_get_email_metadata (void)
+{
+	TrackerDBInterface *iface;
+	gboolean	    create;
+
+	iface = db_interface_get (TRACKER_DB_EMAIL_METADATA, &create);
+
+	if (create) {
+		tracker_db_interface_start_transaction (iface);
+		load_sql_file (iface, "sqlite-service.sql", NULL);
+		load_sql_file (iface, "sqlite-email.sql", NULL);
+		load_sql_file (iface, "sqlite-service-triggers.sql", "!");
+		tracker_db_interface_end_transaction (iface);
+	}
+
+	return iface;
+}
+
+static TrackerDBInterface *
+db_interface_get_email_contents (void)
+{
+	TrackerDBInterface *iface;
+	gboolean	    create;
+
+	iface = db_interface_get (TRACKER_DB_EMAIL_CONTENTS, &create);
+
+	if (create) {
+		tracker_db_interface_start_transaction (iface);
+		load_sql_file (iface, "sqlite-contents.sql", NULL);
+		tracker_db_interface_end_transaction (iface);
+	}
+
+	tracker_db_interface_sqlite_create_function (iface,
+						     "uncompress",
+						     function_uncompress,
+						     1);
+	tracker_db_interface_sqlite_create_function (iface,
+						     "compress",
+						     function_compress,
+						     1);
+
+	return iface;
+}
+
+static gboolean
+db_xesam_get_service_mapping (TrackerDBInterface *iface,
+			      const gchar	 *type,
+			      GList		**list)
+{
+	TrackerDBResultSet *result_set;
+	gboolean	    valid = TRUE;
+
+	result_set = db_exec_proc (iface,
+				   "GetXesamServiceMappings",
+				   type,
+				   NULL);
+
+	if (result_set) {
+		while (valid) {
+			gchar *st;
+
+			tracker_db_result_set_get (result_set, 0, &st, -1);
+			if (strcmp (st, " ") != 0) {
+				*list = g_list_prepend (*list, g_strdup (st));
+			}
+
+			valid = tracker_db_result_set_iter_next (result_set);
+			g_free (st);
+		}
+
+		*list = g_list_reverse (*list);
+		g_object_unref (result_set);
+	}
+
+	result_set = db_exec_proc (iface,
+				   "GetXesamServiceChildren",
+				   type,
+				   NULL);
+	valid = TRUE;
+
+	if (result_set) {
+		while (valid) {
+			gchar *st;
+
+			tracker_db_result_set_get (result_set, 0, &st, -1);
+			db_xesam_get_service_mapping (iface, st, list);
+
+			valid = tracker_db_result_set_iter_next (result_set);
+			g_free (st);
+		}
+
+		g_object_unref (result_set);
+	}
+
+	return TRUE;
+}
+
+static gboolean
+db_xesam_get_metadata_mapping (TrackerDBInterface  *iface,
+			       const gchar	   *type,
+			       GList		  **list)
+{
+	TrackerDBResultSet *result_set;
+	gboolean	    valid = TRUE;
+
+	result_set = db_exec_proc (iface,
+				   "GetXesamMetaDataMappings",
+				   type,
+				   NULL);
+
+	if (result_set) {
+		while (valid) {
+			gchar *st;
+
+			tracker_db_result_set_get (result_set, 0, &st, -1);
+
+			if (strcmp(st, " ") != 0) {
+				*list = g_list_prepend (*list, g_strdup (st));
+			}
+
+			valid = tracker_db_result_set_iter_next (result_set);
+			g_free (st);
+		}
+
+		*list = g_list_reverse (*list);
+		g_object_unref (result_set);
+	}
+
+	result_set = db_exec_proc (iface,
+				   "GetXesamMetaDataChildren",
+				   type,
+				   NULL);
+	valid = TRUE;
+
+	if (result_set) {
+		while (valid) {
+			gchar *st;
+
+			tracker_db_result_set_get (result_set, 0, &st, -1);
+			db_xesam_get_service_mapping (iface, st ,list);
+
+			valid = tracker_db_result_set_iter_next (result_set);
+			g_free (st);
+		}
+
+		g_object_unref (result_set);
+	}
+
+	return TRUE;
+}
+
+static gboolean
+db_xesam_create_lookup (TrackerDBInterface *iface)
+{
+	TrackerDBResultSet *result_set;
+	gboolean	    valid;
+
+	valid = TRUE;
+
+	result_set = db_exec_proc (iface,
+				   "GetXesamServiceParents",
+				   NULL);
+
+	if (result_set) {
+		while (valid) {
+			GList *list = NULL;
+			GList *iter = NULL;
+			gchar *st;
+
+			tracker_db_result_set_get (result_set, 0, &st, -1);
+			db_xesam_get_service_mapping (iface, st, &list);
+
+			iter = g_list_first (list);
+
+			while (iter) {
+				db_exec_proc (iface,
+					      "InsertXesamServiceLookup",
+					      st,
+					      iter->data,
+					      NULL);
+				g_free (iter->data);
+				iter = g_list_next (iter);
+			}
+
+			g_list_free (list);
+
+			valid = tracker_db_result_set_iter_next (result_set);
+		g_free (st);
+		}
+	}
+
+	g_object_unref (result_set);
+
+	valid = TRUE;
+	result_set = db_exec_proc (iface,
+				   "GetXesamMetaDataParents",
+				   NULL);
+
+	if (result_set) {
+		while (valid) {
+			GList *list = NULL;
+			GList *iter = NULL;
+			gchar *st;
+
+			tracker_db_result_set_get (result_set, 0, &st, -1);
+			db_xesam_get_metadata_mapping (iface, st, &list);
+
+			iter = g_list_first (list);
+			while (iter) {
+				db_exec_proc (iface,
+					      "InsertXesamMetaDataLookup",
+					      st,
+					      iter->data,
+					      NULL);
+				g_free (iter->data);
+				iter = g_list_next (iter);
+			}
+
+			g_list_free (list);
+
+			valid = tracker_db_result_set_iter_next (result_set);
+			g_free (st);
+		}
+	}
+
+	g_object_unref (result_set);
+
+	return TRUE;
+}
+
+static TrackerDBInterface *
+db_interface_get_xesam (void)
+{
+	TrackerDBInterface *iface;
+	gboolean	    create;
+
+	iface = db_interface_get (TRACKER_DB_XESAM, &create);
+
+	if (create) {
+		tracker_db_interface_start_transaction (iface);
+		load_sql_file (iface, "sqlite-xesam.sql", NULL);
+		load_service_file_xesam (iface, "xesam.metadata");
+		load_service_file_xesam (iface, "xesam-convenience.metadata");
+		load_service_file_xesam (iface, "xesam-virtual.metadata");
+		load_service_file_xesam (iface, "xesam.service");
+		load_service_file_xesam (iface, "xesam-convenience.service");
+		load_service_file_xesam (iface, "xesam-service.smapping");
+		load_service_file_xesam (iface, "xesam-metadata.mmapping");
+		db_xesam_create_lookup (iface);
+		tracker_db_interface_end_transaction (iface);
+	}
+
+	/* Load static xesam data */
+	db_get_static_xesam_data (iface);
+
+	return iface;
+}
+
+static TrackerDBInterface *
+db_interface_create (TrackerDB db)
+{
+	switch (db) {
+	case TRACKER_DB_UNKNOWN:
+		return NULL;
+
+	case TRACKER_DB_COMMON:
+		return db_interface_get_common ();
+
+	case TRACKER_DB_CACHE:
+		return db_interface_get_cache ();
+
+	case TRACKER_DB_FILE_METADATA:
+		return db_interface_get_file_metadata ();
+
+	case TRACKER_DB_FILE_CONTENTS:
+		return db_interface_get_file_contents ();
+
+	case TRACKER_DB_EMAIL_METADATA:
+		return db_interface_get_email_metadata ();
+
+	case TRACKER_DB_EMAIL_CONTENTS:
+		return db_interface_get_email_contents ();
+
+	case TRACKER_DB_XESAM:
+		return db_interface_get_xesam ();
+	}
+
+	g_critical ("This TrackerDB type:%d->'%s' has no interface set up yet!!",
+		    db,
+		    db_type_to_string (db));
+
+	return NULL;
+}
+
+static void
+db_manager_remove_all (void)
+{
+	guint i;
+
+	g_message ("Removing all database files");
+
+	for (i = 1; i < G_N_ELEMENTS (dbs); i++) {
+		g_message ("Removing database:'%s'",
+			   dbs[i].abs_filename);
+		g_unlink (dbs[i].abs_filename);
+	}
+}
+
+static TrackerDBVersion
+db_get_version (void)
+{
+	TrackerDBVersion  version;
+	gchar            *filename;
+
+	filename = g_build_filename (data_dir, TRACKER_DB_VERSION_FILE, NULL);
+
+	if (G_LIKELY (g_file_test (filename, G_FILE_TEST_EXISTS))) {
+		gchar *contents;
+
+		/* Check version is correct */
+		if (G_LIKELY (g_file_get_contents (filename, &contents, NULL, NULL))) {
+			if (contents && strlen (contents) <= 2) {
+				TrackerDBVersion version;
+
+				version = atoi (contents);
+			} else {
+				g_message ("  Version file content size is either 0 or bigger than expected");
+
+				version = TRACKER_DB_VERSION_UNKNOWN;
+			} 
+
+			g_free (contents);
+		} else {
+			g_message ("  Could not get content of file '%s'", filename);
+
+			version = TRACKER_DB_VERSION_UNKNOWN;
+		}
+	} else {
+		g_message ("  Could not find database version file:'%s'", filename);
+		g_message ("  Current databases are either old or no databases are set up yet");
+
+		version = TRACKER_DB_VERSION_UNKNOWN;
+	}
+
+	g_free (filename);
+
+	return version;
+}
+
+static void
+db_set_version (void)
+{
+	GError *error = NULL;
+	gchar  *filename;
+	gchar  *str;
+
+	filename = g_build_filename (data_dir, TRACKER_DB_VERSION_FILE, NULL);
+	g_message ("  Creating version file '%s'", filename);
+
+	str = g_strdup_printf ("%d", TRACKER_DB_VERSION_NOW);
+
+	if (!g_file_set_contents (filename, str, -1, &error)) {
+		g_message ("  Could not set file contents, %s",
+			   error ? error->message : "no error given");
+		g_clear_error (&error);
+	}
+
+	g_free (str);
+	g_free (filename);
+}
+
+GType
+tracker_db_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			{ TRACKER_DB_COMMON,
+			  "TRACKER_DB_COMMON",
+			  "common" },
+			{ TRACKER_DB_CACHE,
+			  "TRACKER_DB_CACHE",
+			  "cache" },
+			{ TRACKER_DB_FILE_METADATA,
+			  "TRACKER_DB_FILE_METADATA",
+			  "file metadata" },
+			{ TRACKER_DB_FILE_CONTENTS,
+			  "TRACKER_DB_FILE_CONTENTS",
+			  "file contents" },
+			{ TRACKER_DB_EMAIL_METADATA,
+			  "TRACKER_DB_EMAIL_METADATA",
+			  "email metadata" },
+			{ TRACKER_DB_EMAIL_CONTENTS,
+			  "TRACKER_DB_EMAIL_CONTENTS",
+			  "email contents" },
+			{ TRACKER_DB_XESAM,
+			  "TRACKER_DB_XESAM",
+			  "xesam" },
+			{ 0, NULL, NULL }
+		};
+
+		etype = g_enum_register_static ("TrackerDB", values);
+	}
+
+	return etype;
+}
+
+void
+tracker_db_manager_init (TrackerDBManagerFlags	flags,
+			 gboolean	       *first_time)
+{
+	GType		    etype;
+	TrackerDBVersion    version;
+	gchar		   *filename;
+	const gchar	   *dir;
+	gboolean	    need_reindex;
+	guint		    i;
+
+	if (first_time) {
+		*first_time = FALSE;
+	}
+
+	if (initialized) {
+		return;
+	}
+
+	need_reindex = FALSE;
+
+	/* Since we don't reference this enum anywhere, we do
+	 * it here to make sure it exists when we call
+	 * g_type_class_peek(). This wouldn't be necessary if
+	 * it was a param in a GObject for example.
+	 *
+	 * This does mean that we are leaking by 1 reference
+	 * here and should clean it up, but it doesn't grow so
+	 * this is acceptable.
+	 */
+	etype = tracker_db_get_type ();
+	db_type_enum_class_pointer = g_type_class_ref (etype);
+
+	/* Set up locations */
+	g_message ("Setting database locations");
+
+	services_dir = g_build_filename (SHAREDIR,
+					 "tracker",
+					 "services",
+					 NULL);
+	sql_dir = g_build_filename (SHAREDIR,
+				    "tracker",
+				    NULL);
+
+	user_data_dir = g_build_filename (g_get_user_data_dir (),
+					  "tracker",
+					  "data",
+					  NULL);
+
+	data_dir = g_build_filename (g_get_user_cache_dir (),
+				     "tracker",
+				     NULL);
+
+	filename = g_strdup_printf ("tracker-%s", g_get_user_name ());
+	sys_tmp_dir = g_build_filename (g_get_tmp_dir (), filename, NULL);
+	g_free (filename);
+
+	/* Make sure the directories exist */
+	g_message ("Checking database directories exist");
+
+	g_mkdir_with_parents (data_dir, 00755);
+	g_mkdir_with_parents (user_data_dir, 00755);
+	g_mkdir_with_parents (sys_tmp_dir, 00755);
+
+	g_message ("Checking database version");
+
+	version = db_get_version ();
+
+	if (version == TRACKER_DB_VERSION_UNKNOWN ||
+	    version == TRACKER_DB_VERSION_1) {
+		g_message ("  A reindex will be forced");
+		need_reindex = TRUE;
+	}
+
+	if (need_reindex) {
+		db_set_version ();	
+	}
+
+	g_message ("Checking database files exist");
+
+	for (i = 1; i < G_N_ELEMENTS (dbs); i++) {
+		/* Fill absolute path for the database */
+		dir = location_to_directory (dbs[i].location);
+		dbs[i].abs_filename = g_build_filename (dir, dbs[i].file, NULL);
+
+		if (flags & TRACKER_DB_MANAGER_LOW_MEMORY_MODE) {
+			dbs[i].cache_size /= 2;
+		}
+
+		/* Check we have each database in place, if one is
+		 * missing, we reindex, except the cache which we
+		 * expect to be replaced on each startup.
+		 */
+		if (i == TRACKER_DB_CACHE) {
+			continue;
+		}
+
+		/* No need to check for other files not existing (for
+		 * reindex) if one is already missing.
+		 */
+		if (need_reindex) {
+			continue;
+		}
+
+		if (!g_file_test (dbs[i].abs_filename, G_FILE_TEST_EXISTS)) {
+			g_message ("Could not find database file:'%s'", dbs[i].abs_filename);
+			g_message ("One or more database files are missing, a reindex will be forced");
+			need_reindex = TRUE;
+		}
+	}
+
+	/* Add prepared queries */
+	prepared_queries = g_hash_table_new_full (g_str_hash,
+						  g_str_equal,
+						  g_free,
+						  g_free);
+
+	load_prepared_queries ();
+
+	/* Should we reindex? If so, just remove all databases files,
+	 * NOT the paths, note, that these paths are also used for
+	 * other things like the nfs lock file.
+	 */
+	if (flags & TRACKER_DB_MANAGER_FORCE_REINDEX || need_reindex) {
+		if (first_time) {
+			*first_time = TRUE;
+		}
+
+		/* We call an internal version of this function here
+		 * because at the time 'initialized' = FALSE and that
+		 * will cause errors and do nothing.
+		 */
+		g_message ("Cleaning up database files for reindex");
+		db_manager_remove_all ();
+
+		/* In cases where we re-init this module, make sure
+		 * we have cleaned up the ontology before we load all
+		 * new databases.
+		 */
+		tracker_ontology_shutdown ();
+
+		/* Make sure we initialize all other modules we depend on */
+		tracker_ontology_init ();
+
+		/* Now create the databases and close them */
+		g_message ("Creating database files, this may take a few moments...");
+
+		for (i = 1; i < G_N_ELEMENTS (dbs); i++) {
+			dbs[i].iface = db_interface_create (i);
+		}
+
+		/* We don't close the dbs in the same loop as before
+		 * becase some databases need other databases
+		 * attached to be created correctly.
+		 */
+		for (i = 1; i < G_N_ELEMENTS (dbs); i++) {
+			g_object_unref (dbs[i].iface);
+			dbs[i].iface = NULL;
+		}
+
+	} else {
+		/* Make sure we remove and recreate the cache directory in tmp
+		 * each time we start up, this is meant to be a per-run
+		 * thing.
+		 */
+		if (flags & TRACKER_DB_MANAGER_REMOVE_CACHE) {
+			g_message ("Removing cache database:'%s'",
+				   dbs[TRACKER_DB_CACHE].abs_filename);
+			g_unlink (dbs[TRACKER_DB_CACHE].abs_filename);
+		}
+
+		/* Make sure we initialize all other modules we depend on */
+		tracker_ontology_init ();
+	}
+
+	/* Load databases */
+	g_message ("Loading databases files...");
+
+	for (i = 1; i < G_N_ELEMENTS (dbs); i++) {
+		dbs[i].iface = db_interface_create (i);
+	}
+
+	initialized = TRUE;
+}
+
+void
+tracker_db_manager_shutdown (void)
+{
+	guint i;
+
+	if (!initialized) {
+		return;
+	}
+
+	for (i = 1; i < G_N_ELEMENTS (dbs); i++) {
+		if (dbs[i].abs_filename) {
+			g_free (dbs[i].abs_filename);
+			dbs[i].abs_filename = NULL;
+
+			if (dbs[i].iface) {
+				g_object_unref (dbs[i].iface);
+				dbs[i].iface = NULL;
+			}
+		}
+	}
+
+	g_hash_table_unref (prepared_queries);
+	prepared_queries = NULL;
+
+	g_free (data_dir);
+	g_free (user_data_dir);
+	g_free (sys_tmp_dir);
+	g_free (services_dir);
+	g_free (sql_dir);
+
+	if (file_iface)
+		g_object_unref (file_iface);
+	if (email_iface)
+		g_object_unref (email_iface);
+	if (xesam_iface)
+		g_object_unref (xesam_iface);
+
+
+	/* Since we don't reference this enum anywhere, we do
+	 * it here to make sure it exists when we call
+	 * g_type_class_peek(). This wouldn't be necessary if
+	 * it was a param in a GObject for example.
+	 *
+	 * This does mean that we are leaking by 1 reference
+	 * here and should clean it up, but it doesn't grow so
+	 * this is acceptable.
+	 */
+	g_type_class_unref (db_type_enum_class_pointer);
+	db_type_enum_class_pointer = NULL;
+
+	/* Make sure we shutdown all other modules we depend on */
+	tracker_ontology_shutdown ();
+
+	initialized = FALSE;
+}
+
+void
+tracker_db_manager_remove_all (void)
+{
+	g_return_if_fail (initialized != FALSE);
+
+	db_manager_remove_all ();
+}
+
+const gchar *
+tracker_db_manager_get_file (TrackerDB db)
+{
+	g_return_val_if_fail (initialized != FALSE, NULL);
+
+	return dbs[db].abs_filename;
+}
+
+/**
+ * tracker_db_manager_get_db_interfaces:
+ * @num: amount of TrackerDB files wanted
+ * @...: All the files that you want in the connection as TrackerDB items
+ *
+ * Request a database connection where the first requested file gets connected
+ * to and the subsequent requsted files get attached to the connection.
+ *
+ * The caller must g_object_unref the result when finished using it.
+ *
+ * returns: (caller-owns): a database connection
+ **/
+TrackerDBInterface *
+tracker_db_manager_get_db_interfaces (gint num, ...)
+{
+	gint		    n_args;
+	va_list		    args;
+	TrackerDBInterface *connection = NULL;
+
+	g_return_val_if_fail (initialized != FALSE, NULL);
+
+	va_start (args, num);
+	for (n_args = 1; n_args <= num; n_args++) {
+		TrackerDB db = va_arg (args, TrackerDB);
+
+		if (!connection) {
+			connection = tracker_db_interface_sqlite_new (dbs[db].abs_filename);
+			tracker_db_interface_set_procedure_table (connection,
+								  prepared_queries);
+
+			db_set_params (connection,
+				       dbs[db].cache_size,
+				       dbs[db].page_size,
+				       TRUE);
+
+		} else {
+			db_exec_no_reply (connection,
+					  "ATTACH '%s' as '%s'",
+					  dbs[db].abs_filename,
+					  dbs[db].name);
+		}
+
+	}
+	va_end (args);
+
+	return connection;
+}
+
+
+
+/**
+ * tracker_db_manager_get_db_interface:
+ * @db: the database file wanted
+ *
+ * Request a database connection to the database file @db.
+ *
+ * The caller must NOT g_object_unref the result
+ *
+ * returns: (callee-owns): a database connection
+ **/
+TrackerDBInterface *
+tracker_db_manager_get_db_interface (TrackerDB db)
+{
+	g_return_val_if_fail (initialized != FALSE, NULL);
+
+	return dbs[db].iface;
+}
+
+/**
+ * tracker_db_manager_get_db_interface_by_service:
+ * @service: the server for which you'll use the database connection
+ *
+ * Request a database connection that can be used for @service. At this moment
+ * service can either be "Files", "Emails", "Attachments" or "Xesam".
+ *
+ * The caller must NOT g_object_unref the result
+ *
+ * returns: (callee-owns): a database connection
+ **/
+TrackerDBInterface *
+tracker_db_manager_get_db_interface_by_service (const gchar *service)
+{
+	TrackerDBInterface	  *iface;
+	TrackerDBType		   type;
+
+	g_return_val_if_fail (initialized != FALSE, NULL);
+	g_return_val_if_fail (service != NULL, NULL);
+
+	type = tracker_ontology_get_service_db_by_name (service);
+
+	switch (type) {
+	case TRACKER_DB_TYPE_EMAIL:
+
+		if (!email_iface) {
+			email_iface = tracker_db_manager_get_db_interfaces (4,
+									    TRACKER_DB_COMMON,
+									    TRACKER_DB_EMAIL_CONTENTS,
+									    TRACKER_DB_EMAIL_METADATA,
+									    TRACKER_DB_CACHE);
+		}
+
+		iface = email_iface;
+		break;
+
+	case TRACKER_DB_TYPE_XESAM:
+		if (!xesam_iface) {
+			xesam_iface = tracker_db_manager_get_db_interfaces (7,
+									    TRACKER_DB_CACHE,
+									    TRACKER_DB_COMMON,
+									    TRACKER_DB_FILE_CONTENTS,
+									    TRACKER_DB_FILE_METADATA,
+									    TRACKER_DB_EMAIL_CONTENTS,
+									    TRACKER_DB_EMAIL_METADATA,
+									    TRACKER_DB_XESAM);
+		}
+
+		iface = xesam_iface;
+		break;
+
+	case TRACKER_DB_TYPE_FILES:
+	default:
+		if (!file_iface) {
+			file_iface = tracker_db_manager_get_db_interfaces (4,
+									   TRACKER_DB_COMMON,
+									   TRACKER_DB_FILE_CONTENTS,
+									   TRACKER_DB_FILE_METADATA,
+									   TRACKER_DB_CACHE);
+		}
+
+		iface = file_iface;
+		break;
+	}
+
+	return iface;
+}
+
+TrackerDBInterface *
+tracker_db_manager_get_db_interface_by_type (const gchar	  *service,
+					     TrackerDBContentType  content_type)
+{
+	TrackerDBType type;
+	TrackerDB     db;
+
+	g_return_val_if_fail (initialized != FALSE, NULL);
+	g_return_val_if_fail (service != NULL, NULL);
+
+	type = tracker_ontology_get_service_db_by_name (service);
+
+	switch (type) {
+	case TRACKER_DB_TYPE_EMAIL:
+		if (content_type == TRACKER_DB_CONTENT_TYPE_METADATA) {
+			db = TRACKER_DB_EMAIL_METADATA;
+		} else {
+			db = TRACKER_DB_EMAIL_CONTENTS;
+		}
+
+		break;
+	case TRACKER_DB_TYPE_FILES:
+		if (content_type == TRACKER_DB_CONTENT_TYPE_METADATA) {
+			db = TRACKER_DB_FILE_METADATA;
+		} else {
+			db = TRACKER_DB_FILE_CONTENTS;
+		}
+
+		break;
+	default:
+		g_warning ("Database type not supported");
+		return NULL;
+	}
+
+	return tracker_db_manager_get_db_interface (db);
+}
+
+gboolean
+tracker_db_manager_are_db_too_big (void)
+{
+	const gchar *filename_const;
+	gboolean     too_big;
+
+	filename_const = tracker_db_manager_get_file (TRACKER_DB_FILE_METADATA);
+	too_big = tracker_file_get_size (filename_const) > TRACKER_DB_MAX_FILE_SIZE;
+
+	if (too_big) {
+		g_critical ("File metadata database is too big, discontinuing indexing");
+		return TRUE;
+	}
+
+	filename_const = tracker_db_manager_get_file (TRACKER_DB_EMAIL_METADATA);
+	too_big = tracker_file_get_size (filename_const) > TRACKER_DB_MAX_FILE_SIZE;
+
+	if (too_big) {
+		g_critical ("Email metadata database is too big, discontinuing indexing");
+		return TRUE;
+	}
+
+	return FALSE;
+
+}

Added: trunk/src/libtracker-db/tracker-db-manager.h
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-db/tracker-db-manager.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,81 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKER_DB_MANAGER_H__
+#define __TRACKER_DB_MANAGER_H__
+
+#include <glib-object.h>
+
+#include "tracker-db-interface.h"
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_DB (tracker_db_get_type ())
+
+typedef enum {
+	TRACKER_DB_UNKNOWN,
+	TRACKER_DB_COMMON,
+	TRACKER_DB_CACHE,
+	TRACKER_DB_FILE_METADATA,
+	TRACKER_DB_FILE_CONTENTS,
+	TRACKER_DB_EMAIL_METADATA,
+	TRACKER_DB_EMAIL_CONTENTS,
+	TRACKER_DB_XESAM,
+} TrackerDB;
+
+typedef enum {
+	TRACKER_DB_CONTENT_TYPE_METADATA,
+	TRACKER_DB_CONTENT_TYPE_CONTENTS
+} TrackerDBContentType;
+
+typedef enum {
+	TRACKER_DB_MANAGER_FORCE_REINDEX    = 1 << 1,
+	TRACKER_DB_MANAGER_REMOVE_CACHE     = 1 << 2,
+	TRACKER_DB_MANAGER_LOW_MEMORY_MODE  = 1 << 3,
+} TrackerDBManagerFlags;
+
+#define TRACKER_DB_FOR_FILE_SERVICE	"Files"
+#define TRACKER_DB_FOR_EMAIL_SERVICE	"Emails"
+#define TRACKER_DB_FOR_XESAM_SERVICE	"Xesam"
+
+GType	     tracker_db_get_type			    (void) G_GNUC_CONST;
+
+void	     tracker_db_manager_init			    (TrackerDBManagerFlags  flags,
+							     gboolean		   *first_time);
+void	     tracker_db_manager_shutdown		    (void);
+
+void	     tracker_db_manager_remove_all		    (void);
+void	     tracker_db_manager_close_all		    (void);
+
+const gchar *tracker_db_manager_get_file		    (TrackerDB		    db);
+TrackerDBInterface *
+	     tracker_db_manager_get_db_interface	    (TrackerDB		    db);
+TrackerDBInterface *
+	     tracker_db_manager_get_db_interfaces	    (gint num, ...);
+TrackerDBInterface *
+	     tracker_db_manager_get_db_interface_by_service (const gchar	   *service);
+TrackerDBInterface *
+	     tracker_db_manager_get_db_interface_by_type    (const gchar	   *service,
+							     TrackerDBContentType   content_type);
+gboolean     tracker_db_manager_are_db_too_big		    (void);
+
+G_END_DECLS
+
+#endif /* __TRACKER_DB_MANAGER_H__ */

Added: trunk/src/libtracker-gtk/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-gtk/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,31 @@
+include $(top_srcdir)/Makefile.decl
+
+INCLUDES = 				\
+	-I$(top_srcdir)/src/libtracker 	\
+	$(LIBTRACKERGTK_CFLAGS)
+	
+libtracker_gtkincdir = $(includedir)/libtracker-gtk
+
+lib_LTLIBRARIES = libtracker-gtk.la
+
+libtracker_gtkinc_HEADERS =		\
+	tracker-gtk.h			\
+	tracker-utils.h 		\
+	tracker-ui.h			\
+	tracker-metadata-tile.h		\
+	tracker-tag-bar.h		\
+	tracker-keyword-store.h
+
+libtracker_gtk_la_SOURCES =		\
+	tracker-utils.c			\
+	tracker-ui.c 			\
+	tracker-metadata-tile.c		\
+	tracker-tag-bar.c		\
+	tracker-keyword-store.c
+
+libtracker_gtk_la_LIBADD = 		\
+	$(top_builddir)/src/libtracker/libtrackerclient.la \
+	$(LIBTRACKERGTK_LIBS) 
+
+libtracker_gtk_la_LDFLAGS = -version-info 0:0:0
+

Added: trunk/src/libtracker-gtk/tracker-gtk.h
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-gtk/tracker-gtk.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,33 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+ *
+ * @file libtracker-gtk/tracker-gtk.h Main include file
+ *
+ * @Copyright (C) 2007 John Stowers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef TRACKER_GTK_H
+#define TRACKER_GTK_H
+
+#include <tracker-utils.h>
+#include <tracker-ui.h>
+#include <tracker-keyword-store.h>
+#include <tracker-metadata-tile.h>
+#include <tracker-tag-bar.h>
+
+#endif /* TRACKER_GTK_H */
+

Added: trunk/src/libtracker-gtk/tracker-keyword-store.c
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-gtk/tracker-keyword-store.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,256 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+ *
+ * tracker-gtk/keyword-store.c - A derived GtkListStore that maintians a
+ * DBus connection to tracker such that when a new keyword is created it
+ * is automatically inserted here.
+ *
+ * Copyright (C) 2007 John Stowers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "tracker-keyword-store.h"
+#include "tracker-utils.h"
+
+typedef struct _TrackerKeywordStore TrackerKeywordStore;
+
+static void tracker_keyword_store_tree_drag_source_init (GtkTreeDragSourceIface *iface);
+
+static void  tracker_keyword_store_finalize (GObject *object);
+
+
+static void tracker_keyword_store_populate_cb (GPtrArray *result, GError *error, gpointer user_data);
+
+G_DEFINE_TYPE_WITH_CODE (TrackerKeywordStore, tracker_keyword_store, GTK_TYPE_LIST_STORE,
+			G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
+			tracker_keyword_store_tree_drag_source_init))
+
+#define parent_class tracker_keyword_store_parent_class
+
+static void
+tracker_keyword_store_class_init (TrackerKeywordStoreClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	object_class->finalize = tracker_keyword_store_finalize;
+}
+
+static void
+tracker_keyword_store_init (TrackerKeywordStore *store)
+{
+	/* setup the basic class list store properties */
+	GType types[TRACKER_KEYWORD_STORE_NUM_COLUMNS];
+	types[TRACKER_KEYWORD_STORE_KEYWORD]	= G_TYPE_STRING;
+	types[TRACKER_KEYWORD_STORE_IMAGE_URI]	= G_TYPE_STRING;
+
+	gtk_list_store_set_column_types (GTK_LIST_STORE (store),
+					TRACKER_KEYWORD_STORE_NUM_COLUMNS, types);
+
+	//setup private members
+	store->keywords = g_hash_table_new (g_str_hash, g_str_equal);
+	store->tracker_client = tracker_connect (TRUE);
+
+	//populate the liststore asyncronously
+	tracker_keywords_get_list_async (store->tracker_client,
+					SERVICE_FILES,
+					tracker_keyword_store_populate_cb,
+					store);
+}
+
+static gboolean
+tracker_keyword_store_row_draggable (GtkTreeDragSource		*drag_source,
+					GtkTreePath		*path)
+{
+	printf ("ROW DRAGGABLE\n");
+	return TRUE;
+}
+
+static gboolean
+tracker_keyword_store_drag_data_get (GtkTreeDragSource		*drag_source,
+				     GtkTreePath		*path,
+				     GtkSelectionData		*data)
+{
+	gchar *keyword;
+	GtkTreeIter iter;
+	TrackerKeywordStore *store;
+
+	printf ("DRAG DATA GET %s\n",gdk_atom_name (data->target));
+
+	store = TRACKER_KEYWORD_STORE (drag_source);
+
+	gtk_tree_model_get_iter (GTK_TREE_MODEL(store), &iter, path);
+
+	gtk_tree_model_get (GTK_TREE_MODEL(store), &iter, TRACKER_KEYWORD_STORE_KEYWORD, &keyword, -1);
+
+	gtk_selection_data_set_text (data, keyword, strlen (keyword));
+	g_free (keyword);
+	return TRUE;
+}
+
+static gboolean
+tracker_keyword_store_drag_data_delete (GtkTreeDragSource	*drag_source,
+					GtkTreePath		*path)
+{
+	printf ("DRAG DATA DELETE\n");
+	return FALSE;
+}
+
+static void
+tracker_keyword_store_tree_drag_source_init (GtkTreeDragSourceIface *iface)
+{
+	iface->row_draggable = tracker_keyword_store_row_draggable;
+	iface->drag_data_get = tracker_keyword_store_drag_data_get;
+	iface->drag_data_delete = tracker_keyword_store_drag_data_delete;
+}
+
+static void
+tracker_keyword_store_populate_cb (GPtrArray *result, GError *error, gpointer user_data) {
+	GtkTreeIter iter;
+	GtkListStore *list_store = GTK_LIST_STORE (user_data);
+
+	if (!error && result) {
+		gchar *name = NULL;
+		guint i;
+		for (i = 0; i < result->len; i++) {
+			name = ((gchar **)result->pdata[i])[0];
+			if (strlen (name) > 2) {
+				//FIXME: Modify this function when tracker stores emblem images
+				gtk_list_store_insert_with_values (list_store, &iter, 0,
+								TRACKER_KEYWORD_STORE_KEYWORD, name,
+								-1);
+
+			}
+		}
+		g_ptr_array_free (result, TRUE);
+	}
+	g_clear_error (&error);
+}
+
+static void
+tracker_keyword_store_finalize (GObject *object)
+{
+	TrackerKeywordStore *store = TRACKER_KEYWORD_STORE (object);
+
+	if (store->keywords) {
+		g_hash_table_unref (store->keywords);
+	}
+
+	if (store->tracker_client) {
+		tracker_disconnect (store->tracker_client);
+	}
+
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+/**
+ * tracker_keyword_store_new:
+ *
+ * Creates a #GtkListStore with several columns. This store is especially
+ * useful because it also contais a hashtable which retains a gtk_tree_iter
+ * indexed by keyword, for O(1) fast lookups of liststore contents.
+ *
+ * Return value: a new #TrackerKeywordStore.
+ *
+ **/
+GtkListStore *
+tracker_keyword_store_new (void)
+{
+	return g_object_new (TRACKER_TYPE_KEYWORD_STORE, NULL);
+}
+
+/**
+ * Inserts keyword into the liststore
+ * returns true if successful
+ **/
+gboolean
+tracker_keyword_store_insert (	GtkListStore			*store,
+				const char			*keyword,
+				const char			*stock_id
+)
+{
+	GtkTreeIter *iter;
+	TrackerKeywordStore *self;
+
+	g_return_val_if_fail (TRACKER_IS_KEYWORD_STORE(store), FALSE);
+	g_return_val_if_fail (keyword != NULL, FALSE);
+
+	self = TRACKER_KEYWORD_STORE (store);
+
+	if (g_hash_table_lookup (self->keywords, keyword) == NULL)
+	{
+		iter = (GtkTreeIter *)g_new0 (GtkTreeIter, 1);
+		gtk_list_store_insert_with_values (store,
+						iter, 0,
+						TRACKER_KEYWORD_STORE_KEYWORD, keyword,
+						TRACKER_KEYWORD_STORE_IMAGE_URI, stock_id,
+						-1);
+		g_hash_table_insert (self->keywords, g_strdup (keyword), iter);
+		return TRUE;
+
+	}
+	return FALSE;
+}
+
+/**
+ * O(1) lookup of items by keyword from the store
+ * Returns the GtkTreeIter corresponding to the item with keyword or
+ * NULL of it cant be found
+ **/
+GtkTreeIter *
+tracker_keyword_store_lookup (	GtkListStore			*store,
+				const char			*keyword)
+{
+	TrackerKeywordStore *self;
+
+	g_return_val_if_fail (TRACKER_IS_KEYWORD_STORE(store), FALSE);
+	g_return_val_if_fail (keyword != NULL, FALSE);
+
+	self = TRACKER_KEYWORD_STORE (store);
+	return (GtkTreeIter *)g_hash_table_lookup (self->keywords, keyword);
+}
+
+/**
+ * O(1) removal of items by keyword
+ **/
+gboolean
+tracker_keyword_store_remove (	GtkListStore			*store,
+				const char			*keyword)
+{
+	GtkTreeIter *iter;
+	TrackerKeywordStore *self;
+	gboolean a,b;
+
+	iter = tracker_keyword_store_lookup (store, keyword);
+	self = TRACKER_KEYWORD_STORE (store);
+
+	if (iter != NULL) {
+		a = gtk_list_store_remove (store, iter);
+		b = g_hash_table_remove (self->keywords, keyword);
+		return a && b;
+	}
+	return FALSE;
+}
+
+
+
+
+
+
+
+

Added: trunk/src/libtracker-gtk/tracker-keyword-store.h
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-gtk/tracker-keyword-store.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,86 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+ *
+ * tracker-gtk/keyword-store.h - A derived GtkListStore that maintians a
+ * DBus connection to tracker such that when a new keyword is created it
+ * is automatically inserted here.
+ *
+ * Copyright (C) 2007 John Stowers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef TRACKER_KEYWORD_STORE_H
+#define TRACKER_KEYWORD_STORE_H
+
+#include <gtk/gtkliststore.h>
+
+#include <tracker.h>
+
+G_BEGIN_DECLS
+
+typedef enum
+{
+	TRACKER_KEYWORD_STORE_KEYWORD,
+	TRACKER_KEYWORD_STORE_IMAGE_URI,
+	TRACKER_KEYWORD_STORE_NUM_COLUMNS
+} TrackerKeywordStoreColumns;
+
+
+#define TRACKER_TYPE_KEYWORD_STORE	      (tracker_keyword_store_get_type ())
+#define TRACKER_KEYWORD_STORE(obj)	      (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRACKER_TYPE_KEYWORD_STORE, TrackerKeywordStore))
+#define TRACKER_KEYWORD_STORE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), TRACKER_TYPE_KEYWORD_STORE, TrackerKeywordStoreClass))
+#define TRACKER_IS_KEYWORD_STORE(obj)	      (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRACKER_TYPE_KEYWORD_STORE))
+#define TRACKER_IS_KEYWORD_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TRACKER_TYPE_KEYWORD_STORE))
+#define TRACKER_KEYWORD_STORE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), TRACKER_TYPE_KEYWORD_STORE, TrackerKeywordStoreClass))
+
+
+typedef struct _TrackerKeywordStoreClass  TrackerKeywordStoreClass;
+
+struct _TrackerKeywordStore
+{
+	GtkListStore  parent_instance;
+
+	/*< private >*/
+	GHashTable *keywords;
+	TrackerClient *tracker_client;
+};
+
+struct _TrackerKeywordStoreClass
+{
+	GtkListStoreClass  parent_class;
+};
+
+
+GType		tracker_keyword_store_get_type		(void) G_GNUC_CONST;
+
+GtkListStore *	tracker_keyword_store_new		(void);
+
+gboolean
+tracker_keyword_store_insert (GtkListStore		*store,
+			      const char		*keyword,
+			      const char		*stock_id );
+
+GtkTreeIter *
+tracker_keyword_store_lookup (GtkListStore		*store,
+			      const char		*keyword);
+
+gboolean
+tracker_keyword_store_remove (GtkListStore		*store,
+			      const char		*keyword);
+
+G_END_DECLS
+
+#endif	/* TRACKER_KEYWORD_STORE_H */

Added: trunk/src/libtracker-gtk/tracker-metadata-tile.c
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-gtk/tracker-metadata-tile.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,1344 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+ *
+ * Copyright (C) 2007 Neil Jagdish Patel <njpatel gmail com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ *
+ * Author : Neil Jagdish Patel <njpatel gmail com>
+ */
+
+#include <config.h>
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <glib/gi18n-lib.h>
+
+#include "tracker-metadata-tile.h"
+#include "tracker-tag-bar.h"
+
+#define TRACKER_METADATA_TILE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TRACKER_TYPE_METADATA_TILE, TrackerMetadataTilePrivate))
+
+G_DEFINE_TYPE (TrackerMetadataTile, tracker_metadata_tile, GTK_TYPE_EVENT_BOX)
+
+struct TrackerMetadataTilePrivate {
+	TrackerClient *client;
+
+	gboolean visible;
+	gboolean lock;
+
+	gboolean expanded;
+	gchar *type;
+	GdkPixbuf *preview;
+
+	GtkWidget *align;
+
+	GtkWidget *image;
+	GtkWidget *size;
+
+	GtkWidget *expander;
+	GtkWidget *arrow;
+	GtkWidget *title;
+
+	GtkWidget *table;
+
+	GtkWidget *info1;
+	GtkWidget *info2;
+
+	GtkWidget *info3;
+	GtkWidget *info4;
+
+	GtkWidget *info5;
+	GtkWidget *info6;
+
+	GtkWidget *tag_bar;
+};
+
+static GObjectClass *parent_class = NULL;
+
+#define KILOBYTE_FACTOR 1024.0
+#define MEGABYTE_FACTOR (1024.0 * 1024.0)
+#define GIGABYTE_FACTOR (1024.0 * 1024.0 * 1024.0)
+
+/* forward declerations */
+static void  tracker_metadata_tile_class_init	    (TrackerMetadataTileClass *class);
+static void  tracker_metadata_tile_init		    (TrackerMetadataTile      *tile);
+static gboolean tracker_metadata_tile_expose_event(GtkWidget *widget, GdkEventExpose *event);
+static void tracker_metadata_tile_show (TrackerMetadataTile *tile);
+static void _property_to_label (GtkWidget *label, const char *prop, const char *string);
+static void _date_to_label (GtkWidget *label, const char *prop, const char *string);
+static void _year_to_label (GtkWidget *label, const char *prop, const char *string);
+static void _size_to_label (GtkWidget *label, const char *prop, const char *string);
+static void _dimensions_to_label (GtkWidget *label, const char *width, const char *height, const char *string);
+static void _seconds_to_label (GtkWidget *label, const char *prop, const char *string);
+static void _bitrate_to_label (GtkWidget *label, const char *prop, const char *string);
+static void _int_to_label (GtkWidget *label, const char *prop, const char *string);
+
+static inline gboolean is_empty_string (const char *s);
+
+/* structs & enums */
+
+static char *default_keys[] =
+{
+	"File:Name",
+	"File:Path",
+	"File:Modified",
+	"File:Size",
+	"File:Accessed",
+	"File:Mime",
+	NULL
+};
+
+enum {
+	DEFAULT_NAME,
+	DEFAULT_PATH,
+	DEFAULT_MODIFIED,
+	DEFAULT_SIZE,
+	DEFAULT_ACCESSED,
+	DEFAULT_MIME,
+	DEFAULT_N_KEYS
+};
+
+static char *doc_keys[] =
+{
+	"File:Name",
+	"Doc:Subject",
+	"Doc:Author",
+	"Doc:Comments",
+	"Doc:PageCount",
+	"Doc:WordCount",
+	"Doc:Created",
+	NULL
+};
+
+enum {
+	DOC_NAME,
+	DOC_SUBJECT,
+	DOC_AUTHOR,
+	DOC_COMMENTS,
+	DOC_PAGECOUNT,
+	DOC_WORDCOUNT,
+	DOC_CREATED,
+	DOC_N_KEYS
+};
+
+
+
+static char *email_keys[] =
+{
+	"Email:Sender",
+	"Email:Subject",
+	"Email:Date",
+	"Email:SentTo",
+	"Email:CC",
+	"Email:Attachments",
+	NULL
+};
+
+enum {
+	EMAIL_SENDER,
+	EMAIL_SUBJECT,
+	EMAIL_DATE,
+	EMAIL_SENTTO,
+	EMAIL_CC,
+	EMAIL_ATTACHMENTS,
+	EMAIL_N_KEYS
+};
+
+static char *webhistory_keys[] =
+{
+	"Doc:URL",
+	"Doc:Title",
+	"File:Size",
+	"File:Mime",
+	"Doc:Keywords",
+	NULL
+};
+
+enum {
+	WEBHISTORY_URL,
+	WEBHISTORY_TITLE,
+	WEBHISTORY_SIZE,
+	WEBHISTORY_MIME,
+	WEBHISTORY_KEYWORDS,
+	WEBHISTORY_N_KEYS
+};
+
+
+static char *app_keys[] =
+{
+	"App:DisplayName",
+	"App:GenericName",
+	"App:Comment",
+	"App:Categories",
+	NULL
+};
+
+enum {
+	APP_DISPLAYNAME,
+	APP_GENERIC_NAME,
+	APP_COMMENT,
+	APP_CATEGORIES,
+	APP_N_KEYS
+};
+
+
+static char *audio_keys[] =
+{
+	"Audio:Title",
+	"Audio:Artist",
+	"Audio:Album",
+	"Audio:Duration",
+	"Audio:Genre",
+	"Audio:Bitrate",
+	"Audio:ReleaseDate",
+	"Audio:Codec",
+	"File:Size",
+	NULL
+};
+
+enum {
+	AUDIO_TITLE,
+	AUDIO_ARTIST,
+	AUDIO_ALBUM,
+	AUDIO_DURATION,
+	AUDIO_GENRE,
+	AUDIO_BITRATE,
+	AUDIO_RELEASEDATE,
+	AUDIO_CODEC,
+	AUDIO_SIZE,
+	AUDIO_N_KEYS
+};
+
+static char *image_keys[] =
+{
+	"File:Name",
+	"Image:Height",
+	"Image:Width",
+	"Image:Date",
+	"Image:CameraMake",
+	"Image:CameraModel",
+	"Image:Orientation",
+	"Image:Flash",
+	"Image:FocalLength",
+	"Image:ExposureTime",
+	NULL
+};
+
+enum {
+	IMAGE_TITLE,
+	IMAGE_HEIGHT,
+	IMAGE_WIDTH,
+	IMAGE_DATE,
+	IMAGE_CAMERA,
+	IMAGE_MODEL,
+	IMAGE_ORIENT,
+	IMAGE_FLASH,
+	IMAGE_FOCAL,
+	IMAGE_EXPO,
+	IMAGE_N_KEYS
+};
+
+static char *video_keys[] =
+{
+	"File:Name",
+	"Video:Height",
+	"Video:Width",
+	"Video:Author",
+	"Video:FrameRate",
+	"Video:Codec",
+	"Video:Bitrate",
+	"Video:Duration",
+	NULL
+};
+
+enum {
+	VIDEO_TITLE,
+	VIDEO_HEIGHT,
+	VIDEO_WIDTH,
+	VIDEO_AUTHOR,
+	VIDEO_FRAMERATE,
+	VIDEO_CODEC,
+	VIDEO_BITRATE,
+	VIDEO_DURATION,
+	VIDEO_N_KEYS
+};
+
+static inline gboolean
+is_empty_string (const char *s)
+{
+	return s == NULL || s[0] == '\0';
+}
+
+
+static void
+_show_labels (TrackerMetadataTile *tile, gboolean label_visible)
+{
+	TrackerMetadataTilePrivate *priv;
+
+	priv = TRACKER_METADATA_TILE_GET_PRIVATE (tile);
+
+	if (!label_visible) {
+		gtk_widget_hide (priv->info1);
+		gtk_widget_hide (priv->info2);
+		gtk_widget_hide (priv->info3);
+		gtk_widget_hide (priv->info4);
+		gtk_widget_hide (priv->info5);
+		gtk_widget_hide (priv->info6);
+	} else {
+		gtk_widget_show (priv->info1);
+		gtk_widget_show (priv->info2);
+		gtk_widget_show (priv->info3);
+		gtk_widget_show (priv->info4);
+		gtk_widget_show (priv->info5);
+		gtk_widget_show (priv->info6);
+	}
+}
+
+/* populates the metadata tile for a default file */
+static void
+_tile_tracker_populate_default (char **array, GError *error, TrackerMetadataTile *tile )
+{
+	if (error) {
+		g_print ("Error : %s\n", error->message);
+		g_clear_error (&error);
+		gtk_widget_hide (GTK_WIDGET(tile));
+		return;
+	}
+
+	/* create title */
+
+	TrackerMetadataTilePrivate *priv;
+
+	priv = TRACKER_METADATA_TILE_GET_PRIVATE (tile);	/* create title */
+
+	_property_to_label ( priv->title, array[DEFAULT_NAME] , "<span size='large'><b>%s</b></span>");
+
+	/* then set the remaining properties */
+	_property_to_label ( priv->info1, array[DEFAULT_PATH] , _("Path : <b>%s</b>"));
+	_date_to_label ( priv->info2, array[DEFAULT_MODIFIED] , _("Modified : <b>%s</b>"));
+	_size_to_label ( priv->info3, array[DEFAULT_SIZE] , _("Size : <b>%s</b>"));
+	_date_to_label ( priv->info4, array[DEFAULT_ACCESSED] , _("Accessed : <b>%s</b>"));
+	_property_to_label ( priv->info5, array[DEFAULT_MIME] , _("Mime : <b>%s</b>"));
+	_property_to_label ( priv->info6, " " , "%s");
+
+
+
+	tracker_metadata_tile_show (tile);
+	g_strfreev (array);
+}
+
+
+static void
+_tile_tracker_populate_blank (TrackerMetadataTile *tile)
+{
+	TrackerMetadataTilePrivate *priv;
+
+	priv = TRACKER_METADATA_TILE_GET_PRIVATE (tile);
+
+	_show_labels (tile, FALSE);
+
+	/* create title */
+	_property_to_label ( priv->title, " " , "%s");
+	_property_to_label ( priv->info1, " " , "%s");
+	_property_to_label ( priv->info2, " " , "%s");
+	_property_to_label ( priv->info3, " " , "%s");
+	_property_to_label ( priv->info4, " " , "%s");
+	_property_to_label ( priv->info5, " " , "%s");
+	_property_to_label ( priv->info6, " " , "%s");
+
+	tracker_metadata_tile_show (tile);
+}
+
+
+static void
+_tile_tracker_populate_email (char **array, GError *error, TrackerMetadataTile *tile)
+{
+	/* format properties */
+	if (error) {
+		g_print ("META_TILE_ERROR : %s", error->message);
+		g_clear_error (&error);
+		/* hide widget */
+		gtk_widget_hide (GTK_WIDGET(tile));
+		return;
+	}
+
+	TrackerMetadataTilePrivate *priv;
+
+	priv = TRACKER_METADATA_TILE_GET_PRIVATE (tile);
+
+
+	/* create title */
+	_property_to_label ( priv->title, array[EMAIL_SUBJECT] , "<span size='large'><b>%s</b></span>");
+
+	_property_to_label ( priv->info1, array[EMAIL_SENDER] , _("Sender : <b>%s</b>"));
+	_date_to_label ( priv->info2, array[EMAIL_DATE] , _("Date : <b>%s</b>"));
+	_property_to_label ( priv->info3, " " , "%s");
+	_property_to_label ( priv->info4, " " , "%s");
+	_property_to_label ( priv->info5, " " , "%s");
+	_property_to_label ( priv->info6, " " , "%s");
+
+
+	/* free properties */
+	g_strfreev (array);
+
+	tracker_metadata_tile_show (tile);
+
+	_show_labels (tile, FALSE);
+	gtk_widget_show (priv->info1);
+	gtk_widget_show (priv->info2);
+}
+
+
+static void
+_tile_tracker_populate_applications (char **array, GError *error, TrackerMetadataTile *tile)
+{
+	/* format properties */
+	if (error) {
+		g_print ("META_TILE_ERROR : %s", error->message);
+		g_clear_error (&error);
+		/* hide widget */
+		gtk_widget_hide (GTK_WIDGET(tile));
+		return;
+	}
+
+	TrackerMetadataTilePrivate *priv;
+
+	priv = TRACKER_METADATA_TILE_GET_PRIVATE (tile);
+
+
+
+	/* create title */
+	_property_to_label ( priv->title, array[APP_DISPLAYNAME] , "<span size='large'><b>%s</b></span>");
+
+
+	_property_to_label ( priv->info1, array[APP_COMMENT] , _("Comment : <b>%s</b>"));
+	_property_to_label ( priv->info2, array[APP_CATEGORIES] , _("Categories : <b>%s</b>"));
+	_property_to_label ( priv->info4, " " , "%s");
+	_property_to_label ( priv->info5, " " , "%s");
+	_property_to_label ( priv->info6, " " , "%s");
+
+
+	/* free properties */
+	g_strfreev (array);
+
+	tracker_metadata_tile_show (tile);
+	_show_labels (tile, FALSE);
+	gtk_widget_show (priv->info1);
+	gtk_widget_show (priv->info2);
+
+}
+
+
+
+static void
+_tile_tracker_populate_audio (char **array, GError *error, TrackerMetadataTile *tile)
+{
+	/* format properties */
+	if (error) {
+		g_print ("META_TILE_ERROR : %s", error->message);
+		g_clear_error (&error);
+		/* hide widget */
+		gtk_widget_hide (GTK_WIDGET(tile));
+		return;
+	}
+
+	TrackerMetadataTilePrivate *priv;
+
+	priv = TRACKER_METADATA_TILE_GET_PRIVATE (tile);	/* create title */
+
+	char *prop = NULL;
+	/* make nice looking description */
+	GString *str;
+	gboolean artist = FALSE;
+	gboolean album = FALSE;
+
+	str = g_string_new ("<span size='large'><b>%s</b></span>");
+
+	if (!is_empty_string (array[AUDIO_ARTIST])) {
+		artist = TRUE;
+		str = g_string_append (str, " by <span size='large'><i>%s</i></span>");
+	}
+
+	if (!is_empty_string (array[AUDIO_ALBUM])) {
+		album = TRUE;
+		str = g_string_append (str, " from <span size='large'><i>%s</i></span>");
+	}
+
+	if (artist && album) {
+		char *temp1, *temp2, *temp3;
+		temp1 = g_markup_escape_text (array[AUDIO_TITLE], -1);
+		temp2 = g_markup_escape_text (array[AUDIO_ARTIST], -1);
+		temp3 = g_markup_escape_text (array[AUDIO_ALBUM], -1);
+		prop = g_strdup_printf (str->str, temp1, temp2, temp3);
+		g_free (temp1);
+		g_free (temp2);
+		g_free (temp3);
+	} else if (artist) {
+		char *temp1, *temp2;
+		temp1 = g_markup_escape_text (array[AUDIO_TITLE], -1);
+		temp2 = g_markup_escape_text (array[AUDIO_ARTIST], -1);
+		prop = g_strdup_printf (str->str, temp1, temp2);
+		g_free (temp1);
+		g_free (temp2);
+	} else if (album) {
+		char *temp1, *temp2;
+		temp1 = g_markup_escape_text (array[AUDIO_TITLE], -1);
+		temp2 = g_markup_escape_text (array[AUDIO_ALBUM], -1);
+		prop = g_strdup_printf (str->str, temp1, temp2);
+		g_free (temp1);
+		g_free (temp2);
+	} else {
+		char *temp1;
+		temp1 = g_markup_escape_text (array[AUDIO_TITLE], -1);
+		prop = g_strdup_printf (str->str, temp1);
+		g_free (temp1);
+	}
+
+	gtk_label_set_markup (GTK_LABEL (priv->title), prop);
+	g_free (prop);
+	g_string_free (str, TRUE);
+
+	_seconds_to_label ( priv->info1, array[AUDIO_DURATION] , _("Duration : <b>%s</b>"));
+	_property_to_label ( priv->info2, array[AUDIO_GENRE] , _("Genre : <b>%s</b>"));
+	_bitrate_to_label ( priv->info3, array[AUDIO_BITRATE] , _("Bitrate : <b>%s Kbs</b>"));
+	_year_to_label ( priv->info4, array[AUDIO_RELEASEDATE] , _("Year : <b>%s</b>"));
+	_size_to_label ( priv->info5, array[AUDIO_SIZE] , _("Size : <b>%s</b>"));
+	_property_to_label ( priv->info6, array[AUDIO_CODEC] , _("Codec : <b>%s</b>"));
+
+
+
+
+	/* free properties */
+	g_strfreev (array);
+
+	tracker_metadata_tile_show (tile);
+}
+
+/* populates the metadata tile for a image file */
+static void
+_tile_tracker_populate_image (char **array, GError *error, TrackerMetadataTile *tile )
+{
+	if (error) {
+		g_print ("METADATA_TILE_ERROR : %s", error->message);
+		g_clear_error (&error);
+		gtk_widget_hide (GTK_WIDGET(tile));
+		return;
+	}
+
+	/* TODO : check for a normal image file, not all images are photos */
+
+	TrackerMetadataTilePrivate *priv;
+
+	priv = TRACKER_METADATA_TILE_GET_PRIVATE (tile);	/* create title */
+
+	/* create title */
+	_property_to_label ( priv->title, array[IMAGE_TITLE] , "<span size='large'><b>%s</b></span>");
+
+	char *prop = NULL;
+	/* make nice looking description */
+	GString *str;
+	gboolean camera = TRUE;
+	gboolean model = TRUE;
+
+	str = g_string_new ("<span size='large'><b>%s</b></span>");
+
+	if (!is_empty_string (array[IMAGE_CAMERA])) {
+		camera = TRUE;
+		str = g_string_append (str, _(" taken with a <span size='large'><i>%s</i></span>"));
+	}
+
+	if (!is_empty_string (array[IMAGE_MODEL])) {
+		model = TRUE;
+		str = g_string_append (str, _(" <span size='large'><i>%s</i></span>"));
+	}
+	if (camera && model) {
+		prop = g_strdup_printf (str->str, g_markup_escape_text (array[IMAGE_TITLE], -1),
+						  g_markup_escape_text (array[IMAGE_CAMERA], -1),
+						  g_markup_escape_text (array[IMAGE_MODEL], -1));
+	} else if (camera) {
+		prop = g_strdup_printf (str->str, g_markup_escape_text (array[IMAGE_TITLE], -1),
+						  g_markup_escape_text (array[IMAGE_CAMERA], -1));
+	} else if (model) {
+		prop = g_strdup_printf (str->str, g_markup_escape_text (array[IMAGE_TITLE], -1),
+						  g_markup_escape_text (array[IMAGE_MODEL], -1));
+	} else {
+		prop = g_strdup_printf (str->str, g_markup_escape_text (array[IMAGE_TITLE], -1));
+	}
+	gtk_label_set_markup (GTK_LABEL (priv->title), prop);
+	g_free (prop);
+	g_string_free (str, TRUE);
+
+	/* then set the remaining properties */
+	_dimensions_to_label ( priv->info1, array[IMAGE_WIDTH], array[IMAGE_HEIGHT] , _("Dimensions : <b>%d x %d</b>"));
+	_date_to_label ( priv->info2, array[IMAGE_DATE] , _("Date Taken : <b>%s</b>"));
+	_property_to_label ( priv->info3, array[IMAGE_ORIENT] , _("Orientation : <b>%s</b>"));
+	_property_to_label ( priv->info4, array[IMAGE_FLASH] , _("Flash : <b>%s</b>"));
+	_property_to_label ( priv->info5, array[IMAGE_FOCAL] , _("Focal Length : <b>%s</b>"));
+	_property_to_label ( priv->info6, array[IMAGE_EXPO] , _("Exposure Time : <b>%s</b>"));
+
+
+
+
+	tracker_metadata_tile_show (tile);
+	g_strfreev (array);
+}
+
+/* populates the metadata tile for a video file */
+static void
+_tile_tracker_populate_video (char **array, GError *error, TrackerMetadataTile *tile )
+{
+	if (error) {
+		g_print ("METADATA_TILE_ERROR : %s", error->message);
+		g_clear_error (&error);
+		gtk_widget_hide (GTK_WIDGET(tile));
+		return;
+	}
+
+	TrackerMetadataTilePrivate *priv;
+
+	priv = TRACKER_METADATA_TILE_GET_PRIVATE (tile);	/* create title */
+
+	_property_to_label ( priv->title, array[VIDEO_TITLE] , "<span size='large'><b>%s</b></span>");
+
+	/* then set the remaining properties */
+	_dimensions_to_label ( priv->info1, array[VIDEO_WIDTH], array[VIDEO_HEIGHT] , _("Dimensions : <b>%d x %d</b>"));
+	_property_to_label ( priv->info2, array[VIDEO_AUTHOR] , _("Author : <b>%s</b>"));
+	_seconds_to_label ( priv->info3, array[VIDEO_DURATION] , _("Duration : <b>%s</b>"));
+	_property_to_label ( priv->info4, array[VIDEO_BITRATE] , _("Bitrate : <b>%s</b>"));
+	_property_to_label ( priv->info5, array[VIDEO_CODEC] , _("Encoded In : <b>%s</b>"));
+	_property_to_label ( priv->info6, array[VIDEO_FRAMERATE] , _("Framerate : <b>%s</b>"));
+
+
+
+	tracker_metadata_tile_show (tile);
+	g_strfreev (array);
+
+}
+
+/* populates the metadata tile for a document */
+static void
+_tile_tracker_populate_documents (char **array, GError *error, TrackerMetadataTile *tile )
+{
+	if (error) {
+		g_print ("METADATA_TILE_ERROR : %s", error->message);
+		g_clear_error (&error);
+		gtk_widget_hide (GTK_WIDGET(tile));
+		return;
+	}
+
+	TrackerMetadataTilePrivate *priv;
+
+	priv = TRACKER_METADATA_TILE_GET_PRIVATE (tile);
+
+	/* create title */
+	_property_to_label ( priv->title, array[DOC_NAME] , "<span size='large'><b>%s</b></span>");
+
+	/* then set the remaining properties */
+	_property_to_label ( priv->info1, array[DOC_SUBJECT] , _("Subject : <b>%s</b>"));
+	_property_to_label ( priv->info2, array[DOC_AUTHOR] , _("Author : <b>%s</b>"));
+	_int_to_label ( priv->info3, array[DOC_PAGECOUNT] , _("Page Count : <b>%s</b>"));
+	_int_to_label ( priv->info4, array[DOC_WORDCOUNT] , _("Word Count : <b>%s</b>"));
+	_date_to_label ( priv->info5, array[DOC_CREATED] , _("Created : <b>%s</b>"));
+	_property_to_label ( priv->info6, array[DOC_COMMENTS] , _("Comments : <b>%s</b>"));
+
+
+	tracker_metadata_tile_show (tile);
+	g_strfreev (array);
+}
+
+
+/*populates the metadata tile for a web history url */
+static void
+_tile_tracker_populate_webhistory(char **array, GError *error, TrackerMetadataTile *tile )
+{
+	if (error) {
+		g_print ("METADATA_TILE_ERROR : %s", error->message);
+		g_clear_error (&error);
+		gtk_widget_hide (GTK_WIDGET(tile));
+		return;
+	}
+
+	TrackerMetadataTilePrivate *priv;
+
+	priv = TRACKER_METADATA_TILE_GET_PRIVATE (tile);
+
+	/* create title */
+	_property_to_label ( priv->title, array[WEBHISTORY_URL] , "<span size='large'><b>%s</b></span>");
+
+	/* then set the remaining properties */
+	_property_to_label ( priv->info1, array[WEBHISTORY_TITLE] , _("Subject : <b>%s</b>"));
+	_property_to_label ( priv->info2, array[WEBHISTORY_KEYWORDS] , "Keywords: <b>%s</b>");
+
+	tracker_metadata_tile_show (tile);
+	g_strfreev (array);
+
+	_show_labels (tile, FALSE);
+	gtk_widget_show (priv->info1);
+	gtk_widget_show (priv->info2);
+
+}
+
+
+
+/* UTILILTY FUNCTIONS FOR CONVERSIONS */
+
+/* Converts bitrate to kbs */
+static void
+_bitrate_to_label (GtkWidget *label, const char *prop, const char *string)
+{
+	int size;
+	char *format;
+	char *temp;
+
+	size = atoi (prop);
+	size = size/1000;
+
+	format = g_strdup_printf ("%d", size);
+	temp = g_strdup_printf (string, format);
+	gtk_label_set_markup (GTK_LABEL (label), temp);
+	gtk_label_set_selectable (GTK_LABEL (label), TRUE);
+
+	g_free (temp);
+	g_free (format);
+}
+
+/* Converts seconds to time */
+static void
+_seconds_to_label (GtkWidget *label, const char *prop, const char *string)
+{
+	gulong size;
+	char *format;
+	char *temp;
+
+	size = atol (prop);
+	int hours = (int) (size / 3600);
+	int minutes = (int)(size/60) - (hours * 60);
+	int seconds = (int) (size % 60);
+
+	if ( hours > 0 ) {
+		format = g_strdup_printf ("%02d:%02d:%02d", hours, minutes, seconds);
+	} else {
+		format = g_strdup_printf ("%02d:%02d", minutes, seconds);
+	}
+
+	temp = g_strdup_printf (string, format);
+	gtk_label_set_markup (GTK_LABEL (label), temp);
+	gtk_label_set_selectable (GTK_LABEL (label), TRUE);
+
+	g_free (temp);
+	g_free (format);
+}
+
+/* Converts width and height into WxH */
+static void
+_dimensions_to_label (GtkWidget *label, const char *width, const char *height, const char *string)
+{
+	gulong w;
+	gulong h;
+	char *temp;
+
+	w = atol (width);
+	h = atol (height);
+
+	temp = g_strdup_printf (string, w, h);
+	gtk_label_set_markup (GTK_LABEL (label), temp);
+	gtk_label_set_selectable (GTK_LABEL (label), TRUE);
+
+	g_free (temp);
+}
+
+/* taken from gnome_vfs, formats a file size to something normal */
+static gchar *
+format_file_size_for_display (gulong size)
+{
+	if (size < KILOBYTE_FACTOR) {
+		//return g_strdup_printf (dngettext(GETTEXT_PACKAGE, "%u byte", "%u bytes",(guint) size), (guint) size);
+		return g_strdup_printf ("%u bytes", (guint) size);
+	} else {
+		gdouble displayed_size;
+
+		if (size < MEGABYTE_FACTOR) {
+			displayed_size = (gdouble) size / KILOBYTE_FACTOR;
+			return g_strdup_printf (_("%.1f KB"),
+						displayed_size);
+		} else if (size < GIGABYTE_FACTOR) {
+			displayed_size = (gdouble) size / MEGABYTE_FACTOR;
+			return g_strdup_printf (_("%.1f MB"),
+						displayed_size);
+		} else {
+			displayed_size = (gdouble) size / GIGABYTE_FACTOR;
+			return g_strdup_printf (_("%.1f GB"),
+						displayed_size);
+		}
+	}
+}
+
+/* Converts text size to something human readable */
+static void
+_size_to_label (GtkWidget *label, const char *prop, const char *string)
+{
+	gulong size;
+	char *format;
+	char *temp;
+
+	size = atol (prop);
+	format = format_file_size_for_display (size);
+
+	temp = g_strdup_printf (string, format);
+	gtk_label_set_markup (GTK_LABEL (label), temp);
+	gtk_label_set_selectable (GTK_LABEL (label), TRUE);
+
+	g_free (format);
+	g_free (temp);
+}
+
+/* Converts text size to something human readable */
+static void
+_int_to_label (GtkWidget *label, const char *prop, const char *string)
+{
+	gulong size;
+	char *temp;
+	char *format;
+
+	size = atol (prop);
+	format = g_strdup_printf ("%ld", size);
+
+	if (size) {
+		temp = g_strdup_printf (string, format);
+	} else {
+		temp = g_strdup_printf (string, _("Unknown"));
+	}
+	gtk_label_set_markup (GTK_LABEL (label), temp);
+	gtk_label_set_selectable (GTK_LABEL (label), TRUE);
+
+	g_free (temp);
+	g_free (format);
+}
+
+/* Converts ISO date to something human readable */
+static gboolean
+get_time_from_iso (const char *iso, GDate *val)
+{
+	g_return_val_if_fail (val, FALSE);
+
+	time_t my_time = atoi (iso);
+
+	if (my_time != 0) {
+		g_date_set_time_t (val, my_time);
+		return TRUE;
+	} else {
+		return FALSE;
+	}
+}
+
+static void
+_date_to_label (GtkWidget *label, const char *iso, const char *string)
+{
+	GDate val;
+	char *temp = NULL;
+
+	if (string) {
+		if (get_time_from_iso (iso, &val)) {
+			gchar buf[256];
+			g_date_strftime (buf, 256, "%a %d %b %Y", &val);
+			temp = g_strdup_printf (string, buf);
+		}
+	}
+
+	if (!temp) {
+		temp = g_strdup_printf (string, _("Unknown"));
+	}
+
+	gtk_label_set_markup (GTK_LABEL (label), temp);
+	gtk_label_set_selectable (GTK_LABEL (label), TRUE);
+	g_free (temp);
+}
+
+static void
+_year_to_label (GtkWidget *label, const char *iso, const char *string)
+{
+	GDate val;
+	char *temp = NULL;
+
+	if (string) {
+		if (get_time_from_iso (iso, &val)) {
+			gchar buf[32];
+			g_date_strftime (buf, 32, "%Y", &val);
+			temp = g_strdup_printf (string, buf);
+		}
+	}
+
+	if (!temp) {
+		temp = g_strdup_printf (string, _("Unknown"));
+	}
+
+	gtk_label_set_markup (GTK_LABEL (label), temp);
+	gtk_label_set_selectable (GTK_LABEL (label), TRUE);
+	g_free (temp);
+}
+
+/* Checks that a property is valid, parses it to play nicely wth pango */
+static void
+_property_to_label (GtkWidget *label, const char *prop, const char *string)
+{
+	if (!is_empty_string(prop)) {
+		char * temp, *temp2;
+		temp2 = g_markup_escape_text (prop, -1);
+		temp = g_strdup_printf (string, temp2);
+		g_free (temp2);
+		gtk_label_set_markup (GTK_LABEL (label), temp);
+		gtk_label_set_selectable (GTK_LABEL (label), TRUE);
+		g_free (temp);
+	} else {
+		char * temp;
+		temp = g_strdup_printf (string, _("Unknown"));
+		gtk_label_set_markup (GTK_LABEL (label), temp);
+		gtk_label_set_selectable (GTK_LABEL (label), TRUE);
+		g_free (temp);
+	}
+}
+
+
+
+/**
+ * tracker_metadata_tile_set_uri:
+ * @tile: a #TrackerMetadataTile
+ * @uri: the local uri of a file
+ * @type: the tracker type or mime type of the file
+ * @icon: a GdkPixbuf representing the file, or #NULL
+ *
+ * Replaces the current metadata in the tile with metadata for the
+ * uri specified. Can optionally also update the #GtkImage in the tile with
+ * the icon specified.
+ *
+ **/
+
+void
+tracker_metadata_tile_set_uri (TrackerMetadataTile *tile, const gchar *uri,
+							  ServiceType service_type,
+							  const gchar *type,
+							  GdkPixbuf *icon)
+{
+	TrackerMetadataTilePrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_METADATA_TILE (tile));
+
+	priv = TRACKER_METADATA_TILE_GET_PRIVATE (tile);
+
+	gtk_image_clear (GTK_IMAGE (priv->image));
+
+	if (icon) {
+		gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), icon);
+	} else {
+		gtk_widget_hide (priv->image);
+	}
+
+	/* call correct function according to service type */
+	switch (service_type) {
+
+	case SERVICE_MUSIC:
+
+		tracker_metadata_get_async (priv->client, SERVICE_MUSIC,
+					    uri, audio_keys,
+					    (TrackerArrayReply)_tile_tracker_populate_audio,
+					    (gpointer)tile);
+
+		break;
+
+	case SERVICE_EMAILS:
+
+		tracker_metadata_get_async (priv->client, SERVICE_EMAILS,
+					    uri, email_keys,
+					    (TrackerArrayReply)_tile_tracker_populate_email,
+					    (gpointer)tile);
+		break;
+
+
+	case SERVICE_DOCUMENTS:
+
+		tracker_metadata_get_async (priv->client, SERVICE_DOCUMENTS,
+					    uri, doc_keys,
+					    (TrackerArrayReply)_tile_tracker_populate_documents,
+					    (gpointer)tile);
+		break;
+
+	case SERVICE_WEBHISTORY:
+
+		tracker_metadata_get_async (priv->client, SERVICE_WEBHISTORY,
+					    uri, webhistory_keys,
+					    (TrackerArrayReply)_tile_tracker_populate_webhistory,
+					    (gpointer)tile);
+		break;
+
+
+	case SERVICE_IMAGES:
+
+		tracker_metadata_get_async (priv->client, SERVICE_IMAGES,
+					    uri, image_keys,
+					    (TrackerArrayReply)_tile_tracker_populate_image,
+					    (gpointer)tile);
+
+		break;
+
+	case SERVICE_VIDEOS:
+		tracker_metadata_get_async (priv->client, SERVICE_VIDEOS,
+					    uri, video_keys,
+					    (TrackerArrayReply)_tile_tracker_populate_video,
+					    (gpointer)tile);
+
+		break;
+
+	case SERVICE_APPLICATIONS:
+
+		tracker_metadata_get_async (priv->client, SERVICE_APPLICATIONS,
+					    uri, app_keys,
+					    (TrackerArrayReply)_tile_tracker_populate_applications,
+					    (gpointer)tile);
+
+		break;
+
+
+	default:
+
+		if (!uri) {
+			_tile_tracker_populate_blank (tile);
+		} else {
+
+			tracker_metadata_get_async (priv->client, SERVICE_FILES,
+						    uri, default_keys,
+						    (TrackerArrayReply)_tile_tracker_populate_default,
+						    (gpointer)tile);
+		}
+
+		break;
+	}
+
+
+
+	if (uri) {
+		gtk_widget_show (priv->tag_bar);
+		tracker_tag_bar_set_uri (TRACKER_TAG_BAR (priv->tag_bar), service_type, uri);
+	} else {
+		gtk_widget_hide (priv->tag_bar);
+	}
+
+	gtk_widget_queue_draw (GTK_WIDGET (tile));
+}
+
+static void
+tracker_metadata_tile_show (TrackerMetadataTile *tile)
+{
+	g_return_if_fail (TRACKER_IS_METADATA_TILE (tile));
+
+	TrackerMetadataTilePrivate *priv;
+
+	priv = TRACKER_METADATA_TILE_GET_PRIVATE (tile);
+
+	if (priv->expanded) {
+		gtk_widget_show_all (GTK_WIDGET (tile));
+	} else {
+		gtk_widget_show_all (GTK_WIDGET (tile));
+		gtk_widget_hide (priv->table);
+		gtk_widget_hide (priv->image);
+	}
+}
+
+static gboolean
+tracker_metadata_tile_toggle_view (GtkWidget *button, TrackerMetadataTile *tile)
+{
+	TrackerMetadataTilePrivate *priv;
+
+	priv = TRACKER_METADATA_TILE_GET_PRIVATE (tile);
+
+	if (priv->expanded) {
+		gtk_widget_hide (priv->image);
+		gtk_widget_hide (priv->table);
+		gtk_arrow_set (GTK_ARROW (priv->arrow),
+			       GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
+		gtk_alignment_set_padding (GTK_ALIGNMENT (priv->align), 1, 1, 4, 4);
+	} else {
+		gtk_widget_show (priv->image);
+		gtk_widget_show (priv->table);
+		gtk_arrow_set (GTK_ARROW (priv->arrow),
+			       GTK_ARROW_DOWN, GTK_SHADOW_NONE);
+		gtk_alignment_set_padding (GTK_ALIGNMENT (priv->align), 6, 6, 4, 4);
+	}
+	priv->expanded = !priv->expanded;
+	return FALSE;
+}
+
+static void
+draw (GtkWidget *widget, cairo_t *cr)
+{
+	TrackerMetadataTile *tile;
+	TrackerMetadataTilePrivate *priv;
+	double width, height;
+	GtkStyle *style;
+	GdkColor step1;
+	GdkColor step2;
+
+	tile = TRACKER_METADATA_TILE (widget);
+	priv = TRACKER_METADATA_TILE_GET_PRIVATE (tile);
+
+	width = widget->allocation.width;
+	height = widget->allocation.height;
+
+	style = gtk_widget_get_style (widget);
+	step1 = style->base[GTK_STATE_NORMAL];
+	step2 = style->bg[GTK_STATE_SELECTED];
+
+	/* clear window to base[NORMAL] */
+	cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+	gdk_cairo_set_source_color (cr, &step1);
+	cairo_paint (cr);
+
+	cairo_move_to(cr, 0, 0);
+	cairo_set_line_width(cr, 1.0);
+
+	cairo_pattern_t *pat;
+	cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+
+	/* main gradient */
+	pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, height);
+	cairo_pattern_add_color_stop_rgba (pat, 0.0, step2.red/65535.0,
+						     step2.green/65535.0,
+						     step2.blue/65535.0,
+						     0.05);
+	cairo_pattern_add_color_stop_rgba ( pat, 1.0, step2.red/65535.0,
+						      step2.green/65535.0,
+						      step2.blue/65535.0,
+						      0.5);
+
+	cairo_rectangle (cr, 0, 0, width, height);
+	cairo_set_source(cr, pat);
+	cairo_fill(cr);
+	cairo_pattern_destroy(pat);
+
+	/* border line */
+	cairo_set_source_rgba (cr, step2.red/65535.0,
+				   step2.green/65535.0,
+				   step2.blue/65535.0,
+				   0.7);
+	cairo_move_to (cr, 0, 0);
+	cairo_line_to (cr, width, 0);
+	cairo_stroke (cr);
+
+	/* highlight line */
+	cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.5);
+	cairo_move_to (cr, 0, 1);
+	cairo_line_to (cr, width, 1);
+	cairo_stroke (cr);
+
+	if (!priv->expanded)
+		return;
+	/* mime icon */
+	if (priv->preview != NULL) {
+		double x, y;
+		x = width - gdk_pixbuf_get_width (priv->preview);
+		y = height - gdk_pixbuf_get_height (priv->preview)+5;
+		gdk_cairo_set_source_pixbuf  (cr, priv->preview, x, y);
+		cairo_paint_with_alpha (cr, 0.2);
+	}
+	/* watermark */
+	if (priv->type == NULL)
+		return;
+
+	cairo_text_extents_t extents;
+	double x,y;
+	int font_slant = CAIRO_FONT_SLANT_NORMAL;
+	int font_weight = CAIRO_FONT_WEIGHT_NORMAL;
+
+	cairo_select_font_face (cr, "Sans",font_slant, font_weight);
+	cairo_set_font_size (cr, 40);
+
+	cairo_text_extents (cr, priv->type, &extents);
+	x = (width)-(extents.width + extents.x_bearing)-90;
+	y = (height)-(extents.height + extents.y_bearing)-5;
+
+
+	/* shadow */
+	cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.05);
+	cairo_move_to (cr, x-1, y-1);
+	cairo_show_text (cr, priv->type);
+
+	/*main text */
+	cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.1);
+	cairo_move_to (cr, x, y);
+	cairo_show_text (cr, priv->type);
+}
+
+static gboolean
+tracker_metadata_tile_expose_event (GtkWidget *widget, GdkEventExpose *event)
+{
+	cairo_t *cr;
+	cr = gdk_cairo_create (widget->window);
+	draw (widget, cr);
+	cairo_destroy (cr);
+
+	return GTK_WIDGET_CLASS(parent_class)->expose_event(widget, event);
+}
+
+static void
+tracker_metadata_tile_class_init (TrackerMetadataTileClass *klass)
+{
+	GObjectClass *gobject_class;
+	GtkWidgetClass *widget_class;
+
+	parent_class = g_type_class_peek_parent (klass);
+	widget_class = GTK_WIDGET_CLASS(klass);
+
+	gobject_class = G_OBJECT_CLASS (klass);
+	//gobject_class->finalize = finalize;
+
+	widget_class->expose_event = tracker_metadata_tile_expose_event;
+
+	g_type_class_add_private (gobject_class, sizeof (TrackerMetadataTilePrivate));
+}
+
+static void
+tracker_metadata_tile_init (TrackerMetadataTile *tile)
+{
+	GtkWidget *align, *button, *image;
+	GtkWidget *label, *table, *arrow;
+	GtkWidget *tag_bar;
+	GtkWidget *hbox, *vbox, *box;
+
+	TrackerMetadataTilePrivate *priv;
+
+	priv = TRACKER_METADATA_TILE_GET_PRIVATE (tile);
+
+	gtk_widget_set_app_paintable (GTK_WIDGET(tile), TRUE);
+
+	priv->expanded = TRUE;
+	priv->type = NULL;
+	priv->preview = NULL;
+
+	align = gtk_alignment_new (0.5, 0.5, 1, 1);
+	priv->align = align;
+	gtk_alignment_set_padding (GTK_ALIGNMENT (align), 6, 6, 4, 4);
+	gtk_container_add (GTK_CONTAINER (tile), align);
+	gtk_widget_show (align);
+
+	/* main hbox */
+	box = gtk_hbox_new (FALSE, 6);
+	gtk_container_add (GTK_CONTAINER (align), box);
+	gtk_widget_show (box);
+
+	/* Image widget */
+	image = gtk_image_new ();
+	priv->image = image;
+	gtk_box_pack_start (GTK_BOX (box), image, FALSE, FALSE, 0);
+	gtk_widget_show (image);
+
+	/* center vbox */
+	vbox = gtk_vbox_new (FALSE, 4);
+	gtk_box_pack_start (GTK_BOX (box), vbox, TRUE, TRUE, 0);
+	gtk_widget_show (vbox);
+
+	/* arrow & title */
+	button = gtk_button_new ();
+	priv->expander = button;
+	gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+	gtk_button_set_focus_on_click (GTK_BUTTON (button), FALSE);
+	gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+	gtk_widget_show (button);
+	g_signal_connect (G_OBJECT (button), "clicked",
+			  G_CALLBACK (tracker_metadata_tile_toggle_view), (gpointer)tile);
+
+	hbox = gtk_hbox_new (FALSE, 4);
+	gtk_container_add (GTK_CONTAINER(button), hbox);
+	gtk_widget_show (hbox);
+
+	arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
+	priv->arrow = arrow;
+	gtk_box_pack_start (GTK_BOX(hbox), arrow, FALSE, FALSE, 0);
+	gtk_widget_show (arrow);
+
+	label = gtk_label_new (" ");
+	priv->title = label;
+	gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
+	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+	gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
+	gtk_widget_show (label);
+
+	/* info table */
+	table = gtk_table_new (3, 3, FALSE);
+	priv->table = table;
+	gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+	gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0);
+	gtk_widget_show (table);
+
+	label = gtk_label_new (" ");
+	priv->info1 = label;
+	gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
+	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+	gtk_table_attach_defaults (GTK_TABLE(table), label, 0, 1, 0, 1);
+
+	label = gtk_label_new (" ");
+	priv->info2 = label;
+	gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
+	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+	gtk_table_attach_defaults (GTK_TABLE(table), label, 0, 1, 1, 2);
+
+	label = gtk_label_new (" ");
+	priv->info3 = label;
+	gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
+	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+	gtk_table_attach_defaults (GTK_TABLE(table), label, 1, 2, 0, 1);
+
+	label = gtk_label_new (" ");
+	priv->info4 = label;
+	gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
+	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+	gtk_table_attach_defaults (GTK_TABLE(table), label, 1, 2, 1, 2);
+
+	label = gtk_label_new (" ");
+	priv->info5 = label;
+	gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
+	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+	gtk_table_attach_defaults (GTK_TABLE(table), label, 2, 3, 0, 1);
+
+	label = gtk_label_new (" ");
+	priv->info6 = label;
+	gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
+	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+	gtk_table_attach_defaults (GTK_TABLE(table), label, 2, 3, 1, 2);
+
+	/* tag bar */
+	tag_bar = tracker_tag_bar_new ();
+	priv->tag_bar = tag_bar;
+	gtk_widget_show_all (tag_bar);
+
+	gtk_table_attach_defaults (GTK_TABLE (table), tag_bar, 0, 3, 2, 3);
+	gtk_widget_show_all (table);
+}
+
+GtkWidget *
+tracker_metadata_tile_new (void)
+{
+	TrackerClient *client;
+	GtkWidget *tile;
+	TrackerMetadataTilePrivate *priv;
+
+	tile = g_object_new (TRACKER_TYPE_METADATA_TILE, NULL);
+	priv = TRACKER_METADATA_TILE_GET_PRIVATE (tile);
+
+	client = tracker_connect (TRUE);
+	priv->client = client;
+	return tile;
+}

Added: trunk/src/libtracker-gtk/tracker-metadata-tile.h
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-gtk/tracker-metadata-tile.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,59 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+ *
+ * Copyright (C) 2007 Neil Jagdish Patel <njpatel gmail com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ *
+ * Author : Neil Jagdish Patel <njpatel gmail com>
+ */
+
+#ifndef TRACKER_METADATA_TILE_H
+#define TRACKER_METADATA_TILE_H
+
+#include <gtk/gtk.h>
+#include <tracker.h>
+
+
+#define TRACKER_TYPE_METADATA_TILE		(tracker_metadata_tile_get_type ())
+#define TRACKER_METADATA_TILE(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), TRACKER_TYPE_METADATA_TILE, TrackerMetadataTile))
+#define TRACKER_METADATA_TILE_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), TRACKER_TYPE_METADATA_TILE, TrackerMetadataTileClass))
+#define TRACKER_IS_METADATA_TILE(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRACKER_TYPE_METADATA_TILE))
+#define TRACKER_IS_METADATA_TILE_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass), TRACKER_TYPE_METADATA_TILE))
+#define TRACKER_METADATA_TILE_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj), TRACKER_TYPE_METADATA_TILE, TrackerMetadataTileClass))
+
+typedef struct TrackerMetadataTilePrivate TrackerMetadataTilePrivate;
+
+typedef struct TrackerMetadataTile {
+	GtkEventBox parent;
+
+} TrackerMetadataTile;
+
+typedef struct {
+	GtkEventBoxClass parent_class;
+
+} TrackerMetadataTileClass;
+
+GType	   tracker_metadata_tile_get_type  (void);
+
+GtkWidget* tracker_metadata_tile_new	   (void);
+
+void	   tracker_metadata_tile_set_uri (TrackerMetadataTile		*tile,
+					  const gchar			*uri,
+					  ServiceType			service_type,
+					  const gchar			*type,
+					  GdkPixbuf			*icon);
+
+#endif /* TRACKER_METADATA_TILE_H */

Added: trunk/src/libtracker-gtk/tracker-tag-bar.c
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-gtk/tracker-tag-bar.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,432 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+ *
+ * Copyright (C) 2007 Neil Jagdish Patel <njpatel gmail com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ *
+ * Author : Neil Jagdish Patel <njpatel gmail com>
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include <glib/gi18n-lib.h>
+
+#include "tracker-tag-bar.h"
+#include "tracker-utils.h"
+
+
+#define TRACKER_TAG_BAR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TRACKER_TYPE_TAG_BAR, TrackerTagBarPrivate))
+
+G_DEFINE_TYPE (TrackerTagBar, tracker_tag_bar, GTK_TYPE_HBOX);
+
+/* FORWARD DECLARATIONS */
+
+static void _tag_bar_add_tag (TrackerTagBar *bar, GtkWidget *box, const char *tag);
+static void _tag_launch_search (const gchar *tag);
+
+/* STRUCTS & ENUMS */
+typedef struct _TrackerTagBarPrivate TrackerTagBarPrivate;
+
+struct _TrackerTagBarPrivate
+{
+	TrackerClient *client;
+
+	gchar *uri;
+	const gchar *active_tag;
+
+	ServiceType type;
+
+	GtkWidget *tag_box;
+	GtkWidget *add_button;
+	GtkWidget *menu;
+
+	GtkWidget *entry_box;
+	GtkWidget *entry;
+};
+
+/* CALLBACKS */
+static void
+_keywords_reply (char **array, GError *error, TrackerTagBar *bar)
+{
+	TrackerTagBarPrivate *priv;
+	gchar **meta = NULL;
+	gchar * tag = NULL;
+	gint i = 0;
+	GtkWidget *hbox;
+
+	if (error) {
+		g_print ("%s\n", error->message);
+		g_clear_error(&error);
+		return;
+	}
+	if (array == NULL) {
+		return;
+	}
+
+	priv = TRACKER_TAG_BAR_GET_PRIVATE (bar);
+
+	hbox = gtk_hbox_new (FALSE, 5);
+	for (meta = array; *meta; meta++) {
+		tag = meta[0];
+		if (strlen (tag) > 0) {
+			_tag_bar_add_tag (bar, hbox, tag);
+			i++;
+		}
+
+	}
+	if (priv->tag_box) {
+		gtk_widget_destroy(priv->tag_box);
+	}
+	priv->tag_box = hbox;
+	gtk_box_pack_start (GTK_BOX(bar), hbox, FALSE, FALSE, 0);
+
+	gtk_widget_show_all (hbox);
+
+	g_strfreev (array);
+}
+
+static gboolean
+_on_tag_button_press_event (GtkWidget			*button,
+			    GdkEventButton		*event,
+			    TrackerTagBar		*bar)
+{
+	TrackerTagBarPrivate *priv;
+	GtkWidget *label;
+	const gchar *tag;
+
+	priv = TRACKER_TAG_BAR_GET_PRIVATE (bar);
+
+	label = gtk_bin_get_child (GTK_BIN (button));
+	tag = gtk_label_get_text (GTK_LABEL (label));
+
+	switch (event->button) {
+		case 1:
+			_tag_launch_search (tag);
+			break;
+		case 3:
+			priv->active_tag = tag;
+			gtk_menu_popup (GTK_MENU (priv->menu),
+					NULL, NULL, NULL, bar, 3, event->time);
+
+			break;
+		default:
+			break;
+
+	}
+	return FALSE;
+}
+
+static void
+_tag_launch_search (const gchar *tag)
+{
+	GdkScreen *screen;
+	gchar *command;
+	GError *error = NULL;
+
+	screen = gdk_screen_get_default ();
+	command = g_strdup_printf ("tracker-search-tool %s", tag);
+
+	if (! gdk_spawn_command_line_on_screen (screen, command, &error)) {
+		if (error) {
+			g_print ("Error : %s", error->message);
+			g_error_free (error);
+		}
+	}
+	g_free (command);
+}
+
+static void
+search_tag_activate_cb(GtkMenuItem *menu_item, TrackerTagBar *bar)
+{
+	TrackerTagBarPrivate *priv;
+
+	priv = TRACKER_TAG_BAR_GET_PRIVATE (bar);
+
+	_tag_launch_search (priv->active_tag);
+}
+
+static void
+remove_tag_activate_cb(GtkMenuItem *menu_item, TrackerTagBar *bar)
+{
+	TrackerTagBarPrivate *priv;
+	GError *error = NULL;
+	char *args[1];
+
+	priv = TRACKER_TAG_BAR_GET_PRIVATE (bar);
+
+	args[0] = g_strdup (priv->active_tag);
+
+	tracker_keywords_remove(priv->client, priv->type, priv->uri,
+				 args, &error);
+	if (error) {
+		g_print ("Tag Removal Error : %s", error->message);
+		return;
+	}
+	gchar *temp = g_strdup (priv->uri);
+	tracker_tag_bar_set_uri (bar, priv->type, temp);
+	g_free (temp);
+}
+
+static void
+_on_close_add_tag (GtkButton *but, TrackerTagBar *bar)
+{
+	TrackerTagBarPrivate *priv;
+	priv = TRACKER_TAG_BAR_GET_PRIVATE (bar);
+
+	gtk_widget_destroy (priv->entry_box);
+	priv->entry_box = priv->entry = NULL;
+
+	gtk_widget_show (priv->tag_box);
+	gtk_widget_show (priv->add_button);
+}
+
+static void
+_on_apply_add_tag (GtkButton *but, TrackerTagBar *bar)
+{
+	TrackerTagBarPrivate *priv;
+	const gchar *text;
+	gchar **tags;
+	GError *error = NULL;
+
+	priv = TRACKER_TAG_BAR_GET_PRIVATE (bar);
+
+	text = gtk_entry_get_text (GTK_ENTRY (priv->entry));
+
+	if (strcmp (text, "Type tags you want to add here, separated by commas") != 0) {
+
+		tags = g_strsplit (text, ",", 0);
+
+		tracker_keywords_add(priv->client, priv->type, priv->uri,
+				 tags, &error);
+		if (error) {
+			g_print ("Tag Addition Error : %s", error->message);
+			return;
+		}
+	}
+
+	_on_close_add_tag (but, bar);
+	gchar *temp = g_strdup (priv->uri);
+	tracker_tag_bar_set_uri (bar, priv->type, temp);
+	g_free (temp);
+}
+
+static void
+_on_entry_activate (GtkEntry *entry, TrackerTagBar *bar)
+{
+	_on_apply_add_tag (NULL, bar);
+}
+
+static void
+_on_add_tag_clicked (GtkButton *but, TrackerTagBar *bar)
+{
+	TrackerTagBarPrivate *priv;
+	GtkWidget *hbox;
+	GtkWidget *entry;
+	GtkWidget *image;
+	GtkWidget *button;
+
+	priv = TRACKER_TAG_BAR_GET_PRIVATE (bar);
+
+	hbox = gtk_hbox_new (FALSE, 4);
+	priv->entry_box = hbox;
+
+	entry = gtk_entry_new ();
+	priv->entry = entry;
+	gtk_entry_set_text (GTK_ENTRY (entry), _("Type tags you want to add here, separated by commas"));
+	gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1);
+	gtk_box_pack_start (GTK_BOX(hbox), entry, TRUE, TRUE, 0);
+
+	g_signal_connect (G_OBJECT (entry), "activate",
+			  G_CALLBACK (_on_entry_activate), bar);
+
+	image = gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU);
+
+	button = gtk_button_new ();
+	gtk_box_pack_start (GTK_BOX(hbox), button, FALSE, FALSE, 0);
+	gtk_button_set_image (GTK_BUTTON (button), image);
+	gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+
+	g_signal_connect (G_OBJECT (button), "clicked",
+			  G_CALLBACK (_on_close_add_tag), bar);
+
+	image = gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU);
+
+	button = gtk_button_new ();
+	gtk_box_pack_start (GTK_BOX(hbox), button, FALSE, FALSE, 0);
+	gtk_button_set_image (GTK_BUTTON (button), image);
+	gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+
+
+	g_signal_connect (G_OBJECT (button), "clicked",
+			  G_CALLBACK (_on_apply_add_tag), bar);
+
+	gtk_box_pack_start (GTK_BOX (bar), hbox, TRUE, TRUE, 0);
+	gtk_widget_show_all (hbox);
+	gtk_widget_hide (priv->tag_box);
+	gtk_widget_hide (priv->add_button);
+	gtk_widget_grab_focus (entry);
+}
+
+/* UTILS */
+
+static void
+_tag_bar_add_tag (TrackerTagBar *bar, GtkWidget *box, const char *tag)
+{
+	GtkWidget *button;
+	GtkWidget *label;
+	gchar *temp;
+
+	temp = g_strdup_printf ("<b><u>%s</u></b>", tag);
+
+	label = gtk_label_new (" ");
+	gtk_label_set_markup (GTK_LABEL (label), temp);
+
+	button = gtk_button_new ();
+	gtk_container_add (GTK_CONTAINER (button), label);
+	gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+	gtk_button_set_focus_on_click (GTK_BUTTON (button), FALSE);
+	gtk_box_pack_start (GTK_BOX(box), button, FALSE, FALSE, 0);
+
+	tracker_set_atk_relationship(button, ATK_RELATION_LABELLED_BY,
+				     label);
+	tracker_set_atk_relationship(label, ATK_RELATION_LABEL_FOR,
+				     button);
+
+	g_signal_connect (G_OBJECT (button), "button-press-event",
+			  G_CALLBACK (_on_tag_button_press_event), bar);
+
+	g_free (temp);
+}
+
+/* HEADER FUNCTIONS */
+void
+tracker_tag_bar_set_uri (TrackerTagBar *bar, ServiceType type, const gchar *uri)
+{
+	TrackerTagBarPrivate *priv;
+
+	priv = TRACKER_TAG_BAR_GET_PRIVATE (bar);
+
+	if (priv->uri)
+		g_free (priv->uri);
+	priv->uri = g_strdup (uri);
+	priv->type = type;
+
+	tracker_keywords_get_async (priv->client, priv->type, uri,
+				    (TrackerArrayReply)_keywords_reply,
+				    bar);
+}
+
+/* TRACKER TAG BAR NEW */
+static void
+tracker_tag_bar_class_init (TrackerTagBarClass *class)
+{
+	GObjectClass *obj_class;
+	GtkWidgetClass *widget_class;
+
+	obj_class = G_OBJECT_CLASS (class);
+	widget_class = GTK_WIDGET_CLASS (class);
+
+	g_type_class_add_private (obj_class, sizeof (TrackerTagBarPrivate));
+}
+
+static void
+tracker_tag_bar_init (TrackerTagBar *tag_bar)
+{
+	TrackerTagBarPrivate *priv;
+	GtkWidget *hbox;
+	GtkWidget *label;
+	GtkWidget *button;
+	GtkWidget *image;
+	GtkWidget *menu;
+	GtkWidget *item;
+
+	priv = TRACKER_TAG_BAR_GET_PRIVATE (tag_bar);
+
+	priv->uri = NULL;
+	priv->active_tag = NULL;
+
+	gtk_container_set_border_width (GTK_CONTAINER(tag_bar), 0);
+
+	label = gtk_label_new (_("Tags :"));
+	gtk_box_pack_start (GTK_BOX(tag_bar), label, FALSE, TRUE, 0);
+
+	hbox = gtk_hbox_new (FALSE, 4);
+	priv->tag_box = hbox;
+	gtk_box_pack_start (GTK_BOX(tag_bar), hbox, FALSE, TRUE, 0);
+
+	image = gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU);
+
+	button = gtk_button_new ();
+	priv->add_button = button;
+	gtk_box_pack_start (GTK_BOX(tag_bar), button, FALSE, FALSE, 0);
+	gtk_button_set_image (GTK_BUTTON (button), image);
+	gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+
+	tracker_set_atk_relationship(button, ATK_RELATION_LABELLED_BY,
+				     label);
+	tracker_set_atk_relationship(label, ATK_RELATION_LABEL_FOR,
+				     button);
+
+	g_signal_connect (G_OBJECT (button), "clicked",
+			  G_CALLBACK (_on_add_tag_clicked), tag_bar);
+
+	menu = gtk_menu_new();
+	priv->menu = menu;
+
+	/* Search For Tag */
+	item = gtk_image_menu_item_new_with_mnemonic(_("_Search For Tag"));
+	gtk_widget_show(item);
+	gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+
+	g_signal_connect(G_OBJECT(item), "activate",
+					 G_CALLBACK(search_tag_activate_cb), tag_bar);
+
+	image = gtk_image_new_from_stock(GTK_STOCK_FIND, GTK_ICON_SIZE_MENU);
+	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
+	gtk_widget_show(image);
+
+	/* Remove Tag */
+	item = gtk_image_menu_item_new_with_mnemonic(_("_Remove Tag"));
+	gtk_widget_show(item);
+	gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+
+	g_signal_connect(G_OBJECT(item), "activate",
+					 G_CALLBACK(remove_tag_activate_cb), tag_bar);
+
+	image = gtk_image_new_from_stock(GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU);
+	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
+	gtk_widget_show(image);
+}
+
+GtkWidget *
+tracker_tag_bar_new (void)
+{
+	TrackerClient *client;
+	GtkWidget *tag_bar;
+	TrackerTagBarPrivate *priv;
+
+	tag_bar = g_object_new (TRACKER_TYPE_TAG_BAR,
+				"homogeneous", FALSE,
+				"spacing", 0 ,
+				NULL);
+	priv = TRACKER_TAG_BAR_GET_PRIVATE (tag_bar);
+
+	client = tracker_connect (TRUE);
+	priv->client = client;
+	return tag_bar;
+}

Added: trunk/src/libtracker-gtk/tracker-tag-bar.h
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-gtk/tracker-tag-bar.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,66 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+ *
+ * Copyright (C) 2007 Neil Jagdish Patel <njpatel gmail com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ *
+ * Author : Neil Jagdish Patel <njpatel gmail com>
+ */
+
+#ifndef TRACKER_TAG_BAR_H
+#define TRACKER_TAG_BAR_H
+
+#include <gtk/gtk.h>
+#include <tracker.h>
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_TAG_BAR		(tracker_tag_bar_get_type ())
+#define TRACKER_TAG_BAR(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), TRACKER_TYPE_TAG_BAR, TrackerTagBar))
+#define TRACKER_TAG_BAR_CLASS(obj)	(G_TYPE_CHECK_CLASS_CAST ((obj), TRACKER_TAG_BAR, TrackerTagBarClass))
+#define TRACKER_IS_TAG_BAR(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRACKER_TYPE_TAG_BAR))
+#define TRACKER_IS_TAG_BAR_CLASS(obj)	(G_TYPE_CHECK_CLASS_TYPE ((obj), TRACKER_TYPE_TAG_BAR))
+#define TRACKER_TAG_BAR_GET_CLASS	(G_TYPE_INSTANCE_GET_CLASS ((obj), TRACKER_TYPE_TAG_BAR, TrackerTagBarClass))
+
+typedef struct _TrackerTagBar		TrackerTagBar;
+typedef struct _TrackerTagBarClass	TrackerTagBarClass;
+
+struct _TrackerTagBar
+{
+	GtkHBox parent;
+};
+
+struct _TrackerTagBarClass
+{
+	GtkHBoxClass parent_class;
+};
+
+GtkWidget *tracker_tag_bar_new (void);
+
+/*
+uri has to be a local uri i.e.
+'/home/john/doe.mp3' not 'file:///home/john/doe.mp3'
+*/
+void	   tracker_tag_bar_set_uri (TrackerTagBar		*bar,
+				    ServiceType			type,
+				    const gchar			*uri
+				   );
+
+GType tracker_tag_bar_get_type (void);
+
+G_END_DECLS
+
+#endif /* TRACKER_TAG_BAR_H */

Added: trunk/src/libtracker-gtk/tracker-ui.c
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-gtk/tracker-ui.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,64 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+ *
+ * libtracker-gtk/tracker-ui.c - Functions for creating tracker centric UI
+ * elemetents.
+ *
+ * Copyright (C) 2007 John Stowers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include "tracker-keyword-store.h"
+#include "tracker-ui.h"
+
+/**
+ * tracker_render_emblem_pixbuf_cb:
+ *
+ * designed to be used as a gtk_cell_layout_set_cell_data_func. Returns the
+ * emblem pixbuf for the keyword in the ListStore to which it is rendering
+ *
+ **/
+void
+tracker_render_emblem_pixbuf_cb (GtkCellLayout			*cell_layout,
+				 GtkCellRenderer		*cell,
+				 GtkTreeModel			*tree_model,
+				 GtkTreeIter			*iter,
+				 gpointer			icon_theme)
+{
+	char *stock_id;
+	GdkPixbuf *pixbuf;
+	GtkIconTheme *theme;
+
+	theme = GTK_ICON_THEME (icon_theme);
+
+	gtk_tree_model_get (tree_model, iter, TRACKER_KEYWORD_STORE_IMAGE_URI, &stock_id, -1);
+	if (stock_id == NULL) {
+		stock_id = g_strdup ("emblem-generic");
+	}
+
+	pixbuf = gtk_icon_theme_load_icon (theme, stock_id, 24, 0, NULL);
+	if (pixbuf != NULL) {
+		g_object_set (cell, "pixbuf", pixbuf, NULL);
+		g_object_unref (pixbuf);
+	} else {
+		g_warning("ICON NOT FOUND\n");
+	}
+
+	g_free (stock_id);
+}

Added: trunk/src/libtracker-gtk/tracker-ui.h
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-gtk/tracker-ui.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,45 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+ *
+ * libtracker-gtk/tracker-ui.c - Functions for creating tracker centric UI
+ * elemetents.
+ *
+ * Copyright (C) 2007 John Stowers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef TRACKER_UI_H
+#define TRACKER_UI_H
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+/**
+ * The GtkTargetEntry to use as the drag type when
+ * dragging and dropping keywords
+ **/
+const GtkTargetEntry KEYWORD_DRAG_TYPES[] = {
+	{"property/keyword", 0, 0 }
+};
+
+void
+tracker_render_emblem_pixbuf_cb (GtkCellLayout	 *cell_layout,
+				 GtkCellRenderer *cell,
+				 GtkTreeModel	 *tree_model,
+				 GtkTreeIter	 *iter,
+				 gpointer	 user_data);
+
+#endif /* TRACKER_UI_H */

Added: trunk/src/libtracker-gtk/tracker-utils.c
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-gtk/tracker-utils.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,114 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+ *
+ * libtracker-gtk/tracker-utils.c - Grab bag of functions for manuipulating
+ * tracker results into more Gtk friedly types.
+ *
+ * Copyright (C) 2007 John Stowers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <string.h>
+
+#include "tracker-utils.h"
+
+
+GList *
+tracker_keyword_array_to_glist (gchar **array)
+{
+	GList *list = NULL;
+	gchar **meta = NULL;
+
+	if (!array) {
+		return NULL;
+	}
+
+	for (meta = array; *meta; meta++) {
+		gchar *name = g_strdup (*meta);
+		list = g_list_prepend (list, name);
+	}
+
+	return list;
+}
+
+GList *
+tracker_get_all_keywords (TrackerClient *tracker_client)
+{
+	GPtrArray *out_array;
+	GList *list = NULL;
+	GError *error = NULL;
+
+	out_array = tracker_keywords_get_list (tracker_client, SERVICE_FILES, &error);
+
+	if (!error && out_array) {
+		guint i;
+		for (i = 0; i < out_array->len; i++) {
+			gchar **names = out_array->pdata[i];
+			if (names) {
+				gchar *name = names[0];
+				if (strlen (name) > 2) {
+					list = g_list_prepend(list, name);
+				}
+			}
+		}
+		g_ptr_array_free (out_array, TRUE);
+	}
+
+	g_clear_error (&error);
+
+	return list;
+}
+
+/* Creates a tree model containing the keywords in List
+this simple treemodel has a single column containing the keyword name*/
+GtkTreeModel *
+tracker_create_simple_keyword_liststore (const GList *list)
+{
+    GtkListStore *store;
+    const GList *tmp;
+
+    store = gtk_list_store_new (1, G_TYPE_STRING);
+
+    for (tmp = list; tmp; tmp = tmp->next) {
+	    gchar *keyword = keyword = tmp->data;
+
+	    gtk_list_store_insert_with_values (store,
+					       NULL,
+					       0,
+					       0,
+					       keyword,
+					       -1);
+    }
+
+    return GTK_TREE_MODEL (store);
+}
+
+void
+tracker_set_atk_relationship(GtkWidget *obj1, int relation_type,
+			     GtkWidget *obj2)
+{
+	AtkObject *atk_obj1, *atk_obj2, *targets[1];
+	AtkRelationSet *atk_rel_set;
+	AtkRelation *atk_rel;
+
+	atk_obj1 = gtk_widget_get_accessible (GTK_WIDGET (obj1));
+	atk_obj2 = gtk_widget_get_accessible (GTK_WIDGET (obj2));
+	atk_rel_set = atk_object_ref_relation_set (atk_obj1);
+	targets[0] = atk_obj2;
+	atk_rel = atk_relation_new (targets, 1, relation_type);
+	atk_relation_set_add (atk_rel_set, atk_rel);
+	g_object_unref (G_OBJECT (atk_rel));
+}

Added: trunk/src/libtracker-gtk/tracker-utils.h
==============================================================================
--- (empty file)
+++ trunk/src/libtracker-gtk/tracker-utils.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,37 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+ *
+ * libtracker-gtk/tracker-utils.c - Grab bag of functions for manuipulating
+ * tracker results into more Gtk friedly types.
+ *
+ * Copyright (C) 2007 John Stowers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef TRACKER_UTILS_H
+#define TRACKER_UTILS_H
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include <tracker.h>
+
+GList *		tracker_keyword_array_to_glist (gchar **array);
+GList *		tracker_get_all_keywords (TrackerClient *tracker_client);
+GtkTreeModel *	tracker_create_simple_keyword_liststore (const GList *list);
+void		tracker_set_atk_relationship(GtkWidget *obj1, int relation_type,
+					     GtkWidget *obj2);
+#endif /* TRACKER_UTILS_H */

Added: trunk/src/libtracker/COPYING.LIB
==============================================================================
--- (empty file)
+++ trunk/src/libtracker/COPYING.LIB	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,510 @@
+
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+	51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations
+below.
+
+  When we speak of free software, we are referring to freedom of use,
+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 this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it
+becomes a de-facto standard.  To achieve this, non-free programs must
+be allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+                  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control
+compilation and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at least
+    three years, to give the same user the materials specified in
+    Subsection 6a, above, for a charge no more than the cost of
+    performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. 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 not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+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
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply, and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License
+may add an explicit geographical distribution limitation excluding those
+countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the 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
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                            NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "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
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY 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
+LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms
+of the ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.
+It is safest to attach them to the start of each source file to most
+effectively convey the exclusion of warranty; and each file should
+have at least the "copyright" line and a pointer to where the full
+notice is found.
+
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    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
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or
+your school, if any, to sign a "copyright disclaimer" for the library,
+if necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James
+  Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+

Added: trunk/src/libtracker/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/libtracker/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,36 @@
+include $(top_srcdir)/Makefile.decl
+
+INCLUDES = 					\
+	-DLOCALEDIR=\""$(localedir)"\" 		\
+	-DG_LOG_DOMAIN=\"Tracker\"		\
+	-I$(top_srcdir)/src			\
+	$(GLIB2_CFLAGS)				\
+	$(DBUS_CFLAGS)			
+
+lib_LTLIBRARIES = libtrackerclient.la
+
+libtrackerclient_la_SOURCES = tracker.c
+libtrackerclient_la_LDFLAGS = -version-info 0:0:0
+libtrackerclient_la_LIBADD = 			\
+	$(GLIB2_LIBS) 				\
+	$(DBUS_LIBS)				\
+	$(GOBJECT_LIBS)
+	
+include_HEADERS = 				\
+	tracker.h				\
+	$(BUILT_SOURCES)		
+
+# Generate DBus files from XML data.
+dbus_sources = 					\
+	tracker-daemon-glue.h			\
+	tracker-files-glue.h			\
+	tracker-keywords-glue.h			\
+	tracker-metadata-glue.h			\
+	tracker-search-glue.h
+
+%-glue.h: $(top_srcdir)/data/dbus/%.xml
+	$(DBUSBINDINGTOOL) --mode=glib-client --output=$@ --prefix=$(subst -,_,$*) $^
+
+BUILT_SOURCES = $(dbus_sources)
+
+CLEANFILES = $(BUILT_SOURCES)

Added: trunk/src/libtracker/tracker.c
==============================================================================
--- (empty file)
+++ trunk/src/libtracker/tracker.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,1638 @@
+/* Tracker - indexer and metadata database engine
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <string.h>
+
+#include "tracker-daemon-glue.h"
+#include "tracker-files-glue.h"
+#include "tracker-keywords-glue.h"
+#include "tracker-metadata-glue.h"
+#include "tracker-search-glue.h"
+
+#include "tracker.h"
+
+#define TRACKER_SERVICE			"org.freedesktop.Tracker"
+#define TRACKER_OBJECT			"/org/freedesktop/Tracker"
+#define TRACKER_INTERFACE		"org.freedesktop.Tracker"
+#define TRACKER_INTERFACE_METADATA	"org.freedesktop.Tracker.Metadata"
+#define TRACKER_INTERFACE_KEYWORDS	"org.freedesktop.Tracker.Keywords"
+#define TRACKER_INTERFACE_SEARCH	"org.freedesktop.Tracker.Search"
+#define TRACKER_INTERFACE_FILES		"org.freedesktop.Tracker.Files"
+#define TRACKER_INTERFACE_MUSIC		"org.freedesktop.Tracker.Music"
+#define TRACKER_INTERFACE_PLAYLISTS	"org.freedesktop.Tracker.PlayLists"
+
+typedef struct {
+	TrackerArrayReply callback;
+	gpointer	  data;
+} ArrayCallBackStruct;
+
+typedef struct {
+	TrackerGPtrArrayReply callback;
+	gpointer	  data;
+} GPtrArrayCallBackStruct;
+
+typedef struct {
+	TrackerHashTableReply	callback;
+	gpointer		data;
+} HashTableCallBackStruct;
+
+
+typedef struct {
+	TrackerBooleanReply callback;
+	gpointer	  data;
+} BooleanCallBackStruct;
+
+typedef struct {
+	TrackerStringReply callback;
+	gpointer	  data;
+} StringCallBackStruct;
+
+typedef struct {
+	TrackerIntReply callback;
+	gpointer	  data;
+} IntCallBackStruct;
+
+typedef struct {
+	TrackerVoidReply callback;
+	gpointer	  data;
+} VoidCallBackStruct;
+
+
+char *tracker_service_types[] = {
+"Files",
+"Folders",
+"Documents",
+"Images",
+"Music",
+"Videos",
+"Text",
+"Development",
+"Other",
+"VFS",
+"VFSFolders",
+"VFSDocuments",
+"VFSImages",
+"VFSMusic",
+"VFSVideos",
+"VFSText",
+"VFSDevelopment",
+"VFSOther",
+"Conversations",
+"Playlists",
+"Applications",
+"Contacts",
+"Emails",
+"EmailAttachments",
+"Appointments",
+"Tasks",
+"Bookmarks",
+"WebHistory",
+"Projects",
+NULL
+};
+
+
+
+
+char *metadata_types[] = {
+	"index",
+	"string",
+	"numeric",
+	"date",
+	"blob"
+};
+
+
+ServiceType
+tracker_service_name_to_type (const char *service)
+{
+
+	char **st;
+	int i = 0;
+
+	for (st=tracker_service_types; *st; st++) {
+
+		if (g_ascii_strcasecmp (service, *st) == 0) {
+			return i;
+		}
+
+		i++;
+	}
+
+	return SERVICE_OTHER_FILES;
+}
+
+
+char *
+tracker_type_to_service_name (ServiceType s)
+{
+	return g_strdup (tracker_service_types[s]);
+}
+
+
+
+static void
+tracker_array_reply (DBusGProxy *proxy, char **OUT_result, GError *error, gpointer user_data)
+{
+
+	ArrayCallBackStruct *callback_struct;
+
+	callback_struct = user_data;
+
+	(*(TrackerArrayReply) callback_struct->callback ) (OUT_result, error, callback_struct->data);
+
+	g_free (callback_struct);
+}
+
+
+static void
+tracker_hashtable_reply (DBusGProxy *proxy,  GHashTable *OUT_result, GError *error, gpointer user_data)
+{
+
+	HashTableCallBackStruct *callback_struct;
+
+	callback_struct = user_data;
+
+	(*(TrackerHashTableReply) callback_struct->callback ) (OUT_result, error, callback_struct->data);
+
+	g_free (callback_struct);
+}
+
+static void
+tracker_GPtrArray_reply (DBusGProxy *proxy,  GPtrArray *OUT_result, GError *error, gpointer user_data)
+{
+
+	GPtrArrayCallBackStruct *callback_struct;
+
+	callback_struct = user_data;
+
+	(*(TrackerGPtrArrayReply) callback_struct->callback ) (OUT_result, error, callback_struct->data);
+
+	g_free (callback_struct);
+}
+
+
+
+static void
+tracker_string_reply (DBusGProxy *proxy, char *OUT_result, GError *error, gpointer user_data)
+{
+
+	StringCallBackStruct *callback_struct;
+
+	callback_struct = user_data;
+
+	(*(TrackerStringReply) callback_struct->callback ) (OUT_result, error, callback_struct->data);
+
+	g_free (callback_struct);
+}
+
+
+static void
+tracker_int_reply (DBusGProxy *proxy, int OUT_result, GError *error, gpointer user_data)
+{
+
+	IntCallBackStruct *callback_struct;
+
+	callback_struct = user_data;
+
+	(*(TrackerIntReply) callback_struct->callback ) (OUT_result, error, callback_struct->data);
+
+	g_free (callback_struct);
+}
+
+/*
+static void
+tracker_boolean_reply (DBusGProxy *proxy, gboolean OUT_result, GError *error, gpointer user_data)
+{
+
+	BooleanCallBackStruct *callback_struct;
+
+	callback_struct = user_data;
+
+	(*(TrackerBooleanReply) callback_struct->callback ) (OUT_result, error, callback_struct->data);
+
+	g_free (callback_struct);
+}
+*/
+
+
+static void
+tracker_void_reply (DBusGProxy *proxy, GError *error, gpointer user_data)
+{
+
+	VoidCallBackStruct *callback_struct;
+
+	callback_struct = user_data;
+
+	(*(TrackerVoidReply) callback_struct->callback ) (error, callback_struct->data);
+
+	g_free (callback_struct);
+}
+
+
+
+
+
+TrackerClient *
+tracker_connect (gboolean enable_warnings)
+{
+	DBusGConnection *connection;
+	GError *error = NULL;
+	TrackerClient *client = NULL;
+	DBusGProxy *proxy;
+
+	g_type_init ();
+
+	connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
+
+	if (connection == NULL)	{
+		if (enable_warnings) {
+			g_warning("Unable to connect to dbus: %s\n", error->message);
+		}
+		g_error_free (error);
+		return NULL;
+	}
+
+	proxy = dbus_g_proxy_new_for_name (connection,
+			TRACKER_SERVICE,
+			TRACKER_OBJECT,
+			TRACKER_INTERFACE);
+
+	if (!proxy) {
+		if (enable_warnings) {
+			g_warning ("could not create proxy");
+		}
+		return NULL;
+	}
+
+
+	client = g_new (TrackerClient, 1);
+	client->proxy = proxy;
+
+	proxy = dbus_g_proxy_new_for_name (connection,
+			TRACKER_SERVICE,
+			TRACKER_OBJECT "/Metadata",
+			TRACKER_INTERFACE_METADATA);
+
+	client->proxy_metadata = proxy;
+
+	proxy = dbus_g_proxy_new_for_name (connection,
+			TRACKER_SERVICE,
+			TRACKER_OBJECT "/Keywords",
+			TRACKER_INTERFACE_KEYWORDS);
+
+	client->proxy_keywords = proxy;
+
+	proxy = dbus_g_proxy_new_for_name (connection,
+			TRACKER_SERVICE,
+			TRACKER_OBJECT "/Search",
+			TRACKER_INTERFACE_SEARCH);
+
+	client->proxy_search = proxy;
+
+	proxy = dbus_g_proxy_new_for_name (connection,
+			TRACKER_SERVICE,
+			TRACKER_OBJECT "/Files",
+			TRACKER_INTERFACE_FILES);
+
+	client->proxy_files = proxy;
+
+
+
+	return client;
+
+}
+
+void
+tracker_disconnect (TrackerClient *client)
+{
+	g_object_unref (client->proxy);
+	g_object_unref (client->proxy_metadata);
+	g_object_unref (client->proxy_keywords);
+	g_object_unref (client->proxy_search);
+	g_object_unref (client->proxy_files);
+	client->proxy = NULL;
+	client->proxy_metadata = NULL;
+	client->proxy_keywords = NULL;
+	client->proxy_search = NULL;
+	client->proxy_files = NULL;
+
+	g_free (client);
+}
+
+
+
+void
+tracker_cancel_last_call (TrackerClient *client)
+{
+	dbus_g_proxy_cancel_call (client->proxy, client->last_pending_call);
+}
+
+
+
+/* dbus synchronous calls */
+
+
+int
+tracker_get_version (TrackerClient *client, GError **error)
+{
+	int version;
+
+	org_freedesktop_Tracker_get_version (client->proxy, &version, &*error);
+
+	return version;
+}
+
+char *
+tracker_get_status (TrackerClient *client, GError **error)
+{
+	char *status ;
+	org_freedesktop_Tracker_get_status (client->proxy, &status, &*error);
+	return status;
+}
+
+
+GHashTable *
+tracker_get_services (TrackerClient *client, gboolean main_services_only,  GError **error)
+{
+	GHashTable *table;
+
+	if (!org_freedesktop_Tracker_get_services (client->proxy, main_services_only, &table, &*error)) {
+		return NULL;
+	}
+
+	return table;
+
+
+}
+
+
+
+GPtrArray *
+tracker_get_stats (TrackerClient *client,  GError **error)
+{
+	GPtrArray *table;
+
+	if (!org_freedesktop_Tracker_get_stats (client->proxy, &table, &*error)) {
+		return NULL;
+	}
+
+	return table;
+
+
+}
+
+
+void
+tracker_set_bool_option (TrackerClient *client, const char *option, gboolean value, GError **error)
+{
+	org_freedesktop_Tracker_set_bool_option (client->proxy, option, value,	&*error);
+}
+
+void
+tracker_set_int_option (TrackerClient *client, const char *option, int value, GError **error)
+{
+	org_freedesktop_Tracker_set_int_option (client->proxy, option, value,  &*error);
+}
+
+
+void
+tracker_shutdown (TrackerClient *client, gboolean reindex, GError **error)
+{
+	org_freedesktop_Tracker_shutdown (client->proxy, reindex,  &*error);
+}
+
+
+void
+tracker_prompt_index_signals (TrackerClient *client, GError **error)
+{
+	org_freedesktop_Tracker_prompt_index_signals (client->proxy, &*error);
+}
+
+
+
+char **
+tracker_metadata_get (TrackerClient *client, ServiceType service, const char *id, char **keys, GError **error)
+{
+	char **array = NULL;
+	char *service_str = tracker_service_types[service];
+
+	if (!org_freedesktop_Tracker_Metadata_get  (client->proxy_metadata, service_str, id, (const char **)keys, &array, &*error)) {
+		return NULL;
+	}
+
+	return array;
+}
+
+
+void
+tracker_metadata_set (TrackerClient *client, ServiceType service, const char *id, char **keys, char **values, GError **error)
+{
+	char *service_str = tracker_service_types[service];
+
+	org_freedesktop_Tracker_Metadata_set  (client->proxy_metadata, service_str, id, (const char **)keys, (const char **)values, &*error);
+
+}
+
+
+
+void
+tracker_metadata_register_type	(TrackerClient *client, const char *name, MetadataTypes type, GError **error)
+{
+	/* This does nothing now, this API has been removed */
+	g_warning ("%s no longer does anything", __FUNCTION__);
+}
+
+MetaDataTypeDetails *
+tracker_metadata_get_type_details (TrackerClient *client, const char *name, GError **error)
+{
+
+	MetaDataTypeDetails *details = g_new (MetaDataTypeDetails, 1);
+
+	if (!org_freedesktop_Tracker_Metadata_get_type_details (client->proxy_metadata, name, &details->type, &details->is_embedded, &details->is_writeable, &*error)) {
+		g_free (details);
+		return NULL;
+	}
+
+	return details;
+
+}
+
+
+char **
+tracker_metadata_get_registered_types (TrackerClient *client, const char *class, GError **error)
+{
+	char **array = NULL;
+
+	if (!org_freedesktop_Tracker_Metadata_get_registered_types  (client->proxy_metadata, class, &array, &*error)) {
+		return NULL;
+	}
+
+	return array;
+}
+
+
+char **
+tracker_metadata_get_writeable_types (TrackerClient *client, const char *class, GError **error)
+{
+	/* This does nothing now, this API has been removed */
+	g_warning ("%s no longer does anything", __FUNCTION__);
+
+	return NULL;
+}
+
+
+
+char **
+tracker_metadata_get_registered_classes (TrackerClient *client, GError **error)
+{
+	char **array = NULL;
+
+	if (!org_freedesktop_Tracker_Metadata_get_registered_classes  (client->proxy_metadata, &array, &*error)) {
+		return NULL;
+	}
+
+	return array;
+}
+
+
+GPtrArray *
+tracker_metadata_get_unique_values (TrackerClient *client, ServiceType service, char **meta_types, char *query, gboolean descending, int offset, int max_hits, GError **error)
+{
+	GPtrArray *table;
+	char *service_str = tracker_service_types[service];
+
+	if (!org_freedesktop_Tracker_Metadata_get_unique_values (client->proxy_metadata, service_str, (const char **)meta_types, query, descending, offset, max_hits, &table, &*error)) {
+		return NULL;
+	}
+
+	return table;
+}
+
+int
+tracker_metadata_get_sum (TrackerClient *client, ServiceType service, char *field, char *query, GError **error)
+{
+	int sum;
+
+	char *service_str = tracker_service_types[service];
+
+	if (!org_freedesktop_Tracker_Metadata_get_sum (client->proxy_metadata, service_str, field, query, &sum, &*error)) {
+		return -1;
+	}
+
+	return sum;
+}
+
+int
+tracker_metadata_get_count (TrackerClient *client, ServiceType service, char *field, char *query, GError **error)
+{
+	int count;
+
+	char *service_str = tracker_service_types[service];
+
+	if (!org_freedesktop_Tracker_Metadata_get_count (client->proxy_metadata, service_str, field, query, &count, &*error)) {
+		return -1;
+	}
+
+	return count;
+
+}
+
+GPtrArray *
+tracker_metadata_get_unique_values_with_count (TrackerClient *client, ServiceType service, char **meta_types, char *query, char *count, gboolean descending, int offset, int max_hits, GError **error)
+{
+	GPtrArray *table;
+	char *service_str = tracker_service_types[service];
+
+	if (!org_freedesktop_Tracker_Metadata_get_unique_values_with_count (client->proxy_metadata, service_str, (const char **)meta_types, query, count, descending, offset, max_hits, &table, &*error)) {
+		return NULL;
+	}
+
+	return table;
+}
+
+
+GPtrArray *
+tracker_keywords_get_list (TrackerClient *client, ServiceType service, GError **error)
+{
+	GPtrArray *table;
+	char *service_str = tracker_service_types[service];
+
+	if (!org_freedesktop_Tracker_Keywords_get_list (client->proxy_keywords,service_str, &table, &*error)) {
+		return NULL;
+	}
+
+	return table;
+
+
+}
+
+
+char **
+tracker_keywords_get (TrackerClient *client, ServiceType service, const char *id, GError **error)
+{
+	char **array = NULL;
+	char *service_str = tracker_service_types[service];
+
+	if (!org_freedesktop_Tracker_Keywords_get (client->proxy_keywords, service_str, id, &array, &*error)) {
+		return NULL;
+	}
+
+	return array;
+}
+
+
+
+void
+tracker_keywords_add (TrackerClient *client, ServiceType service, const char *id, char **values, GError **error)
+{
+	char *service_str = tracker_service_types[service];
+	org_freedesktop_Tracker_Keywords_add (client->proxy_keywords, service_str, id, (const char **)values, &*error);
+}
+
+
+
+void
+tracker_keywords_remove (TrackerClient *client, ServiceType service, const char *id, char **values, GError **error)
+{
+	char *service_str = tracker_service_types[service];
+
+	org_freedesktop_Tracker_Keywords_remove (client->proxy_keywords, service_str, id, (const char **)values, &*error);
+}
+
+
+
+void
+tracker_keywords_remove_all (TrackerClient *client, ServiceType service, const char *id, GError **error)
+{
+	char *service_str = tracker_service_types[service];
+
+	org_freedesktop_Tracker_Keywords_remove_all (client->proxy_keywords, service_str, id, &*error);
+}
+
+
+char **
+tracker_keywords_search	(TrackerClient *client, int live_query_id, ServiceType service, char **keywords, int offset, int max_hits, GError **error)
+{
+	char **array = NULL;
+	char *service_str = tracker_service_types[service];
+
+	if (!org_freedesktop_Tracker_Keywords_search (client->proxy_keywords, live_query_id, service_str, (const char **)keywords, offset, max_hits, &array, &*error)) {
+		return NULL;
+	}
+
+	return array;
+
+}
+
+
+int
+tracker_search_get_hit_count (TrackerClient *client, ServiceType service, const char *search_text, GError **error)
+{
+
+	int count = 0;
+	char *service_str = tracker_service_types[service];
+
+	if (!org_freedesktop_Tracker_Search_get_hit_count (client->proxy_search, service_str, search_text, &count, &*error)) {
+		return 0;
+	}
+
+	return count;
+
+}
+
+
+GPtrArray *
+tracker_search_get_hit_count_all (TrackerClient *client, const char *search_text, GError **error)
+{
+
+	GPtrArray *array;
+
+	if (!org_freedesktop_Tracker_Search_get_hit_count_all (client->proxy_search, search_text, &array, &*error)) {
+		return NULL;
+	}
+
+	return array;
+
+}
+
+char **
+tracker_search_text (TrackerClient *client, int live_query_id, ServiceType service, const char *search_text, int offset, int max_hits, GError **error)
+{
+	char **array = NULL;
+	char *service_str = tracker_service_types[service];
+
+	if (!org_freedesktop_Tracker_Search_text (client->proxy_search, live_query_id, service_str, search_text,  offset, max_hits, &array, &*error)) {
+		return NULL;
+	}
+
+	return array;
+}
+
+GPtrArray *
+tracker_search_text_detailed (TrackerClient *client, int live_query_id, ServiceType service, const char *search_text, int offset, int max_hits, GError **error)
+{
+	GPtrArray *array;
+	char *service_str = tracker_service_types[service];
+
+	if (!org_freedesktop_Tracker_Search_text_detailed (client->proxy_search, live_query_id, service_str, search_text,  offset, max_hits, &array, &*error)) {
+		return NULL;
+	}
+
+	return array;
+}
+
+char *
+tracker_search_get_snippet (TrackerClient *client, ServiceType service, const char *uri, const char *search_text, GError **error)
+{
+	char *result;
+	char *service_str = tracker_service_types[service];
+
+	if (!org_freedesktop_Tracker_Search_get_snippet (client->proxy_search, service_str, uri, search_text, &result, &*error)) {
+		return NULL;
+	}
+
+	return result;
+
+
+}
+
+
+
+char **
+tracker_search_metadata	(TrackerClient *client, ServiceType service, const char *field, const char* search_text, int offset, int max_hits, GError **error)
+{
+	char **array = NULL;
+	char *service_str = tracker_service_types[service];
+
+	if (!org_freedesktop_Tracker_Search_metadata (client->proxy_search, service_str, field, search_text,  offset, max_hits, &array, &*error)) {
+		return NULL;
+	}
+
+	return array;
+}
+
+
+
+GPtrArray *
+tracker_search_query (TrackerClient *client, int live_query_id, ServiceType service, char **fields, const char *search_text, const char *keywords, const char *query, int offset, int max_hits, gboolean sort_by_service, char **sort_fields, gboolean sort_descending, GError **error)
+{
+	GPtrArray *table;
+	char *service_str = tracker_service_types[service];
+
+	if (!org_freedesktop_Tracker_Search_query (client->proxy_search, live_query_id, service_str, (const char **)fields, search_text, keywords, query, sort_by_service, (const char **)sort_fields, sort_descending, offset, max_hits , &table, &*error)) {
+		return NULL;
+	}
+
+	return table;
+}
+
+char *
+tracker_search_suggest (TrackerClient *client, const char *search_term, int maxdist, GError **error)
+{
+	gchar *result;
+	if (org_freedesktop_Tracker_Search_suggest (client->proxy_search, search_term, maxdist, &result, &*error)) {
+		return result;
+	}
+	return NULL;
+}
+
+
+
+void
+tracker_files_create (TrackerClient *client, const char *uri, gboolean is_directory, const char *mime, int size, int mtime, GError **error)
+{
+	org_freedesktop_Tracker_Files_create (client->proxy_files, uri, is_directory, mime, size, mtime, &*error);
+}
+
+
+void
+tracker_files_delete (TrackerClient *client, const char *uri, GError **error)
+{
+	org_freedesktop_Tracker_Files_delete (client->proxy_files, uri, &*error);
+}
+
+
+char *
+tracker_files_get_text_contents	(TrackerClient *client,  const char *uri, int offset, int max_length, GError **error)
+{
+	char *result;
+
+	if (!org_freedesktop_Tracker_Files_get_text_contents (client->proxy_files, uri, offset, max_length, &result, &*error)) {
+		return NULL;
+	}
+
+	return result;
+
+}
+
+
+
+char *
+tracker_files_search_text_contents (TrackerClient *client,  const char *uri, const char *search_text, int length, GError **error)
+{
+	char *result;
+
+	if (!org_freedesktop_Tracker_Files_search_text_contents (client->proxy_files, uri, search_text, length, &result, &*error)) {
+		return NULL;
+	}
+
+	return result;
+}
+
+
+
+char **
+tracker_files_get_by_service_type (TrackerClient *client,  int live_query_id, ServiceType service, int offset, int max_hits, GError **error)
+{
+	char **array = NULL;
+	char *service_str = tracker_service_types[service];
+
+	if (!org_freedesktop_Tracker_Files_get_by_service_type (client->proxy_files, live_query_id, service_str, offset, max_hits, &array, &*error)) {
+		return NULL;
+	}
+
+	return array;
+}
+
+
+
+char **
+tracker_files_get_by_mime_type	(TrackerClient *client,  int live_query_id, char **mimes, int offset, int max_hits, GError **error)
+{
+	char **array = NULL;
+
+	if (!org_freedesktop_Tracker_Files_get_by_mime_type (client->proxy_files, live_query_id, (const char **)mimes, offset, max_hits, &array, &*error)) {
+		return NULL;
+	}
+
+	return array;
+}
+
+
+
+char **
+tracker_files_get_by_mime_type_vfs (TrackerClient *client,  int live_query_id, char **mimes, int offset, int max_hits, GError **error)
+{
+	char **array = NULL;
+
+	if (!org_freedesktop_Tracker_Files_get_by_mime_type_vfs (client->proxy_files, live_query_id,(const char **) mimes, offset, max_hits, &array, &*error)) {
+		return NULL;
+	}
+
+	return array;
+}
+
+
+
+int
+tracker_files_get_mtime	(TrackerClient *client, const char *uri, GError **error)
+{
+	int result;
+
+	if (!org_freedesktop_Tracker_Files_get_mtime (client->proxy_files, uri, &result, &*error)) {
+		return 0;
+	}
+
+	return result;
+}
+
+
+GPtrArray *
+tracker_files_get_metadata_for_files_in_folder	(TrackerClient *client, int live_query_id, const char *uri, char **fields, GError **error)
+{
+	GPtrArray *table;
+
+	if (!org_freedesktop_Tracker_Files_get_metadata_for_files_in_folder  (client->proxy_files, live_query_id, uri, (const char **)fields, &table, &*error)) {
+		return NULL;
+	}
+
+	return table;
+}
+
+
+
+char **
+tracker_search_metadata_by_text (TrackerClient *client, const char *query,  GError **error)
+{
+
+	char **array = NULL;
+
+	if (!org_freedesktop_Tracker_Search_text (client->proxy_search, -1, "Files", query,  0, 512, &array, &*error)) {
+		return NULL;
+	}
+
+	return array;
+
+}
+
+/* asynchronous calls */
+
+
+
+char **
+tracker_search_metadata_by_text_and_mime (TrackerClient *client, const char *query, const char **mimes, GError **error)
+{
+	char **strs;
+
+	if (!org_freedesktop_Tracker_Files_search_by_text_and_mime  (client->proxy_files, query,(const char **) mimes, &strs, &*error)) {
+		return NULL;
+	}
+	return strs;
+
+}
+
+
+char **
+tracker_search_metadata_by_text_and_mime_and_location (TrackerClient *client, const char *query, const char **mimes, const char *location, GError **error)
+{
+	char **strs;
+
+	if (!org_freedesktop_Tracker_Files_search_by_text_and_mime_and_location (client->proxy_files, query, (const char **)mimes, location, &strs, &*error)) {
+		return NULL;
+	}
+	return strs;
+
+}
+
+
+
+char **
+tracker_search_metadata_by_text_and_location (TrackerClient *client, const char *query, const char *location, GError **error)
+{
+	char **strs;
+
+	if (!org_freedesktop_Tracker_Files_search_by_text_and_location (client->proxy_files, query, location, &strs, &*error)) {
+		return NULL;
+	}
+	return strs;
+
+}
+
+
+
+void
+tracker_get_version_async (TrackerClient *client, TrackerIntReply callback, gpointer user_data)
+{
+
+	IntCallBackStruct *callback_struct;
+
+	callback_struct = g_new (IntCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	client->last_pending_call = org_freedesktop_Tracker_get_version_async (client->proxy, tracker_int_reply, callback_struct);
+
+}
+
+void
+tracker_get_status_async (TrackerClient *client, TrackerStringReply callback, gpointer user_data)
+{
+
+	StringCallBackStruct *callback_struct;
+
+	callback_struct = g_new (StringCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	client->last_pending_call = org_freedesktop_Tracker_get_status_async (client->proxy, tracker_string_reply, callback_struct);
+
+}
+
+
+
+
+void
+tracker_get_services_async	(TrackerClient *client, gboolean main_services_only, TrackerHashTableReply callback, gpointer user_data)
+{
+	HashTableCallBackStruct *callback_struct;
+
+	callback_struct = g_new (HashTableCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	client->last_pending_call = org_freedesktop_Tracker_get_services_async (client->proxy, main_services_only, tracker_hashtable_reply, callback_struct);
+
+}
+
+
+void
+tracker_get_stats_async	(TrackerClient *client,  TrackerGPtrArrayReply callback, gpointer user_data)
+{
+	GPtrArrayCallBackStruct *callback_struct;
+
+	callback_struct = g_new (GPtrArrayCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	client->last_pending_call = org_freedesktop_Tracker_get_stats_async (client->proxy, tracker_GPtrArray_reply, callback_struct);
+
+}
+
+
+
+void
+tracker_set_bool_option_async (TrackerClient *client, const char *option, gboolean value, TrackerVoidReply callback, gpointer user_data)
+{
+	VoidCallBackStruct *callback_struct;
+
+	callback_struct = g_new (VoidCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	client->last_pending_call = org_freedesktop_Tracker_set_bool_option_async  (client->proxy, option, value, tracker_void_reply, callback_struct);
+}
+
+void
+tracker_set_int_option_async (TrackerClient *client, const char *option, int value, TrackerVoidReply callback, gpointer user_data)
+{
+	VoidCallBackStruct *callback_struct;
+
+	callback_struct = g_new (VoidCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	client->last_pending_call = org_freedesktop_Tracker_set_int_option_async  (client->proxy, option, value, tracker_void_reply, callback_struct);
+}
+
+
+void
+tracker_shutdown_async (TrackerClient *client, gboolean reindex, TrackerVoidReply callback, gpointer user_data)
+{
+	VoidCallBackStruct *callback_struct;
+
+	callback_struct = g_new (VoidCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	client->last_pending_call = org_freedesktop_Tracker_shutdown_async  (client->proxy, reindex, tracker_void_reply, callback_struct);
+}
+
+
+void
+tracker_prompt_index_signals_async (TrackerClient *client, TrackerVoidReply callback, gpointer user_data)
+{
+	VoidCallBackStruct *callback_struct;
+
+	callback_struct = g_new (VoidCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	client->last_pending_call = org_freedesktop_Tracker_prompt_index_signals_async	(client->proxy, tracker_void_reply, callback_struct);
+}
+
+
+
+
+
+
+void
+tracker_metadata_get_async (TrackerClient *client, ServiceType service, const char *id, char **keys, TrackerArrayReply callback, gpointer user_data)
+{
+	ArrayCallBackStruct *callback_struct;
+
+	callback_struct = g_new (ArrayCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	char *service_str = tracker_service_types[service];
+
+	client->last_pending_call = org_freedesktop_Tracker_Metadata_get_async (client->proxy_metadata, service_str, id, (const char**)keys, tracker_array_reply, callback_struct);
+
+}
+
+
+void
+tracker_metadata_set_async (TrackerClient *client, ServiceType service, const char *id, char **keys, char **values, TrackerVoidReply callback, gpointer user_data)
+{
+
+	VoidCallBackStruct *callback_struct;
+
+	callback_struct = g_new (VoidCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+
+	char *service_str = tracker_service_types[service];
+
+	client->last_pending_call = org_freedesktop_Tracker_Metadata_set_async	(client->proxy_metadata, service_str, id, (const char **)keys, (const char **)values, tracker_void_reply, callback_struct);
+
+}
+
+
+
+void
+tracker_metadata_register_type_async (TrackerClient *client, const char *name, MetadataTypes type, TrackerVoidReply callback, gpointer user_data)
+{
+
+	VoidCallBackStruct *callback_struct;
+
+	callback_struct = g_new (VoidCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	/* This does nothing now, this API has been removed */
+	g_warning ("%s no longer does anything", __FUNCTION__);
+
+	tracker_void_reply (client->proxy_metadata, NULL, callback_struct);
+}
+
+
+
+
+void
+tracker_metadata_get_registered_types_async (TrackerClient *client, const char *class, TrackerArrayReply callback, gpointer user_data)
+{
+
+	ArrayCallBackStruct *callback_struct;
+
+	callback_struct = g_new (ArrayCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+
+	client->last_pending_call = org_freedesktop_Tracker_Metadata_get_registered_types_async  (client->proxy_metadata, class, tracker_array_reply, callback_struct);
+}
+
+
+void
+tracker_metadata_get_writeable_types_async (TrackerClient *client, const char *class, TrackerArrayReply callback, gpointer user_data)
+{
+
+	ArrayCallBackStruct *callback_struct;
+
+	callback_struct = g_new (ArrayCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+
+	/* This does nothing now, this API has been removed */
+	g_warning ("%s no longer does anything", __FUNCTION__);
+
+	tracker_void_reply (client->proxy_metadata, NULL, callback_struct);
+}
+
+
+
+void
+tracker_metadata_get_registered_classes_async (TrackerClient *client, TrackerArrayReply callback, gpointer user_data)
+{
+	ArrayCallBackStruct *callback_struct;
+
+	callback_struct = g_new (ArrayCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+
+	client->last_pending_call = org_freedesktop_Tracker_Metadata_get_registered_classes_async  (client->proxy_metadata,  tracker_array_reply, callback_struct);
+
+}
+
+
+void
+tracker_metadata_get_unique_values_async (TrackerClient *client, ServiceType service, char **meta_types, const char *query, gboolean descending, int offset, int max_hits, TrackerGPtrArrayReply callback, gpointer user_data)
+{
+
+	GPtrArrayCallBackStruct *callback_struct;
+	char *service_str = tracker_service_types[service];
+
+	callback_struct = g_new (GPtrArrayCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	client->last_pending_call = org_freedesktop_Tracker_Metadata_get_unique_values_async (client->proxy_metadata, service_str, (const char **) meta_types, query, descending, offset, max_hits, tracker_GPtrArray_reply, callback_struct);
+
+}
+
+void
+tracker_metadata_get_sum_async (TrackerClient *client, ServiceType service, char *field, char *query, TrackerIntReply callback, gpointer user_data)
+{
+	IntCallBackStruct *callback_struct;
+	char *service_str = tracker_service_types[service];
+
+	callback_struct = g_new (IntCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	client->last_pending_call = org_freedesktop_Tracker_Metadata_get_sum_async (client->proxy_metadata, service_str, field, query, tracker_int_reply, callback_struct);
+}
+
+
+void
+tracker_metadata_get_count_async (TrackerClient *client, ServiceType service, char *field, char *query, TrackerIntReply callback, gpointer user_data)
+{
+	IntCallBackStruct *callback_struct;
+	char *service_str = tracker_service_types[service];
+
+	callback_struct = g_new (IntCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	client->last_pending_call = org_freedesktop_Tracker_Metadata_get_count_async (client->proxy_metadata, service_str, field, query, tracker_int_reply, callback_struct);
+}
+
+void
+tracker_metadata_get_unique_values_with_count_async (TrackerClient *client, ServiceType service, char **meta_types, const char *query, char *count, gboolean descending, int offset, int max_hits, TrackerGPtrArrayReply callback, gpointer user_data)
+{
+
+	GPtrArrayCallBackStruct *callback_struct;
+	char *service_str = tracker_service_types[service];
+
+	callback_struct = g_new (GPtrArrayCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	client->last_pending_call = org_freedesktop_Tracker_Metadata_get_unique_values_with_count_async (client->proxy_metadata, service_str, (const char **) meta_types, query, count, descending, offset, max_hits, tracker_GPtrArray_reply, callback_struct);
+
+}
+
+
+void
+tracker_keywords_get_list_async (TrackerClient *client, ServiceType service, TrackerGPtrArrayReply callback, gpointer user_data)
+{
+
+	GPtrArrayCallBackStruct *callback_struct;
+
+	callback_struct = g_new (GPtrArrayCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+
+	char *service_str = tracker_service_types[service];
+
+	client->last_pending_call = org_freedesktop_Tracker_Keywords_get_list_async (client->proxy_keywords, service_str, tracker_GPtrArray_reply, callback_struct);
+
+
+}
+
+
+void
+tracker_keywords_get_async (TrackerClient *client, ServiceType service, const char *id, TrackerArrayReply callback, gpointer user_data)
+{
+
+	ArrayCallBackStruct *callback_struct;
+
+	callback_struct = g_new (ArrayCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	char *service_str = tracker_service_types[service];
+
+	client->last_pending_call = org_freedesktop_Tracker_Keywords_get_async (client->proxy_keywords, service_str, id, tracker_array_reply, callback_struct);
+
+}
+
+
+
+void
+tracker_keywords_add_async (TrackerClient *client, ServiceType service, const char *id, char **values, TrackerVoidReply callback, gpointer user_data)
+{
+
+	VoidCallBackStruct *callback_struct;
+
+	callback_struct = g_new (VoidCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	char *service_str = tracker_service_types[service];
+	client->last_pending_call = org_freedesktop_Tracker_Keywords_add_async (client->proxy_keywords, service_str, id, (const char **)values, tracker_void_reply, callback_struct);
+}
+
+
+
+void
+tracker_keywords_remove_async (TrackerClient *client, ServiceType service, const char *id, char **values, TrackerVoidReply callback, gpointer user_data)
+{
+
+	VoidCallBackStruct *callback_struct;
+
+	callback_struct = g_new (VoidCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	char *service_str = tracker_service_types[service];
+
+	client->last_pending_call = org_freedesktop_Tracker_Keywords_remove_async (client->proxy_keywords, service_str, id, (const char **)values, tracker_void_reply, callback_struct);
+}
+
+
+
+void
+tracker_keywords_remove_all_async (TrackerClient *client, ServiceType service, const char *id, TrackerVoidReply callback, gpointer user_data)
+{
+
+	VoidCallBackStruct *callback_struct;
+
+	callback_struct = g_new (VoidCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	char *service_str = tracker_service_types[service];
+
+	client->last_pending_call = org_freedesktop_Tracker_Keywords_remove_all_async (client->proxy_keywords, service_str, id, tracker_void_reply, callback_struct);
+}
+
+
+void
+tracker_keywords_search_async	(TrackerClient *client, int live_query_id, ServiceType service, char **keywords, int offset, int max_hits, TrackerArrayReply callback, gpointer user_data)
+{
+
+	ArrayCallBackStruct *callback_struct;
+
+	callback_struct = g_new (ArrayCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	char *service_str = tracker_service_types[service];
+
+	client->last_pending_call = org_freedesktop_Tracker_Keywords_search_async (client->proxy_keywords, live_query_id, service_str, (const char **)keywords, offset, max_hits, tracker_array_reply, callback_struct);
+
+}
+
+
+void
+tracker_search_text_get_hit_count_async (TrackerClient *client, ServiceType service, const char *search_text, TrackerIntReply callback, gpointer user_data)
+{
+	char *service_str = tracker_service_types[service];
+
+	IntCallBackStruct *callback_struct;
+
+	callback_struct = g_new (IntCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	client->last_pending_call =  org_freedesktop_Tracker_Search_get_hit_count_async (client->proxy_search, service_str, search_text, tracker_int_reply, callback_struct);
+
+
+}
+
+
+void
+tracker_search_text_get_hit_count_all_async (TrackerClient *client, const char *search_text, TrackerGPtrArrayReply callback, gpointer user_data)
+{
+
+	GPtrArrayCallBackStruct *callback_struct;
+
+	callback_struct = g_new (GPtrArrayCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	client->last_pending_call = org_freedesktop_Tracker_Search_get_hit_count_all_async (client->proxy_search, search_text, tracker_GPtrArray_reply, callback_struct);
+
+
+}
+
+void
+tracker_search_text_async (TrackerClient *client, int live_query_id, ServiceType service, const char *search_text, int offset, int max_hits, TrackerArrayReply callback, gpointer user_data)
+{
+
+	ArrayCallBackStruct *callback_struct;
+
+	callback_struct = g_new (ArrayCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	char *service_str = tracker_service_types[service];
+
+	client->last_pending_call = org_freedesktop_Tracker_Search_text_async (client->proxy_search, live_query_id, service_str, search_text, offset, max_hits, tracker_array_reply, callback_struct);
+
+}
+
+
+void
+tracker_search_text_detailed_async (TrackerClient *client, int live_query_id, ServiceType service, const char *search_text, int offset, int max_hits, TrackerGPtrArrayReply callback, gpointer user_data)
+{
+
+	GPtrArrayCallBackStruct *callback_struct;
+
+	callback_struct = g_new (GPtrArrayCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	char *service_str = tracker_service_types[service];
+
+	client->last_pending_call = org_freedesktop_Tracker_Search_text_detailed_async (client->proxy_search, live_query_id, service_str, search_text, offset, max_hits, tracker_GPtrArray_reply, callback_struct);
+
+}
+
+
+void
+tracker_search_get_snippet_async (TrackerClient *client, ServiceType service, const char *uri, const char *search_text, TrackerStringReply callback, gpointer user_data)
+{
+	StringCallBackStruct *callback_struct;
+
+	callback_struct = g_new (StringCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	char *service_str = tracker_service_types[service];
+
+	client->last_pending_call = org_freedesktop_Tracker_Search_get_snippet_async (client->proxy_search, service_str, uri, search_text, tracker_string_reply, callback_struct);
+
+}
+
+
+void
+tracker_search_metadata_async	(TrackerClient *client, ServiceType service, const char *field, const char* search_text, int offset, int max_hits, TrackerArrayReply callback, gpointer user_data)
+{
+
+	ArrayCallBackStruct *callback_struct;
+
+	callback_struct = g_new (ArrayCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	char *service_str = tracker_service_types[service];
+
+	org_freedesktop_Tracker_Search_metadata_async (client->proxy_search, service_str, field, search_text,  offset, max_hits,  tracker_array_reply, callback_struct);
+
+}
+
+void
+tracker_search_query_async (TrackerClient *client, int live_query_id, ServiceType service, char **fields, const char *search_text,  const char *keywords, const char *query, int offset, int max_hits, gboolean sort_by_service, char **sort_fields, gboolean sort_descending, TrackerGPtrArrayReply callback, gpointer user_data)
+{
+	GPtrArrayCallBackStruct *callback_struct;
+
+	callback_struct = g_new (GPtrArrayCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	char *service_str = tracker_service_types[service];
+
+	client->last_pending_call = org_freedesktop_Tracker_Search_query_async (client->proxy_search, live_query_id, service_str, (const char **)fields, search_text, keywords, query, sort_by_service, (const char **)sort_fields, sort_descending, offset, max_hits,	tracker_GPtrArray_reply, callback_struct);
+
+}
+
+
+void
+tracker_search_suggest_async (TrackerClient *client, const char *search_text, int maxdist, TrackerStringReply callback, gpointer user_data)
+{
+
+	StringCallBackStruct *callback_struct;
+
+	callback_struct = g_new (StringCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	client->last_pending_call = org_freedesktop_Tracker_Search_suggest_async (client->proxy_search, search_text, maxdist,  tracker_string_reply, callback_struct);
+
+}
+
+
+void
+tracker_files_create_async (TrackerClient *client, const char *uri, gboolean is_directory, const char *mime, int size, int mtime, TrackerVoidReply callback, gpointer user_data)
+{
+	VoidCallBackStruct *callback_struct;
+
+	callback_struct = g_new (VoidCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	client->last_pending_call = org_freedesktop_Tracker_Files_create_async (client->proxy_files, uri, is_directory, mime, size, mtime, tracker_void_reply, callback_struct);
+}
+
+
+void
+tracker_files_delete_async (TrackerClient *client, const char *uri, TrackerVoidReply callback, gpointer user_data)
+{
+
+	VoidCallBackStruct *callback_struct;
+
+	callback_struct = g_new (VoidCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	client->last_pending_call = org_freedesktop_Tracker_Files_delete_async (client->proxy_files, uri,  tracker_void_reply, callback_struct);
+}
+
+
+void
+tracker_files_get_text_contents_async	(TrackerClient *client,  const char *uri, int offset, int max_length, TrackerStringReply callback, gpointer user_data)
+{
+	StringCallBackStruct *callback_struct;
+
+	callback_struct = g_new (StringCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	client->last_pending_call = org_freedesktop_Tracker_Files_get_text_contents_async (client->proxy_files, uri, offset, max_length,  tracker_string_reply, callback_struct);
+
+}
+
+
+
+void
+tracker_files_search_text_contents_async (TrackerClient *client,  const char *uri, const char *search_text, int length, TrackerStringReply callback, gpointer user_data)
+{
+
+	StringCallBackStruct *callback_struct;
+
+	callback_struct = g_new (StringCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	client->last_pending_call = org_freedesktop_Tracker_Files_search_text_contents_async (client->proxy_files, uri, search_text, length,  tracker_string_reply, callback_struct);
+
+
+}
+
+
+
+void
+tracker_files_get_by_service_type_async (TrackerClient *client,  int live_query_id, ServiceType service, int offset, int max_hits, TrackerArrayReply callback, gpointer user_data)
+{
+	ArrayCallBackStruct *callback_struct;
+
+	callback_struct = g_new (ArrayCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	char *service_str = tracker_service_types[service];
+
+	client->last_pending_call = org_freedesktop_Tracker_Files_get_by_service_type_async (client->proxy_files, live_query_id, service_str, offset, max_hits,  tracker_array_reply, callback_struct);
+}
+
+
+
+void
+tracker_files_get_by_mime_type_async	(TrackerClient *client,  int live_query_id, char **mimes, int offset, int max_hits, TrackerArrayReply callback, gpointer user_data)
+{
+	ArrayCallBackStruct *callback_struct;
+
+	callback_struct = g_new (ArrayCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	client->last_pending_call = org_freedesktop_Tracker_Files_get_by_mime_type_async (client->proxy_files, live_query_id, (const char **)mimes, offset, max_hits,  tracker_array_reply, callback_struct);
+
+}
+
+
+
+void
+tracker_files_get_by_mime_type_vfs_async (TrackerClient *client,  int live_query_id, char **mimes, int offset, int max_hits, TrackerArrayReply callback, gpointer user_data)
+{
+	ArrayCallBackStruct *callback_struct;
+
+	callback_struct = g_new (ArrayCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	client->last_pending_call = org_freedesktop_Tracker_Files_get_by_mime_type_vfs_async (client->proxy_files, live_query_id,(const char **) mimes, offset,  max_hits,  tracker_array_reply, callback_struct);
+
+}
+
+
+
+void
+tracker_files_get_mtime_async	(TrackerClient *client, const char *uri, TrackerIntReply callback, gpointer user_data)
+{
+
+	IntCallBackStruct *callback_struct;
+
+	callback_struct = g_new (IntCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	client->last_pending_call = org_freedesktop_Tracker_Files_get_mtime_async (client->proxy_files, uri,  tracker_int_reply, callback_struct);
+
+}
+
+
+void
+tracker_files_get_metadata_for_files_in_folder_async	(TrackerClient *client, int live_query_id, const char *uri, char **fields, TrackerGPtrArrayReply callback, gpointer user_data)
+{
+	GPtrArrayCallBackStruct *callback_struct;
+
+	callback_struct = g_new (GPtrArrayCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	client->last_pending_call = org_freedesktop_Tracker_Files_get_metadata_for_files_in_folder_async  (client->proxy_files, live_query_id, uri, (const char **)fields,  tracker_GPtrArray_reply, callback_struct);
+
+
+}
+
+
+void
+tracker_search_metadata_by_text_async (TrackerClient *client, const char *query,  TrackerArrayReply callback, gpointer user_data)
+{
+	ArrayCallBackStruct *callback_struct;
+
+	callback_struct = g_new (ArrayCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	char *metadata;
+	char *keywords[2];
+
+	if (strchr (query, ':') != NULL) {
+		metadata = strtok ((char *)query, ":");
+		if(strcoll(metadata,"tag") == 0){
+			keywords[0] = strtok (NULL, ":");
+			keywords[1] = NULL;
+			client->last_pending_call = org_freedesktop_Tracker_Keywords_search_async (client->proxy_keywords, -1, "Files", (const char **)keywords, 0, 512, tracker_array_reply, callback_struct);
+		}
+	}else{
+		client->last_pending_call = org_freedesktop_Tracker_Search_text_async (client->proxy_search, -1, "Files", query,  0, 512, tracker_array_reply, callback_struct);
+	}
+}
+
+void
+tracker_search_metadata_by_text_and_mime_async (TrackerClient *client, const char *query, const char **mimes, TrackerArrayReply callback, gpointer user_data)
+{
+	ArrayCallBackStruct *callback_struct;
+
+	callback_struct = g_new (ArrayCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	client->last_pending_call = org_freedesktop_Tracker_Files_search_by_text_and_mime_async  (client->proxy_files, query,(const char **) mimes,  tracker_array_reply, callback_struct);
+
+}
+
+
+void
+tracker_search_metadata_by_text_and_mime_and_location_async (TrackerClient *client, const char *query, const char **mimes, const char *location, TrackerArrayReply callback, gpointer user_data)
+{
+	ArrayCallBackStruct *callback_struct;
+
+	callback_struct = g_new (ArrayCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	client->last_pending_call = org_freedesktop_Tracker_Files_search_by_text_and_mime_and_location_async (client->proxy_files, query, (const char **)mimes, location,  tracker_array_reply, callback_struct);
+
+}
+
+
+
+void
+tracker_search_metadata_by_text_and_location_async (TrackerClient *client, const char *query, const char *location, TrackerArrayReply callback, gpointer user_data)
+{
+	ArrayCallBackStruct *callback_struct;
+
+	callback_struct = g_new (ArrayCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	client->last_pending_call = org_freedesktop_Tracker_Files_search_by_text_and_location_async (client->proxy_files, query, location,  tracker_array_reply, callback_struct);
+
+}
+

Added: trunk/src/libtracker/tracker.h
==============================================================================
--- (empty file)
+++ trunk/src/libtracker/tracker.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,235 @@
+/* Tracker - indexer and metadata database engine
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef TRACKER_H
+#define TRACKER_H
+
+G_BEGIN_DECLS
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <dbus/dbus-glib.h>
+
+typedef void (*TrackerArrayReply) (char **result, GError *error, gpointer user_data);
+typedef void (*TrackerHashTableReply) (GHashTable *result, GError *error, gpointer user_data);
+typedef void (*TrackerGPtrArrayReply) (GPtrArray *result, GError *error, gpointer user_data);
+typedef void (*TrackerBooleanReply) (gboolean result, GError *error, gpointer user_data);
+typedef void (*TrackerStringReply) (char *result, GError *error, gpointer user_data);
+typedef void (*TrackerIntReply) (int result, GError *error, gpointer user_data);
+typedef void (*TrackerVoidReply) (GError *error, gpointer user_data);
+
+
+
+typedef enum {
+	METADATA_STRING_INDEXABLE,
+	METADATA_STRING,
+	METADATA_NUMERIC,
+	METADATA_DATE
+} MetadataTypes;
+
+typedef enum {
+	SERVICE_FILES,
+	SERVICE_FOLDERS,
+	SERVICE_DOCUMENTS,
+	SERVICE_IMAGES,
+	SERVICE_MUSIC,
+	SERVICE_VIDEOS,
+	SERVICE_TEXT_FILES,
+	SERVICE_DEVELOPMENT_FILES,
+	SERVICE_OTHER_FILES,
+	SERVICE_VFS_FILES,
+	SERVICE_VFS_FOLDERS,
+	SERVICE_VFS_DOCUMENTS,
+	SERVICE_VFS_IMAGES,
+	SERVICE_VFS_MUSIC,
+	SERVICE_VFS_VIDEOS,
+	SERVICE_VFS_TEXT_FILES,
+	SERVICE_VFS_DEVELOPMENT_FILES,
+	SERVICE_VFS_OTHER_FILES,
+	SERVICE_CONVERSATIONS,
+	SERVICE_PLAYLISTS,
+	SERVICE_APPLICATIONS,
+	SERVICE_CONTACTS,
+	SERVICE_EMAILS,
+	SERVICE_EMAILATTACHMENTS,
+	SERVICE_APPOINTMENTS,
+	SERVICE_TASKS,
+	SERVICE_BOOKMARKS,
+	SERVICE_WEBHISTORY,
+	SERVICE_PROJECTS
+} ServiceType;
+
+
+
+
+typedef struct {
+	char *		type;
+	gboolean	is_embedded;
+	gboolean	is_writeable;
+
+} MetaDataTypeDetails;
+
+
+typedef struct {
+	DBusGProxy	*proxy;
+	DBusGProxy	*proxy_metadata;
+	DBusGProxy	*proxy_keywords;
+	DBusGProxy	*proxy_search;
+	DBusGProxy	*proxy_files;
+	DBusGProxyCall	*last_pending_call;
+} TrackerClient;
+
+
+void	tracker_cancel_last_call (TrackerClient *client);
+
+/* you can make multiple connections with tracker_connect and free them with tracker_disconnect */
+TrackerClient * tracker_connect (gboolean enable_warnings);
+void		tracker_disconnect (TrackerClient *client);
+
+
+ServiceType	tracker_service_name_to_type (const char *service);
+char *		tracker_type_to_service_name (ServiceType s);
+
+
+
+/* synchronous calls */
+
+int		tracker_get_version				(TrackerClient *client, GError **error);
+char *		tracker_get_status				(TrackerClient *client, GError **error);
+GHashTable *	tracker_get_services				(TrackerClient *client, gboolean main_services_only, GError **error);
+GPtrArray *	tracker_get_stats				(TrackerClient *client, GError **error);
+
+void		tracker_set_bool_option				(TrackerClient *client, const char *option, gboolean value, GError **error);
+void		tracker_set_int_option				(TrackerClient *client, const char *option, int value, GError **error);
+void		tracker_shutdown				(TrackerClient *client, gboolean reindex, GError **error);
+void		tracker_prompt_index_signals			(TrackerClient *client, GError **error);
+
+char **			tracker_metadata_get				(TrackerClient *client, ServiceType service, const char *id, char **keys, GError **error);
+void			tracker_metadata_set				(TrackerClient *client, ServiceType service, const char *id, char **keys, char **values, GError **error);
+void			tracker_metadata_register_type			(TrackerClient *client, const char *name, MetadataTypes type, GError **error);
+MetaDataTypeDetails *	tracker_metadata_get_type_details		(TrackerClient *client, const char *name, GError **error);
+char **			tracker_metadata_get_registered_types		(TrackerClient *client, const char *classname, GError **error);
+char **			tracker_metadata_get_writeable_types		(TrackerClient *client, const char *classname, GError **error);
+char **			tracker_metadata_get_registered_classes		(TrackerClient *client, GError **error);
+GPtrArray *		tracker_metadata_get_unique_values		(TrackerClient *client, ServiceType service, char **meta_types, char *query, gboolean descending, int offset, int max_hits, GError **error);
+int		tracker_metadata_get_sum			(TrackerClient *client, ServiceType service, char *field, char *query, GError **error);
+int		tracker_metadata_get_count			(TrackerClient *client, ServiceType service, char *field, char *query, GError **error);
+GPtrArray *		tracker_metadata_get_unique_values_with_count	(TrackerClient *client, ServiceType service, char **meta_types, char *query, char *count, gboolean descending, int offset, int max_hits, GError **error);
+
+GPtrArray *	tracker_keywords_get_list			(TrackerClient *client, ServiceType service, GError **error);
+char **		tracker_keywords_get				(TrackerClient *client, ServiceType service, const char *id, GError **error);
+void		tracker_keywords_add				(TrackerClient *client, ServiceType service, const char *id, char **values, GError **error);
+void		tracker_keywords_remove				(TrackerClient *client, ServiceType service, const char *id, char **values, GError **error);
+void		tracker_keywords_remove_all			(TrackerClient *client, ServiceType service, const char *id, GError **error);
+char **		tracker_keywords_search				(TrackerClient *client, int live_query_id, ServiceType service, char **keywords, int offset, int max_hits, GError **error);
+
+
+int		tracker_search_get_hit_count			(TrackerClient *client, ServiceType service, const char *search_text, GError **error);
+GPtrArray *	tracker_search_get_hit_count_all		(TrackerClient *client, const char *search_text, GError **error);
+char **		tracker_search_text				(TrackerClient *client, int live_query_id, ServiceType service, const char *search_text, int offset, int max_hits, GError **error);
+GPtrArray *	tracker_search_text_detailed			(TrackerClient *client, int live_query_id, ServiceType service, const char *search_text, int offset, int max_hits, GError **error);
+char *		tracker_search_get_snippet			(TrackerClient *client, ServiceType service, const char *uri, const char *search_text, GError **error);
+char **		tracker_search_metadata				(TrackerClient *client, ServiceType service, const char *field, const char* search_text, int offset, int max_hits, GError **error);
+GPtrArray *	tracker_search_query				(TrackerClient *client, int live_query_id, ServiceType service, char **fields, const char *search_text, const char *keywords, const char *query, int offset, int max_hits, gboolean sort_by_service, char **sort_fields, gboolean sort_descending, GError **error);
+gchar *		tracker_search_suggest				(TrackerClient *client, const char *search_text, int maxdist, GError **error);
+
+
+void		tracker_files_create				(TrackerClient *client,  const char *uri, gboolean is_directory, const char *mime, int size, int mtime, GError **error);
+void		tracker_files_delete				(TrackerClient *client,  const char *uri, GError **error);
+char *		tracker_files_get_text_contents			(TrackerClient *client,  const char *uri, int offset, int max_length, GError **error);
+char *		tracker_files_search_text_contents		(TrackerClient *client,  const char *uri, const char *search_text, int length, GError **error);
+char **		tracker_files_get_by_service_type		(TrackerClient *client,  int live_query_id, ServiceType service, int offset, int max_hits, GError **error);
+char **		tracker_files_get_by_mime_type			(TrackerClient *client,  int live_query_id, char **mimes, int offset, int max_hits, GError **error);
+char **		tracker_files_get_by_mime_type_vfs		(TrackerClient *client,  int live_query_id, char **mimes, int offset, int max_hits, GError **error);
+
+int		tracker_files_get_mtime				(TrackerClient *client, const char *uri, GError **error);
+GPtrArray *	tracker_files_get_metadata_for_files_in_folder	(TrackerClient *client, int live_query_id, const char *uri, char **fields, GError **error);
+
+
+/* Deprecated calls - Following API specific for nautilus search use only */
+char **		tracker_search_metadata_by_text				(TrackerClient *client, const char *query, GError **error);
+char **		tracker_search_metadata_by_text_and_mime		(TrackerClient *client, const char *query, const char **mimes, GError **error);
+char **		tracker_search_metadata_by_text_and_mime_and_location	(TrackerClient *client, const char *query, const char **mimes, const char *location, GError **error);
+char **		tracker_search_metadata_by_text_and_location		(TrackerClient *client, const char *query, const char *location, GError **error);
+/* end deprecated call list */
+
+
+/* asynchronous calls */
+
+
+void		tracker_get_version_async				(TrackerClient *client,  TrackerIntReply callback, gpointer user_data);
+void		tracker_get_status_async				(TrackerClient *client,  TrackerStringReply callback, gpointer user_data);
+void		tracker_get_services_async				(TrackerClient *client,  gboolean main_services_only,  TrackerHashTableReply callback, gpointer user_data);
+void		tracker_get_stats_async					(TrackerClient *client,  TrackerGPtrArrayReply callback, gpointer user_data);
+
+void		tracker_set_bool_option_async				(TrackerClient *client, const char *option, gboolean value, TrackerVoidReply callback, gpointer user_data);
+void		tracker_set_int_option_async				(TrackerClient *client, const char *option, int value, TrackerVoidReply callback, gpointer user_data);
+void		tracker_shutdown_async					(TrackerClient *client, gboolean reindex, TrackerVoidReply callback, gpointer user_data);
+void		tracker_prompt_index_signals_async			(TrackerClient *client, TrackerVoidReply callback, gpointer user_data);
+
+void		tracker_metadata_get_async				(TrackerClient *client, ServiceType service, const char *id, char **keys, TrackerArrayReply callback, gpointer user_data);
+void		tracker_metadata_set_async				(TrackerClient *client, ServiceType service, const char *id, char **keys, char **values, TrackerVoidReply callback, gpointer user_data);
+void		tracker_metadata_register_type_async			(TrackerClient *client, const char *name, MetadataTypes type, TrackerVoidReply callback, gpointer user_data);
+void		tracker_metadata_get_registered_types_async		(TrackerClient *client, const char *classname, TrackerArrayReply callback, gpointer user_data);
+void		tracker_metadata_get_writeable_types_async		(TrackerClient *client, const char *classname, TrackerArrayReply callback, gpointer user_data);
+void		tracker_metadata_get_registered_classes_async		(TrackerClient *client, TrackerArrayReply callback, gpointer user_data);
+void		tracker_metadata_get_unique_values_async		(TrackerClient *client, ServiceType service, char **meta_types, const char *query, gboolean descending, int offset, int max_hits, TrackerGPtrArrayReply callback, gpointer user_data);
+void		tracker_metadata_get_sum_async				(TrackerClient *client, ServiceType service, char *field, char *query, TrackerIntReply callback, gpointer user_data);
+void		tracker_metadata_get_count_async			(TrackerClient *client, ServiceType service, char *field, char *query, TrackerIntReply callback, gpointer user_data);
+void		tracker_metadata_get_unique_values_with_count_async	(TrackerClient *client, ServiceType service, char **meta_types, const char *query, char *count, gboolean descending, int offset, int max_hits, TrackerGPtrArrayReply callback, gpointer user_data);
+
+void		tracker_keywords_get_list_async				(TrackerClient *client, ServiceType service, TrackerGPtrArrayReply callback, gpointer user_data);
+void		tracker_keywords_get_async				(TrackerClient *client, ServiceType service, const char *id, TrackerArrayReply callback, gpointer user_data);
+void		tracker_keywords_add_async				(TrackerClient *client, ServiceType service, const char *id, char **values, TrackerVoidReply callback, gpointer user_data);
+void		tracker_keywords_remove_async				(TrackerClient *client, ServiceType service, const char *id, char **values, TrackerVoidReply callback, gpointer user_data);
+void		tracker_keywords_remove_all_async			(TrackerClient *client, ServiceType service, const char *id, TrackerVoidReply callback, gpointer user_data);
+void		tracker_keywords_search_async				(TrackerClient *client, int live_query_id, ServiceType service, char **keywords, int offset, int max_hits, TrackerArrayReply callback, gpointer user_data);
+
+void		tracker_search_text_get_hit_count_async			(TrackerClient *client, ServiceType service, const char *search_text, TrackerIntReply callback, gpointer user_data);
+void		tracker_search_text_get_hit_count_all_async		(TrackerClient *client, const char *search_text, TrackerGPtrArrayReply callback, gpointer user_data);
+void		tracker_search_text_async				(TrackerClient *client, int live_query_id, ServiceType service, const char *search_text, int offset, int max_hits, TrackerArrayReply callback, gpointer user_data);
+void		tracker_search_text_detailed_async			(TrackerClient *client, int live_query_id, ServiceType service, const char *search_text, int offset, int max_hits, TrackerGPtrArrayReply callback, gpointer user_data);
+void		tracker_search_get_snippet_async			(TrackerClient *client, ServiceType service, const char *uri, const char *search_text, TrackerStringReply callback, gpointer user_data);
+void		tracker_search_metadata_async				(TrackerClient *client, ServiceType service, const char *field, const char* search_text, int offset, int max_hits, TrackerArrayReply callback, gpointer user_data);
+void		tracker_search_query_async				(TrackerClient *client, int live_query_id, ServiceType service, char **fields, const char *search_text, const char *keywords, const char *query, int offset, int max_hits, gboolean sort_by_service, char **sort_fields, gboolean sort_descending, TrackerGPtrArrayReply callback, gpointer user_data);
+void		tracker_search_suggest_async				(TrackerClient *client, const char *search_text, int maxdist, TrackerStringReply callback, gpointer user_data);
+
+void		tracker_files_create_async				(TrackerClient *client,  const char *uri, gboolean is_directory, const char *mime, int size, int mtime, TrackerVoidReply callback, gpointer user_data);
+void		tracker_files_delete_async				(TrackerClient *client,  const char *uri, TrackerVoidReply callback, gpointer user_data);
+void		tracker_files_get_text_contents_async			(TrackerClient *client,  const char *uri, int offset, int max_length, TrackerStringReply callback, gpointer user_data);
+void		tracker_files_search_text_contents_async		(TrackerClient *client,  const char *uri, const char *search_text, int length, TrackerStringReply callback, gpointer user_data);
+void		tracker_files_get_by_service_type_async			(TrackerClient *client,  int live_query_id, ServiceType service, int offset, int max_hits, TrackerArrayReply callback, gpointer user_data);
+void		tracker_files_get_by_mime_type_async			(TrackerClient *client,  int live_query_id, char **mimes, int offset, int max_hits, TrackerArrayReply callback, gpointer user_data);
+void		tracker_files_get_by_mime_type_vfs_async		(TrackerClient *client,  int live_query_id, char **mimes, int offset, int max_hits, TrackerArrayReply callback, gpointer user_data);
+
+void		tracker_files_get_mtime_async				(TrackerClient *client, const char *uri, TrackerIntReply callback, gpointer user_data);
+void		tracker_files_get_metadata_for_files_in_folder_async	(TrackerClient *client, int live_query_id, const char *uri, char **fields, TrackerGPtrArrayReply callback, gpointer user_data);
+
+
+
+
+/* Deprecated calls - API specific for nautilus search use only. New code should use tracker_search_metadata_matching_text_async instead */
+void tracker_search_metadata_by_text_async				(TrackerClient *client, const char *query, TrackerArrayReply callback, gpointer user_data);
+void tracker_search_metadata_by_text_and_mime_async			(TrackerClient *client, const char *query, const char **mimes, TrackerArrayReply callback, gpointer user_data);
+void tracker_search_metadata_by_text_and_mime_and_location_async	(TrackerClient *client, const char *query, const char **mimes, const char *location, TrackerArrayReply callback, gpointer user_data);
+void tracker_search_metadata_by_text_and_location_async			(TrackerClient *client, const char *query, const char *location, TrackerArrayReply callback, gpointer user_data);
+
+G_END_DECLS
+
+#endif /* TRACKER_H */

Added: trunk/src/qdbm/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/qdbm/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,22 @@
+include $(top_srcdir)/Makefile.decl
+
+INCLUDES = \
+	-D_XOPEN_SOURCE_EXTENDED=1 \
+	-D_GNU_SOURCE=1	\
+	-D__EXTENSIONS__=1 \
+	-D_HPUX_SOURCE=1 \
+	-D_POSIX_MAPPED_FILES=1	\
+	-D_POSIX_SYNCHRONIZED_IO=1 \
+	-DPIC=1 \
+	-D_THREAD_SAFE=1 \
+	-D_REENTRANT=1 \
+	-DNDEBUG \
+	-DMYNOMMAP 
+
+noinst_LTLIBRARIES = libqdbm-private.la
+
+libqdbm_private_la_SOURCES = \
+	depot.c \
+	depot.h \
+	myconf.c \
+	myconf.h

Added: trunk/src/qdbm/depot.c
==============================================================================
--- (empty file)
+++ trunk/src/qdbm/depot.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,2226 @@
+/*************************************************************************************************
+ * Implementation of Depot
+ *							Copyright (C) 2000-2006 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM is free software; you can redistribute it and/or modify it under the terms of the GNU
+ * Lesser General Public License as published by the Free Software Foundation; either version
+ * 2.1 of the License or any later version.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#define QDBM_INTERNAL  1
+
+#include "depot.h"
+#include "myconf.h"
+
+#define DP_FILEMODE    00644		 /* permission of a creating file */
+#define DP_MAGICNUMB   "[DEPOT]\n\f"	 /* magic number on environments of big endian */
+#define DP_MAGICNUML   "[depot]\n\f"	 /* magic number on environments of little endian */
+#define DP_HEADSIZ     48		 /* size of the reagion of the header */
+#define DP_LIBVEROFF   12		 /* offset of the region for the library version */
+#define DP_FLAGSOFF    16		 /* offset of the region for flags */
+#define DP_FSIZOFF     24		 /* offset of the region for the file size */
+#define DP_BNUMOFF     32		 /* offset of the region for the bucket number */
+#define DP_RNUMOFF     40		 /* offset of the region for the record number */
+#define DP_DEFBNUM     8191		 /* default bucket number */
+#define DP_FBPOOLSIZ   16		 /* size of free block pool */
+#define DP_ENTBUFSIZ   128		 /* size of the entity buffer */
+#define DP_STKBUFSIZ   256		 /* size of the stack key buffer */
+#define DP_WRTBUFSIZ   8192		 /* size of the writing buffer */
+#define DP_FSBLKSIZ    4096		 /* size of a block of the file system */
+#define DP_TMPFSUF     MYEXTSTR "dptmp"  /* suffix of a temporary file */
+#define DP_OPTBLOAD    0.25		 /* ratio of bucket loading at optimization */
+#define DP_OPTRUNIT    256		 /* number of records in a process of optimization */
+#define DP_NUMBUFSIZ   32		 /* size of a buffer for a number */
+#define DP_IOBUFSIZ    8192		 /* size of an I/O buffer */
+
+/* get the first hash value */
+#define DP_FIRSTHASH(DP_res, DP_kbuf, DP_ksiz) \
+  do { \
+    int _DP_i; \
+    if((DP_ksiz) == sizeof(int)){ \
+      memcpy(&(DP_res), (DP_kbuf), sizeof(int)); \
+    } else { \
+      (DP_res) = 751; \
+    } \
+    for(_DP_i = 0; _DP_i < (DP_ksiz); _DP_i++){ \
+      (DP_res) = (DP_res) * 31 + ((const unsigned char *)(DP_kbuf))[_DP_i]; \
+    } \
+    (DP_res) = ((DP_res) * 87767623) & 0x7FFFFFFF; \
+  } while(FALSE)
+
+/* get the second hash value */
+#define DP_SECONDHASH(DP_res, DP_kbuf, DP_ksiz) \
+  do { \
+    int _DP_i; \
+    (DP_res) = 19780211; \
+    for(_DP_i = (DP_ksiz) - 1; _DP_i >= 0; _DP_i--){ \
+    (DP_res) = (DP_res) * 37 + ((const unsigned char *)(DP_kbuf))[_DP_i]; \
+    } \
+    (DP_res) = ((DP_res) * 43321879) & 0x7FFFFFFF; \
+  } while(FALSE)
+
+/* get the third hash value */
+#define DP_THIRDHASH(DP_res, DP_kbuf, DP_ksiz) \
+  do { \
+    int _DP_i; \
+    (DP_res) = 774831917; \
+    for(_DP_i = (DP_ksiz) - 1; _DP_i >= 0; _DP_i--){ \
+      (DP_res) = (DP_res) * 29 + ((const unsigned char *)(DP_kbuf))[_DP_i]; \
+    } \
+    (DP_res) = ((DP_res) * 5157883) & 0x7FFFFFFF; \
+  } while(FALSE)
+
+enum {					 /* enumeration for a record header */
+  DP_RHIFLAGS,				 /* offset of flags */
+  DP_RHIHASH,				 /* offset of value of the second hash function */
+  DP_RHIKSIZ,				 /* offset of the size of the key */
+  DP_RHIVSIZ,				 /* offset of the size of the value */
+  DP_RHIPSIZ,				 /* offset of the size of the padding bytes */
+  DP_RHILEFT,				 /* offset of the offset of the left child */
+  DP_RHIRIGHT,				 /* offset of the offset of the right child */
+  DP_RHNUM				 /* number of elements of a header */
+};
+
+enum {					 /* enumeration for the flag of a record */
+  DP_RECFDEL = 1 << 0,			 /* deleted */
+  DP_RECFREUSE = 1 << 1			 /* reusable */
+};
+
+
+/* private function prototypes */
+static int dpbigendian(void);
+static char *dpstrdup(const char *str);
+static int dplock(int fd, int ex, int nb);
+static int dpwrite(int fd, const void *buf, int size);
+static int dpseekwrite(int fd, int off, const void *buf, int size);
+static int dpseekwritenum(int fd, int off, int num);
+static int dpread(int fd, void *buf, int size);
+static int dpseekread(int fd, int off, void *buf, int size);
+static int dpfcopy(int destfd, int destoff, int srcfd, int srcoff);
+static int dpgetprime(int num);
+static int dppadsize(DEPOT *depot, int ksiz, int vsiz);
+static int dprecsize(int *head);
+static int dprechead(DEPOT *depot, int off, int *head, char *ebuf, int *eep);
+static char *dpreckey(DEPOT *depot, int off, int *head);
+static char *dprecval(DEPOT *depot, int off, int *head, int start, int max);
+static int dprecvalwb(DEPOT *depot, int off, int *head, int start, int max, char *vbuf);
+static int dpkeycmp(const char *abuf, int asiz, const char *bbuf, int bsiz);
+static int dprecsearch(DEPOT *depot, const char *kbuf, int ksiz, int hash, int *bip, int *offp,
+		       int *entp, int *head, char *ebuf, int *eep, int delhit);
+static int dprecrewrite(DEPOT *depot, int off, int rsiz, const char *kbuf, int ksiz,
+			const char *vbuf, int vsiz, int hash, int left, int right);
+static int dprecappend(DEPOT *depot, const char *kbuf, int ksiz, const char *vbuf, int vsiz,
+		       int hash, int left, int right);
+static int dprecover(DEPOT *depot, int off, int *head, const char *vbuf, int vsiz, int cat);
+static int dprecdelete(DEPOT *depot, int off, int *head, int reusable);
+static void dpfbpoolcoal(DEPOT *depot);
+static int dpfbpoolcmp(const void *a, const void *b);
+
+
+
+/*************************************************************************************************
+ * public objects
+ *************************************************************************************************/
+
+
+/* String containing the version information. */
+const char *dpversion = _QDBM_VERSION;
+
+
+/* Get a message string corresponding to an error code. */
+const char *dperrmsg(int ecode){
+  switch(ecode){
+  case DP_ENOERR: return "no error";
+  case DP_EFATAL: return "with fatal error";
+  case DP_EMODE: return "invalid mode";
+  case DP_EBROKEN: return "broken database file";
+  case DP_EKEEP: return "existing record";
+  case DP_ENOITEM: return "no item found";
+  case DP_EALLOC: return "memory allocation error";
+  case DP_EMAP: return "memory mapping error";
+  case DP_EOPEN: return "open error";
+  case DP_ECLOSE: return "close error";
+  case DP_ETRUNC: return "trunc error";
+  case DP_ESYNC: return "sync error";
+  case DP_ESTAT: return "stat error";
+  case DP_ESEEK: return "seek error";
+  case DP_EREAD: return "read error";
+  case DP_EWRITE: return "write error";
+  case DP_ELOCK: return "lock error";
+  case DP_EUNLINK: return "unlink error";
+  case DP_EMKDIR: return "mkdir error";
+  case DP_ERMDIR: return "rmdir error";
+  case DP_EMISC: return "miscellaneous error";
+  }
+  return "(invalid ecode)";
+}
+
+
+/* Get a database handle. */
+DEPOT *dpopen(const char *name, int omode, int bnum){
+  char hbuf[DP_HEADSIZ], *map, c, *tname;
+  int i, mode, fd, inode, fsiz, rnum, msiz, *fbpool;
+  struct stat sbuf;
+  time_t mtime;
+  DEPOT *depot;
+  assert(name);
+  mode = O_RDONLY;
+  if(omode & DP_OWRITER){
+    mode = O_RDWR;
+    if(omode & DP_OCREAT) mode |= O_CREAT;
+  }
+
+#if defined(__linux__)
+
+	fd = open(name, mode|O_NOATIME, DP_FILEMODE);
+
+	if (fd == -1) {
+		fd = open(name, mode, DP_FILEMODE);
+	}
+#else
+	fd = open(name, mode, DP_FILEMODE);
+#endif
+
+  if(fd == -1){
+    dpecodeset(DP_EOPEN, __FILE__, __LINE__);
+    return NULL;
+  }
+  if(!(omode & DP_ONOLCK)){
+    if(!dplock(fd, omode & DP_OWRITER, omode & DP_OLCKNB)){
+      close(fd);
+      return NULL;
+    }
+  }
+  if((omode & DP_OWRITER) && (omode & DP_OTRUNC)){
+    if(ftruncate(fd, 0) == -1){
+      close(fd);
+      dpecodeset(DP_ETRUNC, __FILE__, __LINE__);
+      return NULL;
+    }
+  }
+  if(fstat(fd, &sbuf) == -1 || !S_ISREG(sbuf.st_mode) ||
+     (sbuf.st_ino == 0 && lstat(name, &sbuf) == -1)){
+    close(fd);
+    dpecodeset(DP_ESTAT, __FILE__, __LINE__);
+    return NULL;
+  }
+  inode = sbuf.st_ino;
+  mtime = sbuf.st_mtime;
+  fsiz = sbuf.st_size;
+  if((omode & DP_OWRITER) && fsiz == 0){
+    memset(hbuf, 0, DP_HEADSIZ);
+    if(dpbigendian()){
+      memcpy(hbuf, DP_MAGICNUMB, strlen(DP_MAGICNUMB));
+    } else {
+      memcpy(hbuf, DP_MAGICNUML, strlen(DP_MAGICNUML));
+    }
+    sprintf(hbuf + DP_LIBVEROFF, "%d", _QDBM_LIBVER / 100);
+    bnum = bnum < 1 ? DP_DEFBNUM : bnum;
+    bnum = dpgetprime(bnum);
+    memcpy(hbuf + DP_BNUMOFF, &bnum, sizeof(int));
+    rnum = 0;
+    memcpy(hbuf + DP_RNUMOFF, &rnum, sizeof(int));
+    fsiz = DP_HEADSIZ + bnum * sizeof(int);
+    memcpy(hbuf + DP_FSIZOFF, &fsiz, sizeof(int));
+    if(!dpseekwrite(fd, 0, hbuf, DP_HEADSIZ)){
+      close(fd);
+      return NULL;
+    }
+    if(omode & DP_OSPARSE){
+      c = 0;
+      if(!dpseekwrite(fd, fsiz - 1, &c, 1)){
+	close(fd);
+	return NULL;
+      }
+    } else {
+      if(!(map = malloc(bnum * sizeof(int)))){
+	close(fd);
+	dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+	return NULL;
+      }
+      memset(map, 0, bnum * sizeof(int));
+      if(!dpseekwrite(fd, DP_HEADSIZ, map, bnum * sizeof(int))){
+	free(map);
+	close(fd);
+	return NULL;
+      }
+      free(map);
+    }
+  }
+  if(!dpseekread(fd, 0, hbuf, DP_HEADSIZ)){
+    close(fd);
+    dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+    return NULL;
+  }
+  if(!(omode & DP_ONOLCK) &&
+     ((dpbigendian() ? memcmp(hbuf, DP_MAGICNUMB, strlen(DP_MAGICNUMB)) != 0 :
+       memcmp(hbuf, DP_MAGICNUML, strlen(DP_MAGICNUML)) != 0) ||
+      *((int *)(hbuf + DP_FSIZOFF)) != fsiz)){
+    close(fd);
+    dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+    return NULL;
+  }
+  bnum = *((int *)(hbuf + DP_BNUMOFF));
+  rnum = *((int *)(hbuf + DP_RNUMOFF));
+  if(bnum < 1 || rnum < 0 || fsiz < DP_HEADSIZ + bnum * sizeof(int)){
+    close(fd);
+    dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+    return NULL;
+  }
+  msiz = DP_HEADSIZ + bnum * sizeof(int);
+  map = mmap(0, msiz, PROT_READ | ((mode & DP_OWRITER) ? PROT_WRITE : 0), MAP_SHARED, fd, 0);
+  if(map == MAP_FAILED){
+    close(fd);
+    dpecodeset(DP_EMAP, __FILE__, __LINE__);
+    return NULL;
+  }
+  tname = NULL;
+  fbpool = NULL;
+  if(!(depot = malloc(sizeof(DEPOT))) || !(tname = dpstrdup(name)) ||
+     !(fbpool = malloc(DP_FBPOOLSIZ * 2 * sizeof(int)))){
+    free(fbpool);
+    free(tname);
+    free(depot);
+    munmap(map, msiz);
+    close(fd);
+    dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+    return NULL;
+  }
+  depot->name = tname;
+  depot->wmode = (mode & DP_OWRITER);
+  depot->inode = inode;
+  depot->mtime = mtime;
+  depot->fd = fd;
+  depot->fsiz = fsiz;
+  depot->map = map;
+  depot->msiz = msiz;
+  depot->buckets = (int *)(map + DP_HEADSIZ);
+  depot->bnum = bnum;
+  depot->rnum = rnum;
+  depot->fatal = FALSE;
+  depot->ioff = 0;
+  depot->fbpool = fbpool;
+  for(i = 0; i < DP_FBPOOLSIZ * 2; i += 2){
+    depot->fbpool[i] = -1;
+    depot->fbpool[i+1] = -1;
+  }
+  depot->fbpsiz = DP_FBPOOLSIZ * 2;
+  depot->fbpinc = 0;
+  depot->align = 0;
+  return depot;
+}
+
+
+/* Close a database handle. */
+int dpclose(DEPOT *depot){
+  int fatal, err;
+  assert(depot);
+  fatal = depot->fatal;
+  err = FALSE;
+  if(depot->wmode){
+    *((int *)(depot->map + DP_FSIZOFF)) = depot->fsiz;
+    *((int *)(depot->map + DP_RNUMOFF)) = depot->rnum;
+  }
+  if(depot->map != MAP_FAILED){
+    if(munmap(depot->map, depot->msiz) == -1){
+      err = TRUE;
+      dpecodeset(DP_EMAP, __FILE__, __LINE__);
+    }
+  }
+  if(close(depot->fd) == -1){
+    err = TRUE;
+    dpecodeset(DP_ECLOSE, __FILE__, __LINE__);
+  }
+  free(depot->fbpool);
+  free(depot->name);
+  free(depot);
+  if(fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return FALSE;
+  }
+  return err ? FALSE : TRUE;
+}
+
+
+/* Store a record. */
+int dpput(DEPOT *depot, const char *kbuf, int ksiz, const char *vbuf, int vsiz, int dmode){
+  int head[DP_RHNUM], next[DP_RHNUM];
+  int i, hash, bi, off, entoff, ee, newoff, rsiz, nsiz, fdel, mroff, mrsiz, mi, min;
+  char ebuf[DP_ENTBUFSIZ], *tval, *swap;
+  assert(depot && kbuf && vbuf);
+  if(depot->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!depot->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  if(vsiz < 0) vsiz = strlen(vbuf);
+  newoff = -1;
+  DP_SECONDHASH(hash, kbuf, ksiz);
+  switch(dprecsearch(depot, kbuf, ksiz, hash, &bi, &off, &entoff, head, ebuf, &ee, TRUE)){
+  case -1:
+    depot->fatal = TRUE;
+    return FALSE;
+  case 0:
+    fdel = head[DP_RHIFLAGS] & DP_RECFDEL;
+    if(dmode == DP_DKEEP && !fdel){
+      dpecodeset(DP_EKEEP, __FILE__, __LINE__);
+      return FALSE;
+    }
+    if(fdel){
+      head[DP_RHIPSIZ] += head[DP_RHIVSIZ];
+      head[DP_RHIVSIZ] = 0;
+    }
+    rsiz = dprecsize(head);
+    nsiz = DP_RHNUM * sizeof(int) + ksiz + vsiz;
+    if(dmode == DP_DCAT) nsiz += head[DP_RHIVSIZ];
+    if(off + rsiz >= depot->fsiz){
+      if(rsiz < nsiz){
+	head[DP_RHIPSIZ] += nsiz - rsiz;
+	rsiz = nsiz;
+	depot->fsiz = off + rsiz;
+      }
+    } else {
+      while(nsiz > rsiz && off + rsiz < depot->fsiz){
+	if(!dprechead(depot, off + rsiz, next, NULL, NULL)) return FALSE;
+	if(!(next[DP_RHIFLAGS] & DP_RECFREUSE)) break;
+	head[DP_RHIPSIZ] += dprecsize(next);
+	rsiz += dprecsize(next);
+      }
+      for(i = 0; i < depot->fbpsiz; i += 2){
+	if(depot->fbpool[i] >= off && depot->fbpool[i] < off + rsiz){
+	  depot->fbpool[i] = -1;
+	  depot->fbpool[i+1] = -1;
+	}
+      }
+    }
+    if(nsiz <= rsiz){
+      if(!dprecover(depot, off, head, vbuf, vsiz, dmode == DP_DCAT)){
+	depot->fatal = TRUE;
+	return FALSE;
+      }
+    } else {
+      tval = NULL;
+      if(dmode == DP_DCAT){
+	if(ee && DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ] + head[DP_RHIVSIZ] <= DP_ENTBUFSIZ){
+	  if(!(tval = malloc(head[DP_RHIVSIZ] + vsiz + 1))){
+	    dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+	    depot->fatal = TRUE;
+	    return FALSE;
+	  }
+	  memcpy(tval, ebuf + (DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ]), head[DP_RHIVSIZ]);
+	} else {
+	  if(!(tval = dprecval(depot, off, head, 0, -1))){
+	    depot->fatal = TRUE;
+	    return FALSE;
+	  }
+	  if(!(swap = realloc(tval, head[DP_RHIVSIZ] + vsiz + 1))){
+	    free(tval);
+	    dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+	    depot->fatal = TRUE;
+	    return FALSE;
+	  }
+	  tval = swap;
+	}
+	memcpy(tval + head[DP_RHIVSIZ], vbuf, vsiz);
+	vsiz += head[DP_RHIVSIZ];
+	vbuf = tval;
+      }
+      mi = -1;
+      min = -1;
+      for(i = 0; i < depot->fbpsiz; i += 2){
+	if(depot->fbpool[i+1] < nsiz) continue;
+	if(mi == -1 || depot->fbpool[i+1] < min){
+	  mi = i;
+	  min = depot->fbpool[i+1];
+	}
+      }
+      if(mi >= 0){
+	mroff = depot->fbpool[mi];
+	mrsiz = depot->fbpool[mi+1];
+	depot->fbpool[mi] = -1;
+	depot->fbpool[mi+1] = -1;
+      } else {
+	mroff = -1;
+	mrsiz = -1;
+      }
+      if(!dprecdelete(depot, off, head, TRUE)){
+	free(tval);
+	depot->fatal = TRUE;
+	return FALSE;
+      }
+      if(mroff > 0 && nsiz <= mrsiz){
+	if(!dprecrewrite(depot, mroff, mrsiz, kbuf, ksiz, vbuf, vsiz,
+			 hash, head[DP_RHILEFT], head[DP_RHIRIGHT])){
+	  free(tval);
+	  depot->fatal = TRUE;
+	  return FALSE;
+	}
+	newoff = mroff;
+      } else {
+	if((newoff = dprecappend(depot, kbuf, ksiz, vbuf, vsiz,
+				 hash, head[DP_RHILEFT], head[DP_RHIRIGHT])) == -1){
+	  free(tval);
+	  depot->fatal = TRUE;
+	  return FALSE;
+	}
+      }
+      free(tval);
+    }
+    if(fdel) depot->rnum++;
+    break;
+  default:
+    if((newoff = dprecappend(depot, kbuf, ksiz, vbuf, vsiz, hash, 0, 0)) == -1){
+      depot->fatal = TRUE;
+      return FALSE;
+    }
+    depot->rnum++;
+    break;
+  }
+  if(newoff > 0){
+    if(entoff > 0){
+      if(!dpseekwritenum(depot->fd, entoff, newoff)){
+	depot->fatal = TRUE;
+	return FALSE;
+      }
+    } else {
+      depot->buckets[bi] = newoff;
+    }
+  }
+  return TRUE;
+}
+
+
+/* Delete a record. */
+int dpout(DEPOT *depot, const char *kbuf, int ksiz){
+  int head[DP_RHNUM], hash, bi, off, entoff, ee;
+  char ebuf[DP_ENTBUFSIZ];
+  assert(depot && kbuf);
+  if(depot->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!depot->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  DP_SECONDHASH(hash, kbuf, ksiz);
+  switch(dprecsearch(depot, kbuf, ksiz, hash, &bi, &off, &entoff, head, ebuf, &ee, FALSE)){
+  case -1:
+    depot->fatal = TRUE;
+    return FALSE;
+  case 0:
+    break;
+  default:
+    dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!dprecdelete(depot, off, head, FALSE)){
+    depot->fatal = TRUE;
+    return FALSE;
+  }
+  depot->rnum--;
+  return TRUE;
+}
+
+
+/* Retrieve a record. */
+char *dpget(DEPOT *depot, const char *kbuf, int ksiz, int start, int max, int *sp){
+  int head[DP_RHNUM], hash, bi, off, entoff, ee, vsiz;
+  char ebuf[DP_ENTBUFSIZ], *vbuf;
+  assert(depot && kbuf && start >= 0);
+  if(depot->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return NULL;
+  }
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  DP_SECONDHASH(hash, kbuf, ksiz);
+  switch(dprecsearch(depot, kbuf, ksiz, hash, &bi, &off, &entoff, head, ebuf, &ee, FALSE)){
+  case -1:
+    depot->fatal = TRUE;
+    return NULL;
+  case 0:
+    break;
+  default:
+    dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+    return NULL;
+  }
+  if(start > head[DP_RHIVSIZ]){
+    dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+    return NULL;
+  }
+  if(ee && DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ] + head[DP_RHIVSIZ] <= DP_ENTBUFSIZ){
+    head[DP_RHIVSIZ] -= start;
+    if(max < 0){
+      vsiz = head[DP_RHIVSIZ];
+    } else {
+      vsiz = max < head[DP_RHIVSIZ] ? max : head[DP_RHIVSIZ];
+    }
+    if(!(vbuf = malloc(vsiz + 1))){
+      dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+      depot->fatal = TRUE;
+      return NULL;
+    }
+    memcpy(vbuf, ebuf + (DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ] + start), vsiz);
+    vbuf[vsiz] = '\0';
+  } else {
+    if(!(vbuf = dprecval(depot, off, head, start, max))){
+      depot->fatal = TRUE;
+      return NULL;
+    }
+  }
+  if(sp){
+    if(max < 0){
+      *sp = head[DP_RHIVSIZ];
+    } else {
+      *sp = max < head[DP_RHIVSIZ] ? max : head[DP_RHIVSIZ];
+    }
+  }
+  return vbuf;
+}
+
+
+/* Retrieve a record and write the value into a buffer. */
+int dpgetwb(DEPOT *depot, const char *kbuf, int ksiz, int start, int max, char *vbuf){
+  int head[DP_RHNUM], hash, bi, off, entoff, ee, vsiz;
+  char ebuf[DP_ENTBUFSIZ];
+  assert(depot && kbuf && start >= 0 && max >= 0 && vbuf);
+  if(depot->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return -1;
+  }
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  DP_SECONDHASH(hash, kbuf, ksiz);
+  switch(dprecsearch(depot, kbuf, ksiz, hash, &bi, &off, &entoff, head, ebuf, &ee, FALSE)){
+  case -1:
+    depot->fatal = TRUE;
+    return -1;
+  case 0:
+    break;
+  default:
+    dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+    return -1;
+  }
+  if(start > head[DP_RHIVSIZ]){
+    dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+    return -1;
+  }
+  if(ee && DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ] + head[DP_RHIVSIZ] <= DP_ENTBUFSIZ){
+    head[DP_RHIVSIZ] -= start;
+    vsiz = max < head[DP_RHIVSIZ] ? max : head[DP_RHIVSIZ];
+    memcpy(vbuf, ebuf + (DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ] + start), vsiz);
+  } else {
+    if((vsiz = dprecvalwb(depot, off, head, start, max, vbuf)) == -1){
+      depot->fatal = TRUE;
+      return -1;
+    }
+  }
+  return vsiz;
+}
+
+
+/* Get the size of the value of a record. */
+int dpvsiz(DEPOT *depot, const char *kbuf, int ksiz){
+  int head[DP_RHNUM], hash, bi, off, entoff, ee;
+  char ebuf[DP_ENTBUFSIZ];
+  assert(depot && kbuf);
+  if(depot->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return -1;
+  }
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  DP_SECONDHASH(hash, kbuf, ksiz);
+  switch(dprecsearch(depot, kbuf, ksiz, hash, &bi, &off, &entoff, head, ebuf, &ee, FALSE)){
+  case -1:
+    depot->fatal = TRUE;
+    return -1;
+  case 0:
+    break;
+  default:
+    dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+    return -1;
+  }
+  return head[DP_RHIVSIZ];
+}
+
+
+/* Initialize the iterator of a database handle. */
+int dpiterinit(DEPOT *depot){
+  assert(depot);
+  if(depot->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return FALSE;
+  }
+  depot->ioff = 0;
+  return TRUE;
+}
+
+
+/* Get the next key of the iterator. */
+char *dpiternext(DEPOT *depot, int *sp){
+  int off, head[DP_RHNUM], ee;
+  char ebuf[DP_ENTBUFSIZ], *kbuf;
+  assert(depot);
+  if(depot->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return NULL;
+  }
+  off = DP_HEADSIZ + depot->bnum * sizeof(int);
+  off = off > depot->ioff ? off : depot->ioff;
+  while(off < depot->fsiz){
+    if(!dprechead(depot, off, head, ebuf, &ee)){
+      depot->fatal = TRUE;
+      return NULL;
+    }
+    if(head[DP_RHIFLAGS] & DP_RECFDEL){
+      off += dprecsize(head);
+    } else {
+      if(ee && DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ] <= DP_ENTBUFSIZ){
+	if(!(kbuf = malloc(head[DP_RHIKSIZ] + 1))){
+	  dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+	  depot->fatal = TRUE;
+	  return NULL;
+	}
+	memcpy(kbuf, ebuf + (DP_RHNUM * sizeof(int)), head[DP_RHIKSIZ]);
+	kbuf[head[DP_RHIKSIZ]] = '\0';
+      } else {
+	if(!(kbuf = dpreckey(depot, off, head))){
+	  depot->fatal = TRUE;
+	  return NULL;
+	}
+      }
+      depot->ioff = off + dprecsize(head);
+      if(sp) *sp = head[DP_RHIKSIZ];
+      return kbuf;
+    }
+  }
+  dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+  return NULL;
+}
+
+
+/* Set alignment of a database handle. */
+int dpsetalign(DEPOT *depot, int align){
+  assert(depot);
+  if(depot->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!depot->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  depot->align = align;
+  return TRUE;
+}
+
+
+/* Set the size of the free block pool of a database handle. */
+int dpsetfbpsiz(DEPOT *depot, int size){
+  int *fbpool;
+  int i;
+  assert(depot && size >= 0);
+  if(depot->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!depot->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  size *= 2;
+  if(!(fbpool = realloc(depot->fbpool, size * sizeof(int) + 1))){
+    dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+    return FALSE;
+  }
+  for(i = 0; i < size; i += 2){
+    fbpool[i] = -1;
+    fbpool[i+1] = -1;
+  }
+  depot->fbpool = fbpool;
+  depot->fbpsiz = size;
+  return TRUE;
+}
+
+
+
+/* Synchronize contents of updating a database with the file and the device. */
+int dpsync(DEPOT *depot){
+  assert(depot);
+  if(depot->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!depot->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  *((int *)(depot->map + DP_FSIZOFF)) = depot->fsiz;
+  *((int *)(depot->map + DP_RNUMOFF)) = depot->rnum;
+  if(msync(depot->map, depot->msiz, MS_SYNC) == -1){
+    dpecodeset(DP_EMAP, __FILE__, __LINE__);
+    depot->fatal = TRUE;
+    return FALSE;
+  }
+  if(fdatasync(depot->fd) == -1){
+    dpecodeset(DP_ESYNC, __FILE__, __LINE__);
+    depot->fatal = TRUE;
+    return FALSE;
+  }
+  return TRUE;
+}
+
+
+/* Optimize a database. */
+int dpoptimize(DEPOT *depot, int bnum){
+  DEPOT *tdepot;
+  char *name;
+  int i, err, off, head[DP_RHNUM], ee, ksizs[DP_OPTRUNIT], vsizs[DP_OPTRUNIT], unum;
+  char ebuf[DP_ENTBUFSIZ], *kbufs[DP_OPTRUNIT], *vbufs[DP_OPTRUNIT];
+  assert(depot);
+  if(depot->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!depot->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!(name = malloc(strlen(depot->name) + strlen(DP_TMPFSUF) + 1))){
+    dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+    depot->fatal = FALSE;
+    return FALSE;
+  }
+  sprintf(name, "%s%s", depot->name, DP_TMPFSUF);
+  if(bnum < 0){
+    bnum = (int)(depot->rnum * (1.0 / DP_OPTBLOAD)) + 1;
+    if(bnum < DP_DEFBNUM / 2) bnum = DP_DEFBNUM / 2;
+  }
+  if(!(tdepot = dpopen(name, DP_OWRITER | DP_OCREAT | DP_OTRUNC, bnum))){
+    free(name);
+    depot->fatal = TRUE;
+    return FALSE;
+  }
+  free(name);
+  if(!dpsetflags(tdepot, dpgetflags(depot))){
+    dpclose(tdepot);
+    depot->fatal = TRUE;
+    return FALSE;
+  }
+  tdepot->align = depot->align;
+  err = FALSE;
+  off = DP_HEADSIZ + depot->bnum * sizeof(int);
+  unum = 0;
+  while(off < depot->fsiz){
+    if(!dprechead(depot, off, head, ebuf, &ee)){
+      err = TRUE;
+      break;
+    }
+    if(!(head[DP_RHIFLAGS] & DP_RECFDEL)){
+      if(ee && DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ] <= DP_ENTBUFSIZ){
+	if(!(kbufs[unum] = malloc(head[DP_RHIKSIZ] + 1))){
+	  dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+	  err = TRUE;
+	  break;
+	}
+	memcpy(kbufs[unum], ebuf + (DP_RHNUM * sizeof(int)), head[DP_RHIKSIZ]);
+	if(DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ] + head[DP_RHIVSIZ] <= DP_ENTBUFSIZ){
+	  if(!(vbufs[unum] = malloc(head[DP_RHIVSIZ] + 1))){
+	    dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+	    err = TRUE;
+	    break;
+	  }
+	  memcpy(vbufs[unum], ebuf + (DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ]),
+		 head[DP_RHIVSIZ]);
+	} else {
+	  vbufs[unum] = dprecval(depot, off, head, 0, -1);
+	}
+      } else {
+	kbufs[unum] = dpreckey(depot, off, head);
+	vbufs[unum] = dprecval(depot, off, head, 0, -1);
+      }
+      ksizs[unum] = head[DP_RHIKSIZ];
+      vsizs[unum] = head[DP_RHIVSIZ];
+      unum++;
+      if(unum >= DP_OPTRUNIT){
+	for(i = 0; i < unum; i++){
+	  if(kbufs[i] && vbufs[i]){
+	    if(!dpput(tdepot, kbufs[i], ksizs[i], vbufs[i], vsizs[i], DP_DKEEP)) err = TRUE;
+	  } else {
+	    err = TRUE;
+	  }
+	  free(kbufs[i]);
+	  free(vbufs[i]);
+	  if(err) break;
+	}
+	unum = 0;
+      }
+    }
+    off += dprecsize(head);
+    if(err) break;
+  }
+  for(i = 0; i < unum; i++){
+    if(kbufs[i] && vbufs[i]){
+      if(!dpput(tdepot, kbufs[i], ksizs[i], vbufs[i], vsizs[i], DP_DKEEP)) err = TRUE;
+    } else {
+      err = TRUE;
+    }
+    free(kbufs[i]);
+    free(vbufs[i]);
+    if(err) break;
+  }
+  if(!dpsync(tdepot)) err = TRUE;
+  if(err){
+    unlink(tdepot->name);
+    dpclose(tdepot);
+    depot->fatal = TRUE;
+    return FALSE;
+  }
+  if(munmap(depot->map, depot->msiz) == -1){
+    dpclose(tdepot);
+    dpecodeset(DP_EMAP, __FILE__, __LINE__);
+    depot->fatal = TRUE;
+    return FALSE;
+  }
+  depot->map = MAP_FAILED;
+  if(ftruncate(depot->fd, 0) == -1){
+    dpclose(tdepot);
+    unlink(tdepot->name);
+    dpecodeset(DP_ETRUNC, __FILE__, __LINE__);
+    depot->fatal = TRUE;
+    return FALSE;
+  }
+  if(dpfcopy(depot->fd, 0, tdepot->fd, 0) == -1){
+    dpclose(tdepot);
+    unlink(tdepot->name);
+    depot->fatal = TRUE;
+    return FALSE;
+  }
+  depot->fsiz = tdepot->fsiz;
+  depot->bnum = tdepot->bnum;
+  depot->ioff = 0;
+  for(i = 0; i < depot->fbpsiz; i += 2){
+    depot->fbpool[i] = -1;
+    depot->fbpool[i+1] = -1;
+  }
+  depot->msiz = tdepot->msiz;
+  depot->map = mmap(0, depot->msiz, PROT_READ | PROT_WRITE, MAP_SHARED, depot->fd, 0);
+  if(depot->map == MAP_FAILED){
+    dpecodeset(DP_EMAP, __FILE__, __LINE__);
+    depot->fatal = TRUE;
+    return FALSE;
+  }
+  depot->buckets = (int *)(depot->map + DP_HEADSIZ);
+  if(!(name = dpname(tdepot))){
+    dpclose(tdepot);
+    unlink(tdepot->name);
+    depot->fatal = TRUE;
+    return FALSE;
+  }
+  if(!dpclose(tdepot)){
+    free(name);
+    unlink(tdepot->name);
+    depot->fatal = TRUE;
+    return FALSE;
+  }
+  if(unlink(name) == -1){
+    free(name);
+    dpecodeset(DP_EUNLINK, __FILE__, __LINE__);
+    depot->fatal = TRUE;
+    return FALSE;
+  }
+  free(name);
+  return TRUE;
+}
+
+
+/* Get the name of a database. */
+char *dpname(DEPOT *depot){
+  char *name;
+  assert(depot);
+  if(depot->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return NULL;
+  }
+  if(!(name = dpstrdup(depot->name))){
+    dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+    depot->fatal = TRUE;
+    return NULL;
+  }
+  return name;
+}
+
+
+/* Get the size of a database file. */
+int dpfsiz(DEPOT *depot){
+  assert(depot);
+  if(depot->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return -1;
+  }
+  return depot->fsiz;
+}
+
+
+/* Get the number of the elements of the bucket array. */
+int dpbnum(DEPOT *depot){
+  assert(depot);
+  if(depot->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return -1;
+  }
+  return depot->bnum;
+}
+
+
+/* Get the number of the used elements of the bucket array. */
+int dpbusenum(DEPOT *depot){
+  int i, hits;
+  assert(depot);
+  if(depot->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return -1;
+  }
+  hits = 0;
+  for(i = 0; i < depot->bnum; i++){
+    if(depot->buckets[i]) hits++;
+  }
+  return hits;
+}
+
+
+/* Get the number of the records stored in a database. */
+int dprnum(DEPOT *depot){
+  assert(depot);
+  if(depot->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return -1;
+  }
+  return depot->rnum;
+}
+
+
+/* Check whether a database handle is a writer or not. */
+int dpwritable(DEPOT *depot){
+  assert(depot);
+  return depot->wmode;
+}
+
+
+/* Check whether a database has a fatal error or not. */
+int dpfatalerror(DEPOT *depot){
+  assert(depot);
+  return depot->fatal;
+}
+
+
+/* Get the inode number of a database file. */
+int dpinode(DEPOT *depot){
+  assert(depot);
+  return depot->inode;
+}
+
+
+/* Get the last modified time of a database. */
+time_t dpmtime(DEPOT *depot){
+  assert(depot);
+  return depot->mtime;
+}
+
+
+/* Get the file descriptor of a database file. */
+int dpfdesc(DEPOT *depot){
+  assert(depot);
+  return depot->fd;
+}
+
+
+/* Remove a database file. */
+int dpremove(const char *name){
+  struct stat sbuf;
+  DEPOT *depot;
+  assert(name);
+  if(lstat(name, &sbuf) == -1){
+    dpecodeset(DP_ESTAT, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if((depot = dpopen(name, DP_OWRITER | DP_OTRUNC, -1)) != NULL) dpclose(depot);
+  if(unlink(name) == -1){
+    dpecodeset(DP_EUNLINK, __FILE__, __LINE__);
+    return FALSE;
+  }
+  return TRUE;
+}
+
+
+/* Repair a broken database file. */
+int dprepair(const char *name){
+  DEPOT *tdepot;
+  char dbhead[DP_HEADSIZ], *tname, *kbuf, *vbuf;
+  int fd, fsiz, flags, bnum, tbnum, err, head[DP_RHNUM], off, rsiz, ksiz, vsiz;
+  struct stat sbuf;
+  assert(name);
+  if(lstat(name, &sbuf) == -1){
+    dpecodeset(DP_ESTAT, __FILE__, __LINE__);
+    return FALSE;
+  }
+  fsiz = sbuf.st_size;
+  if((fd = open(name, O_RDWR, DP_FILEMODE)) == -1){
+    dpecodeset(DP_EOPEN, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!dpseekread(fd, 0, dbhead, DP_HEADSIZ)){
+    close(fd);
+    return FALSE;
+  }
+  flags = *(int *)(dbhead + DP_FLAGSOFF);
+  bnum = *(int *)(dbhead + DP_BNUMOFF);
+  tbnum = *(int *)(dbhead + DP_RNUMOFF) * 2;
+  if(tbnum < DP_DEFBNUM) tbnum = DP_DEFBNUM;
+  if(!(tname = malloc(strlen(name) + strlen(DP_TMPFSUF) + 1))){
+    dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+    return FALSE;
+  }
+  sprintf(tname, "%s%s", name, DP_TMPFSUF);
+  if(!(tdepot = dpopen(tname, DP_OWRITER | DP_OCREAT | DP_OTRUNC, tbnum))){
+    free(tname);
+    close(fd);
+    return FALSE;
+  }
+  err = FALSE;
+  off = DP_HEADSIZ + bnum * sizeof(int);
+  while(off < fsiz){
+    if(!dpseekread(fd, off, head, DP_RHNUM * sizeof(int))) break;
+    if(head[DP_RHIFLAGS] & DP_RECFDEL){
+      if((rsiz = dprecsize(head)) < 0) break;
+      off += rsiz;
+      continue;
+    }
+    ksiz = head[DP_RHIKSIZ];
+    vsiz = head[DP_RHIVSIZ];
+    if(ksiz >= 0 && vsiz >= 0){
+      kbuf = malloc(ksiz + 1);
+      vbuf = malloc(vsiz + 1);
+      if(kbuf && vbuf){
+	if(dpseekread(fd, off + DP_RHNUM * sizeof(int), kbuf, ksiz) &&
+	   dpseekread(fd, off + DP_RHNUM * sizeof(int) + ksiz, vbuf, vsiz)){
+	  if(!dpput(tdepot, kbuf, ksiz, vbuf, vsiz, DP_DKEEP)) err = TRUE;
+	} else {
+	  err = TRUE;
+	}
+      } else {
+	if(!err) dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+	err = TRUE;
+      }
+      free(vbuf);
+      free(kbuf);
+    } else {
+      if(!err) dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+      err = TRUE;
+    }
+    if((rsiz = dprecsize(head)) < 0) break;
+    off += rsiz;
+  }
+  if(!dpsetflags(tdepot, flags)) err = TRUE;
+  if(!dpsync(tdepot)) err = TRUE;
+  if(ftruncate(fd, 0) == -1){
+    if(!err) dpecodeset(DP_ETRUNC, __FILE__, __LINE__);
+    err = TRUE;
+  }
+  if(dpfcopy(fd, 0, tdepot->fd, 0) == -1) err = TRUE;
+  if(!dpclose(tdepot)) err = TRUE;
+  if(close(fd) == -1){
+    if(!err) dpecodeset(DP_ECLOSE, __FILE__, __LINE__);
+    err = TRUE;
+  }
+  if(unlink(tname) == -1){
+    if(!err) dpecodeset(DP_EUNLINK, __FILE__, __LINE__);
+    err = TRUE;
+  }
+  free(tname);
+  return err ? FALSE : TRUE;
+}
+
+
+/* Dump all records as endian independent data. */
+int dpexportdb(DEPOT *depot, const char *name){
+  char *kbuf, *vbuf, *pbuf;
+  int fd, err, ksiz, vsiz, psiz;
+  assert(depot && name);
+  if(!(dpiterinit(depot))) return FALSE;
+  if((fd = open(name, O_RDWR | O_CREAT | O_TRUNC, DP_FILEMODE)) == -1){
+    dpecodeset(DP_EOPEN, __FILE__, __LINE__);
+    return FALSE;
+  }
+  err = FALSE;
+  while(!err && (kbuf = dpiternext(depot, &ksiz)) != NULL){
+    if((vbuf = dpget(depot, kbuf, ksiz, 0, -1, &vsiz)) != NULL){
+      if((pbuf = malloc(ksiz + vsiz + DP_NUMBUFSIZ * 2)) != NULL){
+	psiz = 0;
+	psiz += sprintf(pbuf + psiz, "%X\n%X\n", ksiz, vsiz);
+	memcpy(pbuf + psiz, kbuf, ksiz);
+	psiz += ksiz;
+	pbuf[psiz++] = '\n';
+	memcpy(pbuf + psiz, vbuf, vsiz);
+	psiz += vsiz;
+	pbuf[psiz++] = '\n';
+	if(!dpwrite(fd, pbuf, psiz)){
+	  dpecodeset(DP_EWRITE, __FILE__, __LINE__);
+	  err = TRUE;
+	}
+	free(pbuf);
+      } else {
+	dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+	err = TRUE;
+      }
+      free(vbuf);
+    } else {
+      err = TRUE;
+    }
+    free(kbuf);
+  }
+  if(close(fd) == -1){
+    if(!err) dpecodeset(DP_ECLOSE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  return !err && !dpfatalerror(depot);
+}
+
+
+/* Load all records from endian independent data. */
+int dpimportdb(DEPOT *depot, const char *name){
+  char mbuf[DP_IOBUFSIZ], *rbuf;
+  int i, j, fd, err, fsiz, off, msiz, ksiz, vsiz, hlen;
+  struct stat sbuf;
+  assert(depot && name);
+  if(!depot->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(dprnum(depot) > 0){
+    dpecodeset(DP_EMISC, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if((fd = open(name, O_RDONLY, DP_FILEMODE)) == -1){
+    dpecodeset(DP_EOPEN, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(fstat(fd, &sbuf) == -1 || !S_ISREG(sbuf.st_mode)){
+    dpecodeset(DP_ESTAT, __FILE__, __LINE__);
+    close(fd);
+    return FALSE;
+  }
+  err = FALSE;
+  fsiz = sbuf.st_size;
+  off = 0;
+  while(!err && off < fsiz){
+    msiz = fsiz - off;
+    if(msiz > DP_IOBUFSIZ) msiz = DP_IOBUFSIZ;
+    if(!dpseekread(fd, off, mbuf, msiz)){
+      err = TRUE;
+      break;
+    }
+    hlen = 0;
+    ksiz = -1;
+    vsiz = -1;
+    for(i = 0; i < msiz; i++){
+      if(mbuf[i] == '\n'){
+	mbuf[i] = '\0';
+	ksiz = strtol(mbuf, NULL, 16);
+	for(j = i + 1; j < msiz; j++){
+	  if(mbuf[j] == '\n'){
+	    mbuf[j] = '\0';
+	    vsiz = strtol(mbuf + i + 1, NULL, 16);
+	    hlen = j + 1;
+	    break;
+	  }
+	}
+	break;
+      }
+    }
+    if(ksiz < 0 || vsiz < 0 || hlen < 4){
+      dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+      err = TRUE;
+      break;
+    }
+    if(hlen + ksiz + vsiz + 2 < DP_IOBUFSIZ){
+      if(!dpput(depot, mbuf + hlen, ksiz, mbuf + hlen + ksiz + 1, vsiz, DP_DKEEP)) err = TRUE;
+    } else {
+      if((rbuf = malloc(ksiz + vsiz + 2)) != NULL){
+	if(dpseekread(fd, off + hlen, rbuf, ksiz + vsiz + 2)){
+	  if(!dpput(depot, rbuf, ksiz, rbuf + ksiz + 1, vsiz, DP_DKEEP)) err = TRUE;
+	} else {
+	  err = TRUE;
+	}
+	free(rbuf);
+      } else {
+	dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+	err = TRUE;
+      }
+    }
+    off += hlen + ksiz + vsiz + 2;
+  }
+  if(close(fd) == -1){
+    if(!err) dpecodeset(DP_ECLOSE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  return !err && !dpfatalerror(depot);
+}
+
+
+/* Retrieve a record directly from a database file. */
+char *dpsnaffle(const char *name, const char* kbuf, int ksiz, int *sp){
+  char hbuf[DP_HEADSIZ], *map, *vbuf, *tkbuf;
+  int fd, fsiz, bnum, msiz, *buckets, hash, thash, head[DP_RHNUM], err, off, vsiz, tksiz, kcmp;
+  struct stat sbuf;
+  assert(name && kbuf);
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  if((fd = open(name, O_RDONLY, DP_FILEMODE)) == -1){
+    dpecodeset(DP_EOPEN, __FILE__, __LINE__);
+    return NULL;
+  }
+  if(fstat(fd, &sbuf) == -1 || !S_ISREG(sbuf.st_mode)){
+    close(fd);
+    dpecodeset(DP_ESTAT, __FILE__, __LINE__);
+    return NULL;
+  }
+  fsiz = sbuf.st_size;
+  if(!dpseekread(fd, 0, hbuf, DP_HEADSIZ)){
+    close(fd);
+    dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+    return NULL;
+  }
+  if(dpbigendian() ? memcmp(hbuf, DP_MAGICNUMB, strlen(DP_MAGICNUMB)) != 0 :
+     memcmp(hbuf, DP_MAGICNUML, strlen(DP_MAGICNUML)) != 0){
+    close(fd);
+    dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+    return NULL;
+  }
+  bnum = *((int *)(hbuf + DP_BNUMOFF));
+  if(bnum < 1 || fsiz < DP_HEADSIZ + bnum * sizeof(int)){
+    close(fd);
+    dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+    return NULL;
+  }
+  msiz = DP_HEADSIZ + bnum * sizeof(int);
+  map = mmap(0, msiz, PROT_READ, MAP_SHARED, fd, 0);
+  if(map == MAP_FAILED){
+    close(fd);
+    dpecodeset(DP_EMAP, __FILE__, __LINE__);
+    return NULL;
+  }
+  buckets = (int *)(map + DP_HEADSIZ);
+  err = FALSE;
+  vbuf = NULL;
+  vsiz = 0;
+  DP_SECONDHASH(hash, kbuf, ksiz);
+  DP_FIRSTHASH(thash, kbuf, ksiz);
+  off = buckets[thash%bnum];
+  while(off != 0){
+    if(!dpseekread(fd, off, head, DP_RHNUM * sizeof(int))){
+      err = TRUE;
+      break;
+    }
+    if(head[DP_RHIKSIZ] < 0 || head[DP_RHIVSIZ] < 0 || head[DP_RHIPSIZ] < 0 ||
+       head[DP_RHILEFT] < 0 || head[DP_RHIRIGHT] < 0){
+      dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+      err = TRUE;
+      break;
+    }
+    thash = head[DP_RHIHASH];
+    if(hash > thash){
+      off = head[DP_RHILEFT];
+    } else if(hash < thash){
+      off = head[DP_RHIRIGHT];
+    } else {
+      tksiz = head[DP_RHIKSIZ];
+      if(!(tkbuf = malloc(tksiz + 1))){
+	dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+	err = TRUE;
+	break;
+      }
+      if(!dpseekread(fd, off + DP_RHNUM * sizeof(int), tkbuf, tksiz)){
+	free(tkbuf);
+	err = TRUE;
+	break;
+      }
+      tkbuf[tksiz] = '\0';
+      kcmp = dpkeycmp(kbuf, ksiz, tkbuf, tksiz);
+      free(tkbuf);
+      if(kcmp > 0){
+	off = head[DP_RHILEFT];
+      } else if(kcmp < 0){
+	off = head[DP_RHIRIGHT];
+      } else if(head[DP_RHIFLAGS] & DP_RECFDEL){
+	break;
+      } else {
+	vsiz = head[DP_RHIVSIZ];
+	if(!(vbuf = malloc(vsiz + 1))){
+	  dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+	  err = TRUE;
+	  break;
+	}
+	if(!dpseekread(fd, off + DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ], vbuf, vsiz)){
+	  free(vbuf);
+	  vbuf = NULL;
+	  err = TRUE;
+	  break;
+	}
+	vbuf[vsiz] = '\0';
+	break;
+      }
+    }
+  }
+  if(vbuf){
+    if(sp) *sp = vsiz;
+  } else if(!err){
+    dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+  }
+  munmap(map, msiz);
+  close(fd);
+  return vbuf;
+}
+
+
+/* Hash function used inside Depot. */
+int dpinnerhash(const char *kbuf, int ksiz){
+  int res;
+  assert(kbuf);
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  DP_FIRSTHASH(res, kbuf, ksiz);
+  return res;
+}
+
+
+/* Hash function which is independent from the hash functions used inside Depot. */
+int dpouterhash(const char *kbuf, int ksiz){
+  int res;
+  assert(kbuf);
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  DP_THIRDHASH(res, kbuf, ksiz);
+  return res;
+}
+
+
+/* Get a natural prime number not less than a number. */
+int dpprimenum(int num){
+  assert(num > 0);
+  return dpgetprime(num);
+}
+
+
+
+/*************************************************************************************************
+ * features for experts
+ *************************************************************************************************/
+
+
+/* Name of the operating system. */
+const char *dpsysname = _QDBM_SYSNAME;
+
+
+/* File descriptor for debugging output. */
+int dpdbgfd = -1;
+
+
+/* Whether this build is reentrant. */
+const int dpisreentrant = _qdbm_ptsafe;
+
+
+/* Set the last happened error code. */
+void dpecodeset(int ecode, const char *file, int line){
+  char iobuf[DP_IOBUFSIZ];
+  assert(ecode >= DP_ENOERR && file && line >= 0);
+  dpecode = ecode;
+  if(dpdbgfd >= 0){
+    fflush(stdout);
+    fflush(stderr);
+    sprintf(iobuf, "* dpecodeset: %s:%d: [%d] %s\n", file, line, ecode, dperrmsg(ecode));
+    dpwrite(dpdbgfd, iobuf, strlen(iobuf));
+  }
+}
+
+
+/* Get the pointer of the variable of the last happened error code. */
+int *dpecodeptr(void){
+  static int defdpecode = DP_ENOERR;
+  void *ptr;
+  if(_qdbm_ptsafe){
+    if(!(ptr = _qdbm_settsd(&defdpecode, sizeof(int), &defdpecode))){
+      defdpecode = DP_EMISC;
+      return &defdpecode;
+    }
+    return (int *)ptr;
+  }
+  return &defdpecode;
+}
+
+
+/* Synchronize updating contents on memory. */
+int dpmemsync(DEPOT *depot){
+  assert(depot);
+  if(depot->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!depot->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  *((int *)(depot->map + DP_FSIZOFF)) = depot->fsiz;
+  *((int *)(depot->map + DP_RNUMOFF)) = depot->rnum;
+  if(msync(depot->map, depot->msiz, MS_SYNC) == -1){
+    dpecodeset(DP_EMAP, __FILE__, __LINE__);
+    depot->fatal = TRUE;
+    return FALSE;
+  }
+  return TRUE;
+}
+
+
+/* Synchronize updating contents on memory, not physically. */
+int dpmemflush(DEPOT *depot){
+  assert(depot);
+  if(depot->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!depot->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  *((int *)(depot->map + DP_FSIZOFF)) = depot->fsiz;
+  *((int *)(depot->map + DP_RNUMOFF)) = depot->rnum;
+  if(mflush(depot->map, depot->msiz, MS_SYNC) == -1){
+    dpecodeset(DP_EMAP, __FILE__, __LINE__);
+    depot->fatal = TRUE;
+    return FALSE;
+  }
+  return TRUE;
+}
+
+
+/* Get flags of a database. */
+int dpgetflags(DEPOT *depot){
+  int flags;
+  assert(depot);
+  memcpy(&flags, depot->map + DP_FLAGSOFF, sizeof(int));
+  return flags;
+}
+
+
+/* Set flags of a database. */
+int dpsetflags(DEPOT *depot, int flags){
+  assert(depot);
+  if(!depot->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  memcpy(depot->map + DP_FLAGSOFF, &flags, sizeof(int));
+  return TRUE;
+}
+
+
+
+/*************************************************************************************************
+ * private objects
+ *************************************************************************************************/
+
+
+/* Check whether the byte order of the platform is big endian or not.
+   The return value is true if bigendian, else, it is false. */
+static int dpbigendian(void){
+  char buf[sizeof(int)];
+  *(int *)buf = 1;
+  return !buf[0];
+}
+
+
+/* Get a copied string.
+   `str' specifies an original string.
+   The return value is a copied string whose region is allocated by `malloc'. */
+static char *dpstrdup(const char *str){
+  int len;
+  char *buf;
+  assert(str);
+  len = strlen(str);
+  if(!(buf = malloc(len + 1))) return NULL;
+  memcpy(buf, str, len + 1);
+  return buf;
+}
+
+
+/* Lock a file descriptor.
+   `fd' specifies a file descriptor.
+   `ex' specifies whether an exclusive lock or a shared lock is performed.
+   `nb' specifies whether to request with non-blocking.
+   The return value is true if successful, else, it is false. */
+static int dplock(int fd, int ex, int nb){
+  struct flock lock;
+  assert(fd >= 0);
+  memset(&lock, 0, sizeof(struct flock));
+  lock.l_type = ex ? F_WRLCK : F_RDLCK;
+  lock.l_whence = SEEK_SET;
+  lock.l_start = 0;
+  lock.l_len = 0;
+  lock.l_pid = 0;
+  while(fcntl(fd, nb ? F_SETLK : F_SETLKW, &lock) == -1){
+    if(errno != EINTR){
+      dpecodeset(DP_ELOCK, __FILE__, __LINE__);
+      return FALSE;
+    }
+  }
+  return TRUE;
+}
+
+
+/* Write into a file.
+   `fd' specifies a file descriptor.
+   `buf' specifies a buffer to write.
+   `size' specifies the size of the buffer.
+   The return value is the size of the written buffer, or, -1 on failure. */
+static int dpwrite(int fd, const void *buf, int size){
+  const char *lbuf;
+  int rv, wb;
+  assert(fd >= 0 && buf && size >= 0);
+  lbuf = buf;
+  rv = 0;
+  do {
+    wb = write(fd, lbuf, size);
+    switch(wb){
+    case -1: if(errno != EINTR) return -1;
+    case 0: break;
+    default:
+      lbuf += wb;
+      size -= wb;
+      rv += wb;
+      break;
+    }
+  } while(size > 0);
+  return rv;
+}
+
+
+/* Write into a file at an offset.
+   `fd' specifies a file descriptor.
+   `off' specifies an offset of the file.
+   `buf' specifies a buffer to write.
+   `size' specifies the size of the buffer.
+   The return value is true if successful, else, it is false. */
+static int dpseekwrite(int fd, int off, const void *buf, int size){
+  assert(fd >= 0 && buf && size >= 0);
+  if(size < 1) return TRUE;
+  if(off < 0){
+    if(lseek(fd, 0, SEEK_END) == -1){
+      dpecodeset(DP_ESEEK, __FILE__, __LINE__);
+      return FALSE;
+    }
+  } else {
+    if(lseek(fd, off, SEEK_SET) != off){
+      dpecodeset(DP_ESEEK, __FILE__, __LINE__);
+      return FALSE;
+    }
+  }
+  if(dpwrite(fd, buf, size) != size){
+    dpecodeset(DP_EWRITE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  return TRUE;
+}
+
+
+/* Write an integer into a file at an offset.
+   `fd' specifies a file descriptor.
+   `off' specifies an offset of the file.
+   `num' specifies an integer.
+   The return value is true if successful, else, it is false. */
+static int dpseekwritenum(int fd, int off, int num){
+  assert(fd >= 0);
+  return dpseekwrite(fd, off, &num, sizeof(int));
+}
+
+
+/* Read from a file and store the data into a buffer.
+   `fd' specifies a file descriptor.
+   `buffer' specifies a buffer to store into.
+   `size' specifies the size to read with.
+   The return value is the size read with, or, -1 on failure. */
+static int dpread(int fd, void *buf, int size){
+  char *lbuf;
+  int i, bs;
+  assert(fd >= 0 && buf && size >= 0);
+  lbuf = buf;
+  for(i = 0; i < size && (bs = read(fd, lbuf + i, size - i)) != 0; i += bs){
+    if(bs == -1 && errno != EINTR) return -1;
+  }
+  return i;
+}
+
+
+/* Read from a file at an offset and store the data into a buffer.
+   `fd' specifies a file descriptor.
+   `off' specifies an offset of the file.
+   `buffer' specifies a buffer to store into.
+   `size' specifies the size to read with.
+   The return value is true if successful, else, it is false. */
+static int dpseekread(int fd, int off, void *buf, int size){
+  char *lbuf;
+  assert(fd >= 0 && off >= 0 && buf && size >= 0);
+  lbuf = (char *)buf;
+  if(lseek(fd, off, SEEK_SET) != off){
+    dpecodeset(DP_ESEEK, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(dpread(fd, lbuf, size) != size){
+    dpecodeset(DP_EREAD, __FILE__, __LINE__);
+    return FALSE;
+  }
+  return TRUE;
+}
+
+
+/* Copy data between files.
+   `destfd' specifies a file descriptor of a destination file.
+   `destoff' specifies an offset of the destination file.
+   `srcfd' specifies a file descriptor of a source file.
+   `srcoff' specifies an offset of the source file.
+   The return value is the size copied with, or, -1 on failure. */
+static int dpfcopy(int destfd, int destoff, int srcfd, int srcoff){
+  char iobuf[DP_IOBUFSIZ];
+  int sum, iosiz;
+  if(lseek(srcfd, srcoff, SEEK_SET) == -1 || lseek(destfd, destoff, SEEK_SET) == -1){
+    dpecodeset(DP_ESEEK, __FILE__, __LINE__);
+    return -1;
+  }
+  sum = 0;
+  while((iosiz = dpread(srcfd, iobuf, DP_IOBUFSIZ)) > 0){
+    if(dpwrite(destfd, iobuf, iosiz) == -1){
+      dpecodeset(DP_EWRITE, __FILE__, __LINE__);
+      return -1;
+    }
+    sum += iosiz;
+  }
+  if(iosiz < 0){
+    dpecodeset(DP_EREAD, __FILE__, __LINE__);
+    return -1;
+  }
+  return sum;
+}
+
+
+/* Get a natural prime number not less than a number.
+   `num' specified a natural number.
+   The return value is a prime number not less than the specified number. */
+static int dpgetprime(int num){
+  int primes[] = {
+    1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 43, 47, 53, 59, 61, 71, 79, 83,
+    89, 103, 109, 113, 127, 139, 157, 173, 191, 199, 223, 239, 251, 283, 317, 349,
+    383, 409, 443, 479, 509, 571, 631, 701, 761, 829, 887, 953, 1021, 1151, 1279,
+    1399, 1531, 1663, 1789, 1913, 2039, 2297, 2557, 2803, 3067, 3323, 3583, 3833,
+    4093, 4603, 5119, 5623, 6143, 6653, 7159, 7673, 8191, 9209, 10223, 11261,
+    12281, 13309, 14327, 15359, 16381, 18427, 20479, 22511, 24571, 26597, 28669,
+    30713, 32749, 36857, 40949, 45053, 49139, 53239, 57331, 61417, 65521, 73727,
+    81919, 90107, 98299, 106487, 114679, 122869, 131071, 147451, 163819, 180221,
+    196597, 212987, 229373, 245759, 262139, 294911, 327673, 360439, 393209, 425977,
+    458747, 491503, 524287, 589811, 655357, 720887, 786431, 851957, 917503, 982981,
+    1048573, 1179641, 1310719, 1441771, 1572853, 1703903, 1835003, 1966079,
+    2097143, 2359267, 2621431, 2883577, 3145721, 3407857, 3670013, 3932153,
+    4194301, 4718579, 5242877, 5767129, 6291449, 6815741, 7340009, 7864301,
+    8388593, 9437179, 10485751, 11534329, 12582893, 13631477, 14680063, 15728611,
+    16777213, 18874367, 20971507, 23068667, 25165813, 27262931, 29360087, 31457269,
+    33554393, 37748717, 41943023, 46137319, 50331599, 54525917, 58720253, 62914549,
+    67108859, 75497467, 83886053, 92274671, 100663291, 109051903, 117440509,
+    125829103, 134217689, 150994939, 167772107, 184549373, 201326557, 218103799,
+    234881011, 251658227, 268435399, 301989881, 335544301, 369098707, 402653171,
+    436207613, 469762043, 503316469, 536870909, 603979769, 671088637, 738197503,
+    805306357, 872415211, 939524087, 1006632947, 1073741789, 1207959503,
+    1342177237, 1476394991, 1610612711, 1744830457, 1879048183, 2013265907, -1
+  };
+  int i;
+  assert(num > 0);
+  for(i = 0; primes[i] > 0; i++){
+    if(num <= primes[i]) return primes[i];
+  }
+  return primes[i-1];
+}
+
+
+/* Get the padding size of a record.
+   `vsiz' specifies the size of the value of a record.
+   The return value is the padding size of a record. */
+static int dppadsize(DEPOT *depot, int ksiz, int vsiz){
+  int pad;
+  assert(depot && vsiz >= 0);
+  if(depot->align > 0){
+    return depot->align - (depot->fsiz + DP_RHNUM * sizeof(int) + ksiz + vsiz) % depot->align;
+  } else if(depot->align < 0){
+    pad = (int)(vsiz * (2.0 / (1 << -(depot->align))));
+    if(vsiz + pad >= DP_FSBLKSIZ){
+      if(vsiz <= DP_FSBLKSIZ) pad = 0;
+      if(depot->fsiz % DP_FSBLKSIZ == 0){
+	return (pad / DP_FSBLKSIZ) * DP_FSBLKSIZ + DP_FSBLKSIZ -
+	  (depot->fsiz + DP_RHNUM * sizeof(int) + ksiz + vsiz) % DP_FSBLKSIZ;
+      } else {
+	return (pad / (DP_FSBLKSIZ / 2)) * (DP_FSBLKSIZ / 2) + (DP_FSBLKSIZ / 2) -
+	  (depot->fsiz + DP_RHNUM * sizeof(int) + ksiz + vsiz) % (DP_FSBLKSIZ / 2);
+      }
+    } else {
+      return pad >= DP_RHNUM * sizeof(int) ? pad : DP_RHNUM * sizeof(int);
+    }
+  }
+  return 0;
+}
+
+
+/* Get the size of a record in a database file.
+   `head' specifies the header of  a record.
+   The return value is the size of a record in a database file. */
+static int dprecsize(int *head){
+  assert(head);
+  return DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ] + head[DP_RHIVSIZ] + head[DP_RHIPSIZ];
+}
+
+
+/* Read the header of a record.
+   `depot' specifies a database handle.
+   `off' specifies an offset of the database file.
+   `head' specifies a buffer for the header.
+   `ebuf' specifies the pointer to the entity buffer.
+   `eep' specifies the pointer to a variable to which whether ebuf was used is assigned.
+   The return value is true if successful, else, it is false. */
+static int dprechead(DEPOT *depot, int off, int *head, char *ebuf, int *eep){
+  assert(depot && off >= 0 && head);
+  if(off > depot->fsiz){
+    dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(ebuf){
+    *eep = FALSE;
+    if(off < depot->fsiz - DP_ENTBUFSIZ){
+      *eep = TRUE;
+      if(!dpseekread(depot->fd, off, ebuf, DP_ENTBUFSIZ)) return FALSE;
+      memcpy(head, ebuf, DP_RHNUM * sizeof(int));
+      if(head[DP_RHIKSIZ] < 0 || head[DP_RHIVSIZ] < 0 || head[DP_RHIPSIZ] < 0 ||
+	 head[DP_RHILEFT] < 0 || head[DP_RHIRIGHT] < 0){
+	dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+	return FALSE;
+      }
+      return TRUE;
+    }
+  }
+  if(!dpseekread(depot->fd, off, head, DP_RHNUM * sizeof(int))) return FALSE;
+  if(head[DP_RHIKSIZ] < 0 || head[DP_RHIVSIZ] < 0 || head[DP_RHIPSIZ] < 0 ||
+     head[DP_RHILEFT] < 0 || head[DP_RHIRIGHT] < 0){
+    dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+    return FALSE;
+  }
+  return TRUE;
+}
+
+
+/* Read the entitiy of the key of a record.
+   `depot' specifies a database handle.
+   `off' specifies an offset of the database file.
+   `head' specifies the header of a record.
+   The return value is a key data whose region is allocated by `malloc', or NULL on failure. */
+static char *dpreckey(DEPOT *depot, int off, int *head){
+  char *kbuf;
+  int ksiz;
+  assert(depot && off >= 0);
+  ksiz = head[DP_RHIKSIZ];
+  if(!(kbuf = malloc(ksiz + 1))){
+    dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+    return NULL;
+  }
+  if(!dpseekread(depot->fd, off + DP_RHNUM * sizeof(int), kbuf, ksiz)){
+    free(kbuf);
+    return NULL;
+  }
+  kbuf[ksiz] = '\0';
+  return kbuf;
+}
+
+
+/* Read the entitiy of the value of a record.
+   `depot' specifies a database handle.
+   `off' specifies an offset of the database file.
+   `head' specifies the header of a record.
+   `start' specifies the offset address of the beginning of the region of the value to be read.
+   `max' specifies the max size to be read.  If it is negative, the size to read is unlimited.
+   The return value is a value data whose region is allocated by `malloc', or NULL on failure. */
+static char *dprecval(DEPOT *depot, int off, int *head, int start, int max){
+  char *vbuf;
+  int vsiz;
+  assert(depot && off >= 0 && start >= 0);
+  head[DP_RHIVSIZ] -= start;
+  if(max < 0){
+    vsiz = head[DP_RHIVSIZ];
+  } else {
+    vsiz = max < head[DP_RHIVSIZ] ? max : head[DP_RHIVSIZ];
+  }
+  if(!(vbuf = malloc(vsiz + 1))){
+    dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+    return NULL;
+  }
+  if(!dpseekread(depot->fd, off + DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ] + start, vbuf, vsiz)){
+    free(vbuf);
+    return NULL;
+  }
+  vbuf[vsiz] = '\0';
+  return vbuf;
+}
+
+
+/* Read the entitiy of the value of a record and write it into a given buffer.
+   `depot' specifies a database handle.
+   `off' specifies an offset of the database file.
+   `head' specifies the header of a record.
+   `start' specifies the offset address of the beginning of the region of the value to be read.
+   `max' specifies the max size to be read.  It shuld be less than the size of the writing buffer.
+   If successful, the return value is the size of the written data, else, it is -1. */
+static int dprecvalwb(DEPOT *depot, int off, int *head, int start, int max, char *vbuf){
+  int vsiz;
+  assert(depot && off >= 0 && start >= 0 && max >= 0 && vbuf);
+  head[DP_RHIVSIZ] -= start;
+  vsiz = max < head[DP_RHIVSIZ] ? max : head[DP_RHIVSIZ];
+  if(!dpseekread(depot->fd, off + DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ] + start, vbuf, vsiz))
+    return -1;
+  return vsiz;
+}
+
+
+/* Compare two keys.
+   `abuf' specifies the pointer to the region of the former.
+   `asiz' specifies the size of the region.
+   `bbuf' specifies the pointer to the region of the latter.
+   `bsiz' specifies the size of the region.
+   The return value is 0 if two equals, positive if the formar is big, else, negative. */
+static int dpkeycmp(const char *abuf, int asiz, const char *bbuf, int bsiz){
+  assert(abuf && asiz >= 0 && bbuf && bsiz >= 0);
+  if(asiz > bsiz) return 1;
+  if(asiz < bsiz) return -1;
+  return memcmp(abuf, bbuf, asiz);
+}
+
+
+/* Search for a record.
+   `depot' specifies a database handle.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region.
+   `hash' specifies the second hash value of the key.
+   `bip' specifies the pointer to the region to assign the index of the corresponding record.
+   `offp' specifies the pointer to the region to assign the last visited node in the hash chain,
+   or, -1 if the hash chain is empty.
+   `entp' specifies the offset of the last used joint, or, -1 if the hash chain is empty.
+   `head' specifies the pointer to the region to store the header of the last visited record in.
+   `ebuf' specifies the pointer to the entity buffer.
+   `eep' specifies the pointer to a variable to which whether ebuf was used is assigned.
+   `delhit' specifies whether a deleted record corresponds or not.
+   The return value is 0 if successful, 1 if there is no corresponding record, -1 on error. */
+static int dprecsearch(DEPOT *depot, const char *kbuf, int ksiz, int hash, int *bip, int *offp,
+		       int *entp, int *head, char *ebuf, int *eep, int delhit){
+  int off, entoff, thash, kcmp;
+  char stkey[DP_STKBUFSIZ], *tkey;
+  assert(depot && kbuf && ksiz >= 0 && hash >= 0 && bip && offp && entp && head && ebuf && eep);
+  DP_FIRSTHASH(thash, kbuf, ksiz);
+  *bip = thash % depot->bnum;
+  off = depot->buckets[*bip];
+  *offp = -1;
+  *entp = -1;
+  entoff = -1;
+  *eep = FALSE;
+  while(off != 0){
+    if(!dprechead(depot, off, head, ebuf, eep)) return -1;
+    thash = head[DP_RHIHASH];
+    if(hash > thash){
+      entoff = off + DP_RHILEFT * sizeof(int);
+      off = head[DP_RHILEFT];
+    } else if(hash < thash){
+      entoff = off + DP_RHIRIGHT * sizeof(int);
+      off = head[DP_RHIRIGHT];
+    } else {
+      if(*eep && DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ] <= DP_ENTBUFSIZ){
+	kcmp = dpkeycmp(kbuf, ksiz, ebuf + (DP_RHNUM * sizeof(int)), head[DP_RHIKSIZ]);
+      } else if(head[DP_RHIKSIZ] > DP_STKBUFSIZ){
+	if(!(tkey = dpreckey(depot, off, head))) return -1;
+	kcmp = dpkeycmp(kbuf, ksiz, tkey, head[DP_RHIKSIZ]);
+	free(tkey);
+      } else {
+	if(!dpseekread(depot->fd, off + DP_RHNUM * sizeof(int), stkey, head[DP_RHIKSIZ]))
+	  return -1;
+	kcmp = dpkeycmp(kbuf, ksiz, stkey, head[DP_RHIKSIZ]);
+      }
+      if(kcmp > 0){
+	entoff = off + DP_RHILEFT * sizeof(int);
+	off = head[DP_RHILEFT];
+      } else if(kcmp < 0){
+	entoff = off + DP_RHIRIGHT * sizeof(int);
+	off = head[DP_RHIRIGHT];
+      } else {
+	if(!delhit && (head[DP_RHIFLAGS] & DP_RECFDEL)){
+	  entoff = off + DP_RHILEFT * sizeof(int);
+	  off = head[DP_RHILEFT];
+	} else {
+	  *offp = off;
+	  *entp = entoff;
+	  return 0;
+	}
+      }
+    }
+  }
+  *offp = off;
+  *entp = entoff;
+  return 1;
+}
+
+
+/* Overwrite a record.
+   `depot' specifies a database handle.
+   `off' specifies the offset of the database file.
+   `rsiz' specifies the size of the existing record.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region.
+   `vbuf' specifies the pointer to the region of a value.
+   `vsiz' specifies the size of the region.
+   `hash' specifies the second hash value of the key.
+   `left' specifies the offset of the left child.
+   `right' specifies the offset of the right child.
+   The return value is true if successful, or, false on failure. */
+static int dprecrewrite(DEPOT *depot, int off, int rsiz, const char *kbuf, int ksiz,
+			const char *vbuf, int vsiz, int hash, int left, int right){
+  char ebuf[DP_WRTBUFSIZ];
+  int i, head[DP_RHNUM], asiz, hoff, koff, voff, mi, min, size;
+  assert(depot && off >= 1 && rsiz > 0 && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+  head[DP_RHIFLAGS] = 0;
+  head[DP_RHIHASH] = hash;
+  head[DP_RHIKSIZ] = ksiz;
+  head[DP_RHIVSIZ] = vsiz;
+  head[DP_RHIPSIZ] = rsiz - sizeof(head) - ksiz - vsiz;
+  head[DP_RHILEFT] = left;
+  head[DP_RHIRIGHT] = right;
+  asiz = sizeof(head) + ksiz + vsiz;
+  if(depot->fbpsiz > DP_FBPOOLSIZ * 4 && head[DP_RHIPSIZ] > asiz){
+    rsiz = (head[DP_RHIPSIZ] - asiz) / 2 + asiz;
+    head[DP_RHIPSIZ] -= rsiz;
+  } else {
+    rsiz = 0;
+  }
+  if(asiz <= DP_WRTBUFSIZ){
+    memcpy(ebuf, head, sizeof(head));
+    memcpy(ebuf + sizeof(head), kbuf, ksiz);
+    memcpy(ebuf + sizeof(head) + ksiz, vbuf, vsiz);
+    if(!dpseekwrite(depot->fd, off, ebuf, asiz)) return FALSE;
+  } else {
+    hoff = off;
+    koff = hoff + sizeof(head);
+    voff = koff + ksiz;
+    if(!dpseekwrite(depot->fd, hoff, head, sizeof(head)) ||
+       !dpseekwrite(depot->fd, koff, kbuf, ksiz) || !dpseekwrite(depot->fd, voff, vbuf, vsiz))
+      return FALSE;
+  }
+  if(rsiz > 0){
+    off += sizeof(head) + ksiz + vsiz + head[DP_RHIPSIZ];
+    head[DP_RHIFLAGS] = DP_RECFDEL | DP_RECFREUSE;
+    head[DP_RHIHASH] = hash;
+    head[DP_RHIKSIZ] = ksiz;
+    head[DP_RHIVSIZ] = vsiz;
+    head[DP_RHIPSIZ] = rsiz - sizeof(head) - ksiz - vsiz;
+    head[DP_RHILEFT] = 0;
+    head[DP_RHIRIGHT] = 0;
+    if(!dpseekwrite(depot->fd, off, head, sizeof(head))) return FALSE;
+    size = dprecsize(head);
+    mi = -1;
+    min = -1;
+    for(i = 0; i < depot->fbpsiz; i += 2){
+      if(depot->fbpool[i] == -1){
+	depot->fbpool[i] = off;
+	depot->fbpool[i+1] = size;
+	dpfbpoolcoal(depot);
+	mi = -1;
+	break;
+      }
+      if(mi == -1 || depot->fbpool[i+1] < min){
+	mi = i;
+	min = depot->fbpool[i+1];
+      }
+    }
+    if(mi >= 0 && size > min){
+      depot->fbpool[mi] = off;
+      depot->fbpool[mi+1] = size;
+      dpfbpoolcoal(depot);
+    }
+  }
+  return TRUE;
+}
+
+
+/* Write a record at the end of a database file.
+   `depot' specifies a database handle.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region.
+   `vbuf' specifies the pointer to the region of a value.
+   `vsiz' specifies the size of the region.
+   `hash' specifies the second hash value of the key.
+   `left' specifies the offset of the left child.
+   `right' specifies the offset of the right child.
+   The return value is the offset of the record, or, -1 on failure. */
+static int dprecappend(DEPOT *depot, const char *kbuf, int ksiz, const char *vbuf, int vsiz,
+		       int hash, int left, int right){
+  char ebuf[DP_WRTBUFSIZ], *hbuf;
+  int head[DP_RHNUM], asiz, psiz, off;
+  assert(depot && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+  psiz = dppadsize(depot, ksiz, vsiz);
+  head[DP_RHIFLAGS] = 0;
+  head[DP_RHIHASH] = hash;
+  head[DP_RHIKSIZ] = ksiz;
+  head[DP_RHIVSIZ] = vsiz;
+  head[DP_RHIPSIZ] = psiz;
+  head[DP_RHILEFT] = left;
+  head[DP_RHIRIGHT] = right;
+  asiz = sizeof(head) + ksiz + vsiz + psiz;
+  off = depot->fsiz;
+  if(asiz <= DP_WRTBUFSIZ){
+    memcpy(ebuf, head, sizeof(head));
+    memcpy(ebuf + sizeof(head), kbuf, ksiz);
+    memcpy(ebuf + sizeof(head) + ksiz, vbuf, vsiz);
+    memset(ebuf + sizeof(head) + ksiz + vsiz, 0, psiz);
+    if(!dpseekwrite(depot->fd, off, ebuf, asiz)) return -1;
+  } else {
+    if(!(hbuf = malloc(asiz))){
+      dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+      return -1;
+    }
+    memcpy(hbuf, head, sizeof(head));
+    memcpy(hbuf + sizeof(head), kbuf, ksiz);
+    memcpy(hbuf + sizeof(head) + ksiz, vbuf, vsiz);
+    memset(hbuf + sizeof(head) + ksiz + vsiz, 0, psiz);
+    if(!dpseekwrite(depot->fd, off, hbuf, asiz)){
+      free(hbuf);
+      return -1;
+    }
+    free(hbuf);
+  }
+  depot->fsiz += asiz;
+  return off;
+}
+
+
+/* Overwrite the value of a record.
+   `depot' specifies a database handle.
+   `off' specifies the offset of the database file.
+   `head' specifies the header of the record.
+   `vbuf' specifies the pointer to the region of a value.
+   `vsiz' specifies the size of the region.
+   `cat' specifies whether it is concatenate mode or not.
+   The return value is true if successful, or, false on failure. */
+static int dprecover(DEPOT *depot, int off, int *head, const char *vbuf, int vsiz, int cat){
+  int i, hsiz, hoff, voff;
+  assert(depot && off >= 0 && head && vbuf && vsiz >= 0);
+  for(i = 0; i < depot->fbpsiz; i += 2){
+    if(depot->fbpool[i] == off){
+      depot->fbpool[i] = -1;
+      depot->fbpool[i+1] = -1;
+      break;
+    }
+  }
+  if(cat){
+    head[DP_RHIFLAGS] = 0;
+    head[DP_RHIPSIZ] -= vsiz;
+    head[DP_RHIVSIZ] += vsiz;
+    hsiz = DP_RHNUM * sizeof(int);
+    hoff = off;
+    voff = hoff + DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ] + head[DP_RHIVSIZ] - vsiz;
+  } else {
+    head[DP_RHIFLAGS] = 0;
+    head[DP_RHIPSIZ] += head[DP_RHIVSIZ] - vsiz;
+    head[DP_RHIVSIZ] = vsiz;
+    hsiz = DP_RHNUM * sizeof(int);
+    hoff = off;
+    voff = hoff + DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ];
+  }
+  if(!dpseekwrite(depot->fd, hoff, head, hsiz) ||
+     !dpseekwrite(depot->fd, voff, vbuf, vsiz)) return FALSE;
+  return TRUE;
+}
+
+
+/* Delete a record.
+   `depot' specifies a database handle.
+   `off' specifies the offset of the database file.
+   `head' specifies the header of the record.
+   `reusable' specifies whether the region is reusable or not.
+   The return value is true if successful, or, false on failure. */
+static int dprecdelete(DEPOT *depot, int off, int *head, int reusable){
+  int i, mi, min, size;
+  assert(depot && off >= 0 && head);
+  if(reusable){
+    size = dprecsize(head);
+    mi = -1;
+    min = -1;
+    for(i = 0; i < depot->fbpsiz; i += 2){
+      if(depot->fbpool[i] == -1){
+	depot->fbpool[i] = off;
+	depot->fbpool[i+1] = size;
+	dpfbpoolcoal(depot);
+	mi = -1;
+	break;
+      }
+      if(mi == -1 || depot->fbpool[i+1] < min){
+	mi = i;
+	min = depot->fbpool[i+1];
+      }
+    }
+    if(mi >= 0 && size > min){
+      depot->fbpool[mi] = off;
+      depot->fbpool[mi+1] = size;
+      dpfbpoolcoal(depot);
+    }
+  }
+  return dpseekwritenum(depot->fd, off + DP_RHIFLAGS * sizeof(int),
+			DP_RECFDEL | (reusable ? DP_RECFREUSE : 0));
+}
+
+
+/* Make contiguous records of the free block pool coalesce.
+   `depot' specifies a database handle. */
+static void dpfbpoolcoal(DEPOT *depot){
+  int i;
+  assert(depot);
+  if(depot->fbpinc++ <= depot->fbpsiz / 4) return;
+  depot->fbpinc = 0;
+  qsort(depot->fbpool, depot->fbpsiz / 2, sizeof(int) * 2, dpfbpoolcmp);
+  for(i = 2; i < depot->fbpsiz; i += 2){
+    if(depot->fbpool[i-2] > 0 &&
+       depot->fbpool[i-2] + depot->fbpool[i-1] - depot->fbpool[i] == 0){
+      depot->fbpool[i] = depot->fbpool[i-2];
+      depot->fbpool[i+1] += depot->fbpool[i-1];
+      depot->fbpool[i-2] = -1;
+      depot->fbpool[i-1] = -1;
+    }
+  }
+}
+
+
+/* Compare two records of the free block pool.
+   `a' specifies the pointer to one record.
+   `b' specifies the pointer to the other record.
+   The return value is 0 if two equals, positive if the formar is big, else, negative. */
+static int dpfbpoolcmp(const void *a, const void *b){
+  assert(a && b);
+  return *(int *)a - *(int *)b;
+}
+
+
+
+/* END OF FILE */

Added: trunk/src/qdbm/depot.h
==============================================================================
--- (empty file)
+++ trunk/src/qdbm/depot.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,492 @@
+/*************************************************************************************************
+ * The basic API of QDBM
+ *							Copyright (C) 2000-2006 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM is free software; you can redistribute it and/or modify it under the terms of the GNU
+ * Lesser General Public License as published by the Free Software Foundation; either version
+ * 2.1 of the License or any later version.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#ifndef _DEPOT_H			 /* duplication check */
+#define _DEPOT_H
+
+#if defined(__cplusplus)		 /* export for C++ */
+extern "C" {
+#endif
+
+
+#include <stdlib.h>
+#include <time.h>
+
+
+#if defined(_MSC_VER) && !defined(QDBM_INTERNAL) && !defined(QDBM_STATIC)
+#define MYEXTERN extern __declspec(dllimport)
+#else
+#define MYEXTERN extern
+#endif
+
+
+
+/*************************************************************************************************
+ * API
+ *************************************************************************************************/
+
+
+typedef struct {			 /* type of structure for a database handle */
+  char *name;				 /* name of the database file */
+  int wmode;				 /* whether to be writable */
+  int inode;				 /* inode of the database file */
+  time_t mtime;				 /* last modified time of the database */
+  int fd;				 /* file descriptor of the database file */
+  int fsiz;				 /* size of the database file */
+  char *map;				 /* pointer to the mapped memory */
+  int msiz;				 /* size of the mapped memory */
+  int *buckets;				 /* pointer to the bucket array */
+  int bnum;				 /* number of the bucket array */
+  int rnum;				 /* number of records */
+  int fatal;				 /* whether a fatal error occured */
+  int ioff;				 /* offset of the iterator */
+  int *fbpool;				 /* free block pool */
+  int fbpsiz;				 /* size of the free block pool */
+  int fbpinc;				 /* incrementor of update of the free block pool */
+  int align;				 /* basic size of alignment */
+} DEPOT;
+
+enum {					 /* enumeration for error codes */
+  DP_ENOERR,				 /* no error */
+  DP_EFATAL,				 /* with fatal error */
+  DP_EMODE,				 /* invalid mode */
+  DP_EBROKEN,				 /* broken database file */
+  DP_EKEEP,				 /* existing record */
+  DP_ENOITEM,				 /* no item found */
+  DP_EALLOC,				 /* memory allocation error */
+  DP_EMAP,				 /* memory mapping error */
+  DP_EOPEN,				 /* open error */
+  DP_ECLOSE,				 /* close error */
+  DP_ETRUNC,				 /* trunc error */
+  DP_ESYNC,				 /* sync error */
+  DP_ESTAT,				 /* stat error */
+  DP_ESEEK,				 /* seek error */
+  DP_EREAD,				 /* read error */
+  DP_EWRITE,				 /* write error */
+  DP_ELOCK,				 /* lock error */
+  DP_EUNLINK,				 /* unlink error */
+  DP_EMKDIR,				 /* mkdir error */
+  DP_ERMDIR,				 /* rmdir error */
+  DP_EMISC				 /* miscellaneous error */
+};
+
+enum {					 /* enumeration for open modes */
+  DP_OREADER = 1 << 0,			 /* open as a reader */
+  DP_OWRITER = 1 << 1,			 /* open as a writer */
+  DP_OCREAT = 1 << 2,			 /* a writer creating */
+  DP_OTRUNC = 1 << 3,			 /* a writer truncating */
+  DP_ONOLCK = 1 << 4,			 /* open without locking */
+  DP_OLCKNB = 1 << 5,			 /* lock without blocking */
+  DP_OSPARSE = 1 << 6			 /* create as a sparse file */
+};
+
+enum {					 /* enumeration for write modes */
+  DP_DOVER,				 /* overwrite an existing value */
+  DP_DKEEP,				 /* keep an existing value */
+  DP_DCAT				 /* concatenate values */
+};
+
+
+/* String containing the version information. */
+MYEXTERN const char *dpversion;
+
+
+/* Last happened error code. */
+#define dpecode        (*dpecodeptr())
+
+
+/* Get a message string corresponding to an error code.
+   `ecode' specifies an error code.
+   The return value is the message string of the error code. The region of the return value
+   is not writable. */
+const char *dperrmsg(int ecode);
+
+
+/* Get a database handle.
+   `name' specifies the name of a database file.
+   `omode' specifies the connection mode: `DP_OWRITER' as a writer, `DP_OREADER' as a reader.
+   If the mode is `DP_OWRITER', the following may be added by bitwise or: `DP_OCREAT', which
+   means it creates a new database if not exist, `DP_OTRUNC', which means it creates a new
+   database regardless if one exists.  Both of `DP_OREADER' and `DP_OWRITER' can be added to by
+   bitwise or: `DP_ONOLCK', which means it opens a database file without file locking, or
+   `DP_OLCKNB', which means locking is performed without blocking.  `DP_OCREAT' can be added to
+   by bitwise or: `DP_OSPARSE', which means it creates a database file as a sparse file.
+   `bnum' specifies the number of elements of the bucket array.  If it is not more than 0,
+   the default value is specified.  The size of a bucket array is determined on creating,
+   and can not be changed except for by optimization of the database.  Suggested size of a
+   bucket array is about from 0.5 to 4 times of the number of all records to store.
+   The return value is the database handle or `NULL' if it is not successful.
+   While connecting as a writer, an exclusive lock is invoked to the database file.
+   While connecting as a reader, a shared lock is invoked to the database file.  The thread
+   blocks until the lock is achieved.  If `DP_ONOLCK' is used, the application is responsible
+   for exclusion control. */
+DEPOT *dpopen(const char *name, int omode, int bnum);
+
+
+/* Close a database handle.
+   `depot' specifies a database handle.
+   If successful, the return value is true, else, it is false.
+   Because the region of a closed handle is released, it becomes impossible to use the handle.
+   Updating a database is assured to be written when the handle is closed.  If a writer opens
+   a database but does not close it appropriately, the database will be broken. */
+int dpclose(DEPOT *depot);
+
+
+/* Store a record.
+   `depot' specifies a database handle connected as a writer.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.	If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   `vbuf' specifies the pointer to the region of a value.
+   `vsiz' specifies the size of the region of the value.  If it is negative, the size is
+   assigned with `strlen(vbuf)'.
+   `dmode' specifies behavior when the key overlaps, by the following values: `DP_DOVER',
+   which means the specified value overwrites the existing one, `DP_DKEEP', which means the
+   existing value is kept, `DP_DCAT', which means the specified value is concatenated at the
+   end of the existing value.
+   If successful, the return value is true, else, it is false. */
+int dpput(DEPOT *depot, const char *kbuf, int ksiz, const char *vbuf, int vsiz, int dmode);
+
+
+/* Delete a record.
+   `depot' specifies a database handle connected as a writer.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.	If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   If successful, the return value is true, else, it is false.	False is returned when no
+   record corresponds to the specified key. */
+int dpout(DEPOT *depot, const char *kbuf, int ksiz);
+
+
+/* Retrieve a record.
+   `depot' specifies a database handle.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.	If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   `start' specifies the offset address of the beginning of the region of the value to be read.
+   `max' specifies the max size to be read.  If it is negative, the size to read is unlimited.
+   `sp' specifies the pointer to a variable to which the size of the region of the return
+   value is assigned.  If it is `NULL', it is not used.
+   If successful, the return value is the pointer to the region of the value of the
+   corresponding record, else, it is `NULL'.  `NULL' is returned when no record corresponds to
+   the specified key or the size of the value of the corresponding record is less than `start'.
+   Because an additional zero code is appended at the end of the region of the return value,
+   the return value can be treated as a character string.  Because the region of the return
+   value is allocated with the `malloc' call, it should be released with the `free' call if it
+   is no longer in use. */
+char *dpget(DEPOT *depot, const char *kbuf, int ksiz, int start, int max, int *sp);
+
+
+/* Retrieve a record and write the value into a buffer.
+   `depot' specifies a database handle.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.	If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   `start' specifies the offset address of the beginning of the region of the value to be read.
+   `max' specifies the max size to be read.  It shuld be equal to or less than the size of the
+   writing buffer.
+   `vbuf' specifies the pointer to a buffer into which the value of the corresponding record is
+   written.
+   If successful, the return value is the size of the written data, else, it is -1.  -1 is
+   returned when no record corresponds to the specified key or the size of the value of the
+   corresponding record is less than `start'.
+   Note that no additional zero code is appended at the end of the region of the writing buffer. */
+int dpgetwb(DEPOT *depot, const char *kbuf, int ksiz, int start, int max, char *vbuf);
+
+
+/* Get the size of the value of a record.
+   `depot' specifies a database handle.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.	If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   If successful, the return value is the size of the value of the corresponding record, else,
+   it is -1.
+   Because this function does not read the entity of a record, it is faster than `dpget'. */
+int dpvsiz(DEPOT *depot, const char *kbuf, int ksiz);
+
+
+/* Initialize the iterator of a database handle.
+   `depot' specifies a database handle.
+   If successful, the return value is true, else, it is false.
+   The iterator is used in order to access the key of every record stored in a database. */
+int dpiterinit(DEPOT *depot);
+
+
+/* Get the next key of the iterator.
+   `depot' specifies a database handle.
+   `sp' specifies the pointer to a variable to which the size of the region of the return
+   value is assigned.  If it is `NULL', it is not used.
+   If successful, the return value is the pointer to the region of the next key, else, it is
+   `NULL'.  `NULL' is returned when no record is to be get out of the iterator.
+   Because an additional zero code is appended at the end of the region of the return value,
+   the return value can be treated as a character string.  Because the region of the return
+   value is allocated with the `malloc' call, it should be released with the `free' call if
+   it is no longer in use.  It is possible to access every record by iteration of calling
+   this function.  However, it is not assured if updating the database is occurred while the
+   iteration.  Besides, the order of this traversal access method is arbitrary, so it is not
+   assured that the order of storing matches the one of the traversal access. */
+char *dpiternext(DEPOT *depot, int *sp);
+
+
+/* Set alignment of a database handle.
+   `depot' specifies a database handle connected as a writer.
+   `align' specifies the size of alignment.
+   If successful, the return value is true, else, it is false.
+   If alignment is set to a database, the efficiency of overwriting values is improved.
+   The size of alignment is suggested to be average size of the values of the records to be
+   stored.  If alignment is positive, padding whose size is multiple number of the alignment
+   is placed.  If alignment is negative, as `vsiz' is the size of a value, the size of padding
+   is calculated with `(vsiz / pow(2, abs(align) - 1))'.  Because alignment setting is not
+   saved in a database, you should specify alignment every opening a database. */
+int dpsetalign(DEPOT *depot, int align);
+
+
+/* Set the size of the free block pool of a database handle.
+   `depot' specifies a database handle connected as a writer.
+   `size' specifies the size of the free block pool of a database.
+   If successful, the return value is true, else, it is false.
+   The default size of the free block pool is 16.  If the size is greater, the space efficiency
+   of overwriting values is improved with the time efficiency sacrificed. */
+int dpsetfbpsiz(DEPOT *depot, int size);
+
+
+/* Synchronize updating contents with the file and the device.
+   `depot' specifies a database handle connected as a writer.
+   If successful, the return value is true, else, it is false.
+   This function is useful when another process uses the connected database file. */
+int dpsync(DEPOT *depot);
+
+
+/* Optimize a database.
+   `depot' specifies a database handle connected as a writer.
+   `bnum' specifies the number of the elements of the bucket array.  If it is not more than 0,
+   the default value is specified.
+   If successful, the return value is true, else, it is false.
+   In an alternating succession of deleting and storing with overwrite or concatenate,
+   dispensable regions accumulate.  This function is useful to do away with them. */
+int dpoptimize(DEPOT *depot, int bnum);
+
+
+/* Get the name of a database.
+   `depot' specifies a database handle.
+   If successful, the return value is the pointer to the region of the name of the database,
+   else, it is `NULL'.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call if it is no longer in use. */
+char *dpname(DEPOT *depot);
+
+
+/* Get the size of a database file.
+   `depot' specifies a database handle.
+   If successful, the return value is the size of the database file, else, it is -1. */
+int dpfsiz(DEPOT *depot);
+
+
+/* Get the number of the elements of the bucket array.
+   `depot' specifies a database handle.
+   If successful, the return value is the number of the elements of the bucket array, else, it
+   is -1. */
+int dpbnum(DEPOT *depot);
+
+
+/* Get the number of the used elements of the bucket array.
+   `depot' specifies a database handle.
+   If successful, the return value is the number of the used elements of the bucket array,
+   else, it is -1.
+   This function is inefficient because it accesses all elements of the bucket array. */
+int dpbusenum(DEPOT *depot);
+
+
+/* Get the number of the records stored in a database.
+   `depot' specifies a database handle.
+   If successful, the return value is the number of the records stored in the database, else,
+   it is -1. */
+int dprnum(DEPOT *depot);
+
+
+/* Check whether a database handle is a writer or not.
+   `depot' specifies a database handle.
+   The return value is true if the handle is a writer, false if not. */
+int dpwritable(DEPOT *depot);
+
+
+/* Check whether a database has a fatal error or not.
+   `depot' specifies a database handle.
+   The return value is true if the database has a fatal error, false if not. */
+int dpfatalerror(DEPOT *depot);
+
+
+/* Get the inode number of a database file.
+   `depot' specifies a database handle.
+   The return value is the inode number of the database file. */
+int dpinode(DEPOT *depot);
+
+
+/* Get the last modified time of a database.
+   `depot' specifies a database handle.
+   The return value is the last modified time of the database. */
+time_t dpmtime(DEPOT *depot);
+
+
+/* Get the file descriptor of a database file.
+   `depot' specifies a database handle.
+   The return value is the file descriptor of the database file.
+   Handling the file descriptor of a database file directly is not suggested. */
+int dpfdesc(DEPOT *depot);
+
+
+/* Remove a database file.
+   `name' specifies the name of a database file.
+   If successful, the return value is true, else, it is false. */
+int dpremove(const char *name);
+
+
+/* Repair a broken database file.
+   `name' specifies the name of a database file.
+   If successful, the return value is true, else, it is false.
+   There is no guarantee that all records in a repaired database file correspond to the original
+   or expected state. */
+int dprepair(const char *name);
+
+
+/* Dump all records as endian independent data.
+   `depot' specifies a database handle.
+   `name' specifies the name of an output file.
+   If successful, the return value is true, else, it is false. */
+int dpexportdb(DEPOT *depot, const char *name);
+
+
+/* Load all records from endian independent data.
+   `depot' specifies a database handle connected as a writer.  The database of the handle must
+   be empty.
+   `name' specifies the name of an input file.
+   If successful, the return value is true, else, it is false. */
+int dpimportdb(DEPOT *depot, const char *name);
+
+
+/* Retrieve a record directly from a database file.
+   `name' specifies the name of a database file.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.	If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   `sp' specifies the pointer to a variable to which the size of the region of the return
+   value is assigned.  If it is `NULL', it is not used.
+   If successful, the return value is the pointer to the region of the value of the
+   corresponding record, else, it is `NULL'.  `NULL' is returned when no record corresponds to
+   the specified key.
+   Because an additional zero code is appended at the end of the region of the return value,
+   the return value can be treated as a character string.  Because the region of the return
+   value is allocated with the `malloc' call, it should be released with the `free' call if it
+   is no longer in use.  Although this function can be used even while the database file is
+   locked by another process, it is not assured that recent updated is reflected. */
+char *dpsnaffle(const char *name, const char *kbuf, int ksiz, int *sp);
+
+
+/* Hash function used inside Depot.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.	If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   The return value is the hash value of 31 bits length computed from the key.
+   This function is useful when an application calculates the state of the inside bucket array. */
+int dpinnerhash(const char *kbuf, int ksiz);
+
+
+/* Hash function which is independent from the hash functions used inside Depot.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.	If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   The return value is the hash value of 31 bits length computed from the key.
+   This function is useful when an application uses its own hash algorithm outside Depot. */
+int dpouterhash(const char *kbuf, int ksiz);
+
+
+/* Get a natural prime number not less than a number.
+   `num' specified a natural number.
+   The return value is a natural prime number not less than the specified number.
+   This function is useful when an application determines the size of a bucket array of its
+   own hash algorithm. */
+int dpprimenum(int num);
+
+
+
+/*************************************************************************************************
+ * features for experts
+ *************************************************************************************************/
+
+
+#define _QDBM_VERSION  "1.8.75"
+#define _QDBM_LIBVER   1411
+
+
+/* Name of the operating system. */
+MYEXTERN const char *dpsysname;
+
+
+/* File descriptor for debugging output. */
+MYEXTERN int dpdbgfd;
+
+
+/* Whether this build is reentrant. */
+MYEXTERN const int dpisreentrant;
+
+
+/* Set the last happened error code.
+   `ecode' specifies the error code.
+   `line' specifies the number of the line where the error happened. */
+void dpecodeset(int ecode, const char *file, int line);
+
+
+/* Get the pointer of the variable of the last happened error code.
+   The return value is the pointer of the variable. */
+int *dpecodeptr(void);
+
+
+/* Synchronize updating contents on memory.
+   `depot' specifies a database handle connected as a writer.
+   If successful, the return value is true, else, it is false. */
+int dpmemsync(DEPOT *depot);
+
+
+/* Synchronize updating contents on memory, not physically.
+   `depot' specifies a database handle connected as a writer.
+   If successful, the return value is true, else, it is false. */
+int dpmemflush(DEPOT *depot);
+
+
+/* Get flags of a database.
+   `depot' specifies a database handle.
+   The return value is the flags of a database. */
+int dpgetflags(DEPOT *depot);
+
+
+/* Set flags of a database.
+   `depot' specifies a database handle connected as a writer.
+   `flags' specifies flags to set.  Least ten bits are reserved for internal use.
+   If successful, the return value is true, else, it is false. */
+int dpsetflags(DEPOT *depot, int flags);
+
+
+
+#undef MYEXTERN
+
+#if defined(__cplusplus)		 /* export for C++ */
+}
+#endif
+
+#endif					 /* duplication check */
+
+
+/* END OF FILE */

Added: trunk/src/qdbm/myconf.c
==============================================================================
--- (empty file)
+++ trunk/src/qdbm/myconf.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,1113 @@
+/*************************************************************************************************
+ * Emulation of system calls
+ *							Copyright (C) 2000-2006 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM is free software; you can redistribute it and/or modify it under the terms of the GNU
+ * Lesser General Public License as published by the Free Software Foundation; either version
+ * 2.1 of the License or any later version.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include "myconf.h"
+
+
+
+/*************************************************************************************************
+ * for dosish filesystems
+ *************************************************************************************************/
+
+
+#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) || defined(_SYS_CYGWIN_)
+
+
+#define DOSPATHBUFSIZ  8192
+
+
+int _qdbm_win32_lstat(const char *pathname, struct stat *buf){
+  char pbuf[DOSPATHBUFSIZ], *p;
+  int inode;
+  if(stat(pathname, buf) == -1) return -1;
+  if(GetFullPathName(pathname, DOSPATHBUFSIZ, pbuf, &p) != 0){
+    inode = 11003;
+    for(p = pbuf; *p != '\0'; p++){
+      inode = inode * 31 + *(unsigned char *)p;
+    }
+    buf->st_ino = (inode * 911) & 0x7FFF;
+  }
+  return 0;
+}
+
+
+#endif
+
+
+
+/*************************************************************************************************
+ * for POSIX thread
+ *************************************************************************************************/
+
+
+#if defined(MYPTHREAD)
+
+
+#include <pthread.h>
+
+
+#define PTKEYMAX       8
+
+
+struct { void *ptr; pthread_key_t key; } _qdbm_ptkeys[PTKEYMAX];
+int _qdbm_ptknum = 0;
+
+
+static void *_qdbm_gettsd(void *ptr, int size, const void *initval);
+
+
+void *_qdbm_settsd(void *ptr, int size, const void *initval){
+  static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+  char *val;
+  if((val = _qdbm_gettsd(ptr, size, initval)) != NULL) return val;
+  if(pthread_mutex_lock(&mutex) != 0) return NULL;
+  if((val = _qdbm_gettsd(ptr, size, initval)) != NULL){
+    pthread_mutex_unlock(&mutex);
+    return val;
+  }
+  if(_qdbm_ptknum >= PTKEYMAX){
+    pthread_mutex_unlock(&mutex);
+    return NULL;
+  }
+  _qdbm_ptkeys[_qdbm_ptknum].ptr = ptr;
+  if(pthread_key_create(&(_qdbm_ptkeys[_qdbm_ptknum].key), free) != 0){
+    pthread_mutex_unlock(&mutex);
+    return NULL;
+  }
+  if(!(val = malloc(size))){
+    pthread_key_delete(_qdbm_ptkeys[_qdbm_ptknum].key);
+    pthread_mutex_unlock(&mutex);
+    return NULL;
+  }
+  memcpy(val, initval, size);
+  if(pthread_setspecific(_qdbm_ptkeys[_qdbm_ptknum].key, val) != 0){
+    free(val);
+    pthread_key_delete(_qdbm_ptkeys[_qdbm_ptknum].key);
+    pthread_mutex_unlock(&mutex);
+    return NULL;
+  }
+  _qdbm_ptknum++;
+  pthread_mutex_unlock(&mutex);
+  return val;
+}
+
+
+static void *_qdbm_gettsd(void *ptr, int size, const void *initval){
+  char *val;
+  int i;
+  for(i = 0; i < _qdbm_ptknum; i++){
+    if(_qdbm_ptkeys[i].ptr == ptr){
+      if(!(val = pthread_getspecific(_qdbm_ptkeys[i].key))){
+	if(!(val = malloc(size))) return NULL;
+	memcpy(val, initval, size);
+	if(pthread_setspecific(_qdbm_ptkeys[i].key, val) != 0){
+	  free(val);
+	  return NULL;
+	}
+      }
+      return val;
+    }
+  }
+  return NULL;
+}
+
+
+#endif
+
+
+
+/*************************************************************************************************
+ * for systems without mmap
+ *************************************************************************************************/
+
+
+#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_)
+
+
+#define MMFDESCMAX     2048
+
+
+struct { void *start; HANDLE handle; } mmhandles[MMFDESCMAX];
+int mmhnum = 0;
+CRITICAL_SECTION mmcsec;
+
+
+static void _qdbm_delete_mmap_env(void);
+
+
+void *_qdbm_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset){
+  static volatile long first = TRUE;
+  static volatile long ready = FALSE;
+  HANDLE handle;
+  int i;
+  if(InterlockedExchange((void *)&first, FALSE)){
+    InitializeCriticalSection(&mmcsec);
+    atexit(_qdbm_delete_mmap_env);
+    InterlockedExchange((void *)&ready, TRUE);
+  }
+  while(!InterlockedCompareExchange((void *)&ready, TRUE, TRUE)){
+    Sleep(1);
+  }
+  if(fd < 0 || flags & MAP_FIXED) return MAP_FAILED;
+  if(!(handle = CreateFileMapping((HANDLE)_get_osfhandle(fd), NULL,
+				  (prot & PROT_WRITE) ? PAGE_READWRITE : PAGE_READONLY,
+				  0, length, NULL))) return MAP_FAILED;
+  if(!(start = MapViewOfFile(handle, (prot & PROT_WRITE) ? FILE_MAP_WRITE : FILE_MAP_READ,
+			     0, 0, length))){
+    CloseHandle(handle);
+    return MAP_FAILED;
+  }
+  EnterCriticalSection(&mmcsec);
+  if(mmhnum >= MMFDESCMAX - 1){
+    UnmapViewOfFile(start);
+    CloseHandle(handle);
+    LeaveCriticalSection(&mmcsec);
+    return MAP_FAILED;
+  }
+  for(i = 0; i < MMFDESCMAX; i++){
+    if(!mmhandles[i].start){
+      mmhandles[i].start = start;
+      mmhandles[i].handle = handle;
+      break;
+    }
+  }
+  mmhnum++;
+  LeaveCriticalSection(&mmcsec);
+  return start;
+}
+
+
+int _qdbm_munmap(void *start, size_t length){
+  HANDLE handle;
+  int i;
+  EnterCriticalSection(&mmcsec);
+  handle = NULL;
+  for(i = 0; i < MMFDESCMAX; i++){
+    if(mmhandles[i].start == start){
+      handle = mmhandles[i].handle;
+      mmhandles[i].start = NULL;
+      mmhandles[i].handle = NULL;
+      break;
+    }
+  }
+  if(!handle){
+    LeaveCriticalSection(&mmcsec);
+    return -1;
+  }
+  mmhnum--;
+  LeaveCriticalSection(&mmcsec);
+  if(!UnmapViewOfFile(start)){
+    CloseHandle(handle);
+    return -1;
+  }
+  if(!CloseHandle(handle)) return -1;
+  return 0;
+}
+
+
+int _qdbm_msync(const void *start, size_t length, int flags){
+  if(!FlushViewOfFile(start, length)) return -1;
+  return 0;
+}
+
+
+static void _qdbm_delete_mmap_env(void){
+  DeleteCriticalSection(&mmcsec);
+}
+
+
+#elif defined(_SYS_FREEBSD_) || defined(_SYS_NETBSD_) || defined(_SYS_OPENBSD_) || \
+  defined(_SYS_AIX_) || defined(_SYS_RISCOS_) || defined(MYNOMMAP)
+
+
+void *_qdbm_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset){
+  char *buf, *wp;
+  int rv, rlen;
+  if(flags & MAP_FIXED) return MAP_FAILED;
+  if(lseek(fd, SEEK_SET, offset) == -1) return MAP_FAILED;
+  if(!(buf = malloc(sizeof(int) * 3 + length))) return MAP_FAILED;
+  wp = buf;
+  *(int *)wp = fd;
+  wp += sizeof(int);
+  *(int *)wp = offset;
+  wp += sizeof(int);
+  *(int *)wp = prot;
+  wp += sizeof(int);
+  rlen = 0;
+  while((rv = read(fd, wp + rlen, length - rlen)) > 0){
+    rlen += rv;
+  }
+  if(rv == -1 || rlen != (int) length){
+    free(buf);
+    return MAP_FAILED;
+  }
+  return wp;
+}
+
+
+int _qdbm_munmap(void *start, size_t length){
+  char *buf, *rp;
+  int fd, offset, prot, rv, wlen;
+  buf = (char *)start - sizeof(int) * 3;
+  rp = buf;
+  fd = *(int *)rp;
+  rp += sizeof(int);
+  offset = *(int *)rp;
+  rp += sizeof(int);
+  prot = *(int *)rp;
+  rp += sizeof(int);
+  if(prot & PROT_WRITE){
+    if(lseek(fd, offset, SEEK_SET) == -1){
+      free(buf);
+      return -1;
+    }
+    wlen = 0;
+    while(wlen < (int)length){
+      rv = write(fd, rp + wlen, length - wlen);
+      if(rv == -1){
+	if(errno == EINTR) continue;
+	free(buf);
+	return -1;
+      }
+      wlen += rv;
+    }
+  }
+  free(buf);
+  return 0;
+}
+
+
+int _qdbm_msync(const void *start, size_t length, int flags){
+  char *buf, *rp;
+  int fd, offset, prot, rv, wlen;
+  buf = (char *)start - sizeof(int) * 3;
+  rp = buf;
+  fd = *(int *)rp;
+  rp += sizeof(int);
+  offset = *(int *)rp;
+  rp += sizeof(int);
+  prot = *(int *)rp;
+  rp += sizeof(int);
+  if(prot & PROT_WRITE){
+    if(lseek(fd, offset, SEEK_SET) == -1) return -1;
+    wlen = 0;
+    while(wlen < (int)length){
+      rv = write(fd, rp + wlen, length - wlen);
+      if(rv == -1){
+	if(errno == EINTR) continue;
+	return -1;
+      }
+      wlen += rv;
+    }
+  }
+  return 0;
+}
+
+
+#endif
+
+
+
+/*************************************************************************************************
+ * for reentrant time routines
+ *************************************************************************************************/
+
+
+#if defined(_SYS_LINUX_) || defined(_SYS_FREEBSD_) || defined(_SYS_OPENBSD_) || \
+  defined(_SYS_NETBSD_) || defined(_SYS_SUNOS_) || defined(_SYS_HPUX_) || \
+  defined(_SYS_MACOSX_) || defined(_SYS_CYGWIN_)
+
+
+struct tm *_qdbm_gmtime(const time_t *timep, struct tm *result){
+  return gmtime_r(timep, result);
+}
+
+
+struct tm *_qdbm_localtime(const time_t *timep, struct tm *result){
+  return localtime_r(timep, result);
+}
+
+
+# else
+
+
+struct tm *_qdbm_gmtime(const time_t *timep, struct tm *result){
+  return gmtime(timep);
+}
+
+
+struct tm *_qdbm_localtime(const time_t *timep, struct tm *result){
+  return localtime(timep);
+}
+
+
+# endif
+
+
+
+/*************************************************************************************************
+ * for systems without times
+ *************************************************************************************************/
+
+
+#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_)
+
+
+clock_t _qdbm_times(struct tms *buf){
+  buf->tms_utime = clock();
+  buf->tms_stime = 0;
+  buf->tms_cutime = 0;
+  buf->tms_cstime = 0;
+  return 0;
+}
+
+
+#endif
+
+
+
+/*************************************************************************************************
+ * for Win32
+ *************************************************************************************************/
+
+
+#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_)
+
+
+#define WINLOCKWAIT    100
+
+
+int _qdbm_win32_fcntl(int fd, int cmd, struct flock *lock){
+  HANDLE fh;
+  DWORD opt;
+  OVERLAPPED ol;
+  fh = (HANDLE)_get_osfhandle(fd);
+  opt = (cmd == F_SETLK) ? LOCKFILE_FAIL_IMMEDIATELY : 0;
+  if(lock->l_type == F_WRLCK) opt |= LOCKFILE_EXCLUSIVE_LOCK;
+  memset(&ol, 0, sizeof(OVERLAPPED));
+  ol.Offset = INT_MAX;
+  ol.OffsetHigh = 0;
+  ol.hEvent = 0;
+  if(!LockFileEx(fh, opt, 0, 1, 0, &ol)){
+    if(GetLastError() == ERROR_CALL_NOT_IMPLEMENTED){
+      while(TRUE){
+	if(LockFile(fh, 0, 0, 1, 0)) return 0;
+	Sleep(WINLOCKWAIT);
+      }
+    }
+    return -1;
+  }
+  return 0;
+}
+
+
+#endif
+
+
+#if defined(_SYS_MSVC_)
+
+
+DIR *_qdbm_win32_opendir(const char *name){
+  char expr[8192];
+  int len;
+  DIR *dir;
+  HANDLE fh;
+  WIN32_FIND_DATA data;
+  len = strlen(name);
+  if(len > 0 && name[len-1] == MYPATHCHR){
+    sprintf(expr, "%s*", name);
+  } else {
+    sprintf(expr, "%s%c*", name, MYPATHCHR);
+  }
+  if((fh = FindFirstFile(expr, &data)) == INVALID_HANDLE_VALUE) return NULL;
+  if(!(dir = malloc(sizeof(DIR)))){
+    FindClose(fh);
+    return NULL;
+  }
+  dir->fh = fh;
+  dir->data = data;
+  dir->first = TRUE;
+  return dir;
+}
+
+
+int _qdbm_win32_closedir(DIR *dir){
+  if(!FindClose(dir->fh)){
+    free(dir);
+    return -1;
+  }
+  free(dir);
+  return 0;
+}
+
+
+struct dirent *_qdbm_win32_readdir(DIR *dir){
+  if(dir->first){
+    sprintf(dir->de.d_name, "%s", dir->data.cFileName);
+    dir->first = FALSE;
+    return &(dir->de);
+  }
+  if(!FindNextFile(dir->fh, &(dir->data))) return NULL;
+  sprintf(dir->de.d_name, "%s", dir->data.cFileName);
+  return &(dir->de);
+}
+
+
+#endif
+
+
+
+/*************************************************************************************************
+ * for checking information of the system
+ *************************************************************************************************/
+
+
+#if defined(_SYS_LINUX_)
+
+
+int _qdbm_vmemavail(size_t size){
+  char buf[4096], *rp;
+  int fd, rv, bsiz;
+  double avail;
+  if((fd = open("/proc/meminfo", O_RDONLY, 00644)) == -1) return TRUE;
+  rv = TRUE;
+  if((bsiz = read(fd, buf, sizeof(buf) - 1)) > 0){
+    buf[bsiz] = '\0';
+    avail = -1;
+    if((rp = strstr(buf, "MemFree:")) != NULL){
+      rp = strchr(rp, ':') + 1;
+      avail = strtod(rp, NULL) * 1024.0;
+      if((rp = strstr(buf, "SwapFree:")) != NULL){
+	rp = strchr(rp, ':') + 1;
+	avail += strtod(rp, NULL) * 1024.0;
+      }
+      if(size >= avail) rv = FALSE;
+    }
+  }
+  close(fd);
+  return rv;
+}
+
+
+#elif defined(_SYS_MSVC_) || defined(_SYS_MINGW_) || defined(_SYS_CYGWIN_)
+
+
+int _qdbm_vmemavail(size_t size){
+  MEMORYSTATUS sbuf;
+  sbuf.dwLength = sizeof(MEMORYSTATUS);
+  GlobalMemoryStatus(&sbuf);
+  return size < sbuf.dwAvailVirtual;
+}
+
+
+#else
+
+
+int _qdbm_vmemavail(size_t size){
+  return TRUE;
+}
+
+
+#endif
+
+
+
+/*************************************************************************************************
+ * for ZLIB
+ *************************************************************************************************/
+
+
+#if defined(MYZLIB)
+
+
+#include <zlib.h>
+
+#define ZLIBBUFSIZ     8192
+
+
+static char *_qdbm_deflate_impl(const char *ptr, int size, int *sp, int mode);
+static char *_qdbm_inflate_impl(const char *ptr, int size, int *sp, int mode);
+static unsigned int _qdbm_getcrc_impl(const char *ptr, int size);
+
+
+char *(*_qdbm_deflate)(const char *, int, int *, int) = _qdbm_deflate_impl;
+char *(*_qdbm_inflate)(const char *, int, int *, int) = _qdbm_inflate_impl;
+unsigned int (*_qdbm_getcrc)(const char *, int) = _qdbm_getcrc_impl;
+
+
+static char *_qdbm_deflate_impl(const char *ptr, int size, int *sp, int mode){
+  z_stream zs;
+  char *buf, *swap;
+  unsigned char obuf[ZLIBBUFSIZ];
+  int rv, asiz, bsiz, osiz;
+  if(size < 0) size = strlen(ptr);
+  zs.zalloc = Z_NULL;
+  zs.zfree = Z_NULL;
+  zs.opaque = Z_NULL;
+  switch(mode){
+  case _QDBM_ZMRAW:
+    if(deflateInit2(&zs, 5, Z_DEFLATED, -15, 7, Z_DEFAULT_STRATEGY) != Z_OK)
+      return NULL;
+    break;
+  case _QDBM_ZMGZIP:
+    if(deflateInit2(&zs, 6, Z_DEFLATED, 15 + 16, 9, Z_DEFAULT_STRATEGY) != Z_OK)
+      return NULL;
+    break;
+  default:
+    if(deflateInit2(&zs, 6, Z_DEFLATED, 15, 8, Z_DEFAULT_STRATEGY) != Z_OK)
+      return NULL;
+    break;
+  }
+  asiz = size + 16;
+  if(asiz < ZLIBBUFSIZ) asiz = ZLIBBUFSIZ;
+  if(!(buf = malloc(asiz))){
+    deflateEnd(&zs);
+    return NULL;
+  }
+  bsiz = 0;
+  zs.next_in = (unsigned char *)ptr;
+  zs.avail_in = size;
+  zs.next_out = obuf;
+  zs.avail_out = ZLIBBUFSIZ;
+  while((rv = deflate(&zs, Z_FINISH)) == Z_OK){
+    osiz = ZLIBBUFSIZ - zs.avail_out;
+    if(bsiz + osiz > asiz){
+      asiz = asiz * 2 + osiz;
+      if(!(swap = realloc(buf, asiz))){
+	free(buf);
+	deflateEnd(&zs);
+	return NULL;
+      }
+      buf = swap;
+    }
+    memcpy(buf + bsiz, obuf, osiz);
+    bsiz += osiz;
+    zs.next_out = obuf;
+    zs.avail_out = ZLIBBUFSIZ;
+  }
+  if(rv != Z_STREAM_END){
+    free(buf);
+    deflateEnd(&zs);
+    return NULL;
+  }
+  osiz = ZLIBBUFSIZ - zs.avail_out;
+  if(bsiz + osiz + 1 > asiz){
+    asiz = asiz * 2 + osiz;
+    if(!(swap = realloc(buf, asiz))){
+      free(buf);
+      deflateEnd(&zs);
+      return NULL;
+    }
+    buf = swap;
+  }
+  memcpy(buf + bsiz, obuf, osiz);
+  bsiz += osiz;
+  buf[bsiz] = '\0';
+  if(mode == _QDBM_ZMRAW) bsiz++;
+  *sp = bsiz;
+  deflateEnd(&zs);
+  return buf;
+}
+
+
+static char *_qdbm_inflate_impl(const char *ptr, int size, int *sp, int mode){
+  z_stream zs;
+  char *buf, *swap;
+  unsigned char obuf[ZLIBBUFSIZ];
+  int rv, asiz, bsiz, osiz;
+  zs.zalloc = Z_NULL;
+  zs.zfree = Z_NULL;
+  zs.opaque = Z_NULL;
+  switch(mode){
+  case _QDBM_ZMRAW:
+    if(inflateInit2(&zs, -15) != Z_OK) return NULL;
+    break;
+  case _QDBM_ZMGZIP:
+    if(inflateInit2(&zs, 15 + 16) != Z_OK) return NULL;
+    break;
+  default:
+    if(inflateInit2(&zs, 15) != Z_OK) return NULL;
+    break;
+  }
+  asiz = size * 2 + 16;
+  if(asiz < ZLIBBUFSIZ) asiz = ZLIBBUFSIZ;
+  if(!(buf = malloc(asiz))){
+    inflateEnd(&zs);
+    return NULL;
+  }
+  bsiz = 0;
+  zs.next_in = (unsigned char *)ptr;
+  zs.avail_in = size;
+  zs.next_out = obuf;
+  zs.avail_out = ZLIBBUFSIZ;
+  while((rv = inflate(&zs, Z_NO_FLUSH)) == Z_OK){
+    osiz = ZLIBBUFSIZ - zs.avail_out;
+    if(bsiz + osiz >= asiz){
+      asiz = asiz * 2 + osiz;
+      if(!(swap = realloc(buf, asiz))){
+	free(buf);
+	inflateEnd(&zs);
+	return NULL;
+      }
+      buf = swap;
+    }
+    memcpy(buf + bsiz, obuf, osiz);
+    bsiz += osiz;
+    zs.next_out = obuf;
+    zs.avail_out = ZLIBBUFSIZ;
+  }
+  if(rv != Z_STREAM_END){
+    free(buf);
+    inflateEnd(&zs);
+    return NULL;
+  }
+  osiz = ZLIBBUFSIZ - zs.avail_out;
+  if(bsiz + osiz >= asiz){
+    asiz = asiz * 2 + osiz;
+    if(!(swap = realloc(buf, asiz))){
+      free(buf);
+      inflateEnd(&zs);
+      return NULL;
+    }
+    buf = swap;
+  }
+  memcpy(buf + bsiz, obuf, osiz);
+  bsiz += osiz;
+  buf[bsiz] = '\0';
+  if(sp) *sp = bsiz;
+  inflateEnd(&zs);
+  return buf;
+}
+
+
+static unsigned int _qdbm_getcrc_impl(const char *ptr, int size){
+  int crc;
+  if(size < 0) size = strlen(ptr);
+  crc = crc32(0, Z_NULL, 0);
+  return crc32(crc, (unsigned char *)ptr, size);
+}
+
+
+#else
+
+
+char *(*_qdbm_deflate)(const char *, int, int *, int) = NULL;
+char *(*_qdbm_inflate)(const char *, int, int *, int) = NULL;
+unsigned int (*_qdbm_getcrc)(const char *, int) = NULL;
+
+
+#endif
+
+
+
+/*************************************************************************************************
+ * for LZO
+ *************************************************************************************************/
+
+
+#if defined(MYLZO)
+
+
+#include <lzo/lzo1x.h>
+
+
+static char *_qdbm_lzoencode_impl(const char *ptr, int size, int *sp);
+static char *_qdbm_lzodecode_impl(const char *ptr, int size, int *sp);
+
+
+int _qdbm_lzo_init = FALSE;
+char *(*_qdbm_lzoencode)(const char *, int, int *) = _qdbm_lzoencode_impl;
+char *(*_qdbm_lzodecode)(const char *, int, int *) = _qdbm_lzodecode_impl;
+
+
+static char *_qdbm_lzoencode_impl(const char *ptr, int size, int *sp){
+  char wrkmem[LZO1X_1_MEM_COMPRESS];
+  lzo_bytep buf;
+  lzo_uint bsiz;
+  if(!_qdbm_lzo_init){
+    if(lzo_init() != LZO_E_OK) return NULL;
+    _qdbm_lzo_init = TRUE;
+  }
+  if(size < 0) size = strlen(ptr);
+  if(!(buf = malloc(size + size / 16 + 80))) return NULL;
+  if(lzo1x_1_compress((lzo_bytep)ptr, size, buf, &bsiz, wrkmem) != LZO_E_OK){
+    free(buf);
+    return NULL;
+  }
+  buf[bsiz] = '\0';
+  *sp = bsiz;
+  return (char *)buf;
+}
+
+
+static char *_qdbm_lzodecode_impl(const char *ptr, int size, int *sp){
+  lzo_bytep buf;
+  lzo_uint bsiz;
+  int rat, rv;
+  if(!_qdbm_lzo_init){
+    if(lzo_init() != LZO_E_OK) return NULL;
+    _qdbm_lzo_init = TRUE;
+  }
+  rat = 6;
+  while(TRUE){
+    bsiz = (size + 256) * rat + 3;
+    if(!(buf = malloc(bsiz + 1))) return NULL;
+    rv = lzo1x_decompress_safe((lzo_bytep)(ptr), size, buf, &bsiz, NULL);
+    if(rv == LZO_E_OK){
+      break;
+    } else if(rv == LZO_E_OUTPUT_OVERRUN){
+      free(buf);
+      rat *= 2;
+    } else {
+      free(buf);
+      return NULL;
+    }
+  }
+  buf[bsiz] = '\0';
+  if(sp) *sp = bsiz;
+  return (char *)buf;
+}
+
+
+#else
+
+
+char *(*_qdbm_lzoencode)(const char *, int, int *) = NULL;
+char *(*_qdbm_lzodecode)(const char *, int, int *) = NULL;
+
+
+#endif
+
+
+
+/*************************************************************************************************
+ * for BZIP2
+ *************************************************************************************************/
+
+
+#if defined(MYBZIP)
+
+
+#include <bzlib.h>
+
+#define BZIPBUFSIZ     8192
+
+
+static char *_qdbm_bzencode_impl(const char *ptr, int size, int *sp);
+static char *_qdbm_bzdecode_impl(const char *ptr, int size, int *sp);
+
+
+char *(*_qdbm_bzencode)(const char *, int, int *) = _qdbm_bzencode_impl;
+char *(*_qdbm_bzdecode)(const char *, int, int *) = _qdbm_bzdecode_impl;
+
+
+static char *_qdbm_bzencode_impl(const char *ptr, int size, int *sp){
+  bz_stream zs;
+  char *buf, *swap, obuf[BZIPBUFSIZ];
+  int rv, asiz, bsiz, osiz;
+  if(size < 0) size = strlen(ptr);
+  zs.bzalloc = NULL;
+  zs.bzfree = NULL;
+  zs.opaque = NULL;
+  if(BZ2_bzCompressInit(&zs, 9, 0, 30) != BZ_OK) return NULL;
+  asiz = size + 16;
+  if(asiz < BZIPBUFSIZ) asiz = BZIPBUFSIZ;
+  if(!(buf = malloc(asiz))){
+    BZ2_bzCompressEnd(&zs);
+    return NULL;
+  }
+  bsiz = 0;
+  zs.next_in = (char *)ptr;
+  zs.avail_in = size;
+  zs.next_out = obuf;
+  zs.avail_out = BZIPBUFSIZ;
+  while((rv = BZ2_bzCompress(&zs, BZ_FINISH)) == BZ_FINISH_OK){
+    osiz = BZIPBUFSIZ - zs.avail_out;
+    if(bsiz + osiz > asiz){
+      asiz = asiz * 2 + osiz;
+      if(!(swap = realloc(buf, asiz))){
+	free(buf);
+	BZ2_bzCompressEnd(&zs);
+	return NULL;
+      }
+      buf = swap;
+    }
+    memcpy(buf + bsiz, obuf, osiz);
+    bsiz += osiz;
+    zs.next_out = obuf;
+    zs.avail_out = BZIPBUFSIZ;
+  }
+  if(rv != BZ_STREAM_END){
+    free(buf);
+    BZ2_bzCompressEnd(&zs);
+    return NULL;
+  }
+  osiz = BZIPBUFSIZ - zs.avail_out;
+  if(bsiz + osiz + 1 > asiz){
+    asiz = asiz * 2 + osiz;
+    if(!(swap = realloc(buf, asiz))){
+      free(buf);
+      BZ2_bzCompressEnd(&zs);
+      return NULL;
+    }
+    buf = swap;
+  }
+  memcpy(buf + bsiz, obuf, osiz);
+  bsiz += osiz;
+  buf[bsiz] = '\0';
+  *sp = bsiz;
+  BZ2_bzCompressEnd(&zs);
+  return buf;
+}
+
+
+static char *_qdbm_bzdecode_impl(const char *ptr, int size, int *sp){
+  bz_stream zs;
+  char *buf, *swap, obuf[BZIPBUFSIZ];
+  int rv, asiz, bsiz, osiz;
+  zs.bzalloc = NULL;
+  zs.bzfree = NULL;
+  zs.opaque = NULL;
+  if(BZ2_bzDecompressInit(&zs, 0, 0) != BZ_OK) return NULL;
+  asiz = size * 2 + 16;
+  if(asiz < BZIPBUFSIZ) asiz = BZIPBUFSIZ;
+  if(!(buf = malloc(asiz))){
+    BZ2_bzDecompressEnd(&zs);
+    return NULL;
+  }
+  bsiz = 0;
+  zs.next_in = (char *)ptr;
+  zs.avail_in = size;
+  zs.next_out = obuf;
+  zs.avail_out = BZIPBUFSIZ;
+  while((rv = BZ2_bzDecompress(&zs)) == BZ_OK){
+    osiz = BZIPBUFSIZ - zs.avail_out;
+    if(bsiz + osiz >= asiz){
+      asiz = asiz * 2 + osiz;
+      if(!(swap = realloc(buf, asiz))){
+	free(buf);
+	BZ2_bzDecompressEnd(&zs);
+	return NULL;
+      }
+      buf = swap;
+    }
+    memcpy(buf + bsiz, obuf, osiz);
+    bsiz += osiz;
+    zs.next_out = obuf;
+    zs.avail_out = BZIPBUFSIZ;
+  }
+  if(rv != BZ_STREAM_END){
+    free(buf);
+    BZ2_bzDecompressEnd(&zs);
+    return NULL;
+  }
+  osiz = BZIPBUFSIZ - zs.avail_out;
+  if(bsiz + osiz >= asiz){
+    asiz = asiz * 2 + osiz;
+    if(!(swap = realloc(buf, asiz))){
+      free(buf);
+      BZ2_bzDecompressEnd(&zs);
+      return NULL;
+    }
+    buf = swap;
+  }
+  memcpy(buf + bsiz, obuf, osiz);
+  bsiz += osiz;
+  buf[bsiz] = '\0';
+  if(sp) *sp = bsiz;
+  BZ2_bzDecompressEnd(&zs);
+  return buf;
+}
+
+
+#else
+
+
+char *(*_qdbm_bzencode)(const char *, int, int *) = NULL;
+char *(*_qdbm_bzdecode)(const char *, int, int *) = NULL;
+
+
+#endif
+
+
+
+/*************************************************************************************************
+ * for ICONV
+ *************************************************************************************************/
+
+
+#if defined(MYICONV)
+
+
+#include <iconv.h>
+
+#define ICONVCHECKSIZ  32768
+#define ICONVMISSMAX   256
+#define ICONVALLWRAT   0.001
+
+
+static char *_qdbm_iconv_impl(const char *ptr, int size,
+			      const char *icode, const char *ocode, int *sp, int *mp);
+static const char *_qdbm_encname_impl(const char *ptr, int size);
+static int _qdbm_encmiss(const char *ptr, int size, const char *icode, const char *ocode);
+
+
+char *(*_qdbm_iconv)(const char *, int, const char *, const char *,
+		     int *, int *) = _qdbm_iconv_impl;
+const char *(*_qdbm_encname)(const char *, int) = _qdbm_encname_impl;
+
+
+static char *_qdbm_iconv_impl(const char *ptr, int size,
+			      const char *icode, const char *ocode, int *sp, int *mp){
+  iconv_t ic;
+  char *obuf, *wp, *rp;
+  size_t isiz, osiz;
+  int miss;
+  if(size < 0) size = strlen(ptr);
+  isiz = size;
+  if((ic = iconv_open(ocode, icode)) == (iconv_t)-1) return NULL;
+  osiz = isiz * 5;
+  if(!(obuf = malloc(osiz + 1))){
+    iconv_close(ic);
+    return NULL;
+  }
+  wp = obuf;
+  rp = (char *)ptr;
+  miss = 0;
+  while(isiz > 0){
+    if(iconv(ic, (void *)&rp, &isiz, &wp, &osiz) == -1){
+      if(errno == EILSEQ && (*rp == 0x5c || *rp == 0x7e)){
+	*wp = *rp;
+	wp++;
+	rp++;
+	isiz--;
+      } else if(errno == EILSEQ || errno == EINVAL){
+	rp++;
+	isiz--;
+	miss++;
+      } else {
+	break;
+      }
+    }
+  }
+  *wp = '\0';
+  if(iconv_close(ic) == -1){
+    free(obuf);
+    return NULL;
+  }
+  if(sp) *sp = wp - obuf;
+  if(mp) *mp = miss;
+  return obuf;
+}
+
+
+static const char *_qdbm_encname_impl(const char *ptr, int size){
+  const char *hypo;
+  int i, miss, cr;
+  if(size < 0) size = strlen(ptr);
+  if(size > ICONVCHECKSIZ) size = ICONVCHECKSIZ;
+  if(size >= 2 && (!memcmp(ptr, "\xfe\xff", 2) || !memcmp(ptr, "\xff\xfe", 2))) return "UTF-16";
+  for(i = 0; i < size - 1; i += 2){
+    if(ptr[i] == 0 && ptr[i+1] != 0) return "UTF-16BE";
+    if(ptr[i+1] == 0 && ptr[i] != 0) return "UTF-16LE";
+  }
+  for(i = 0; i < size - 3; i++){
+    if(ptr[i] == 0x1b){
+      i++;
+      if(ptr[i] == '(' && strchr("BJHI", ptr[i+1])) return "ISO-2022-JP";
+      if(ptr[i] == '$' && strchr("@B(", ptr[i+1])) return "ISO-2022-JP";
+    }
+  }
+  if(_qdbm_encmiss(ptr, size, "US-ASCII", "UTF-16BE") < 1) return "US-ASCII";
+  if(_qdbm_encmiss(ptr, size, "UTF-8", "UTF-16BE") < 1) return "UTF-8";
+  hypo = NULL;
+  cr = FALSE;
+  for(i = 0; i < size; i++){
+    if(ptr[i] == 0xd){
+      cr = TRUE;
+      break;
+    }
+  }
+  if(cr){
+    if((miss = _qdbm_encmiss(ptr, size, "Shift_JIS", "EUC-JP")) < 1) return "Shift_JIS";
+    if(!hypo && miss / (double)size <= ICONVALLWRAT) hypo = "Shift_JIS";
+    if((miss = _qdbm_encmiss(ptr, size, "EUC-JP", "UTF-16BE")) < 1) return "EUC-JP";
+    if(!hypo && miss / (double)size <= ICONVALLWRAT) hypo = "EUC-JP";
+  } else {
+    if((miss = _qdbm_encmiss(ptr, size, "EUC-JP", "UTF-16BE")) < 1) return "EUC-JP";
+    if(!hypo && miss / (double)size <= ICONVALLWRAT) hypo = "EUC-JP";
+    if((miss = _qdbm_encmiss(ptr, size, "Shift_JIS", "EUC-JP")) < 1) return "Shift_JIS";
+    if(!hypo && miss / (double)size <= ICONVALLWRAT) hypo = "Shift_JIS";
+  }
+  if((miss = _qdbm_encmiss(ptr, size, "UTF-8", "UTF-16BE")) < 1) return "UTF-8";
+  if(!hypo && miss / (double)size <= ICONVALLWRAT) hypo = "UTF-8";
+  if((miss = _qdbm_encmiss(ptr, size, "CP932", "UTF-16BE")) < 1) return "CP932";
+  if(!hypo && miss / (double)size <= ICONVALLWRAT) hypo = "CP932";
+  return hypo ? hypo : "ISO-8859-1";
+}
+
+
+static int _qdbm_encmiss(const char *ptr, int size, const char *icode, const char *ocode){
+  iconv_t ic;
+  char obuf[ICONVCHECKSIZ], *wp, *rp;
+  size_t isiz, osiz;
+  int miss;
+  isiz = size;
+  if((ic = iconv_open(ocode, icode)) == (iconv_t)-1) return ICONVMISSMAX;
+  miss = 0;
+  rp = (char *)ptr;
+  while(isiz > 0){
+    osiz = ICONVCHECKSIZ;
+    wp = obuf;
+    if(iconv(ic, (void *)&rp, &isiz, &wp, &osiz) == -1){
+      if(errno == EILSEQ || errno == EINVAL){
+	rp++;
+	isiz--;
+	miss++;
+	if(miss >= ICONVMISSMAX) break;
+      } else {
+	break;
+      }
+    }
+  }
+  if(iconv_close(ic) == -1) return ICONVMISSMAX;
+  return miss;
+}
+
+
+#else
+
+
+char *(*_qdbm_iconv)(const char *, int, const char *, const char *, int *, int *) = NULL;
+const char *(*_qdbm_encname)(const char *, int) = NULL;
+
+
+#endif
+
+
+
+/*************************************************************************************************
+ * common settings
+ *************************************************************************************************/
+
+
+int _qdbm_dummyfunc(void){
+  return 0;
+}
+
+
+
+/* END OF FILE */

Added: trunk/src/qdbm/myconf.h
==============================================================================
--- (empty file)
+++ trunk/src/qdbm/myconf.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,593 @@
+/*************************************************************************************************
+ * System configurations for QDBM
+ *							Copyright (C) 2000-2006 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM is free software; you can redistribute it and/or modify it under the terms of the GNU
+ * Lesser General Public License as published by the Free Software Foundation; either version
+ * 2.1 of the License or any later version.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#ifndef _MYCONF_H			 /* duplication check */
+#define _MYCONF_H
+
+#if defined(__cplusplus)		 /* export for C++ */
+extern "C" {
+#endif
+
+
+
+/*************************************************************************************************
+ * system discrimination
+ *************************************************************************************************/
+
+
+#if defined(__linux__)
+
+#define _SYS_LINUX_
+#define _QDBM_SYSNAME  "Linux"
+
+#elif defined(__FreeBSD__)
+
+#define _SYS_FREEBSD_
+#define _QDBM_SYSNAME  "FreeBSD"
+
+#elif defined(__NetBSD__)
+
+#define _SYS_NETBSD_
+#define _QDBM_SYSNAME  "NetBSD"
+
+#elif defined(__OpenBSD__)
+
+#define _SYS_OPENBSD_
+#define _QDBM_SYSNAME  "OpenBSD"
+
+#elif defined(__sun__)
+
+#define _SYS_SUNOS_
+#define _QDBM_SYSNAME  "SunOS"
+
+#elif defined(__hpux)
+
+#define _SYS_HPUX_
+#define _QDBM_SYSNAME  "HP-UX"
+
+#elif defined(__osf)
+
+#define _SYS_TRU64_
+#define _QDBM_SYSNAME  "Tru64"
+
+#elif defined(_AIX)
+
+#define _SYS_AIX_
+#define _QDBM_SYSNAME  "AIX"
+
+#elif defined(__APPLE__) && defined(__MACH__)
+
+#define _SYS_MACOSX_
+#define _QDBM_SYSNAME  "Mac OS X"
+
+#elif defined(_MSC_VER)
+
+#define _SYS_MSVC_
+#define _QDBM_SYSNAME  "Windows (VC++)"
+
+#elif defined(_WIN32)
+
+#define _SYS_MINGW_
+#define _QDBM_SYSNAME  "Windows (MinGW)"
+
+#elif defined(__CYGWIN__)
+
+#define _SYS_CYGWIN_
+#define _QDBM_SYSNAME  "Windows (Cygwin)"
+
+#elif defined(__riscos__) || defined(__riscos)
+
+#define _SYS_RISCOS_
+#define _QDBM_SYSNAME  "RISC OS"
+
+#else
+
+#define _SYS_GENERIC_
+#define _QDBM_SYSNAME  "Generic"
+
+#endif
+
+
+
+/*************************************************************************************************
+ * general headers
+ *************************************************************************************************/
+
+
+#if defined(_SYS_MSVC_)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <time.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <direct.h>
+#include <windows.h>
+#include <io.h>
+
+#elif defined(_SYS_MINGW_)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <time.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <windows.h>
+#include <io.h>
+
+#elif defined(_SYS_CYGWIN_)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <time.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/times.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <windows.h>
+#include <io.h>
+
+#else
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <time.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/times.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+#endif
+
+
+
+/*************************************************************************************************
+ * notation of filesystems
+ *************************************************************************************************/
+
+
+#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_)
+
+#define MYPATHCHR	'\\'
+#define MYPATHSTR	"\\"
+#define MYEXTCHR	'.'
+#define MYEXTSTR	"."
+#define MYCDIRSTR	"."
+#define MYPDIRSTR	".."
+
+#elif defined(_SYS_RISCOS_)
+
+#define MYPATHCHR	'.'
+#define MYPATHSTR	"."
+#define MYEXTCHR	'/'
+#define MYEXTSTR	"/"
+#define MYCDIRSTR	"@"
+#define MYPDIRSTR	"^"
+
+#else
+
+#define MYPATHCHR	'/'
+#define MYPATHSTR	"/"
+#define MYEXTCHR	'.'
+#define MYEXTSTR	"."
+#define MYCDIRSTR	"."
+#define MYPDIRSTR	".."
+
+#endif
+
+
+
+/*************************************************************************************************
+ * for dosish filesystems
+ *************************************************************************************************/
+
+
+#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) || defined(_SYS_CYGWIN_)
+
+#undef UNICODE
+#undef open
+
+#define \
+  open(pathname, flags, mode) \
+  open(pathname, flags | O_BINARY, mode)
+
+#define \
+  lstat(pathname, buf) \
+  _qdbm_win32_lstat(pathname, buf)
+
+int _qdbm_win32_lstat(const char *pathname, struct stat *buf);
+
+#else
+
+#undef O_BINARY
+#undef O_TEXT
+#undef setmode
+
+#define O_BINARY	   0
+#define O_TEXT		   1
+
+#define \
+  setmode(fd, mode) \
+  (O_BINARY)
+
+#endif
+
+
+
+/*************************************************************************************************
+ * for POSIX thread
+ *************************************************************************************************/
+
+
+#if defined(MYPTHREAD)
+
+#define _qdbm_ptsafe	   TRUE
+
+void *_qdbm_settsd(void *ptr, int size, const void *initval);
+
+#else
+
+#define _qdbm_ptsafe	   FALSE
+
+#define \
+  _qdbm_settsd(ptr, size, initval) \
+  (NULL)
+
+#endif
+
+
+
+/*************************************************************************************************
+ * for systems without file locking
+ *************************************************************************************************/
+
+
+#if defined(_SYS_RISCOS_) || defined(MYNOLOCK)
+
+#undef fcntl
+
+#define \
+  fcntl(fd, cmd, lock) \
+  (0)
+
+#endif
+
+
+
+/*************************************************************************************************
+ * for systems without mmap
+ *************************************************************************************************/
+
+
+#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) || \
+  defined(_SYS_FREEBSD_) || defined(_SYS_NETBSD_) || defined(_SYS_OPENBSD_) || \
+  defined(_SYS_AIX_) || defined(_SYS_RISCOS_) || defined(MYNOMMAP)
+
+#undef PROT_EXEC
+#undef PROT_READ
+#undef PROT_WRITE
+#undef PROT_NONE
+#undef MAP_FIXED
+#undef MAP_SHARED
+#undef MAP_PRIVATE
+#undef MAP_FAILED
+#undef MS_ASYNC
+#undef MS_SYNC
+#undef MS_INVALIDATE
+#undef mmap
+#undef munmap
+#undef msync
+#undef mflush
+
+#define PROT_EXEC      (1 << 0)
+#define PROT_READ      (1 << 1)
+#define PROT_WRITE     (1 << 2)
+#define PROT_NONE      (1 << 3)
+#define MAP_FIXED      1
+#define MAP_SHARED     2
+#define MAP_PRIVATE    3
+#define MAP_FAILED     ((void *)-1)
+#define MS_ASYNC       (1 << 0)
+#define MS_SYNC        (1 << 1)
+#define MS_INVALIDATE  (1 << 2)
+
+#define \
+  mmap(start, length, prot, flags, fd, offset) \
+  _qdbm_mmap(start, length, prot, flags, fd, offset)
+
+#define \
+  munmap(start, length) \
+  _qdbm_munmap(start, length)
+
+#define \
+  msync(start, length, flags) \
+  _qdbm_msync(start, length, flags)
+
+#define \
+  mflush(start, length, flags) \
+  _qdbm_msync(start, length, flags)
+
+void *_qdbm_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
+int _qdbm_munmap(void *start, size_t length);
+int _qdbm_msync(const void *start, size_t length, int flags);
+
+#else
+
+#undef mflush
+#define \
+  mflush(start, length, flags) \
+  (0)
+
+#endif
+
+
+
+/*************************************************************************************************
+ * for reentrant time routines
+ *************************************************************************************************/
+
+
+struct tm *_qdbm_gmtime(const time_t *timep, struct tm *result);
+struct tm *_qdbm_localtime(const time_t *timep, struct tm *result);
+
+
+
+/*************************************************************************************************
+ * for systems without times
+ *************************************************************************************************/
+
+
+#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_)
+
+#undef times
+#undef sysconf
+
+struct tms {
+  clock_t tms_utime;
+  clock_t tms_stime;
+  clock_t tms_cutime;
+  clock_t tms_cstime;
+};
+
+#define \
+  times(buf) \
+  _qdbm_times(buf)
+
+#define \
+  sysconf(name) \
+  (CLOCKS_PER_SEC)
+
+clock_t _qdbm_times(struct tms *buf);
+
+#endif
+
+
+
+/*************************************************************************************************
+ * for Win32
+ *************************************************************************************************/
+
+
+#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_)
+
+#undef F_WRLCK
+#undef F_RDLCK
+#undef F_SETLK
+#undef F_SETLKW
+#undef fcntl
+#undef ftruncate
+#undef fsync
+#undef mkdir
+#undef rename
+
+#define F_WRLCK        0
+#define F_RDLCK        1
+#define F_SETLK        0
+#define F_SETLKW       1
+
+struct flock {
+  int l_type;
+  int l_whence;
+  int l_start;
+  int l_len;
+  int l_pid;
+};
+
+#define \
+  fcntl(fd, cmd, lock) \
+  _qdbm_win32_fcntl(fd, cmd, lock)
+
+#define \
+  ftruncate(fd, length) \
+  _chsize(fd, length)
+
+#define \
+  fsync(fd) \
+  (0)
+
+#define \
+  mkdir(pathname, mode) \
+  mkdir(pathname)
+
+#define \
+  rename(oldpath, newpath) \
+  (unlink(newpath), rename(oldpath, newpath))
+
+int _qdbm_win32_fcntl(int fd, int cmd, struct flock *lock);
+
+#endif
+
+
+#if defined(_SYS_MSVC_)
+
+#undef S_ISDIR
+#undef S_ISREG
+#undef opendir
+#undef closedir
+#undef readdir
+
+#define S_ISDIR(x)     (x & _S_IFDIR)
+#define S_ISREG(x)     (x & _S_IFREG)
+
+struct dirent {
+  char d_name[1024];
+};
+
+typedef struct {
+  HANDLE fh;
+  WIN32_FIND_DATA data;
+  struct dirent de;
+  int first;
+} DIR;
+
+#define \
+  opendir(name) \
+  _qdbm_win32_opendir(name)
+
+#define \
+  closedir(dir) \
+  _qdbm_win32_closedir(dir)
+
+#define \
+  readdir(dir) \
+  _qdbm_win32_readdir(dir)
+
+DIR *_qdbm_win32_opendir(const char *name);
+
+int _qdbm_win32_closedir(DIR *dir);
+
+struct dirent *_qdbm_win32_readdir(DIR *dir);
+
+#endif
+
+
+
+/*************************************************************************************************
+ * for checking information of the system
+ *************************************************************************************************/
+
+
+int _qdbm_vmemavail(size_t size);
+
+
+
+/*************************************************************************************************
+ * for ZLIB
+ *************************************************************************************************/
+
+
+enum {
+  _QDBM_ZMZLIB,
+  _QDBM_ZMRAW,
+  _QDBM_ZMGZIP
+};
+
+
+extern char *(*_qdbm_deflate)(const char *, int, int *, int);
+
+extern char *(*_qdbm_inflate)(const char *, int, int *, int);
+
+extern unsigned int (*_qdbm_getcrc)(const char *, int);
+
+
+
+/*************************************************************************************************
+ * for LZO
+ *************************************************************************************************/
+
+
+extern char *(*_qdbm_lzoencode)(const char *, int, int *);
+
+extern char *(*_qdbm_lzodecode)(const char *, int, int *);
+
+
+
+/*************************************************************************************************
+ * for BZIP2
+ *************************************************************************************************/
+
+
+extern char *(*_qdbm_bzencode)(const char *, int, int *);
+
+extern char *(*_qdbm_bzdecode)(const char *, int, int *);
+
+
+
+/*************************************************************************************************
+ * for ICONV
+ *************************************************************************************************/
+
+
+extern char *(*_qdbm_iconv)(const char *, int, const char *, const char *, int *, int *);
+
+extern const char *(*_qdbm_encname)(const char *, int);
+
+
+
+/*************************************************************************************************
+ * common settings
+ *************************************************************************************************/
+
+
+#undef TRUE
+#define TRUE	       1
+#undef FALSE
+#define FALSE	       0
+
+#define sizeof(a)      ((int)sizeof(a))
+
+int _qdbm_dummyfunc(void);
+
+
+
+#if defined(__cplusplus)		 /* export for C++ */
+}
+#endif
+
+#endif					 /* duplication check */
+
+
+/* END OF FILE */

Added: trunk/src/tracker-applet/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/tracker-applet/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,43 @@
+include $(top_srcdir)/Makefile.decl
+
+INCLUDES =								\
+	-DSHAREDIR=\""$(datadir)"\"					\
+	-DLOCALEDIR=\""$(localedir)"\" 					\
+	-I$(top_srcdir)/src						\
+	$(TRACKERAPPLET_CFLAGS)						\
+	$(LIBGLADE_CFLAGS)
+
+bin_PROGRAMS = tracker-applet
+
+tracker_applet_LDADD = 							\
+	$(top_builddir)/src/libtracker/libtrackerclient.la 		\
+	$(top_builddir)/src/libtracker-common/libtracker-common.la 	\
+	$(TRACKERAPPLET_LIBS)						\
+	$(LIBGLADE_LIBS)
+
+tracker_applet_SOURCES = 						\
+	tracker-applet.c 						\
+	tracker-applet.h 						\
+	tracker-applet-marshallers.c 					\
+	tracker-applet-marshallers.h
+
+icondir = $(datadir)/tracker/icons
+
+icon_DATA = 								\
+	tracker-applet-default.png 					\
+	tracker-applet-indexing1.png 					\
+	tracker-applet-indexing2.png 					\
+	tracker-applet-paused.png
+
+ INTLTOOL_DESKTOP_RULE@
+
+autostartdir = $(sysconfdir)/xdg/autostart
+autostart_in_files = tracker-applet.desktop.in
+autostart_DATA = $(autostart_in_files:.desktop.in=.desktop)
+
+pkgdata_DATA = tracker-applet-prefs.glade
+
+
+EXTRA_DIST = $(icon_DATA) $(pkgdata_DATA)
+
+CLEANFILES = $(autostart_DATA)

Added: trunk/src/tracker-applet/tracker-applet-default.png
==============================================================================
Binary file. No diff available.

Added: trunk/src/tracker-applet/tracker-applet-indexing1.png
==============================================================================
Binary file. No diff available.

Added: trunk/src/tracker-applet/tracker-applet-indexing2.png
==============================================================================
Binary file. No diff available.

Added: trunk/src/tracker-applet/tracker-applet-marshallers.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-applet/tracker-applet-marshallers.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,143 @@
+#include <glib-object.h>
+#include "tracker-applet-marshallers.h"
+
+
+#ifdef G_ENABLE_DEBUG
+#define g_marshal_value_peek_boolean(v)  g_value_get_boolean (v)
+#define g_marshal_value_peek_char(v)	 g_value_get_char (v)
+#define g_marshal_value_peek_uchar(v)	 g_value_get_uchar (v)
+#define g_marshal_value_peek_int(v)	 g_value_get_int (v)
+#define g_marshal_value_peek_uint(v)	 g_value_get_uint (v)
+#define g_marshal_value_peek_long(v)	 g_value_get_long (v)
+#define g_marshal_value_peek_ulong(v)	 g_value_get_ulong (v)
+#define g_marshal_value_peek_int64(v)	 g_value_get_int64 (v)
+#define g_marshal_value_peek_uint64(v)	 g_value_get_uint64 (v)
+#define g_marshal_value_peek_enum(v)	 g_value_get_enum (v)
+#define g_marshal_value_peek_flags(v)	 g_value_get_flags (v)
+#define g_marshal_value_peek_float(v)	 g_value_get_float (v)
+#define g_marshal_value_peek_double(v)	 g_value_get_double (v)
+#define g_marshal_value_peek_string(v)	 (char*) g_value_get_string (v)
+#define g_marshal_value_peek_param(v)	 g_value_get_param (v)
+#define g_marshal_value_peek_boxed(v)	 g_value_get_boxed (v)
+#define g_marshal_value_peek_pointer(v)  g_value_get_pointer (v)
+#define g_marshal_value_peek_object(v)	 g_value_get_object (v)
+#else /* !G_ENABLE_DEBUG */
+/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
+ *	    Do not access GValues directly in your code. Instead, use the
+ *	    g_value_get_*() functions
+ */
+#define g_marshal_value_peek_boolean(v)  (v)->data[0].v_int
+#define g_marshal_value_peek_char(v)	 (v)->data[0].v_int
+#define g_marshal_value_peek_uchar(v)	 (v)->data[0].v_uint
+#define g_marshal_value_peek_int(v)	 (v)->data[0].v_int
+#define g_marshal_value_peek_uint(v)	 (v)->data[0].v_uint
+#define g_marshal_value_peek_long(v)	 (v)->data[0].v_long
+#define g_marshal_value_peek_ulong(v)	 (v)->data[0].v_ulong
+#define g_marshal_value_peek_int64(v)	 (v)->data[0].v_int64
+#define g_marshal_value_peek_uint64(v)	 (v)->data[0].v_uint64
+#define g_marshal_value_peek_enum(v)	 (v)->data[0].v_long
+#define g_marshal_value_peek_flags(v)	 (v)->data[0].v_ulong
+#define g_marshal_value_peek_float(v)	 (v)->data[0].v_float
+#define g_marshal_value_peek_double(v)	 (v)->data[0].v_double
+#define g_marshal_value_peek_string(v)	 (v)->data[0].v_pointer
+#define g_marshal_value_peek_param(v)	 (v)->data[0].v_pointer
+#define g_marshal_value_peek_boxed(v)	 (v)->data[0].v_pointer
+#define g_marshal_value_peek_pointer(v)  (v)->data[0].v_pointer
+#define g_marshal_value_peek_object(v)	 (v)->data[0].v_pointer
+#endif /* !G_ENABLE_DEBUG */
+
+
+/* VOID:STRING,BOOLEAN,BOOLEAN,BOOLEAN,BOOLEAN,BOOLEAN (/dev/stdin:1) */
+void
+tracker_VOID__STRING_BOOLEAN_BOOLEAN_BOOLEAN_BOOLEAN_BOOLEAN (GClosure	   *closure,
+							      GValue	   *return_value,
+							      guint	    n_param_values,
+							      const GValue *param_values,
+							      gpointer	    invocation_hint,
+							      gpointer	    marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__STRING_BOOLEAN_BOOLEAN_BOOLEAN_BOOLEAN_BOOLEAN) (gpointer	  data1,
+										     gpointer	  arg_1,
+										     gboolean	  arg_2,
+										     gboolean	  arg_3,
+										     gboolean	  arg_4,
+										     gboolean	  arg_5,
+										     gboolean	  arg_6,
+										     gboolean	  arg_7,
+										     gpointer	  data2);
+  register GMarshalFunc_VOID__STRING_BOOLEAN_BOOLEAN_BOOLEAN_BOOLEAN_BOOLEAN callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+
+  g_return_if_fail (n_param_values == 8);
+
+  if (G_CCLOSURE_SWAP_DATA (closure))
+    {
+      data1 = closure->data;
+      data2 = g_value_peek_pointer (param_values + 0);
+    }
+  else
+    {
+      data1 = g_value_peek_pointer (param_values + 0);
+      data2 = closure->data;
+    }
+  callback = (GMarshalFunc_VOID__STRING_BOOLEAN_BOOLEAN_BOOLEAN_BOOLEAN_BOOLEAN) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+	    g_marshal_value_peek_string (param_values + 1),
+	    g_marshal_value_peek_boolean (param_values + 2),
+	    g_marshal_value_peek_boolean (param_values + 3),
+	    g_marshal_value_peek_boolean (param_values + 4),
+	    g_marshal_value_peek_boolean (param_values + 5),
+	    g_marshal_value_peek_boolean (param_values + 6),
+	    g_marshal_value_peek_boolean (param_values + 7),
+	    data2);
+}
+
+/* VOID:INT (/dev/stdin:2) */
+
+/* VOID:STRING,STRING,INT,INT,INT,DOUBLE (/dev/stdin:3) */
+void
+tracker_VOID__STRING_STRING_INT_INT_INT_DOUBLE (GClosure     *closure,
+						GValue	     *return_value,
+						guint	      n_param_values,
+						const GValue *param_values,
+						gpointer      invocation_hint,
+						gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__STRING_STRING_INT_INT_INT_DOUBLE) (gpointer     data1,
+								       gpointer     arg_1,
+								       gpointer     arg_2,
+								       gint	    arg_3,
+								       gint	    arg_4,
+								       gint	    arg_5,
+								       gdouble	    arg_6,
+								       gpointer     data2);
+  register GMarshalFunc_VOID__STRING_STRING_INT_INT_INT_DOUBLE callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+
+  g_return_if_fail (n_param_values == 7);
+
+  if (G_CCLOSURE_SWAP_DATA (closure))
+    {
+      data1 = closure->data;
+      data2 = g_value_peek_pointer (param_values + 0);
+    }
+  else
+    {
+      data1 = g_value_peek_pointer (param_values + 0);
+      data2 = closure->data;
+    }
+  callback = (GMarshalFunc_VOID__STRING_STRING_INT_INT_INT_DOUBLE) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+	    g_marshal_value_peek_string (param_values + 1),
+	    g_marshal_value_peek_string (param_values + 2),
+	    g_marshal_value_peek_int (param_values + 3),
+	    g_marshal_value_peek_int (param_values + 4),
+	    g_marshal_value_peek_int (param_values + 5),
+	    g_marshal_value_peek_double (param_values + 6),
+	    data2);
+}
+

Added: trunk/src/tracker-applet/tracker-applet-marshallers.h
==============================================================================
--- (empty file)
+++ trunk/src/tracker-applet/tracker-applet-marshallers.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,30 @@
+#ifndef __tracker_MARSHAL_H__
+#define __tracker_MARSHAL_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/* VOID:STRING,BOOLEAN,BOOLEAN,BOOLEAN,BOOLEAN,BOOLEAN (/dev/stdin:1) */
+extern void tracker_VOID__STRING_BOOLEAN_BOOLEAN_BOOLEAN_BOOLEAN_BOOLEAN (GClosure     *closure,
+									  GValue       *return_value,
+									  guint		n_param_values,
+									  const GValue *param_values,
+									  gpointer	invocation_hint,
+									  gpointer	marshal_data);
+
+/* VOID:INT (/dev/stdin:2) */
+#define tracker_VOID__INT	g_cclosure_marshal_VOID__INT
+
+/* VOID:STRING,STRING,INT,INT,INT (/dev/stdin:3) */
+extern void tracker_VOID__STRING_STRING_INT_INT_INT_DOUBLE (GClosure	 *closure,
+							    GValue	 *return_value,
+							    guint	  n_param_values,
+							    const GValue *param_values,
+							    gpointer	  invocation_hint,
+							    gpointer	  marshal_data);
+
+G_END_DECLS
+
+#endif /* __tracker_MARSHAL_H__ */
+

Added: trunk/src/tracker-applet/tracker-applet-paused.png
==============================================================================
Binary file. No diff available.

Added: trunk/src/tracker-applet/tracker-applet-prefs.glade
==============================================================================
--- (empty file)
+++ trunk/src/tracker-applet/tracker-applet-prefs.glade	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,231 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--Generated with glade3 3.4.0 on Sun Mar  2 22:41:46 2008 -->
+<glade-interface>
+  <widget class="GtkWindow" id="wnd_prefs">
+    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+    <property name="title" translatable="yes">Applet Preferences</property>
+    <property name="resizable">False</property>
+    <property name="modal">True</property>
+    <property name="window_position">GTK_WIN_POS_CENTER_ALWAYS</property>
+    <property name="destroy_with_parent">True</property>
+    <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+    <property name="skip_taskbar_hint">True</property>
+    <property name="skip_pager_hint">True</property>
+    <child>
+      <widget class="GtkVBox" id="vbox1">
+        <property name="visible">True</property>
+        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+        <property name="spacing">8</property>
+        <child>
+          <widget class="GtkVBox" id="vbox24">
+            <property name="visible">True</property>
+            <property name="border_width">12</property>
+            <property name="spacing">19</property>
+            <child>
+              <widget class="GtkFrame" id="fraThrottling2">
+                <property name="visible">True</property>
+                <property name="label_xalign">0</property>
+                <property name="shadow_type">GTK_SHADOW_NONE</property>
+                <child>
+                  <widget class="GtkAlignment" id="alignment8">
+                    <property name="visible">True</property>
+                    <property name="top_padding">6</property>
+                    <property name="left_padding">12</property>
+                    <child>
+                      <widget class="GtkVBox" id="vbox25">
+                        <property name="visible">True</property>
+                        <property name="spacing">6</property>
+                        <child>
+                          <widget class="GtkCheckButton" id="chk_animate">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                            <property name="label" translatable="yes">Animate _icon when indexing</property>
+                            <property name="use_underline">True</property>
+                            <property name="response_id">0</property>
+                            <property name="draw_indicator">True</property>
+                            <signal name="toggled" handler="chk_animate_toggled_cb" object="icon"/>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="lblAnimation">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">&lt;b&gt;Animation&lt;/b&gt;</property>
+                    <property name="use_markup">True</property>
+                  </widget>
+                  <packing>
+                    <property name="type">label_item</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkFrame" id="frame6">
+                <property name="visible">True</property>
+                <property name="label_xalign">0</property>
+                <property name="shadow_type">GTK_SHADOW_NONE</property>
+                <child>
+                  <widget class="GtkAlignment" id="alignment11">
+                    <property name="visible">True</property>
+                    <property name="top_padding">6</property>
+                    <property name="left_padding">12</property>
+                    <child>
+                      <widget class="GtkVBox" id="vbox27">
+                        <property name="visible">True</property>
+                        <property name="spacing">6</property>
+                        <child>
+                          <widget class="GtkCheckButton" id="chk_show_icon">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="label" translatable="yes">_Hide Icon (except when displaying messages to user)</property>
+                            <property name="use_underline">True</property>
+                            <property name="response_id">0</property>
+                            <property name="draw_indicator">True</property>
+                            <signal name="toggled" handler="chk_show_icon_toggled_cb" object="icon"/>
+                          </widget>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                          </packing>
+                        </child>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="label40">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">&lt;b&gt;Visibility&lt;/b&gt;</property>
+                    <property name="use_markup">True</property>
+                  </widget>
+                  <packing>
+                    <property name="type">label_item</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkFrame" id="frame4">
+                <property name="visible">True</property>
+                <property name="label_xalign">0</property>
+                <property name="shadow_type">GTK_SHADOW_NONE</property>
+                <child>
+                  <widget class="GtkAlignment" id="alignment10">
+                    <property name="visible">True</property>
+                    <property name="top_padding">6</property>
+                    <property name="left_padding">12</property>
+                    <child>
+                      <widget class="GtkVBox" id="vbox26">
+                        <property name="visible">True</property>
+                        <property name="spacing">6</property>
+                        <child>
+                          <widget class="GtkRadioButton" id="opt_pause_off">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">_Off</property>
+                            <property name="use_underline">True</property>
+                            <property name="response_id">0</property>
+                            <property name="draw_indicator">True</property>
+                            <signal name="group_changed" handler="opt_pause_off_group_changed_cb" object="icon"/>
+                          </widget>
+                          <packing>
+                            <property name="expand">False</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="GtkRadioButton" id="opt_pause_index">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">_Automatically pause all indexing when computer is in active use</property>
+                            <property name="use_underline">True</property>
+                            <property name="response_id">0</property>
+                            <property name="active">True</property>
+                            <property name="draw_indicator">True</property>
+                            <property name="group">opt_pause_off</property>
+                          </widget>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="GtkRadioButton" id="opt_pause_merge">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">Automatically _pause if indexing may degrade performance of other applications in active use</property>
+                            <property name="use_underline">True</property>
+                            <property name="response_id">0</property>
+                            <property name="active">True</property>
+                            <property name="draw_indicator">True</property>
+                            <property name="group">opt_pause_off</property>
+                          </widget>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="position">2</property>
+                          </packing>
+                        </child>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="label39">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">&lt;b&gt;Smart Pausing&lt;/b&gt;</property>
+                    <property name="use_markup">True</property>
+                  </widget>
+                  <packing>
+                    <property name="type">label_item</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="padding">6</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkHButtonBox" id="hbuttonbox1">
+            <property name="visible">True</property>
+            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+            <property name="border_width">4</property>
+            <property name="homogeneous">True</property>
+            <property name="layout_style">GTK_BUTTONBOX_END</property>
+            <child>
+              <widget class="GtkButton" id="btn_close">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                <property name="label">gtk-close</property>
+                <property name="use_stock">True</property>
+                <property name="response_id">1</property>
+                <signal name="clicked" handler="on_btn_close_clicked"/>
+              </widget>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">6</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+</glade-interface>

Added: trunk/src/tracker-applet/tracker-applet.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-applet/tracker-applet.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,2162 @@
+/* Tracker Applet - tray icon for the tracker indexing daemon
+ *
+ * Copyright (C) 2007, Saleem Abdulrasool <compnerd gentoo org>
+ * Copyright (C) 2007, Jamie McCracken <jamiemcc blueyonder co uk>
+ * Copyright (C) 2008, Nokia
+ *
+ * Portions derived from xscreensaver and gnome-screensaver
+ * Copyright (c) 1991-2004 Jamie Zawinski <jwz jwz org>
+ * Copyright (C) 2004-2006 William Jon McCann <mccann jhu edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <time.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <X11/X.h>
+#include <gdk/gdkx.h>
+#include <gdk/gdkkeysyms.h>
+
+#include <gtk/gtk.h>
+
+#include <libnotify/notify.h>
+
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-bindings.h>
+
+#include <glade/glade.h>
+
+#include <libtracker/tracker.h>
+
+#include <libtracker-common/tracker-utils.h>
+
+#include "tracker-applet.h"
+#include "tracker-applet-marshallers.h"
+
+#define PROGRAM			"tracker-applet"
+#define PROGRAM_NAME		N_("Tracker Applet")
+
+#define HOMEPAGE		"http://www.tracker-project.org/";
+#define DESCRIPTION		"An applet for tracker"
+
+#define DBUS_SERVICE_TRACKER	"org.freedesktop.Tracker"
+#define DBUS_PATH_TRACKER	"/org/freedesktop/tracker"
+#define DBUS_INTERFACE_TRACKER	"org.freedesktop.Tracker"
+
+#define TRAY_ICON_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), TYPE_TRAY_ICON, TrayIconPrivate))
+
+typedef enum {
+	ICON_DEFAULT,
+	ICON_PAUSED,
+	ICON_INDEX1,
+	ICON_INDEX2,
+} IndexIcon;
+
+typedef enum {
+	INDEX_INITIALIZING,
+	INDEX_IDLE,
+	INDEX_BUSY,
+	INDEX_MERGING
+} IndexStateEnum;
+
+typedef enum {
+	PAUSE_NONE,
+	PAUSE_INTERNAL,
+	PAUSE_BATTERY
+} PauseStateEnum;
+
+typedef enum {
+	AUTO_PAUSE_NONE,
+	AUTO_PAUSE_INDEXING,
+	AUTO_PAUSE_MERGING
+} AutoPauseEnum;
+
+typedef struct {
+	gchar *name;
+	gchar *label;
+	GtkWidget *stat_label;
+} Stat_Info;
+
+typedef struct _TrayIconPrivate {
+	GtkStatusIcon *icon;
+	GKeyFile *keyfile;
+	gchar *filename;
+
+	/* Settings */
+	gboolean auto_hide;
+	gboolean disabled;
+	gboolean show_animation;
+	gboolean reindex;
+	AutoPauseEnum auto_pause_setting;
+
+	/* Auto pause vars */
+	gboolean auto_pause_timer_active;
+	time_t auto_pause_last_time_event;
+
+	gboolean user_pause;
+	gboolean auto_pause;
+
+	/* States */
+	IndexStateEnum index_state;
+	PauseStateEnum pause_state;
+	IndexIcon index_icon;
+	gboolean animated;
+	gboolean animated_timer_active;
+	gboolean is_watching_events;
+	gboolean email_indexing;
+	gboolean indexer_stopped;
+
+	/* Status hints */
+	gint items_done;
+	gint items_remaining;
+	gint items_total;
+	gdouble seconds_elapsed;
+
+	/* Main window */
+	GtkMenu *menu;
+
+	gboolean initial_index_msg_shown;
+
+	/* Tracker connection */
+	TrackerClient *tracker;
+
+	/* Stats window table shown */
+	gboolean stat_window_active;
+	gboolean stat_request_pending;
+
+	/* Prefs window */
+	GtkWidget *prefs_window;
+	GtkWidget *chk_animate;
+	GtkWidget *chk_show_icon;
+	GtkWidget *opt_pause_off;
+	GtkWidget *opt_pause_index;
+	GtkWidget *opt_pause_merge;
+	GtkWidget *btn_close;
+} TrayIconPrivate;
+
+static void set_auto_pause (TrayIcon *icon,
+			    gboolean  pause);
+
+static TrayIcon *main_icon;
+
+static gchar *initial_index_1;
+static gchar *initial_index_2;
+
+static gchar *index_icons[4] = {
+	"tracker-applet-default.png",
+	"tracker-applet-paused.png",
+	"tracker-applet-indexing1.png",
+	"tracker-applet-indexing2.png"
+};
+
+static Stat_Info stat_info[13] = {
+	{"Files", NULL, NULL},
+	{"Folders", NULL, NULL},
+	{"Documents", NULL, NULL},
+	{"Images", NULL, NULL},
+	{"Music", NULL, NULL},
+	{"Videos", NULL, NULL},
+	{"Text", NULL, NULL},
+	{"Development", NULL, NULL},
+	{"Other", NULL, NULL},
+	{"Applications", NULL, NULL},
+	{"Conversations", NULL, NULL},
+	{"Emails", NULL, NULL},
+	{NULL, NULL, NULL},
+};
+
+static gboolean disable_daemon_start;
+
+static GOptionEntry entries[] = {
+	{ "disable-daemon-start", 'd', 0, G_OPTION_ARG_NONE, &disable_daemon_start,
+	  NULL,
+	  NULL
+	},
+	{ NULL }
+};
+
+static gboolean
+query_pointer_timeout (Window window)
+{
+	Window root;
+	Window child;
+	int root_x;
+	int root_y;
+	int win_x;
+	int win_y;
+	unsigned int mask;
+
+	gdk_error_trap_push ();
+	XQueryPointer (GDK_DISPLAY (),
+		       window,
+		       &root, &child, &root_x, &root_y, &win_x, &win_y,
+		       &mask);
+	gdk_display_sync (gdk_display_get_default ());
+	gdk_error_trap_pop ();
+
+	return FALSE;
+}
+
+static void
+set_status_hint (TrayIcon *icon)
+{
+	TrayIconPrivate *priv;
+	const char *status = NULL;
+	GString *hint;
+
+	priv = TRAY_ICON_GET_PRIVATE (icon);
+
+	hint = g_string_new ("Tracker");
+
+	switch (priv->index_state) {
+	case INDEX_INITIALIZING:
+		status = _("Initializing");
+		break;
+	case INDEX_IDLE:
+		status = _("Idle");
+		break;
+	case INDEX_BUSY:
+		status = _("Indexing");
+		break;
+	case INDEX_MERGING:
+		status = _("Merging");
+		break;
+	}
+
+	if (status) {
+		g_string_append (hint, " : ");
+		g_string_append (hint, status);
+	}
+
+	if (priv->user_pause) {
+		status = _("paused by user");
+	} else if (priv->auto_pause) {
+		status = _("paused by system");
+	} else {
+		switch (priv->pause_state) {
+		case PAUSE_INTERNAL:
+			status = _("paused by system");
+			break;
+#if 0
+		case PAUSE_BATTERY:
+			/* FIXME: We need to check if we are on the
+			 * battery first, this state purely means we
+			 * WILL pause on battery.
+			 */
+			status = _("paused by battery");
+			break;
+#endif
+		default:
+		case PAUSE_NONE:
+			status = NULL;
+			break;
+		}
+	}
+
+	if (status) {
+		g_string_append_printf (hint, " (%s)", status);
+	}
+
+	if (priv->index_state == INDEX_BUSY) {
+		gchar *str1;
+		gchar *str2;
+
+		str1 = tracker_seconds_estimate_to_string (priv->seconds_elapsed,
+							   FALSE,
+							   priv->items_done,
+							   priv->items_remaining);
+		str2 = tracker_seconds_to_string (priv->seconds_elapsed, FALSE);
+
+		if (str1) {
+			str1[0] = g_ascii_toupper (str1[0]);
+		}
+
+		if (str2) {
+			str2[0] = g_ascii_toupper (str2[0]);
+		}
+
+		g_string_append_printf (hint,
+					"\n"
+					"\n"
+					"%s : %d of %d\n"
+					"%s : %s\n"
+					"%s : %s",
+					_("Done"),
+					priv->items_done,
+					priv->items_total,
+					_("Estimated"),
+					str1,
+					_("Elapsed"),
+					str2);
+
+		g_free (str2);
+		g_free (str1);
+	}
+
+	if (priv->index_state == INDEX_MERGING) {
+		g_string_append_printf (hint, " %d/%d indexes being merged",
+					priv->items_done,
+					priv->items_total);
+	}
+
+	tray_icon_set_tooltip (icon, hint->str);
+
+	g_string_free (hint, TRUE);
+}
+
+static gboolean
+can_auto_pause (TrayIcon *icon)
+{
+	TrayIconPrivate *priv;
+
+	priv = TRAY_ICON_GET_PRIVATE (icon);
+
+	if (priv->user_pause ||
+	    priv->pause_state == PAUSE_BATTERY ||
+	    priv->disabled ||
+	    priv->indexer_stopped) {
+		return FALSE;
+	}
+
+	switch (priv->auto_pause_setting) {
+	case AUTO_PAUSE_NONE:
+		return FALSE;
+	case AUTO_PAUSE_INDEXING:
+		return priv->index_state != INDEX_IDLE;
+	case AUTO_PAUSE_MERGING:
+		return priv->index_state == INDEX_MERGING;
+	}
+
+	return TRUE;
+}
+
+static void
+set_tracker_icon (TrayIconPrivate *priv)
+{
+	const gchar *name;
+	gchar *path;
+
+	name = index_icons[priv->index_icon];
+	path = g_build_filename (SHAREDIR,
+				 "tracker",
+				 "icons",
+				 name,
+				 NULL);
+
+	if (g_file_test (path, G_FILE_TEST_EXISTS)) {
+		gtk_status_icon_set_from_file (priv->icon, path);
+	} else {
+		g_critical ("Could not find icon:'%s'", path);
+	}
+
+	g_free (path);
+}
+
+static gboolean
+set_icon (TrayIconPrivate *priv)
+{
+	if (!priv->user_pause) {
+		if (priv->index_state == INDEX_INITIALIZING ||
+		    priv->index_state == INDEX_IDLE) {
+			priv->animated = FALSE;
+			priv->animated_timer_active = FALSE;
+
+			if (priv->index_icon != ICON_DEFAULT) {
+				priv->index_icon = ICON_DEFAULT;
+				set_tracker_icon (priv);
+			}
+
+			return FALSE;
+		}
+	}
+
+	if (priv->user_pause ||
+	    priv->auto_pause ||
+	    priv->pause_state != PAUSE_NONE) {
+		if (priv->index_icon != ICON_PAUSED) {
+			priv->index_icon = ICON_PAUSED;
+			set_tracker_icon (priv);
+		}
+
+		priv->animated = FALSE;
+		priv->animated_timer_active = FALSE;
+
+		return FALSE;
+	}
+
+	if (priv->index_state != INDEX_INITIALIZING &&
+	    priv->index_state != INDEX_IDLE) {
+		if (priv->index_icon == ICON_INDEX2 || !priv->show_animation) {
+			priv->index_icon = ICON_DEFAULT;
+		} else if (priv->index_icon != ICON_INDEX1) {
+			priv->index_icon = ICON_INDEX1;
+		} else {
+			priv->index_icon = ICON_INDEX2;
+		}
+
+		set_tracker_icon (priv);
+
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+static gboolean
+auto_pause_timeout (gpointer data)
+{
+	TrayIcon *icon;
+	TrayIconPrivate *priv;
+	time_t t;
+
+	icon = data;
+	priv = TRAY_ICON_GET_PRIVATE (icon);
+
+	t = time (NULL);
+
+	if (priv->indexer_stopped) {
+		return FALSE;
+	}
+
+	if (t >= priv->auto_pause_last_time_event + 2) {
+		set_auto_pause (icon, FALSE);
+		return FALSE;
+	}
+
+	dbus_g_proxy_begin_call (priv->tracker->proxy,
+				 "PromptIndexSignals",
+				 NULL,
+				 NULL,
+				 NULL,
+				 G_TYPE_INVALID);
+
+	return TRUE;
+}
+
+static void
+set_auto_pause (TrayIcon *icon,
+		gboolean  pause)
+{
+	TrayIconPrivate *priv;
+
+	priv = TRAY_ICON_GET_PRIVATE (icon);
+
+	/* Do not pause/unpause if in user pause  */
+	if (priv->user_pause) {
+		priv->auto_pause_timer_active = FALSE;
+		priv->auto_pause = FALSE;
+		return;
+	}
+
+	priv->auto_pause = pause;
+
+	if (pause) {
+		priv->auto_pause_last_time_event = time (NULL);
+
+		if (!priv->auto_pause_timer_active) {
+			g_timeout_add_seconds (2, auto_pause_timeout, icon);
+
+			priv->auto_pause_timer_active = TRUE;
+			tracker_set_bool_option (priv->tracker, "Pause", TRUE, NULL);
+		}
+
+		priv->animated = FALSE;
+	} else {
+		priv->auto_pause_timer_active = FALSE;
+		priv->auto_pause = FALSE;
+
+		tracker_set_bool_option (priv->tracker, "Pause", FALSE,  NULL);
+	}
+
+	set_icon (priv);
+}
+
+static void
+start_auto_pause_timer (TrayIcon *icon)
+{
+	TrayIconPrivate *priv;
+
+	priv = TRAY_ICON_GET_PRIVATE (icon);
+
+	if (!can_auto_pause (icon)) {
+		return;
+	}
+
+	priv->auto_pause_last_time_event = time (NULL);
+
+	if (!priv->auto_pause_timer_active) {
+		g_timeout_add_seconds (2, auto_pause_timeout, icon);
+		set_auto_pause (icon, TRUE);
+	}
+}
+
+static void
+set_user_pause (TrayIcon *icon,
+		gboolean  pause)
+{
+	TrayIconPrivate *priv;
+
+	priv = TRAY_ICON_GET_PRIVATE (icon);
+	priv->user_pause = pause;
+
+	tracker_set_bool_option (priv->tracker, "Pause", pause, NULL);
+}
+
+static void
+notice_events_inner (Window   window,
+		     gboolean enable,
+		     gboolean top)
+{
+	XWindowAttributes attrs;
+	unsigned long events;
+	Window root;
+	Window parent;
+	Window *kids;
+	unsigned int nkids;
+	int status;
+	GdkWindow *gwindow;
+
+	gwindow = gdk_window_lookup (window);
+	if (gwindow != NULL && window != GDK_ROOT_WINDOW ()) {
+		/* If it's one of ours, don't mess up its event mask. */
+		return;
+	}
+
+	kids = NULL;
+	status = XQueryTree (GDK_DISPLAY (),
+			     window,
+			     &root,
+			     &parent,
+			     &kids,
+			     &nkids);
+
+	if (status == 0) {
+		if (kids != NULL) {
+			XFree (kids);
+		}
+
+		return;
+	}
+
+	if (window == root) {
+		top = FALSE;
+	}
+
+	memset (&attrs, 0, sizeof (attrs));
+	XGetWindowAttributes (GDK_DISPLAY (), window, &attrs);
+
+	if (enable) {
+		/* Select for KeyPress on all windows that already have it selected */
+		events = ((attrs.all_event_masks | attrs.
+			   do_not_propagate_mask) & KeyPressMask);
+
+		/* Keep already selected events.  This is important when the
+		 * window == GDK_ROOT_WINDOW () since the mask will contain
+		 * StructureNotifyMask that is essential for RANDR support */
+		events |= attrs.your_event_mask;
+
+		/* Select for SubstructureNotify on all windows */
+		events |= SubstructureNotifyMask;
+
+		/* Select for PropertyNotify events to get user time changes */
+		events |= PropertyChangeMask;
+
+		/* As with keypress events, only select mouse motion events
+		 * for windows which already have them selected. */
+		events |=
+			((attrs.all_event_masks | attrs.
+			  do_not_propagate_mask) & (PointerMotionMask |
+						    PointerMotionHintMask));
+	} else {
+		/* We want to disable all events */
+
+		/* Don't mess up the root window */
+		if (window == GDK_ROOT_WINDOW ()) {
+			events = attrs.your_event_mask;
+		} else {
+			events = 0;
+		}
+	}
+
+	/* Select for SubstructureNotify on all windows.
+	 * Select for KeyPress on all windows that already have it selected.
+	 *
+	 * Note that we can't select for ButtonPress, because of X braindamage:
+	 * only one client at a time may select for ButtonPress on a given
+	 * window, though any number can select for KeyPress.  Someone explain
+	 * *that* to me.
+	 *
+	 * So, if the user spends a while clicking the mouse without ever moving
+	 * the mouse or touching the keyboard, we won't know that they've been
+	 * active, and the screensaver will come on.  That sucks, but I don't
+	 * know how to get around it.
+	 *
+	 * Since X presents mouse wheels as clicks, this applies to those, too:
+	 * scrolling through a document using only the mouse wheel doesn't
+	 * count as activity...  Fortunately, /proc/interrupts helps, on
+	 * systems that have it.  Oh, if it's a PS/2 mouse, not serial or USB.
+	 * This sucks!
+	 */
+	XSelectInput (GDK_DISPLAY (), window, events);
+
+	if (top && (events & KeyPressMask)) {
+		/* Only mention one window per tree */
+		top = FALSE;
+	}
+
+	if (kids != NULL) {
+		while (nkids > 0) {
+			notice_events_inner (kids[--nkids], enable, top);
+		}
+
+		XFree (kids);
+	}
+}
+
+static void
+notice_events (Window	window,
+	       gboolean enable)
+{
+	gdk_error_trap_push ();
+
+	notice_events_inner (window, enable, TRUE);
+
+	gdk_display_sync (gdk_display_get_default ());
+	gdk_error_trap_pop ();
+}
+
+static inline void
+start_notice_events (Window window)
+{
+	notice_events (window, TRUE);
+}
+
+static GdkFilterReturn
+filter_x_events (GdkXEvent *xevent,
+		 GdkEvent  *event,
+		 gpointer   data)
+{
+	XEvent *ev;
+	TrayIcon *icon;
+
+	icon = data;
+	ev = xevent;
+
+	switch (ev->xany.type) {
+	case KeyPress:
+	case KeyRelease:
+	case ButtonPress:
+	case ButtonRelease:
+		start_auto_pause_timer (icon);
+		break;
+
+	case PropertyNotify:
+		if (ev->xproperty.atom == gdk_x11_get_xatom_by_name ("_NET_WM_USER_TIME")) {
+			start_auto_pause_timer (icon);
+		}
+		break;
+
+	case CreateNotify: {
+		Window window;
+
+		window = ev->xcreatewindow.window;
+		start_notice_events (window);
+
+		break;
+	}
+
+	case MotionNotify:
+		if (ev->xmotion.is_hint) {
+			/* need to respond to hints so we continue to get events */
+			g_timeout_add (1000,
+				       (GSourceFunc) query_pointer_timeout,
+				       GINT_TO_POINTER (ev->xmotion.window));
+		}
+
+		start_auto_pause_timer (icon);
+		break;
+
+	default:
+		break;
+	}
+
+	return GDK_FILTER_CONTINUE;
+}
+
+static inline void
+stop_notice_events (Window window)
+{
+	notice_events (window, FALSE);
+}
+
+static void
+start_watching_events (TrayIcon *icon)
+{
+	TrayIconPrivate *priv;
+
+	priv = TRAY_ICON_GET_PRIVATE (icon);
+
+	if (priv->is_watching_events) {
+		return;
+	}
+
+	gdk_window_add_filter (NULL, (GdkFilterFunc) filter_x_events, icon);
+	start_notice_events (DefaultRootWindow (GDK_DISPLAY ()));
+	priv->is_watching_events = TRUE;
+}
+
+static void
+stop_watching_events (TrayIcon *icon)
+{
+	TrayIconPrivate *priv;
+
+	priv = TRAY_ICON_GET_PRIVATE (icon);
+
+	if (!priv->is_watching_events) {
+		return;
+	}
+
+	stop_notice_events (DefaultRootWindow (GDK_DISPLAY ()));
+	gdk_window_remove_filter (NULL, (GdkFilterFunc) filter_x_events, icon);
+	priv->is_watching_events = FALSE;
+}
+
+static void
+tray_icon_class_init (TrayIconClass *klass)
+{
+	g_type_class_add_private (klass, sizeof (TrayIconPrivate));
+}
+
+static void
+activate_icon (GtkStatusIcon *icon,
+	       gpointer       data)
+{
+	GError *error = NULL;
+	const gchar *command = "tracker-search-tool";
+
+	g_print ("Spawning command:'%s'\n", command);
+
+	if (!g_spawn_command_line_async (command, &error)) {
+		g_warning ("Unable to execute command:'%s', %s",
+			   command,
+			   error->message);
+		g_error_free (error);
+	}
+}
+
+static void
+search_menu_activated (GtkMenuItem *item,
+		       gpointer     data)
+{
+	activate_icon (NULL, NULL);
+}
+
+static void
+pause_menu_toggled (GtkCheckMenuItem *item,
+		    gpointer	      data)
+{
+	TrayIcon *icon;
+
+	icon = TRAY_ICON (data);
+
+	set_user_pause (icon,
+			gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (item)));
+}
+
+static inline void
+set_auto_pause_setting (TrayIcon      *icon,
+			AutoPauseEnum  auto_pause)
+{
+	TrayIconPrivate *priv;
+
+	priv = TRAY_ICON_GET_PRIVATE (icon);
+
+	priv->auto_pause_setting = auto_pause;
+}
+
+static void
+save_options (TrayIcon *icon)
+{
+	TrayIconPrivate *priv;
+	GError *error = NULL;
+	guint length;
+	gchar *contents;
+
+	priv = TRAY_ICON_GET_PRIVATE (icon);
+
+	g_key_file_set_boolean (priv->keyfile,
+				"Applet", "AnimateWhenIndexing",
+				gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->chk_animate)));
+	g_key_file_set_boolean (priv->keyfile,
+				"Applet", "AutoHideIcon",
+				gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->chk_show_icon)));
+	g_key_file_set_integer (priv->keyfile, "Applet", "SmartPause",
+				priv->auto_pause_setting);
+
+	contents = g_key_file_to_data (priv->keyfile, &length, &error);
+
+	if (error) {
+		g_error ("failed: g_key_file_to_data(): %s\n",
+			 error->message);
+		return;
+	}
+
+	g_file_set_contents (priv->filename, contents, -1, NULL);
+	g_free (contents);
+}
+
+static void
+prefs_closed (GtkWidget *widget,
+	      gpointer	 data)
+{
+	TrayIconPrivate *priv;
+
+	priv = TRAY_ICON_GET_PRIVATE (data);
+
+	save_options (data);
+
+	gtk_widget_destroy (priv->prefs_window);
+}
+
+static void
+opt_pause_off_group_changed_cb (GtkToggleButton *check_button,
+				gpointer	 user_data)
+{
+	TrayIcon *icon;
+	TrayIconPrivate *priv;
+	const gchar *name;
+
+	if (!gtk_toggle_button_get_active (check_button)) {
+		return;
+	}
+
+	icon = user_data;
+	priv = TRAY_ICON_GET_PRIVATE (icon);
+	name = gtk_widget_get_name (GTK_WIDGET (check_button));
+
+	if (g_str_equal (name, "opt_pause_off")) {
+		priv->auto_pause_setting = AUTO_PAUSE_NONE;
+		priv->auto_pause = FALSE;
+		stop_watching_events (icon);
+		return;
+	}
+
+	if (g_str_equal (name, "opt_pause_index")) {
+		priv->auto_pause_setting = AUTO_PAUSE_INDEXING;
+
+		if (can_auto_pause (icon)) {
+			start_watching_events (icon);
+		} else {
+			stop_watching_events (icon);
+		}
+
+		return;
+	}
+
+	if (g_str_equal (name, "opt_pause_merge")) {
+		priv->auto_pause_setting = AUTO_PAUSE_MERGING;
+
+		if (can_auto_pause (icon)) {
+			start_watching_events (icon);
+		} else {
+			stop_watching_events (icon);
+		}
+
+		return;
+	}
+}
+
+static void
+chk_animate_toggled_cb (GtkToggleButton *check_button,
+			gpointer	 user_data)
+{
+	TrayIcon *icon;
+	TrayIconPrivate *priv;
+
+	icon = user_data;
+	priv = TRAY_ICON_GET_PRIVATE (icon);
+
+	priv->show_animation = gtk_toggle_button_get_active (check_button);
+}
+
+static void
+chk_show_icon_toggled_cb (GtkToggleButton *check_button,
+			  gpointer	   user_data)
+{
+	TrayIcon *icon;
+	TrayIconPrivate *priv;
+
+	icon = user_data;
+	priv = TRAY_ICON_GET_PRIVATE (icon);
+
+	if (gtk_toggle_button_get_active (check_button)) {
+		priv->auto_hide = TRUE;
+		gtk_status_icon_set_visible (priv->icon, FALSE);
+	} else {
+		priv->auto_hide = FALSE;
+		if (!priv->disabled) {
+			gtk_status_icon_set_visible (priv->icon, TRUE);
+		}
+	}
+}
+
+static void
+create_prefs (TrayIcon *icon)
+{
+	TrayIconPrivate *priv;
+	GladeXML *glade;
+	gchar *filename;
+
+	priv = TRAY_ICON_GET_PRIVATE (icon);
+	filename = g_build_filename (SHAREDIR,
+				     "tracker",
+				     "tracker-applet-prefs.glade",
+				     NULL);
+	glade = glade_xml_new (filename, NULL, NULL);
+
+	if (!glade) {
+		g_error ("Unable to find locate '%s'", filename);
+		g_free (filename);
+		priv->prefs_window = NULL;
+		return;
+	}
+
+	g_free (filename);
+
+	priv->prefs_window = glade_xml_get_widget (glade, "wnd_prefs");
+	gtk_widget_hide (priv->prefs_window);
+	gtk_window_set_deletable (GTK_WINDOW (priv->prefs_window), FALSE);
+
+	priv->chk_animate = glade_xml_get_widget (glade, "chk_animate");
+	priv->chk_show_icon = glade_xml_get_widget (glade, "chk_show_icon");
+	priv->opt_pause_off = glade_xml_get_widget (glade, "opt_pause_off");
+	priv->opt_pause_index =
+		glade_xml_get_widget (glade, "opt_pause_index");
+	priv->opt_pause_merge =
+		glade_xml_get_widget (glade, "opt_pause_merge");
+	priv->btn_close = glade_xml_get_widget (glade, "btn_close");
+
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->chk_animate),
+				      priv->show_animation);
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->chk_show_icon),
+				      priv->auto_hide);
+
+	switch (priv->auto_pause_setting) {
+	case AUTO_PAUSE_NONE:
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON
+					      (priv->opt_pause_off), TRUE);
+		break;
+
+	case AUTO_PAUSE_INDEXING:
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON
+					      (priv->opt_pause_index), TRUE);
+		break;
+
+	case AUTO_PAUSE_MERGING:
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON
+					      (priv->opt_pause_merge), TRUE);
+		break;
+	}
+
+	/* connect signal handlers */
+	g_signal_connect (GTK_TOGGLE_BUTTON (priv->chk_animate), "toggled",
+			  G_CALLBACK (chk_animate_toggled_cb), main_icon);
+	g_signal_connect (GTK_TOGGLE_BUTTON (priv->chk_show_icon), "toggled",
+			  G_CALLBACK (chk_show_icon_toggled_cb), main_icon);
+	g_signal_connect (GTK_TOGGLE_BUTTON (priv->opt_pause_off), "toggled",
+			  G_CALLBACK (opt_pause_off_group_changed_cb),
+			  main_icon);
+	g_signal_connect (GTK_TOGGLE_BUTTON (priv->opt_pause_index),
+			  "toggled",
+			  G_CALLBACK (opt_pause_off_group_changed_cb),
+			  main_icon);
+	g_signal_connect (GTK_TOGGLE_BUTTON (priv->opt_pause_merge),
+			  "toggled",
+			  G_CALLBACK (opt_pause_off_group_changed_cb),
+			  main_icon);
+	g_signal_connect (priv->btn_close, "clicked",
+			  G_CALLBACK (prefs_closed), main_icon);
+}
+
+static void
+restart_tracker (GtkDialog *dialog,
+		 gint	    response,
+		 TrayIcon  *icon)
+{
+	gtk_widget_destroy (GTK_WIDGET (dialog));
+
+	if (response == GTK_RESPONSE_YES) {
+		TrayIconPrivate *priv;
+
+		g_print ("Attempting to restart tracker\n");
+
+		priv = TRAY_ICON_GET_PRIVATE (icon);
+		priv->reindex = TRUE;
+
+		dbus_g_proxy_begin_call (priv->tracker->proxy,
+					 "Shutdown",
+					 NULL,
+					 NULL,
+					 NULL,
+					 G_TYPE_BOOLEAN,
+					 TRUE, G_TYPE_INVALID);
+	}
+}
+
+static void
+reindex (GtkMenuItem *item,
+	 TrayIcon    *icon)
+{
+	GtkWidget *dialog;
+	gchar	  *primary;
+	gchar	  *secondary;
+
+	primary = g_strdup (_("Re-index your system?"));
+	secondary =
+		g_strdup (_("Indexing can take a long time. "
+			    "Are you sure you want to re-index?"));
+
+	dialog = gtk_message_dialog_new (NULL,
+					 GTK_DIALOG_MODAL,
+					 GTK_MESSAGE_WARNING,
+					 GTK_BUTTONS_YES_NO, primary);
+
+	gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+						  secondary);
+
+	g_free (primary);
+	g_free (secondary);
+
+	gtk_window_set_title (GTK_WINDOW (dialog), "");
+	gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+	gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 14);
+
+	g_signal_connect (G_OBJECT (dialog), "response",
+			  G_CALLBACK (restart_tracker), icon);
+	gtk_widget_show (dialog);
+
+}
+
+static void
+applet_preferences_menu_activated (GtkMenuItem *item,
+				   gpointer	data)
+{
+	TrayIconPrivate *priv;
+
+	priv = TRAY_ICON_GET_PRIVATE (data);
+
+	create_prefs (data);
+
+	gtk_widget_show (priv->prefs_window);
+}
+
+static void
+preferences_menu_activated (GtkMenuItem *item,
+			    gpointer	 data)
+{
+	GError *error = NULL;
+	const gchar *command = "tracker-preferences";
+
+	g_print ("Spawning command:'%s'\n", command);
+
+	if (!g_spawn_command_line_async (command, &error)) {
+		g_warning ("Unable to execute command:'%s', %s",
+			   command,
+			   error->message);
+		g_error_free (error);
+	}
+}
+
+static void
+stat_window_free (GtkWidget *widget,
+		  gint	     arg,
+		  gpointer   data)
+{
+	TrayIcon *icon;
+	TrayIconPrivate *priv;
+
+	icon = data;
+	priv = TRAY_ICON_GET_PRIVATE (icon);
+
+	priv->stat_window_active = FALSE;
+
+	gtk_widget_destroy (widget);
+}
+
+static gchar *
+get_stat_value (gchar	    ***stat_array,
+		const gchar   *stat)
+{
+	gchar **array;
+	gint i = 0;
+
+	while (stat_array[i][0]) {
+		array = stat_array[i];
+
+		if (array[0] && strcasecmp (stat, array[0]) == 0) {
+			return array[1];
+		}
+
+		i++;
+	}
+
+	return NULL;
+}
+
+static void
+update_stats (GPtrArray *array,
+	      GError	*error,
+	      gpointer	 data)
+{
+	TrayIcon *icon;
+	TrayIconPrivate *priv;
+	gchar ***pdata;
+	guint i;
+
+	icon = data;
+	priv = TRAY_ICON_GET_PRIVATE (icon);
+
+	if (error) {
+		g_warning ("Could not update statistics, %s",
+			   error->message);
+		g_error_free (error);
+		priv->stat_request_pending = FALSE;
+		return;
+	}
+
+	if (!array) {
+		return;
+	}
+
+	i = array->len;
+
+	if (i < 1 || !priv->stat_window_active) {
+		g_ptr_array_free (array, TRUE);
+		return;
+	}
+
+	pdata = (gchar ***) array->pdata;
+
+	for (i = 0; i < 12; i++) {
+		gtk_label_set_text (GTK_LABEL (stat_info[i].stat_label),
+				    get_stat_value (pdata, stat_info[i].name));
+	}
+
+	g_ptr_array_free (array, TRUE);
+
+	priv->stat_request_pending = FALSE;
+}
+
+static void
+refresh_stats (TrayIcon *icon)
+{
+	TrayIconPrivate *priv;
+
+	priv = TRAY_ICON_GET_PRIVATE (icon);
+
+	if (!priv->stat_window_active || priv->stat_request_pending) {
+		return;
+	}
+
+	priv->stat_request_pending = TRUE;
+
+	tracker_get_stats_async (priv->tracker,
+				 (TrackerGPtrArrayReply) update_stats,
+				 icon);
+}
+
+static void
+statistics_menu_activated (GtkMenuItem *item,
+			   gpointer	data)
+{
+	TrayIcon *icon;
+	TrayIconPrivate *priv;
+	GtkWidget *dialog;
+	GtkWidget *table;
+	GtkWidget *title_label;
+	GtkWidget *label_to_add;
+	GtkWidget *dialog_hbox;
+	GtkWidget *info_icon;
+	gint i;
+
+	icon = data;
+	priv = TRAY_ICON_GET_PRIVATE (icon);
+
+	dialog = gtk_dialog_new_with_buttons (_("Statistics"),
+					      NULL,
+					      GTK_DIALOG_NO_SEPARATOR
+					      |
+					      GTK_DIALOG_DESTROY_WITH_PARENT,
+					      GTK_STOCK_CLOSE,
+					      GTK_RESPONSE_CLOSE,
+					      NULL);
+
+	gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+	gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+	gtk_window_set_icon_name (GTK_WINDOW (dialog), "gtk-info");
+	gtk_window_set_type_hint (GTK_WINDOW (dialog),
+				  GDK_WINDOW_TYPE_HINT_DIALOG);
+	gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
+
+	table = gtk_table_new (13, 2, TRUE);
+	gtk_table_set_row_spacings (GTK_TABLE (table), 4);
+	gtk_table_set_col_spacings (GTK_TABLE (table), 65);
+	gtk_container_set_border_width (GTK_CONTAINER (table), 8);
+
+	title_label = gtk_label_new (NULL);
+	gtk_label_set_markup (GTK_LABEL (title_label),
+			      _("<span weight=\"bold\" size=\"larger\">Index statistics</span>"));
+	gtk_misc_set_alignment (GTK_MISC (title_label), 0, 0);
+	gtk_table_attach_defaults (GTK_TABLE (table), title_label, 0, 2, 0,
+				   1);
+
+	for (i = 0; i < 12; i++) {
+		label_to_add = gtk_label_new (stat_info[i].label);
+
+		gtk_label_set_selectable (GTK_LABEL (label_to_add), TRUE);
+		gtk_misc_set_alignment (GTK_MISC (label_to_add), 0, 0);
+		gtk_table_attach_defaults (GTK_TABLE (table), label_to_add, 0,
+					   1, i + 1, i + 2);
+
+		stat_info[i].stat_label = gtk_label_new ("");
+
+		gtk_label_set_selectable (GTK_LABEL (stat_info[i].stat_label),
+					  TRUE);
+		gtk_misc_set_alignment (GTK_MISC (stat_info[i].stat_label), 0,
+					0);
+		gtk_table_attach_defaults (GTK_TABLE (table),
+					   stat_info[i].stat_label, 1, 2,
+					   i + 1, i + 2);
+	}
+
+	priv->stat_window_active = TRUE;
+
+	refresh_stats (icon);
+
+	dialog_hbox = gtk_hbox_new (FALSE, 12);
+	info_icon = gtk_image_new_from_stock (GTK_STOCK_DIALOG_INFO,
+					      GTK_ICON_SIZE_DIALOG);
+	gtk_misc_set_alignment (GTK_MISC (info_icon), 0, 0);
+	gtk_container_add (GTK_CONTAINER (dialog_hbox), info_icon);
+	gtk_container_add (GTK_CONTAINER (dialog_hbox), table);
+
+	gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox),
+			   dialog_hbox);
+
+	g_signal_connect (G_OBJECT (dialog), "response",
+			  G_CALLBACK (stat_window_free),
+			  icon);
+
+	gtk_widget_show_all (dialog);
+}
+
+static void
+open_uri (GtkWindow  *parent,
+	  const char *uri)
+{
+	GtkWidget *dialog;
+	GdkScreen *screen;
+	GError *error = NULL;
+	gchar *cmdline;
+
+	screen = gtk_window_get_screen (parent);
+
+	cmdline = g_strconcat ("xdg-open ", uri, NULL);
+
+	if (gdk_spawn_command_line_on_screen (screen, cmdline, &error) == FALSE) {
+		dialog = gtk_message_dialog_new (parent,
+						 GTK_DIALOG_DESTROY_WITH_PARENT,
+						 GTK_MESSAGE_ERROR,
+						 GTK_BUTTONS_CLOSE,
+						 error->message);
+		gtk_dialog_run (GTK_DIALOG (dialog));
+		gtk_widget_destroy (dialog);
+		g_error_free (error);
+	}
+
+	g_free (cmdline);
+}
+
+static void
+about_url_hook (GtkAboutDialog *dialog,
+		const gchar    *url,
+		gpointer	data)
+{
+	open_uri (GTK_WINDOW (dialog), url);
+}
+
+static void
+about_email_hook (GtkAboutDialog *dialog,
+		  const gchar	 *email,
+		  gpointer	  data)
+{
+	gchar *uri;
+
+	uri = g_strconcat ("mailto:";, email, NULL);
+	open_uri (GTK_WINDOW (dialog), uri);
+	g_free (uri);
+}
+
+static void
+about_menu_activated (GtkMenuItem * item, gpointer data)
+{
+	const gchar *authors[] = {
+		"Jamie McCracken <jamiemcc at gnome.org>",
+		"Saleem Abdulrasool <compnerd at compnerd.org>"
+			"Laurent Aguerreche <laurent.aguerreche at free fr>",
+		"Luca Ferretti <elle uca libero it>",
+		"Eugenio <me at eugesoftware com>",
+		"Michael Biebl <mbiebl at gmail com>",
+		"Edward Duffy <eduffy at gmail com>",
+		"Gergan Penkov <gergan at gmail com>",
+		"Deji Akingunola <dakingun gmail com>",
+		"Julien <julienc psychologie-fr org>",
+		"Tom <tpgww onepost net>",
+		"Samuel Cormier-Iijima <sciyoshi at gmail com>",
+		"Eskil Bylund <eskil at letterboxes org>",
+		"Ulrik Mikaelsson <ulrik mikaelsson gmail com>",
+		"tobutaz <tobutaz gmail com>",
+		"Mikkel Kamstrup Erlandsen <mikkel kamstrup gmail com>",
+		"Baptiste Mille-Mathias <baptiste.millemathias gmail com>",
+		"Richard Quirk <quirky zoom co uk>",
+		"Marcus Fritzsch <fritschy at googlemail com>",
+		"Jedy Wang <Jedy Wang at Sun COM>",
+		"Anders Aagaard <aagaande at gmail com>",
+		"Fabien VALLON <fabien at sonappart net>",
+		"Jaime Frutos Morales <acidborg at gmail com>",
+		"Christoph Laimburg <christoph laimburg at rolmail net>",
+		NULL
+	};
+
+	const gchar *documenters[] = {
+		NULL
+	};
+
+	const gchar *license[] = {
+		N_("Tracker is free software; you can redistribute it and/or modify " "it under the terms of the GNU General Public License as published by " "the Free Software Foundation; either version 2 of the License, or " "(at your option) any later version."),
+		N_("Tracker 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."),
+		N_("You should have received a copy of the GNU General Public License " "along with Tracker; if not, write to the Free Software Foundation, Inc., " "59 Temple Place, Suite 330, Boston, MA  02111-1307  USA")
+	};
+
+	gchar *license_trans;
+
+	license_trans = g_strjoin ("\n\n", _(license[0]), _(license[1]),
+				   _(license[2]), NULL);
+
+	/* Make URLs and email clickable in about dialog */
+	gtk_about_dialog_set_url_hook (about_url_hook, NULL, NULL);
+	gtk_about_dialog_set_email_hook (about_email_hook, NULL, NULL);
+
+	gtk_show_about_dialog (NULL,
+			       "version", VERSION,
+			       "comments",
+			       _
+			       ("Tracker is a tool designed to extract info and metadata about your personal data so that it can be searched easily and quickly"),
+			       "copyright",
+			       _("Copyright \xC2\xA9 2005-2008 "
+				 "The Tracker authors"), "license",
+			       license_trans, "wrap-license", TRUE, "authors",
+			       authors, "documenters", documenters,
+			       /* Translators should localize the following string
+				* which will be displayed at the bottom of the about
+				* box to give credit to the translator(s).
+				*/
+			       "translator-credits", _("translator-credits"),
+			       "logo-icon-name", "tracker",
+			       "website", "http://www.tracker-project.org/";,
+			       "website-label", _("Tracker Web Site"), NULL);
+
+	g_free (license_trans);
+}
+
+static void
+quit_menu_activated (GtkMenuItem *item,
+		     gpointer	  data)
+{
+	gtk_main_quit ();
+}
+
+static void
+create_context_menu (TrayIcon *icon)
+{
+	TrayIconPrivate *priv;
+	GtkWidget *item, *image;
+
+	priv = TRAY_ICON_GET_PRIVATE (icon);
+	priv->menu = (GtkMenu *) gtk_menu_new ();
+
+	item = gtk_check_menu_item_new_with_mnemonic (_("_Pause All Indexing"));
+	gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), FALSE);
+	g_signal_connect (G_OBJECT (item), "toggled",
+			  G_CALLBACK (pause_menu_toggled), icon);
+	gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), item);
+
+	item = gtk_separator_menu_item_new ();
+	gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), item);
+
+
+	item = gtk_image_menu_item_new_with_mnemonic (_("_Search"));
+	image = gtk_image_new_from_icon_name (GTK_STOCK_FIND,
+					      GTK_ICON_SIZE_MENU);
+	gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
+	g_signal_connect (G_OBJECT (item), "activate",
+			  G_CALLBACK (search_menu_activated), icon);
+	gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), item);
+
+
+	item = gtk_image_menu_item_new_with_mnemonic (_("_Re-index"));
+	image = gtk_image_new_from_icon_name (GTK_STOCK_FIND,
+					      GTK_ICON_SIZE_MENU);
+	gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
+	g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (reindex),
+			  icon);
+	gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), item);
+
+	item = gtk_image_menu_item_new_with_mnemonic (_("_Preferences"));
+	image = gtk_image_new_from_icon_name (GTK_STOCK_PREFERENCES,
+					      GTK_ICON_SIZE_MENU);
+	gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
+	g_signal_connect (G_OBJECT (item), "activate",
+			  G_CALLBACK (applet_preferences_menu_activated),
+			  icon);
+	gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), item);
+
+	item = gtk_image_menu_item_new_with_mnemonic (_("_Indexer Preferences"));
+	image = gtk_image_new_from_icon_name (GTK_STOCK_PREFERENCES,
+					      GTK_ICON_SIZE_MENU);
+	gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
+	g_signal_connect (G_OBJECT (item), "activate",
+			  G_CALLBACK (preferences_menu_activated), icon);
+	gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), item);
+
+	item = gtk_image_menu_item_new_with_mnemonic (_("S_tatistics"));
+	image = gtk_image_new_from_icon_name (GTK_STOCK_INFO,
+					      GTK_ICON_SIZE_MENU);
+	gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
+	g_signal_connect (G_OBJECT (item), "activate",
+			  G_CALLBACK (statistics_menu_activated), icon);
+	gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), item);
+
+	item = gtk_image_menu_item_new_with_mnemonic (_("_About"));
+	image = gtk_image_new_from_icon_name (GTK_STOCK_ABOUT,
+					      GTK_ICON_SIZE_MENU);
+	gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
+	g_signal_connect (G_OBJECT (item), "activate",
+			  G_CALLBACK (about_menu_activated), icon);
+	gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), item);
+
+	item = gtk_separator_menu_item_new ();
+	gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), item);
+
+	item = gtk_image_menu_item_new_with_mnemonic (_("_Quit"));
+	image = gtk_image_new_from_icon_name (GTK_STOCK_QUIT,
+					      GTK_ICON_SIZE_MENU);
+	gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
+	g_signal_connect (G_OBJECT (item), "activate",
+			  G_CALLBACK (quit_menu_activated), icon);
+	gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), item);
+
+	gtk_widget_show_all (GTK_WIDGET (priv->menu));
+}
+
+static void
+index_finished (DBusGProxy *proxy,
+		gdouble     seconds_elapsed,
+		TrayIcon   *icon)
+{
+	gchar *str;
+
+	str = tracker_seconds_to_string (seconds_elapsed, FALSE);
+	tray_icon_show_message (icon,
+				"%s in %s.\n"
+				"\n"
+				"%s",
+				_("Tracker has finished indexing your system"),
+				str,
+				_("You can now perform searches by clicking here"));
+	g_free (str);
+
+	stop_watching_events (icon);
+}
+
+static void
+index_state_changed (DBusGProxy  *proxy,
+		     const gchar *state,
+		     gboolean	  initial_index,
+		     gboolean	  in_merge,
+		     gboolean	  is_manual_paused,
+		     gboolean	  is_battery_paused,
+		     gboolean	  is_io_paused,
+		     gboolean	  is_indexing_enabled,
+		     TrayIcon	 *icon)
+{
+	TrayIconPrivate *priv;
+	gboolean paused;
+
+	if (!state) {
+		return;
+	}
+
+	priv = TRAY_ICON_GET_PRIVATE (icon);
+	priv->indexer_stopped = FALSE;
+
+	paused = FALSE;
+
+	if (!is_indexing_enabled) {
+		priv->disabled = TRUE;
+		gtk_status_icon_set_visible (priv->icon, FALSE);
+		return;
+	} else {
+		priv->disabled = FALSE;
+		if (!priv->auto_hide) {
+			gtk_status_icon_set_visible (priv->icon, TRUE);
+		}
+	}
+
+	if (!priv->initial_index_msg_shown && initial_index) {
+		priv->initial_index_msg_shown = TRUE;
+		g_usleep (G_USEC_PER_SEC / 10);
+		tray_icon_show_message (icon,
+					"%s\n"
+					"\n"
+					"%s\n",
+					initial_index_1,
+					initial_index_2);
+	}
+
+	priv->animated = FALSE;
+	priv->pause_state = PAUSE_NONE;
+
+	/* Set pause states if applicable */
+	if (is_manual_paused) {
+		stop_watching_events (icon);
+
+		if (!priv->auto_pause) {
+			priv->user_pause = TRUE;
+		}
+
+		paused = TRUE;
+	} else if (is_battery_paused) {
+		priv->pause_state = PAUSE_BATTERY;
+		paused = TRUE;
+	} else if (is_io_paused) {
+		paused = TRUE;
+		priv->pause_state = PAUSE_INTERNAL;
+	}
+
+	if (in_merge) {
+		priv->index_state = INDEX_MERGING;
+		priv->animated = TRUE;
+	} else if (g_ascii_strcasecmp (state, "Initializing") == 0) {
+		priv->index_state = INDEX_INITIALIZING;
+	} else if (g_ascii_strcasecmp (state, "Idle") == 0) {
+		priv->index_state = INDEX_IDLE;
+	} else {
+		priv->index_state = INDEX_BUSY;
+		priv->animated = TRUE;
+	}
+
+	set_icon (priv);
+
+	/* Should we animate? */
+	if (!paused && priv->animated && priv->show_animation) {
+		if (!priv->animated_timer_active) {
+			priv->animated_timer_active = TRUE;
+		}
+	}
+
+	set_status_hint (icon);
+
+	if (can_auto_pause (icon)) {
+		start_watching_events (icon);
+	} else {
+		stop_watching_events (icon);
+	}
+}
+
+static void
+index_progress_changed (DBusGProxy  *proxy,
+			const gchar *service,
+			const gchar *uri,
+			gint	     items_done,
+			gint	     items_remaining,
+			gint	     items_total,
+			gdouble      seconds_elapsed,
+			TrayIcon    *icon)
+{
+	TrayIconPrivate *priv;
+
+	priv = TRAY_ICON_GET_PRIVATE (icon);
+
+	priv->items_done = items_done;
+	priv->items_remaining = items_remaining;
+	priv->items_total = items_total;
+	priv->seconds_elapsed = seconds_elapsed;
+
+	priv->email_indexing = strcmp (service, "Emails") == 0;
+
+	g_print ("Indexed %d/%d, seconds elapsed:%f\n",
+		 items_done,
+		 items_total,
+		 seconds_elapsed);
+
+	set_status_hint (icon);
+	set_icon (priv);
+
+	/* Update stat window if its active */
+	refresh_stats (icon);
+}
+
+static void
+init_settings (TrayIcon *icon)
+{
+	TrayIconPrivate *priv;
+
+	priv = TRAY_ICON_GET_PRIVATE (icon);
+
+	priv->index_state = INDEX_INITIALIZING;
+	priv->pause_state = PAUSE_NONE;
+	priv->auto_pause_setting = AUTO_PAUSE_MERGING;
+	priv->index_icon = ICON_DEFAULT;
+	priv->animated = FALSE;
+	priv->animated_timer_active = FALSE;
+	priv->user_pause = FALSE;
+	priv->auto_pause = FALSE;
+	priv->auto_hide = FALSE;
+	priv->disabled = FALSE;
+	priv->show_animation = TRUE;
+	priv->auto_pause_timer_active = FALSE;
+	priv->is_watching_events = FALSE;
+	priv->initial_index_msg_shown = FALSE;
+	priv->stat_window_active = FALSE;
+	priv->stat_request_pending = FALSE;
+	priv->indexer_stopped = FALSE;
+
+	set_tracker_icon (priv);
+}
+
+static void
+name_owner_changed (DBusGProxy	*proxy,
+		    const gchar *name,
+		    const gchar *prev_owner,
+		    const gchar *new_owner,
+		    gpointer	 data)
+{
+	if (!g_str_equal (name, DBUS_SERVICE_TRACKER)) {
+		return;
+	}
+
+	if (g_str_equal (new_owner, "")) {
+		TrayIconPrivate *priv;
+
+		priv = TRAY_ICON_GET_PRIVATE (data);
+
+		/* Tracker has exited so reset status and make
+		 * invisible until trackerd relaunched.
+		 */
+		index_state_changed (proxy,
+				     "Idle",
+				     FALSE,
+				     FALSE,
+				     FALSE,
+				     FALSE,
+				     FALSE,
+				     FALSE,
+				     data);
+
+		init_settings (data);
+		gtk_status_icon_set_visible (priv->icon, FALSE);
+		priv->indexer_stopped = TRUE;
+
+		g_print ("The Tracker daemon has exited (%s)\n",
+			 priv->reindex ? "reindexing" : "not reindexing");
+
+		if (priv->reindex) {
+			GError *error = NULL;
+			const gchar *command = "trackerd";
+
+			priv->reindex = FALSE;
+
+			g_print ("Restarting Tracker daemon in 1 second\n");
+			g_usleep (G_USEC_PER_SEC);
+
+			g_print ("Spawning command:'%s'\n", command);
+
+			if (!g_spawn_command_line_async (command, &error)) {
+				g_warning ("Unable to execute command:'%s', %s",
+					   command,
+					   error->message);
+				g_error_free (error);
+			}
+		}
+	}
+}
+
+static gboolean
+setup_dbus_connection (TrayIcon *icon)
+{
+	TrayIconPrivate *priv;
+	DBusGConnection *connection;
+	DBusGProxy *dbus_proxy;
+
+	priv = TRAY_ICON_GET_PRIVATE (icon);
+
+	priv->tracker = tracker_connect (FALSE);
+
+	if (!priv->tracker) {
+		g_printerr ("Could not connect to the Tracker daemon\n");
+		exit (EXIT_FAILURE);
+	}
+
+	/* Set signal handlers */
+	dbus_g_object_register_marshaller (tracker_VOID__STRING_BOOLEAN_BOOLEAN_BOOLEAN_BOOLEAN_BOOLEAN,
+					   G_TYPE_NONE,
+					   G_TYPE_STRING,
+					   G_TYPE_BOOLEAN,
+					   G_TYPE_BOOLEAN,
+					   G_TYPE_BOOLEAN,
+					   G_TYPE_BOOLEAN,
+					   G_TYPE_BOOLEAN,
+					   G_TYPE_BOOLEAN,
+					   G_TYPE_INVALID);
+
+	dbus_g_object_register_marshaller (tracker_VOID__STRING_STRING_INT_INT_INT_DOUBLE,
+					   G_TYPE_NONE,
+					   G_TYPE_STRING,
+					   G_TYPE_STRING,
+					   G_TYPE_INT,
+					   G_TYPE_INT,
+					   G_TYPE_INT,
+					   G_TYPE_DOUBLE,
+					   G_TYPE_INVALID);
+
+	dbus_g_proxy_add_signal (priv->tracker->proxy,
+				 "IndexStateChange",
+				 G_TYPE_STRING,
+				 G_TYPE_BOOLEAN,
+				 G_TYPE_BOOLEAN,
+				 G_TYPE_BOOLEAN,
+				 G_TYPE_BOOLEAN,
+				 G_TYPE_BOOLEAN,
+				 G_TYPE_BOOLEAN,
+				 G_TYPE_INVALID);
+
+	dbus_g_proxy_add_signal (priv->tracker->proxy,
+				 "IndexProgress",
+				 G_TYPE_STRING,
+				 G_TYPE_STRING,
+				 G_TYPE_INT,
+				 G_TYPE_INT,
+				 G_TYPE_INT,
+				 G_TYPE_DOUBLE,
+				 G_TYPE_INVALID);
+
+	dbus_g_proxy_add_signal (priv->tracker->proxy,
+				 "IndexFinished",
+				 G_TYPE_DOUBLE,
+				 G_TYPE_INVALID);
+
+	dbus_g_proxy_connect_signal (priv->tracker->proxy,
+				     "IndexStateChange",
+				     G_CALLBACK (index_state_changed),
+				     icon,
+				     NULL);
+
+	dbus_g_proxy_connect_signal (priv->tracker->proxy,
+				     "IndexProgress",
+				     G_CALLBACK (index_progress_changed),
+				     icon,
+				     NULL);
+
+	dbus_g_proxy_connect_signal (priv->tracker->proxy,
+				     "IndexFinished",
+				     G_CALLBACK (index_finished),
+				     icon,
+				     NULL);
+
+	connection = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
+	dbus_proxy = dbus_g_proxy_new_for_name (connection,
+						DBUS_SERVICE_DBUS,
+						DBUS_PATH_DBUS,
+						DBUS_INTERFACE_DBUS);
+
+	dbus_g_proxy_add_signal (dbus_proxy,
+				 "NameOwnerChanged",
+				 G_TYPE_STRING,
+				 G_TYPE_STRING,
+				 G_TYPE_STRING, G_TYPE_INVALID);
+
+	dbus_g_proxy_connect_signal (dbus_proxy,
+				     "NameOwnerChanged",
+				     G_CALLBACK (name_owner_changed),
+				     icon, NULL);
+
+	/* Prompt for updated signals */
+	dbus_g_proxy_begin_call (priv->tracker->proxy,
+				 "PromptIndexSignals",
+				 NULL,
+				 NULL,
+				 NULL,
+				 G_TYPE_INVALID);
+
+	return FALSE;
+}
+
+static void
+tray_icon_clicked (GtkStatusIcon *status_icon,
+		   guint	  button,
+		   guint	  timestamp,
+		   gpointer	  data)
+{
+	TrayIcon *icon;
+	TrayIconPrivate *priv;
+
+	icon = TRAY_ICON (data);
+	priv = TRAY_ICON_GET_PRIVATE (icon);
+
+	gtk_menu_popup (GTK_MENU (priv->menu),
+			NULL, NULL,
+			gtk_status_icon_position_menu,
+			status_icon,
+			button,
+			timestamp);
+}
+
+static void
+tray_icon_init (GTypeInstance *instance,
+		gpointer       klass)
+{
+	TrayIcon *icon;
+	TrayIconPrivate *priv;
+
+	icon = TRAY_ICON (instance);
+	priv = TRAY_ICON_GET_PRIVATE (icon);
+
+	priv->icon = gtk_status_icon_new ();
+
+	init_settings (icon);
+
+	priv->reindex = FALSE;
+
+	g_signal_connect (G_OBJECT (priv->icon), "activate",
+			  G_CALLBACK (activate_icon),
+			  instance);
+	g_signal_connect (G_OBJECT (priv->icon), "popup-menu",
+			  G_CALLBACK (tray_icon_clicked),
+			  instance);
+
+	/* Build context menu */
+	create_context_menu (icon);
+
+	gtk_status_icon_set_visible (priv->icon, FALSE);
+
+	setup_dbus_connection (icon);
+}
+
+void
+tray_icon_set_tooltip (TrayIcon    *icon,
+		       const gchar *format,
+		       ...)
+{
+	TrayIconPrivate *priv;
+	gchar *tooltip = NULL;
+	va_list args;
+
+	priv = TRAY_ICON_GET_PRIVATE (icon);
+
+	va_start (args, format);
+	tooltip = g_strdup_vprintf (format, args);
+	va_end (args);
+
+	gtk_status_icon_set_tooltip (priv->icon, tooltip);
+
+	g_free (tooltip);
+}
+
+void
+tray_icon_show_message (TrayIcon    *icon,
+			const gchar *message,
+			...)
+{
+	TrayIconPrivate *priv;
+	NotifyNotification *notification;
+	gchar *msg = NULL;
+	va_list args;
+
+	priv = TRAY_ICON_GET_PRIVATE (icon);
+
+	va_start (args, message);
+	msg = g_strdup_vprintf (message, args);
+	va_end (args);
+
+	if (priv->disabled) {
+		return;
+	}
+
+	if (!priv->auto_hide && !gtk_status_icon_get_visible (priv->icon)) {
+		gtk_status_icon_set_visible (priv->icon, TRUE);
+	}
+
+	notification =
+		notify_notification_new_with_status_icon ("Tracker",
+							  msg,
+							  NULL,
+							  priv->icon);
+
+	notify_notification_set_urgency (notification, NOTIFY_URGENCY_NORMAL);
+	notify_notification_show (notification, NULL);
+
+	g_object_unref (notification);
+
+	g_free (msg);
+}
+
+GType
+tray_icon_get_type (void)
+{
+	static GType type = 0;
+
+	if (type == 0) {
+		static const GTypeInfo info = {
+			sizeof (TrayIconClass),
+			NULL,
+			NULL,
+			(GClassInitFunc) tray_icon_class_init,
+			NULL,
+			NULL,
+			sizeof (TrayIcon),
+			0,
+			tray_icon_init
+		};
+
+		type = g_type_register_static (G_TYPE_OBJECT,
+					       "TrayIconType",
+					       &info,
+					       0);
+	}
+
+	return type;
+}
+
+static gboolean
+load_options (TrayIcon *icon)
+{
+	TrayIconPrivate *priv;
+	GError *error = NULL;
+
+	priv = TRAY_ICON_GET_PRIVATE (icon);
+
+	if (!priv->keyfile) {
+		priv->keyfile = g_key_file_new ();
+	}
+
+	if (!g_file_test (priv->filename, G_FILE_TEST_EXISTS)) {
+		gchar *tracker_dir;
+		gchar *contents;
+
+		tracker_dir = g_build_path (g_get_user_config_dir (),
+					    "tracker",
+					    NULL);
+
+		if (!g_file_test (tracker_dir, G_FILE_TEST_EXISTS)) {
+			g_mkdir_with_parents (tracker_dir, 0700);
+		}
+
+		g_free (tracker_dir);
+
+		contents = g_strconcat ("[Applet]\n",
+					"AnimateWhenIndexing=true\n"
+					"\n",
+					"AutoHideIcon=false\n"
+					"\n",
+					"SmartPause=2\n",
+					NULL);
+
+		g_file_set_contents (priv->filename,
+				     contents,
+				     strlen (contents),
+				     NULL);
+		g_free (contents);
+	}
+
+	if (!g_key_file_load_from_file (priv->keyfile,
+					priv->filename,
+					G_KEY_FILE_KEEP_COMMENTS,
+					&error) || error) {
+		if (error) {
+			g_warning ("Call to g_key_file_load_from_file() failed for filename:'%s', %s\n",
+				   priv->filename,
+				   error->message);
+		}
+
+		priv->show_animation = TRUE;
+		priv->auto_hide = FALSE;
+		priv->auto_pause_setting = AUTO_PAUSE_MERGING;
+
+		return FALSE;
+	}
+
+	if (g_key_file_has_key (priv->keyfile,
+				"Applet",
+				"AnimateWhenIndexing",
+				NULL)) {
+		priv->show_animation = g_key_file_get_boolean (priv->keyfile,
+							       "Applet",
+							       "AnimateWhenIndexing",
+							       NULL);
+	} else {
+		priv->show_animation = TRUE;
+	}
+
+	if (g_key_file_has_key (priv->keyfile,
+				"Applet",
+				"AutoHideIcon",
+				NULL)) {
+		priv->auto_hide = g_key_file_get_boolean (priv->keyfile,
+							  "Applet",
+							  "AutoHideIcon",
+							  NULL);
+	} else {
+		priv->auto_hide = FALSE;
+	}
+
+	if (g_key_file_has_key (priv->keyfile,
+				"Applet",
+				"SmartPause",
+				NULL)) {
+		priv->auto_pause_setting = g_key_file_get_integer (priv->keyfile,
+								   "Applet",
+								   "SmartPause",
+								   NULL);
+	} else {
+		priv->auto_pause_setting = AUTO_PAUSE_MERGING;
+	}
+
+	switch (priv->auto_pause_setting) {
+	case AUTO_PAUSE_NONE:
+		priv->auto_pause_setting = AUTO_PAUSE_NONE;
+		priv->auto_pause = FALSE;
+		stop_watching_events (icon);
+		break;
+	case AUTO_PAUSE_INDEXING:
+		if (can_auto_pause (icon)) {
+			start_watching_events (icon);
+		} else {
+			stop_watching_events (icon);
+		}
+		break;
+	case AUTO_PAUSE_MERGING:
+		if (can_auto_pause (icon)) {
+			start_watching_events (icon);
+		} else {
+			stop_watching_events (icon);
+		}
+		break;
+	}
+
+	return TRUE;
+}
+
+int
+main (int argc, char *argv[])
+{
+	TrayIconPrivate *priv;
+	GOptionContext *context;
+
+	bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+	textdomain (GETTEXT_PACKAGE);
+
+	context = g_option_context_new (_("- Tracker applet for quick control of "
+					  "your desktop search tools"));
+
+	g_option_context_add_main_entries (context, entries, NULL);
+	g_option_context_parse (context, &argc, &argv, NULL);
+	g_option_context_free (context);
+
+	g_print ("Initializing GTK+\n");
+
+	gtk_init (&argc, &argv);
+
+	g_print ("Initializing libnotify\n");
+
+	if (!notify_is_initted () &&
+	    !notify_init (PROGRAM_NAME)) {
+		g_warning ("Call to notify_init() failed\n");
+		return EXIT_FAILURE;
+	}
+
+	g_print ("Initializing strings\n");
+
+	gtk_window_set_default_icon_name ("tracker");
+	g_set_application_name (_("Tracker"));
+
+	initial_index_1 =
+		_("Your computer is about to be indexed so "
+		  "you can perform fast searches of your files and emails");
+	initial_index_2 =
+		_("You can pause indexing at any time and "
+		  "configure index settings by right clicking here");
+
+	stat_info[0].label = _("Files:");
+	stat_info[1].label = _("    Folders:");
+	stat_info[2].label = _("    Documents:");
+	stat_info[3].label = _("    Images:");
+	stat_info[4].label = _("    Music:");
+	stat_info[5].label = _("    Videos:");
+	stat_info[6].label = _("    Text:");
+	stat_info[7].label = _("    Development:");
+	stat_info[8].label = _("    Other:");
+	stat_info[9].label = _("Applications:");
+	stat_info[10].label = _("Conversations:");
+	stat_info[11].label = _("Emails:");
+
+	g_print ("Initializing tray icon\n");
+
+	main_icon = g_object_new (TYPE_TRAY_ICON, NULL);
+
+	priv = TRAY_ICON_GET_PRIVATE (main_icon);
+
+	g_print ("Initializing config\n");
+
+	priv->keyfile = NULL;
+	priv->filename = g_build_filename (g_get_user_config_dir (),
+					   "tracker"
+					   "tracker-applet.cfg",
+					   NULL);
+
+	if (!load_options (main_icon)) {
+		return EXIT_FAILURE;
+	}
+
+	g_print ("Starting main loop\n");
+
+	gtk_main ();
+
+	g_print ("Shutting down\n");
+
+	notify_uninit ();
+
+	return EXIT_SUCCESS;
+}

Added: trunk/src/tracker-applet/tracker-applet.desktop.in.in
==============================================================================
--- (empty file)
+++ trunk/src/tracker-applet/tracker-applet.desktop.in.in	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,16 @@
+[Desktop Entry]
+Encoding=UTF-8
+_Name=Tracker Applet
+_Comment=Control and monitor the Tracker search and indexing service
+Icon=
+Exec=tracker-applet
+Terminal=false
+Type=Application
+Categories=
+NoDisplay=true
+OnlyShowIn=GNOME;XFCE;
+X-GNOME-Bugzilla-Bugzilla=GNOME
+X-GNOME-Bugzilla-Product=tracker
+X-GNOME-Bugzilla-Component=Tracker Applet
+X-GNOME-Bugzilla-Version= VERSION@
+X-GNOME-Autostart-enabled=true

Added: trunk/src/tracker-applet/tracker-applet.h
==============================================================================
--- (empty file)
+++ trunk/src/tracker-applet/tracker-applet.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,61 @@
+/* Tracker - indexer and metadata database engine
+ * Copyright (C) 2007, Saleem Abdulrasool <compnerd gentoo org>
+ * Copyright (C) 2007, Jamie McCracken <jamiemcc blueyonder co uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKER_APPLET_H__
+#define __TRACKER_APPLET_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <gtk/gtkstock.h>
+#include <gtk/gtkmenuitem.h>
+#include <gtk/gtkstatusicon.h>
+#include <gtk/gtkcheckmenuitem.h>
+#include <gtk/gtkimagemenuitem.h>
+#include <gtk/gtkseparatormenuitem.h>
+
+#define TYPE_TRAY_ICON		    (tray_icon_get_type())
+#define TRAY_ICON(obj)		    (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_TRAY_ICON, TrayIcon))
+#define TRAY_ICON_CLASS(klass)	    (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_TRAY_ICON, TrayIconClass))
+#define IS_TRAY_ICON(obj)	    (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_TRAY_ICON))
+#define IS_TRAY_ICON_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_TRAY_ICON))
+#define TRAY_ICON_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_TRAY_ICON, TrayIconClass))
+
+typedef struct _TrayIcon
+{
+   GObject parent;
+} TrayIcon;
+
+typedef struct _TrayIconClass
+{
+   GObjectClass parent_class;
+} TrayIconClass;
+
+GType
+tray_icon_get_type (void);
+
+void
+tray_icon_set_tooltip (TrayIcon *icon, const gchar *format, ...);
+
+void
+tray_icon_show_message (TrayIcon *icon, const gchar *message, ...);
+
+#endif
+

Added: trunk/src/tracker-extract/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/tracker-extract/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,164 @@
+include $(top_srcdir)/Makefile.decl
+
+module_flags = -module -avoid-version -no-undefined
+modulesdir = $(libdir)/tracker/extract-modules
+
+INCLUDES = 								\
+	$(GLIB2_CFLAGS) 						\
+	$(GMODULE_CFLAGS) 						\
+	$(EXEMPI_CFLAGS) 						\
+	$(LIBJPEG_CFLAGS) 						\
+	$(LIBTIFF_CFLAGS) 						\
+	$(LIBEXIF_CFLAGS) 						\
+	$(LIBGSF_CFLAGS) 						\
+	$(LIBXML2_CFLAGS) 						\
+	$(LIBPNG_CFLAGS) 						\
+	$(POPPLER_GLIB_CFLAGS) 						\
+	$(GSTREAMER_CFLAGS) 						\
+	$(XINE_CFLAGS) 							\
+	-I$(top_srcdir)/src 						\
+	-DMODULES_DIR=\"$(modulesdir)\"
+
+modules_LTLIBRARIES = 							\
+	libextract-abw.la 						\
+	libextract-imagemagick.la 					\
+	libextract-mp3.la				 		\
+	libextract-mplayer.la 						\
+	libextract-oasis.la 						\
+	libextract-png.la 						\
+	libextract-ps.la 						\
+	libextract-totem.la
+
+if HAVE_EXEMPI
+modules_LTLIBRARIES += libextract-xmp.la
+endif
+
+if HAVE_LIBJPEG
+modules_LTLIBRARIES += libextract-jpeg.la
+endif
+
+if HAVE_LIBTIFF
+modules_LTLIBRARIES += libextract-tiff.la
+endif
+
+if HAVE_LIBXML2
+modules_LTLIBRARIES += libextract-html.la
+endif
+
+if HAVE_LIBGSF
+modules_LTLIBRARIES += libextract-msoffice.la
+endif
+
+if HAVE_POPPLER_GLIB
+modules_LTLIBRARIES += libextract-pdf.la
+endif
+
+if HAVE_GSTREAMER
+modules_LTLIBRARIES += libextract-gstreamer.la
+endif
+
+if HAVE_LIBXINE
+modules_LTLIBRARIES += libextract-xine.la
+endif
+
+# Common XMP Sources
+xmp_sources = 								\
+	tracker-xmp.c							\
+	tracker-xmp.h
+
+# ABW
+libextract_abw_la_SOURCES = tracker-extract-abw.c
+libextract_abw_la_LDFLAGS = $(module_flags)
+libextract_abw_la_LIBADD = $(GLIB2_LIBS)
+
+# Imagemagick
+libextract_imagemagick_la_SOURCES = tracker-extract-imagemagick.c $(xmp_sources)
+libextract_imagemagick_la_LDFLAGS = $(module_flags)
+libextract_imagemagick_la_LIBADD = $(GLIB2_LIBS) $(EXEMPI_LIBS)
+
+# MP3
+libextract_mp3_la_SOURCES = tracker-extract-mp3.c
+libextract_mp3_la_LDFLAGS = $(module_flags)
+libextract_mp3_la_LIBADD = $(GLIB2_LIBS)
+
+# MPlayer
+libextract_mplayer_la_SOURCES = tracker-extract-mplayer.c
+libextract_mplayer_la_LDFLAGS = $(module_flags)
+libextract_mplayer_la_LIBADD = $(GLIB2_LIBS)
+
+# Oasis
+libextract_oasis_la_SOURCES = tracker-extract-oasis.c
+libextract_oasis_la_LDFLAGS = $(module_flags)
+libextract_oasis_la_LIBADD = $(GLIB2_LIBS)
+
+# PNG
+libextract_png_la_SOURCES = tracker-extract-png.c $(xmp_sources)
+libextract_png_la_LDFLAGS = $(module_flags)
+libextract_png_la_LIBADD = $(GLIB2_LIBS) $(LIBPNG_LIBS) $(EXEMPI_LIBS)
+
+# PS
+libextract_ps_la_SOURCES = tracker-extract-ps.c
+libextract_ps_la_LDFLAGS = $(module_flags)
+libextract_ps_la_LIBADD = 						\
+	$(GLIB2_LIBS) 							\
+	$(top_builddir)/src/libtracker-common/libtracker-common.la
+
+# Totem
+libextract_totem_la_SOURCES = tracker-extract-totem.c
+libextract_totem_la_LDFLAGS = $(module_flags)
+libextract_totem_la_LIBADD = $(GLIB2_LIBS)
+
+# XMP
+libextract_xmp_la_SOURCES = tracker-extract-xmp.c $(xmp_sources)
+libextract_xmp_la_LDFLAGS = $(module_flags)
+libextract_xmp_la_LIBADD = $(GLIB2_LIBS) $(EXEMPI_LIBS)
+
+# HTML
+libextract_html_la_SOURCES = tracker-extract-html.c
+libextract_html_la_LDFLAGS = $(module_flags)
+libextract_html_la_LIBADD = $(GLIB2_LIBS) $(LIBXML2_LIBS)
+
+# MS Office
+libextract_msoffice_la_SOURCES = tracker-extract-msoffice.c
+libextract_msoffice_la_LDFLAGS = $(module_flags)
+libextract_msoffice_la_LIBADD = $(GLIB2_LIBS) $(LIBGSF_LIBS)
+
+# PDF
+libextract_pdf_la_SOURCES = tracker-extract-pdf.c $(xmp_sources)
+libextract_pdf_la_LDFLAGS = $(module_flags)
+libextract_pdf_la_LIBADD = $(GLIB2_LIBS) $(POPPLER_GLIB_LIBS) $(EXEMPI_LIBS)
+
+# GStreamer
+libextract_gstreamer_la_SOURCES = tracker-extract-gstreamer.c
+libextract_gstreamer_la_LDFLAGS = $(module_flags)
+libextract_gstreamer_la_LIBADD = $(GLIB2_LIBS) $(GSTREAMER_LIBS)
+
+# Xine
+libextract_xine_la_SOURCES = tracker-extract-libxine.c
+libextract_xine_la_LDFLAGS = $(module_flags)
+libextract_xine_la_LIBADD = $(GLIB2_LIBS) $(XINE_LIBS)
+
+# JPEG
+libextract_jpeg_la_SOURCES = tracker-extract-jpeg.c $(xmp_sources)
+libextract_jpeg_la_LDFLAGS = $(module_flags)
+libextract_jpeg_la_LIBADD = $(GLIB2_LIBS) $(LIBJPEG_LIBS) $(LIBEXIF_LIBS) $(EXEMPI_LIBS)
+
+# TIFF
+libextract_tiff_la_SOURCES = tracker-extract-tiff.c $(xmp_sources)
+libextract_tiff_la_LDFLAGS = $(module_flags)
+libextract_tiff_la_LIBADD = $(GLIB2_LIBS) $(LIBTIFF_LIBS) $(EXEMPI_LIBS)
+
+#
+# Binaries
+#
+libexec_PROGRAMS = tracker-extract
+
+tracker_extract_SOURCES = 						\
+	tracker-extract.c						\
+	tracker-extract.h
+
+tracker_extract_LDADD = 						\
+	$(top_builddir)/src/libtracker-common/libtracker-common.la	\
+	$(GLIB2_LIBS)							\
+	$(GTHREAD_LIBS)							\
+	$(GMODULE_LIBS)

Added: trunk/src/tracker-extract/tracker-extract-abw.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-extract/tracker-extract-abw.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,119 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include "tracker-extract.h"
+
+static void extract_abw (const gchar *filename,
+			 GHashTable  *metadata);
+
+static TrackerExtractorData data[] = {
+	{ "application/x-abiword", extract_abw },
+	{ NULL, NULL }
+};
+
+static void
+extract_abw (const gchar *filename,
+	     GHashTable  *metadata)
+{
+	gint  fd;
+	FILE *f;
+
+#if defined(__linux__)
+	if ((fd = g_open (filename, (O_RDONLY | O_NOATIME))) == -1) {
+#else
+	if ((fd = g_open (filename, O_RDONLY)) == -1) {
+#endif
+		return;
+	}
+
+	if ((f = fdopen (fd, "r"))) {
+		gchar  *line;
+		gsize  length;
+		gssize read_char;
+
+		line = NULL;
+		length = 0;
+
+		while ((read_char = getline (&line, &length, f)) != -1) {
+			if (g_str_has_suffix (line, "</m>\n")) {
+				line[read_char - 5] = '\0';
+			}
+			if (g_str_has_prefix (line, "<m key=\"dc.title\">")) {
+				g_hash_table_insert (metadata,
+						     g_strdup ("Doc:Title"),
+						     g_strdup (line + 18));
+			}
+			else if (g_str_has_prefix (line, "<m key=\"dc.subject\">")) {
+				g_hash_table_insert (metadata,
+						     g_strdup ("Doc:Subject"),
+						     g_strdup (line + 20));
+			}
+			else if (g_str_has_prefix (line, "<m key=\"dc.creator\">")) {
+				g_hash_table_insert (metadata,
+						     g_strdup ("Doc:Author"),
+						     g_strdup (line + 20));
+			}
+			else if (g_str_has_prefix (line, "<m key=\"abiword.keywords\">")) {
+				g_hash_table_insert (metadata,
+						     g_strdup ("Doc:Keywords"),
+						     g_strdup (line + 26));
+			}
+			else if (g_str_has_prefix (line, "<m key=\"dc.description\">")) {
+				g_hash_table_insert (metadata,
+						     g_strdup ("Doc:Comments"),
+						     g_strdup (line + 24));
+			}
+
+			g_free (line);
+			line = NULL;
+			length = 0;
+		}
+
+		if (line) {
+			g_free (line);
+		}
+
+		fclose (f);
+	} else {
+		close (fd);
+	}
+}
+
+TrackerExtractorData *
+tracker_get_extractor_data (void)
+{
+	return data;
+}

Added: trunk/src/tracker-extract/tracker-extract-exif.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-extract/tracker-extract-exif.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,170 @@
+/* Tracker Extract - extracts embedded metadata from files
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "tracker-extract.h"
+#include <stdio.h>
+#include <string.h>
+#include <glib.h>
+#include <libexif/exif-data.h>
+
+#define EXIF_DATE_FORMAT "%Y:%m:%d %H:%M:%S"
+
+static gchar *
+date_to_iso8601 (gchar *exif_date)
+{
+	/* ex; date "2007:04:15 15:35:58"
+	   To
+	   ex. "2007-04-15T17:35:58+0200 where +0200 is localtime
+	*/
+	return tracker_generic_date_to_iso8601 (exif_date, EXIF_DATE_FORMAT);
+}
+
+
+static gchar *
+fix_focal_length (gchar *fl)
+{
+	return g_strndup (fl, (strstr (fl, "mm") - fl));
+}
+
+
+static gchar *
+fix_flash (gchar *flash)
+{
+	if (g_str_has_prefix (flash, "No")) {
+		return g_strdup ("0");
+	} else {
+		return g_strdup ("1");
+	}
+}
+
+
+static gchar *
+fix_fnumber (gchar *fn)
+{
+	if (fn && fn[0] == 'F') {
+		fn[0] = ' ';
+
+	} else if (fn && fn[0] == 'f' && fn[1] == '/') {
+		fn[0] = ' ', fn[1] = ' ';
+	}
+
+	return fn;
+}
+
+
+static gchar *
+fix_exposure_time (gchar *et)
+{
+	gchar *sep = strchr (et, '/');
+
+	if (sep) {
+		gdouble fraction = g_ascii_strtod (sep+1, NULL);
+
+		if (fraction > 0) {
+			gdouble val = 1.0f / fraction;
+			char str_value[30];
+
+			g_ascii_dtostr (str_value, 30, val);
+			return g_strdup (str_value);
+		}
+	}
+
+	return et;
+}
+
+
+typedef gchar * (*PostProcessor) (gchar *);
+
+
+typedef struct {
+	ExifTag       tag;
+	gchar	      *name;
+	PostProcessor post;
+} TagType;
+
+
+TagType tags[] = {
+	{ EXIF_TAG_PIXEL_Y_DIMENSION, "Image:Height", NULL },
+	{ EXIF_TAG_PIXEL_X_DIMENSION, "Image:Width", NULL },
+	{ EXIF_TAG_RELATED_IMAGE_WIDTH, "Image:Width", NULL },
+	{ EXIF_TAG_DOCUMENT_NAME, "Image:Title", NULL },
+	/* { -1, "Image:Album", NULL }, */
+	{ EXIF_TAG_DATE_TIME, "Image:Date", date_to_iso8601 },
+	/* { -1, "Image:Keywords", NULL }, */
+	{ EXIF_TAG_ARTIST, "Image:Creator", NULL },
+	{ EXIF_TAG_USER_COMMENT, "Image:Comments", NULL },
+	{ EXIF_TAG_IMAGE_DESCRIPTION, "Image:Description", NULL },
+	{ EXIF_TAG_SOFTWARE, "Image:Software", NULL },
+	{ EXIF_TAG_MAKE, "Image:CameraMake", NULL },
+	{ EXIF_TAG_MODEL, "Image:CameraModel", NULL },
+	{ EXIF_TAG_ORIENTATION, "Image:Orientation", NULL },
+	{ EXIF_TAG_EXPOSURE_PROGRAM, "Image:ExposureProgram", NULL },
+	{ EXIF_TAG_EXPOSURE_TIME, "Image:ExposureTime", fix_exposure_time },
+	{ EXIF_TAG_FNUMBER, "Image:FNumber", fix_fnumber },
+	{ EXIF_TAG_FLASH, "Image:Flash", fix_flash },
+	{ EXIF_TAG_FOCAL_LENGTH, "Image:FocalLength", fix_focal_length },
+	{ EXIF_TAG_ISO_SPEED_RATINGS, "Image:ISOSpeed", NULL },
+	{ EXIF_TAG_METERING_MODE, "Image:MeteringMode", NULL },
+	{ EXIF_TAG_WHITE_BALANCE, "Image:WhiteBalance", NULL },
+	{ EXIF_TAG_COPYRIGHT, "File:Copyright", NULL },
+	{ -1, NULL, NULL }
+};
+
+
+static void
+tracker_extract_exif (const gchar *filename, GHashTable *metadata)
+{
+	ExifData *exif;
+	TagType  *p;
+
+	exif = exif_data_new_from_file (filename);
+
+	for (p = tags; p->name; ++p) {
+		ExifEntry *entry = exif_data_get_entry (exif, p->tag);
+
+		if (entry) {
+			gchar buffer[1024];
+
+			exif_entry_get_value (entry, buffer, 1024);
+
+			if (p->post) {
+				g_hash_table_insert (metadata, g_strdup (p->name),
+						     g_strdup ((*p->post) (buffer)));
+			} else {
+				g_hash_table_insert (metadata, g_strdup (p->name),
+						     g_strdup (buffer));
+			}
+		}
+	}
+}
+
+
+TrackerExtractorData data[] = {
+	{ "image/jpeg", tracker_extract_exif },
+	{ NULL, NULL }
+};
+
+
+TrackerExtractorData *
+tracker_get_extractor_data (void)
+{
+	return data;
+}

Added: trunk/src/tracker-extract/tracker-extract-gstreamer.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-extract/tracker-extract-gstreamer.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,851 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Laurent Aguerreche (laurent aguerreche free fr)
+ * Copyright (C) 2007, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+/*
+ * This file has been wrote starting from file bacon-video-widget-gst-0.10.c
+ * from Totem:
+ *
+ * Copyright (C) 2003-2006 the GStreamer project
+ *	Julien Moutte <julien moutte net>
+ *	Ronald Bultje <rbultje ronald bitfreak net>
+ *	Tim-Philipp MÃller <tim centricular net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * The Totem project hereby grant permission for non-gpl compatible GStreamer
+ * plugins to be used and distributed together with GStreamer and Totem. This
+ * permission is above and beyond the permissions granted by the GPL license
+ * Totem is covered by.
+ *
+ * Monday 7th February 2005: Christian Schaller: Add exemption clause.
+ * See license_change file for details.
+ *
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <glib.h>
+#include <gst/gst.h>
+
+#include "tracker-extract.h"
+
+typedef enum {
+	EXTRACT_MIME_UNDEFINED=0,
+	EXTRACT_MIME_AUDIO,
+	EXTRACT_MIME_VIDEO,
+	EXTRACT_MIME_IMAGE
+} ExtractMime;
+
+typedef struct {
+	GstElement	*playbin;
+
+	GstTagList	*tagcache;
+	GstTagList	*audiotags;
+	GstTagList	*videotags;
+
+	GstMessageType	ignore_messages_mask;
+
+	gboolean	has_audio;
+	gboolean	has_video;
+
+	ExtractMime	mime;
+
+	gint		video_height;
+	gint		video_width;
+	gint		video_fps_n;
+	gint		video_fps_d;
+	gint		audio_channels;
+	gint		audio_samplerate;
+} MetadataExtractor;
+
+static void extract_gstreamer_audio (const gchar *uri, GHashTable *metadata);
+static void extract_gstreamer_video (const gchar *uri, GHashTable *metadata);
+static void extract_gstreamer_image (const gchar *uri, GHashTable *metadata);
+
+static TrackerExtractorData data[] = {
+	{ "audio/*", extract_gstreamer_audio },
+	{ "video/*", extract_gstreamer_video },
+	{ "image/*", extract_gstreamer_image },
+	{ NULL, NULL }
+};
+
+static void
+caps_set (GObject	    *object,
+	  MetadataExtractor *extractor,
+	  const gchar	    *type)
+{
+	GstPad	     *pad;
+	GstStructure *s;
+	GstCaps	     *caps;
+
+	pad = GST_PAD (object);
+
+	if (!(caps = gst_pad_get_negotiated_caps (pad))) {
+		return;
+	}
+
+	s = gst_caps_get_structure (caps, 0);
+
+	if (s) {
+		if (!strcmp (type, "audio")) {
+			if ((extractor->audio_channels != -1 &&
+			     extractor->audio_samplerate != -1) ||
+			    !(gst_structure_get_int (s, "channels", &extractor->audio_channels) &&
+			      gst_structure_get_int (s, "rate", &extractor->audio_samplerate))) {
+				return;
+			}
+		} else if (!strcmp (type, "video")) {
+			if ((extractor->video_fps_n != -1 &&
+			     extractor->video_fps_d != -1 &&
+			     extractor->video_width != -1 &&
+			     extractor->video_height != -1) ||
+			    !(gst_structure_get_fraction (s, "framerate", &extractor->video_fps_n, &extractor->video_fps_d) &&
+			      gst_structure_get_int (s, "width", &extractor->video_width) &&
+			      gst_structure_get_int (s, "height", &extractor->video_height))) {
+				return;
+			}
+		} else {
+			g_assert_not_reached ();
+		}
+	}
+
+	gst_caps_unref (caps);
+}
+
+static void
+caps_set_audio (GObject		  *object,
+		MetadataExtractor *extractor)
+{
+	g_return_if_fail (object);
+	g_return_if_fail (extractor);
+
+	caps_set (object, extractor, "audio");
+}
+
+static void
+caps_set_video (GObject		  *object,
+		MetadataExtractor *extractor)
+{
+	g_return_if_fail (object);
+	g_return_if_fail (extractor);
+
+	caps_set (object, extractor, "video");
+}
+
+static void
+update_stream_info (MetadataExtractor *extractor)
+{
+	GList  *streaminfo;
+	GstPad *audiopad, *videopad;
+
+	g_return_if_fail (extractor);
+
+	streaminfo = NULL;
+	audiopad = videopad = NULL;
+
+	g_object_get (extractor->playbin, "stream-info", &streaminfo, NULL);
+	streaminfo = g_list_copy (streaminfo);
+	g_list_foreach (streaminfo, (GFunc) g_object_ref, NULL);
+
+	for ( ; streaminfo; streaminfo = streaminfo->next) {
+		GObject	   *info;
+		gint	    type;
+		GParamSpec *pspec;
+		GEnumValue *val;
+
+		info = streaminfo->data;
+
+		if (!info) {
+			continue;
+		}
+
+		type = -1;
+
+		g_object_get (info, "type", &type, NULL);
+		pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (info), "type");
+		val = g_enum_get_value (G_PARAM_SPEC_ENUM (pspec)->enum_class, type);
+
+		if (!strcmp (val->value_nick, "audio")) {
+			extractor->has_audio = TRUE;
+			if (!audiopad) {
+				g_object_get (info, "object", &audiopad, NULL);
+			}
+		} else if (!strcmp (val->value_nick, "video")) {
+			extractor->has_video = TRUE;
+			if (!videopad) {
+				g_object_get (info, "object", &videopad, NULL);
+			}
+		}
+	}
+
+	if (audiopad) {
+		GstCaps *caps;
+
+		if ((caps = gst_pad_get_negotiated_caps (audiopad))) {
+			caps_set_audio (G_OBJECT (audiopad), extractor);
+			gst_caps_unref (caps);
+		}
+	}
+
+	if (videopad) {
+		GstCaps *caps;
+
+		if ((caps = gst_pad_get_negotiated_caps (videopad))) {
+			caps_set_video (G_OBJECT (videopad), extractor);
+			gst_caps_unref (caps);
+		}
+	}
+
+	g_list_foreach (streaminfo, (GFunc) g_object_unref, NULL);
+	g_list_free (streaminfo);
+}
+
+static void
+gst_bus_cb (GstBus	      *bus,
+	    GstMessage	      *message,
+	    MetadataExtractor *extractor)
+{
+	GstMessageType msg_type;
+
+	g_return_if_fail (bus);
+	g_return_if_fail (message);
+	g_return_if_fail (extractor);
+
+	msg_type = GST_MESSAGE_TYPE (message);
+
+	/* Somebody else is handling the message, probably in
+	 * poll_for_state_change.
+	 */
+	if (extractor->ignore_messages_mask & msg_type) {
+		gchar *src_name;
+
+		src_name = gst_object_get_name (message->src);
+		GST_LOG ("Ignoring %s message from element %s as requested",
+			 gst_message_type_get_name (msg_type), src_name);
+		g_free (src_name);
+
+		return;
+	}
+
+	switch (msg_type) {
+	case GST_MESSAGE_ERROR: {
+		GstMessage *message  = NULL;
+		GError	   *gsterror = NULL;
+		gchar	   *debug    = NULL;
+
+		gst_message_parse_error (message, &gsterror, &debug);
+		g_warning ("Error: %s (%s)", gsterror->message, debug);
+
+		gst_message_unref (message);
+		g_error_free (gsterror);
+		g_free (debug);
+	}
+		break;
+
+	case GST_MESSAGE_STATE_CHANGED: {
+		GstState old_state, new_state;
+
+		old_state = new_state = GST_STATE_NULL;
+
+		gst_message_parse_state_changed (message,
+						 &old_state,
+						 &new_state,
+						 NULL);
+
+		if (old_state == new_state) {
+			break;
+		}
+
+		/* We only care about playbin (pipeline) state changes */
+		if (GST_MESSAGE_SRC (message) != GST_OBJECT (extractor->playbin)) {
+			break;
+		}
+
+		if (old_state == GST_STATE_READY && new_state == GST_STATE_PAUSED) {
+			update_stream_info (extractor);
+		} else if (old_state == GST_STATE_PAUSED && new_state == GST_STATE_READY) {
+			/* Clean metadata cache */
+			if (extractor->tagcache) {
+				gst_tag_list_free (extractor->tagcache);
+				extractor->tagcache = NULL;
+			}
+
+			if (extractor->audiotags) {
+				gst_tag_list_free (extractor->audiotags);
+				extractor->audiotags = NULL;
+			}
+
+			if (extractor->videotags) {
+				gst_tag_list_free (extractor->videotags);
+				extractor->videotags = NULL;
+			}
+
+			extractor->has_audio = extractor->has_video = FALSE;
+
+			extractor->video_fps_n = extractor->video_fps_d = -1;
+			extractor->video_height = extractor->video_width = -1;
+			extractor->audio_channels = -1;
+			extractor->audio_samplerate = -1;
+		}
+
+		break;
+	}
+
+	case GST_MESSAGE_TAG: {
+		GstTagList	  *tag_list, *result;
+		GstElementFactory *f;
+
+		tag_list = NULL;
+
+		gst_message_parse_tag (message, &tag_list);
+
+		GST_DEBUG ("Tags: %" GST_PTR_FORMAT, tag_list);
+
+		/* All tags */
+		result = gst_tag_list_merge (extractor->tagcache,
+					     tag_list,
+					     GST_TAG_MERGE_KEEP);
+
+		if (extractor->tagcache) {
+			gst_tag_list_free (extractor->tagcache);
+		}
+
+		extractor->tagcache = result;
+
+		/* media-type-specific tags */
+		if (GST_IS_ELEMENT (message->src) &&
+		    (f = gst_element_get_factory (GST_ELEMENT (message->src)))) {
+			GstTagList  **cache;
+			const gchar  *klass;
+
+			cache = NULL;
+			klass = gst_element_factory_get_klass (f);
+
+			if (g_strrstr (klass, "Audio")) {
+				cache = &extractor->audiotags;
+			} else if (g_strrstr (klass, "Video")) {
+				cache = &extractor->videotags;
+			}
+
+			if (cache) {
+				result = gst_tag_list_merge (*cache,
+							     tag_list,
+							     GST_TAG_MERGE_KEEP);
+				if (*cache) {
+					gst_tag_list_free (*cache);
+				}
+
+				*cache = result;
+			}
+		}
+
+		gst_tag_list_free (tag_list);
+
+		break;
+	}
+
+	default:
+		break;
+	}
+}
+
+static void
+add_int64_info (GHashTable *metadata,
+		gchar	   *key,
+		gint64	    info)
+{
+	gchar *str_info;
+
+	str_info = g_strdup_printf ("%" G_GINT64_FORMAT, info);
+	g_hash_table_insert (metadata, key, str_info);
+}
+
+static void
+add_uint_info (GHashTable *metadata,
+	       gchar	  *key,
+	       guint	   info)
+{
+	gchar *str_info;
+
+	str_info = g_strdup_printf ("%d", info);
+	g_hash_table_insert (metadata, key, str_info);
+}
+
+static void
+add_string_gst_tag (GHashTable	*metadata,
+		    const gchar *key,
+		    GstTagList	*tag_list,
+		    const gchar *tag)
+{
+	gchar	 *s;
+	gboolean  ret;
+
+	s = NULL;
+	ret = gst_tag_list_get_string (tag_list, tag, &s);
+
+	if (s) {
+		if (ret && s[0] != '\0') {
+			g_hash_table_insert (metadata,
+					     g_strdup (key),
+					     s);
+		} else {
+			g_free (s);
+		}
+	}
+}
+
+static void
+add_uint_gst_tag (GHashTable  *metadata,
+		  const gchar *key,
+		  GstTagList  *tag_list,
+		  const gchar *tag)
+{
+	gboolean ret;
+	guint	 n;
+
+	ret = gst_tag_list_get_uint (tag_list, tag, &n);
+
+	if (ret) {
+		g_hash_table_insert (metadata,
+				     g_strdup (key),
+				     g_strdup_printf ("%d", n));
+	}
+}
+
+static void
+add_double_gst_tag (GHashTable	*metadata,
+		    const gchar *key,
+		    GstTagList	*tag_list,
+		    const gchar *tag)
+{
+	gboolean ret;
+	gdouble	 n;
+
+	ret = gst_tag_list_get_double (tag_list, tag, &n);
+
+	if (ret) {
+		g_hash_table_insert (metadata,
+				     g_strdup (key),
+				     g_strdup_printf ("%f", n));
+	}
+}
+
+static void
+add_year_of_gdate_gst_tag (GHashTable  *metadata,
+			   const gchar *key,
+			   GstTagList  *tag_list,
+			   const gchar *tag)
+{
+	GDate	 *date;
+	gboolean  ret;
+
+	date = NULL;
+	ret = gst_tag_list_get_date (tag_list, tag, &date);
+
+	if (ret) {
+		gchar buf[10];
+
+		if (g_date_strftime (buf, 10, "%Y", date)) {
+			g_hash_table_insert (metadata,
+					     g_strdup (key),
+					     g_strdup (buf));
+		}
+	}
+
+	if (date) {
+		g_date_free (date);
+	}
+}
+
+static gint64
+get_media_duration (MetadataExtractor *extractor)
+{
+	gint64	  duration;
+	GstFormat fmt;
+
+	g_return_val_if_fail (extractor, -1);
+	g_return_val_if_fail (extractor->playbin, -1);
+
+	fmt = GST_FORMAT_TIME;
+
+	duration = -1;
+
+	if (gst_element_query_duration (extractor->playbin,
+					&fmt,
+					&duration) &&
+	    duration >= 0) {
+		return duration / GST_SECOND;
+	} else {
+		return -1;
+	}
+}
+
+static void
+extract_metadata (MetadataExtractor *extractor,
+		  GHashTable	    *metadata)
+{
+	g_return_if_fail (extractor);
+	g_return_if_fail (metadata);
+
+	if (extractor->audio_channels >= 0) {
+		add_uint_info (metadata,
+			       g_strdup ("Audio:Channels"),
+			       extractor->audio_channels);
+	}
+
+	if (extractor->audio_samplerate >= 0) {
+		add_uint_info (metadata,
+			       g_strdup ("Audio:Samplerate"),
+			       extractor->audio_samplerate);
+	}
+
+	if (extractor->video_height >= 0) {
+		if (extractor->mime == EXTRACT_MIME_IMAGE) {
+			add_uint_info (metadata,
+				       g_strdup ("Image:Height"),
+				       extractor->video_height);
+		} else {
+			add_uint_info (metadata,
+				       g_strdup ("Video:Height"),
+				       extractor->video_height);
+		}
+	}
+
+	if (extractor->video_width >= 0) {
+		if (extractor->mime == EXTRACT_MIME_IMAGE) {
+			add_uint_info (metadata,
+				       g_strdup ("Image:Width"),
+				       extractor->video_height);
+		} else {
+			add_uint_info (metadata,
+				       g_strdup ("Video:Width"),
+				       extractor->video_height);
+		}
+	}
+
+	if (extractor->video_fps_n >= 0 && extractor->video_fps_d >= 0) {
+		add_uint_info (metadata,
+			       g_strdup ("Video:FrameRate"),
+			       ((extractor->video_fps_n + extractor->video_fps_d / 2) / extractor->video_fps_d));
+	}
+
+	if (extractor->tagcache) {
+		gint64 duration;
+
+		/* Audio */
+		add_string_gst_tag (metadata, "Audio:Album", extractor->tagcache, GST_TAG_ALBUM);
+		add_uint_gst_tag (metadata, "Audio:AlbumTrackCount", extractor->tagcache, GST_TAG_TRACK_COUNT);
+		add_uint_gst_tag (metadata, "Audio:TrackNo", extractor->tagcache, GST_TAG_TRACK_NUMBER);
+		add_uint_gst_tag (metadata, "Audio:DiscNo", extractor->tagcache, GST_TAG_ALBUM_VOLUME_NUMBER);
+		add_string_gst_tag (metadata, "Audio:Performer", extractor->tagcache, GST_TAG_PERFORMER);
+		add_double_gst_tag (metadata, "Audio:TrackGain", extractor->tagcache, GST_TAG_TRACK_GAIN);
+		add_double_gst_tag (metadata, "Audio:PeakTrackGain", extractor->tagcache, GST_TAG_TRACK_PEAK);
+		add_double_gst_tag (metadata, "Audio:AlbumGain", extractor->tagcache, GST_TAG_ALBUM_GAIN);
+		add_double_gst_tag (metadata, "Audio:AlbumPeakGain", extractor->tagcache, GST_TAG_ALBUM_PEAK);
+		add_year_of_gdate_gst_tag (metadata, "Audio:ReleaseDate", extractor->tagcache, GST_TAG_DATE);
+		add_string_gst_tag (metadata, "Audio:Genre", extractor->tagcache, GST_TAG_GENRE);
+		add_string_gst_tag (metadata, "Audio:Codec", extractor->tagcache, GST_TAG_AUDIO_CODEC);
+
+		/* Video */
+		add_string_gst_tag (metadata, "Video:Codec", extractor->tagcache, GST_TAG_VIDEO_CODEC);
+
+		/* General */
+		add_string_gst_tag (metadata, "File:Copyright", extractor->tagcache, GST_TAG_COPYRIGHT);
+		add_string_gst_tag (metadata, "File:License", extractor->tagcache, GST_TAG_LICENSE);
+		add_string_gst_tag (metadata, "DC:Coverage", extractor->tagcache, GST_TAG_LOCATION);
+
+		duration = get_media_duration (extractor);
+
+		if (extractor->mime == EXTRACT_MIME_IMAGE &&
+		    extractor->has_video) {
+			add_string_gst_tag (metadata, "Image:Title", extractor->tagcache, GST_TAG_TITLE);
+			add_string_gst_tag (metadata, "Image:Comments", extractor->tagcache, GST_TAG_COMMENT);
+			add_string_gst_tag (metadata, "Image:Author", extractor->tagcache, GST_TAG_ARTIST);
+
+		} else if (extractor->has_video) {
+			add_string_gst_tag (metadata, "Video:Title", extractor->tagcache, GST_TAG_TITLE);
+			add_string_gst_tag (metadata, "Video:Comments", extractor->tagcache, GST_TAG_COMMENT);
+
+			/* FIXME: is it a good idea to use GST_TAG_ARTIST as author?! */
+			add_string_gst_tag (metadata, "Video:Author", extractor->tagcache, GST_TAG_ARTIST);
+			add_string_gst_tag (metadata, "File:Copyright", extractor->tagcache, GST_TAG_COPYRIGHT);
+
+			if (duration >= 0) {
+				add_int64_info (metadata, g_strdup ("Video:Duration"), duration);
+			}
+		} else if (extractor->has_audio) {
+			/* No video? So we assume we are treating a song */
+			add_string_gst_tag (metadata, "Audio:Title", extractor->tagcache, GST_TAG_TITLE);
+			add_string_gst_tag (metadata, "Audio:Artist", extractor->tagcache, GST_TAG_ARTIST);
+			add_string_gst_tag (metadata, "Audio:Comment", extractor->tagcache, GST_TAG_COMMENT);
+
+			if (duration >= 0) {
+				add_int64_info (metadata, g_strdup ("Audio:Duration"), duration);
+			}
+		}
+	}
+
+	if (extractor->audiotags) {
+		add_uint_gst_tag (metadata, "Audio:Bitrate", extractor->tagcache, GST_TAG_BITRATE);
+	}
+
+	if (extractor->videotags) {
+		add_uint_gst_tag (metadata, "Video:Bitrate", extractor->tagcache, GST_TAG_BITRATE);
+	}
+}
+
+static gboolean
+poll_for_state_change (MetadataExtractor *extractor,
+		       GstState		  state)
+{
+	GstBus	       *bus;
+	GstMessageType	events, saved_events;
+
+	g_return_val_if_fail (extractor, FALSE);
+	g_return_val_if_fail (extractor->playbin, FALSE);
+
+	bus = gst_element_get_bus (extractor->playbin);
+
+	events =
+		GST_MESSAGE_STATE_CHANGED |
+		GST_MESSAGE_ERROR |
+		GST_MESSAGE_EOS;
+
+	saved_events = extractor->ignore_messages_mask;
+
+	if (extractor->playbin) {
+		/* we do want the main handler to process state changed messages for
+		 * playbin as well, otherwise it won't hook up the timeout etc. */
+		extractor->ignore_messages_mask |= (events ^ GST_MESSAGE_STATE_CHANGED);
+	} else {
+		extractor->ignore_messages_mask |= events;
+	}
+
+	for (;;) {
+		GstMessage *message;
+		GstElement *src;
+
+		message = gst_bus_poll (bus, events, GST_SECOND * 5);
+
+		if (!message) {
+			goto timed_out;
+		}
+
+		src = (GstElement *) GST_MESSAGE_SRC (message);
+
+		switch (GST_MESSAGE_TYPE (message)) {
+		case GST_MESSAGE_STATE_CHANGED: {
+			GstState old, new, pending;
+
+			old = new = pending = GST_STATE_NULL;
+
+			if (src == extractor->playbin) {
+				gst_message_parse_state_changed (message, &old, &new, &pending);
+
+				if (new == state) {
+					gst_message_unref (message);
+					goto success;
+				}
+			}
+
+			break;
+		}
+
+		case GST_MESSAGE_ERROR: {
+			gchar  *debug	 = NULL;
+			GError *gsterror = NULL;
+
+			gst_message_parse_error (message, &gsterror, &debug);
+
+			g_warning ("Error: %s (%s)", gsterror->message, debug);
+
+			g_error_free (gsterror);
+			gst_message_unref (message);
+			g_free (debug);
+			goto error;
+
+			break;
+		}
+
+		case GST_MESSAGE_EOS: {
+			g_warning ("Media file could not be played.");
+			gst_message_unref (message);
+			goto error;
+			break;
+		}
+
+		default:
+			g_assert_not_reached ();
+			break;
+		}
+
+		gst_message_unref (message);
+	}
+
+	g_assert_not_reached ();
+
+ success:
+	/* State change succeeded */
+	GST_DEBUG ("state change to %s succeeded",
+		   gst_element_state_get_name (state));
+	extractor->ignore_messages_mask = saved_events;
+	return TRUE;
+
+ timed_out:
+	/* It's taking a long time to open  */
+	GST_DEBUG ("state change to %s timed out, returning success",
+		   gst_element_state_get_name (state));
+	extractor->ignore_messages_mask = saved_events;
+	return TRUE;
+
+ error:
+	/* Already set *error */
+	GST_DEBUG ("error while waiting for state change to %s",
+		   gst_element_state_get_name (state));
+	extractor->ignore_messages_mask = saved_events;
+	return FALSE;
+}
+
+static void
+tracker_extract_gstreamer (const gchar *uri,
+			   GHashTable  *metadata,
+			   ExtractMime	type)
+{
+	MetadataExtractor *extractor;
+	gchar		  *mrl;
+	GstElement	  *fakesink_audio, *fakesink_video;
+	GstBus		  *bus;
+
+	g_return_if_fail (uri);
+	g_return_if_fail (metadata);
+
+	g_type_init ();
+
+	gst_init (NULL, NULL);
+
+	/* set up */
+	extractor = g_slice_new0 (MetadataExtractor);
+
+	extractor->tagcache = NULL;
+	extractor->audiotags = extractor->videotags = NULL;
+
+	extractor->has_audio = extractor->has_video = FALSE;
+
+	extractor->video_fps_n = extractor->video_fps_d = -1;
+	extractor->video_height = extractor->video_width = -1;
+	extractor->audio_channels = -1;
+	extractor->audio_samplerate = -1;
+
+	extractor->ignore_messages_mask = 0;
+
+	extractor->mime = type;
+
+	extractor->playbin = gst_element_factory_make ("playbin", "playbin");
+
+	/* Add bus callback */
+	bus = gst_element_get_bus (GST_ELEMENT (extractor->playbin));
+	gst_bus_add_signal_watch (bus);
+	g_signal_connect (bus, "message", G_CALLBACK (gst_bus_cb), extractor);
+	gst_object_unref (bus);
+
+	mrl = g_strconcat ("file://", uri, NULL);
+
+	/* Set playbin object */
+	g_object_set (G_OBJECT (extractor->playbin), "uri", mrl, NULL);
+	g_free (mrl);
+
+	fakesink_audio = gst_element_factory_make ("fakesink", "fakesink-audio");
+	g_object_set (G_OBJECT (extractor->playbin), "audio-sink", fakesink_audio, NULL);
+
+	fakesink_video = gst_element_factory_make ("fakesink", "fakesink-video");
+	g_object_set (G_OBJECT (extractor->playbin), "video-sink", fakesink_video, NULL);
+
+
+	/* start to parse infos and extract them */
+	gst_element_set_state (extractor->playbin, GST_STATE_PAUSED);
+
+	poll_for_state_change (extractor, GST_STATE_PAUSED);
+
+	extract_metadata (extractor, metadata);
+
+	/* Check that we have the minimum data. FIXME We should not need to do this */
+	if (!g_hash_table_lookup (metadata, "Audio:Title")) {
+		g_hash_table_insert (metadata, g_strdup ("Audio:Title"), g_strdup ("tracker:unknown"));
+	}
+
+	if (!g_hash_table_lookup (metadata, "Audio:Album")) {
+		g_hash_table_insert (metadata, g_strdup ("Audio:Album"), g_strdup ("tracker:unknown"));
+	}
+
+	if (!g_hash_table_lookup (metadata, "Audio:Artist")) {
+		g_hash_table_insert (metadata, g_strdup ("Audio:Artist"), g_strdup ("tracker:unknown"));
+	}
+
+	if (!g_hash_table_lookup (metadata, "Audio:Genre")) {
+		g_hash_table_insert (metadata, g_strdup ("Audio:Genre"), g_strdup ("tracker:unknown"));
+	}
+
+	/* Also clean up */
+	gst_element_set_state (extractor->playbin, GST_STATE_NULL);
+
+	gst_object_unref (GST_OBJECT (extractor->playbin));
+
+	g_slice_free (MetadataExtractor, extractor);
+}
+
+static void
+extract_gstreamer_audio (const gchar *uri, GHashTable *metadata)
+{
+	tracker_extract_gstreamer (uri, metadata, EXTRACT_MIME_AUDIO);
+}
+
+static void
+extract_gstreamer_video (const gchar *uri, GHashTable *metadata)
+{
+	tracker_extract_gstreamer (uri, metadata, EXTRACT_MIME_VIDEO);
+}
+
+static void
+extract_gstreamer_image (const gchar *uri, GHashTable *metadata)
+{
+	tracker_extract_gstreamer (uri, metadata, EXTRACT_MIME_IMAGE);
+}
+
+TrackerExtractorData *
+tracker_get_extractor_data (void)
+{
+	return data;
+}

Added: trunk/src/tracker-extract/tracker-extract-html.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-extract/tracker-extract-html.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,228 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007, Jason Kivlighn (jkivlighn gmail com)
+ * Copyright (C) 2008, Nokia
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <glib.h>
+
+#include <libxml/HTMLparser.h>
+
+#include "tracker-extract.h"
+
+typedef enum {
+	READ_TITLE,
+} tag_type;
+
+typedef struct {
+	GHashTable *metadata;
+	tag_type current;
+} HTMLParseInfo;
+
+static void extract_html (const gchar *filename,
+			  GHashTable  *metadata);
+
+static TrackerExtractorData data[] = {
+	{ "text/html",		   extract_html },
+	{ "application/xhtml+xml", extract_html },
+	{ NULL, NULL }
+};
+
+static gboolean
+has_attribute (const xmlChar **atts,
+	       const gchar    *attr,
+	       const gchar    *val)
+{
+	gint i;
+
+	if (!(atts && attr && val)) {
+		return FALSE;
+	}
+
+	for (i = 0; atts[i] && atts[i + 1]; i += 2) {
+		if (strcasecmp ((gchar*) atts[i], attr) == 0) {
+			if (strcasecmp ((gchar*) atts[i + 1], val) == 0) {
+				return TRUE;
+			}
+		}
+	}
+
+	return FALSE;
+}
+
+static const xmlChar *
+lookup_attribute (const xmlChar **atts,
+		  const gchar	 *attr)
+{
+	gint i;
+
+	if (!atts || !attr) {
+		return NULL;
+	}
+
+	for (i = 0; atts[i] && atts[i + 1]; i += 2) {
+		if (strcasecmp ((gchar*) atts[i], attr) == 0) {
+			return atts[i + 1];
+		}
+	}
+
+	return NULL;
+}
+
+void
+startElement (void	     *info,
+	      const xmlChar  *name,
+	      const xmlChar **atts)
+{
+	if (!(info && name)) {
+		return;
+	}
+
+	/* Look for RDFa triple describing the license */
+	if (strcasecmp ((gchar*) name, "a") == 0) {
+		/* This tag is a license.  Ignore, however, if it is
+		 * referring to another document.
+		 */
+		if (has_attribute (atts, "rel", "license") &&
+		    has_attribute (atts, "about", NULL) == FALSE) {
+			const xmlChar *href;
+
+			href = lookup_attribute (atts, "href");
+
+			if (href) {
+				g_hash_table_insert (((HTMLParseInfo*) info)->metadata,
+						     g_strdup ("File:License"),
+						     g_strdup ((gchar*)  href));
+			}
+		}
+	} else if (strcasecmp ((gchar*)name, "title") == 0) {
+		((HTMLParseInfo*) info)->current = READ_TITLE;
+	} else if (strcasecmp ((gchar*)name, "meta") == 0) {
+		if (has_attribute (atts, "name", "Author")) {
+			const xmlChar *author;
+
+			author = lookup_attribute (atts, "content");
+
+			if (author) {
+				g_hash_table_insert (((HTMLParseInfo*) info)->metadata,
+						     g_strdup ("Doc:Author"),
+						     g_strdup ((gchar*) author));
+			}
+		}
+
+		if (has_attribute (atts, "name", "DC.Description")) {
+			const xmlChar *desc;
+
+			desc = lookup_attribute (atts,"content");
+
+			if (desc) {
+				g_hash_table_insert (((HTMLParseInfo*) info)->metadata,
+						     g_strdup ("Doc:Comments"),
+						     g_strdup ((gchar*) desc));
+			}
+		}
+
+		if (has_attribute (atts, "name", "KEYWORDS") ||
+		    has_attribute (atts, "name", "keywords")) {
+			const xmlChar *keywords;
+
+			keywords = lookup_attribute (atts, "content");
+
+			if (keywords) {
+				g_hash_table_insert (((HTMLParseInfo*) info)->metadata,
+						     g_strdup ("Doc:Keywords"),
+						     g_strdup ((gchar*) keywords));
+			}
+		}
+	}
+}
+
+void
+characters (void	  *info,
+	    const xmlChar *ch,
+	    int		   len)
+{
+	switch (((HTMLParseInfo*) info)->current) {
+	case READ_TITLE:
+		g_hash_table_insert (((HTMLParseInfo*) info)->metadata,
+				     g_strdup ("Doc:Title"),
+				     g_strdup ((gchar*) ch));
+		break;
+	default:
+		break;
+	}
+
+	((HTMLParseInfo*) info)->current = -1;
+}
+
+static void
+extract_html (const gchar *filename,
+	      GHashTable  *metadata)
+{
+	xmlSAXHandler SAXHandlerStruct = {
+			NULL, /* internalSubset */
+			NULL, /* isStandalone */
+			NULL, /* hasInternalSubset */
+			NULL, /* hasExternalSubset */
+			NULL, /* resolveEntity */
+			NULL, /* getEntity */
+			NULL, /* entityDecl */
+			NULL, /* notationDecl */
+			NULL, /* attributeDecl */
+			NULL, /* elementDecl */
+			NULL, /* unparsedEntityDecl */
+			NULL, /* setDocumentLocator */
+			NULL, /* startDocument */
+			NULL, /* endDocument */
+			startElement, /* startElement */
+			NULL, /* endElement */
+			NULL, /* reference */
+			characters, /* characters */
+			NULL, /* ignorableWhitespace */
+			NULL, /* processingInstruction */
+			NULL, /* comment */
+			NULL, /* xmlParserWarning */
+			NULL, /* xmlParserError */
+			NULL, /* xmlParserError */
+			NULL, /* getParameterEntity */
+			NULL, /* cdataBlock */
+			NULL, /* externalSubset */
+			1,    /* initialized */
+			NULL, /* private */
+			NULL, /* startElementNsSAX2Func */
+			NULL, /* endElementNsSAX2Func */
+			NULL  /* xmlStructuredErrorFunc */
+	};
+
+	HTMLParseInfo	info = { metadata, -1 };
+
+	htmlDocPtr doc;
+	doc = htmlSAXParseFile (filename, NULL, &SAXHandlerStruct, &info);
+	if (doc) {
+		xmlFreeDoc (doc);
+	}
+}
+
+TrackerExtractorData *
+tracker_get_extractor_data (void)
+{
+	return data;
+}

Added: trunk/src/tracker-extract/tracker-extract-imagemagick.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-extract/tracker-extract-imagemagick.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,104 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include <libtracker-common/tracker-os-dependant.h>
+
+#include "tracker-extract.h"
+#include "tracker-xmp.h"
+
+static void
+tracker_extract_imagemagick (const gchar *filename, GHashTable *metadata)
+{
+	gchar *argv[6];
+	gchar *identify;
+	gchar **lines;
+	gint  exit_status;
+
+	g_return_if_fail (filename != NULL);
+
+	/* imagemagick crashes trying to extract from xcf files */
+	if (g_str_has_suffix (filename, ".xcf")) {
+		return;
+	}
+
+	argv[0] = g_strdup ("identify");
+	argv[1] = g_strdup ("-format");
+	argv[2] = g_strdup ("%w;\\n%h;\\n%c;\\n");
+	if (g_str_has_suffix (filename, ".xcf")) {
+		argv[3] = g_strdup (filename);
+		argv[4] = NULL;
+	}
+	else {
+		argv[3] = g_strdup ("-ping");
+		argv[4] = g_strdup (filename);
+	}
+	argv[5] = NULL;
+
+	if (tracker_spawn (argv, 10, &identify, &exit_status)) {
+		if (exit_status == EXIT_SUCCESS) {
+			lines = g_strsplit (identify, ";\n", 4);
+			g_hash_table_insert (metadata, g_strdup ("Image:Width"), g_strdup (lines[0]));
+			g_hash_table_insert (metadata, g_strdup ("Image:Height"), g_strdup (lines[1]));
+			g_hash_table_insert (metadata, g_strdup ("Image:Comments"), g_strdup (g_strescape (lines[2], "")));
+		}
+	}
+
+#ifdef HAVE_EXEMPI
+
+	/* convert is buggy atm so disable temporarily */
+
+	return;
+
+	gchar *xmp;
+
+	argv[0] = g_strdup ("convert");
+	argv[1] = g_strdup (filename);
+	argv[2] = g_strdup ("xmp:-");
+	argv[3] = NULL;
+
+	if (tracker_spawn (argv, 10, &xmp, &exit_status)) {
+		if (exit_status == EXIT_SUCCESS && xmp) {
+			tracker_read_xmp (xmp, strlen (xmp), metadata);
+		}
+	}
+#endif
+}
+
+
+TrackerExtractorData data[] = {
+	{ "image/*", tracker_extract_imagemagick },
+	{ NULL, NULL }
+};
+
+
+TrackerExtractorData *
+tracker_get_extractor_data (void)
+{
+	return data;
+}

Added: trunk/src/tracker-extract/tracker-extract-jpeg.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-extract/tracker-extract-jpeg.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,307 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include <jpeglib.h>
+
+#include "tracker-extract.h"
+#include "tracker-xmp.h"
+
+static void extract_jpeg (const gchar *filename,
+			  GHashTable  *metadata);
+
+static TrackerExtractorData data[] = {
+	{ "image/jpeg", extract_jpeg },
+	{ NULL, NULL }
+};
+
+#ifdef HAVE_EXEMPI
+#define XMP_NAMESPACE	     "http://ns.adobe.com/xap/1.0/\x00";
+#define XMP_NAMESPACE_LENGTH 29
+#endif /* HAVE_EXEMPI */
+
+#ifdef HAVE_LIBEXIF
+
+#include <libexif/exif-data.h>
+
+#define EXIF_DATE_FORMAT "%Y:%m:%d %H:%M:%S"
+
+typedef gchar * (*PostProcessor) (const gchar *);
+
+typedef struct {
+	ExifTag       tag;
+	gchar	     *name;
+	PostProcessor post;
+} TagType;
+
+static gchar *date_to_iso8601	(const gchar *exif_date);
+static gchar *fix_focal_length	(const gchar *fl);
+static gchar *fix_flash		(const gchar *flash);
+static gchar *fix_fnumber	(const gchar *fn);
+static gchar *fix_exposure_time (const gchar *et);
+
+static TagType tags[] = {
+	{ EXIF_TAG_PIXEL_Y_DIMENSION, "Image:Height", NULL },
+	{ EXIF_TAG_PIXEL_X_DIMENSION, "Image:Width", NULL },
+	{ EXIF_TAG_RELATED_IMAGE_WIDTH, "Image:Width", NULL },
+	{ EXIF_TAG_DOCUMENT_NAME, "Image:Title", NULL },
+	/* { -1, "Image:Album", NULL }, */
+	{ EXIF_TAG_DATE_TIME, "Image:Date", date_to_iso8601 },
+	/* { -1, "Image:Keywords", NULL }, */
+	{ EXIF_TAG_ARTIST, "Image:Creator", NULL },
+	{ EXIF_TAG_USER_COMMENT, "Image:Comments", NULL },
+	{ EXIF_TAG_IMAGE_DESCRIPTION, "Image:Description", NULL },
+	{ EXIF_TAG_SOFTWARE, "Image:Software", NULL },
+	{ EXIF_TAG_MAKE, "Image:CameraMake", NULL },
+	{ EXIF_TAG_MODEL, "Image:CameraModel", NULL },
+	{ EXIF_TAG_ORIENTATION, "Image:Orientation", NULL },
+	{ EXIF_TAG_EXPOSURE_PROGRAM, "Image:ExposureProgram", NULL },
+	{ EXIF_TAG_EXPOSURE_TIME, "Image:ExposureTime", fix_exposure_time },
+	{ EXIF_TAG_FNUMBER, "Image:FNumber", fix_fnumber },
+	{ EXIF_TAG_FLASH, "Image:Flash", fix_flash },
+	{ EXIF_TAG_FOCAL_LENGTH, "Image:FocalLength", fix_focal_length },
+	{ EXIF_TAG_ISO_SPEED_RATINGS, "Image:ISOSpeed", NULL },
+	{ EXIF_TAG_METERING_MODE, "Image:MeteringMode", NULL },
+	{ EXIF_TAG_WHITE_BALANCE, "Image:WhiteBalance", NULL },
+	{ EXIF_TAG_COPYRIGHT, "File:Copyright", NULL },
+	{ -1, NULL, NULL }
+};
+
+static gchar *
+date_to_iso8601 (const gchar *exif_date)
+{
+	/* From: ex; date "2007:04:15 15:35:58"
+	 * To  : ex. "2007-04-15T17:35:58+0200 where +0200 is localtime
+	 */
+	return tracker_generic_date_to_iso8601 (exif_date, EXIF_DATE_FORMAT);
+}
+
+static gchar *
+fix_focal_length (const gchar *fl)
+{
+	return g_strndup (fl, strstr (fl, "mm") - fl);
+}
+
+static gchar *
+fix_flash (const gchar *flash)
+{
+	if (g_str_has_prefix (flash, "No")) {
+		return g_strdup ("0");
+	} else {
+		return g_strdup ("1");
+	}
+}
+
+static gchar *
+fix_fnumber (const gchar *fn)
+{
+	gchar *new_fn;
+
+	if (!fn) {
+		return NULL;
+	}
+
+	new_fn = g_strdup (fn);
+
+	if (new_fn[0] == 'F') {
+		new_fn[0] = ' ';
+	} else if (fn[0] == 'f' && new_fn[1] == '/') {
+		new_fn[0] = new_fn[1] = ' ';
+	}
+
+	return new_fn;
+}
+
+static gchar *
+fix_exposure_time (const gchar *et)
+{
+	gchar *sep;
+
+	sep = strchr (et, '/');
+
+	if (sep) {
+		gdouble fraction;
+
+		fraction = g_ascii_strtod (sep + 1, NULL);
+
+		if (fraction > 0.0) {
+			gdouble val;
+			gchar	buf[G_ASCII_DTOSTR_BUF_SIZE];
+
+			val = 1.0f / fraction;
+			g_ascii_dtostr (buf, sizeof(buf), val);
+
+			return g_strdup (buf);
+		}
+	}
+
+	return g_strdup (et);
+}
+
+#endif /* HAVE_LIBEXIF */
+
+static void
+read_exif (const unsigned char *buffer,
+	   size_t		len,
+	   GHashTable	       *metadata)
+{
+#ifdef HAVE_LIBEXIF
+	ExifData *exif;
+	TagType  *p;
+
+	exif = exif_data_new_from_data ((unsigned char *) buffer, len);
+
+	for (p = tags; p->name; ++p) {
+		ExifEntry *entry;
+
+		entry = exif_data_get_entry (exif, p->tag);
+
+		if (entry) {
+			gchar buffer[1024];
+
+			exif_entry_get_value (entry, buffer, 1024);
+
+			if (p->post) {
+				g_hash_table_insert (metadata,
+						     g_strdup (p->name),
+						     (*p->post) (buffer));
+			} else {
+				g_hash_table_insert (metadata,
+						     g_strdup (p->name),
+						     g_strdup (buffer));
+			}
+		}
+	}
+#endif /* HAVE_LIBEXIF */
+}
+
+static void
+extract_jpeg (const gchar *filename,
+	      GHashTable  *metadata)
+{
+	struct jpeg_decompress_struct  cinfo;
+	struct jpeg_error_mgr	       jerr;
+	struct jpeg_marker_struct     *marker;
+	FILE			      *jpeg;
+	gint			       fd_jpeg;
+
+	if ((fd_jpeg = g_open (filename, O_RDONLY)) == -1) {
+		return;
+	}
+
+	if ((jpeg = fdopen (fd_jpeg, "rb"))) {
+		gchar *str;
+		gsize  len;
+
+		cinfo.err = jpeg_std_error (&jerr);
+		jpeg_create_decompress (&cinfo);
+
+		jpeg_save_markers (&cinfo, JPEG_COM, 0xFFFF);
+		jpeg_save_markers (&cinfo, JPEG_APP0 + 1, 0xFFFF);
+
+		jpeg_stdio_src (&cinfo, jpeg);
+
+		jpeg_read_header (&cinfo, TRUE);
+
+		/* FIXME? It is possible that there are markers after SOS,
+		 * but there shouldn't be. Should we decompress the whole file?
+		 *
+		 * jpeg_start_decompress(&cinfo);
+		 * jpeg_finish_decompress(&cinfo);
+		 *
+		 * jpeg_calc_output_dimensions(&cinfo);
+		*/
+
+		marker = (struct jpeg_marker_struct *) &cinfo.marker_list;
+
+		while (marker) {
+			switch (marker->marker) {
+			case JPEG_COM:
+				str = (gchar*) marker->data;
+				len = marker->data_length;
+
+				g_hash_table_insert (metadata,
+						     g_strdup ("Image:Comments"),
+						     g_strndup (str, len));
+				break;
+
+			case JPEG_APP0+1:
+#if defined(HAVE_LIBEXIF)
+				if (strncmp ("Exif", (gchar*) (marker->data), 5) == 0) {
+					read_exif ((unsigned char*) marker->data,
+						   marker->data_length,
+						   metadata);
+				}
+#endif /* HAVE_LIBEXIF */
+
+#if defined(HAVE_EXEMPI)
+				str = (gchar*) marker->data;
+				len = marker->data_length;
+
+				if (strncmp (XMP_NAMESPACE, str, XMP_NAMESPACE_LENGTH) == 0) {
+					tracker_read_xmp (str + XMP_NAMESPACE_LENGTH,
+							  len - XMP_NAMESPACE_LENGTH,
+							  metadata);
+				}
+#endif /* HAVE_EXEMPI */
+				break;
+
+			default:
+				marker = marker->next;
+				continue;
+			}
+
+			marker = marker->next;
+		}
+
+		/* We want native size to have priority over EXIF, XMP etc */
+		g_hash_table_insert (metadata,
+				     g_strdup ("Image:Width"),
+				     g_strdup_printf ("%u", cinfo.image_width));
+		g_hash_table_insert (metadata,
+				     g_strdup ("Image:Height"),
+				     g_strdup_printf ("%u", cinfo.image_height));
+
+		jpeg_destroy_decompress (&cinfo);
+
+		fclose (jpeg);
+	} else {
+		close (fd_jpeg);
+	}
+}
+
+TrackerExtractorData *
+tracker_get_extractor_data (void)
+{
+	return data;
+}

Added: trunk/src/tracker-extract/tracker-extract-libxine.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-extract/tracker-extract-libxine.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,255 @@
+/* Tracker - audio/video metadata extraction based on Xine
+ * Copyright (C) 2006, Laurent Aguerreche (laurent aguerreche free fr)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <xine.h>
+#include <glib.h>
+
+#include "tracker-extract.h"
+
+
+static void
+add_uint32_info (GHashTable *metadata, char *key, uint32_t info)
+{
+	char *str_info;
+
+	str_info = g_strdup_printf ("%d", info);
+	g_hash_table_insert (metadata, key, str_info);
+}
+
+
+/* Take an absolute path to a file and fill a hashtable with metadata.
+ */
+void
+tracker_extract_xine (gchar *uri, GHashTable *metadata)
+{
+	xine_t		  *xine_base;
+	xine_audio_port_t *audio_port;
+	xine_video_port_t *video_port;
+	xine_stream_t	  *stream;
+	char		  *mrl;
+
+	gboolean	  has_audio;
+	gboolean	  has_video;
+
+	int		  pos_stream;
+	int		  pos_time;
+	int		  length_time;
+
+	const char	  *comment;
+	const char	  *title;
+	const char	  *author;
+	const char	  *album;
+	const char	  *year;
+	const char	  *genre;
+	const char	  *track;
+
+	g_return_if_fail (uri && metadata);
+
+
+	xine_base = xine_new ();
+
+	if (!xine_base) {
+		return;
+	}
+
+	xine_init (xine_base);
+
+	audio_port = xine_open_audio_driver (xine_base, NULL, NULL);
+	video_port = xine_open_video_driver (xine_base, NULL, XINE_VISUAL_TYPE_NONE, NULL);
+
+	if (!audio_port || !video_port) {
+		xine_exit (xine_base);
+		return;
+	}
+
+	stream = xine_stream_new (xine_base, audio_port, video_port);
+
+	if (!stream) {
+		xine_close_audio_driver (xine_base, audio_port);
+		xine_close_video_driver (xine_base, video_port);
+		xine_exit (xine_base);
+		return;
+	}
+
+	mrl = g_strconcat ("file://", uri, NULL);
+
+	if (!xine_open (stream, mrl)) {
+		g_free (mrl);
+		xine_dispose (stream);
+		xine_close_audio_driver (xine_base, audio_port);
+		xine_close_video_driver (xine_base, video_port);
+		xine_exit (xine_base);
+		return;
+	}
+
+	g_free (mrl);
+
+
+	has_audio = xine_get_stream_info (stream, XINE_STREAM_INFO_HAS_AUDIO);
+	has_video = xine_get_stream_info (stream, XINE_STREAM_INFO_HAS_VIDEO);
+
+
+	if (xine_get_pos_length (stream, &pos_stream, &pos_time, &length_time)) {
+		if (length_time >= 0) {
+			uint32_t duration;
+
+			duration = (uint32_t) length_time / 1000; /* from miliseconds to seconds */
+
+			if (has_video) {
+				add_uint32_info (metadata, g_strdup ("Video:Duration"), duration);
+			} else if (has_audio) {
+				add_uint32_info (metadata, g_strdup ("Audio:Duration"), duration);
+			}
+		}
+	}
+
+
+	/* Video */
+
+	if (has_video) {
+		uint32_t   n, n0;
+		const char *video_codec;
+
+		n  = xine_get_stream_info (stream, XINE_STREAM_INFO_VIDEO_HEIGHT);
+		n0 = xine_get_stream_info (stream, XINE_STREAM_INFO_VIDEO_WIDTH);
+		if (n > 0 && n0 > 0) {
+			add_uint32_info (metadata, g_strdup ("Video:Height"), n);
+			add_uint32_info (metadata, g_strdup ("Video:Width"), n0);
+		}
+
+		n = xine_get_stream_info (stream, XINE_STREAM_INFO_FRAME_DURATION);
+		if (n > 0) {
+			/* 90000 because it is how is done in Xine! */
+			add_uint32_info (metadata, g_strdup ("Video:FrameRate"), 90000 / n);
+		}
+
+		n = xine_get_stream_info (stream, XINE_STREAM_INFO_VIDEO_BITRATE);
+		if (n > 0) {
+			add_uint32_info (metadata, g_strdup ("Video:Bitrate"), n);
+		}
+
+		video_codec = xine_get_meta_info (stream, XINE_META_INFO_VIDEOCODEC);
+		if (video_codec) {
+			g_hash_table_insert (metadata, g_strdup ("Video:Codec"), g_strdup (video_codec));
+		}
+	}
+
+
+	/* Audio */
+
+	if (has_audio) {
+		uint32_t   n;
+		const char *audio_codec;
+
+		n = xine_get_stream_info (stream, XINE_STREAM_INFO_AUDIO_BITRATE);
+		if (n > 0) {
+			add_uint32_info (metadata, g_strdup ("Audio:Bitrate"), n);
+		}
+
+		n = xine_get_stream_info (stream, XINE_STREAM_INFO_AUDIO_SAMPLERATE);
+		if (n > 0) {
+			add_uint32_info (metadata, g_strdup ("Audio:Samplerate"), n);
+		}
+
+		n = xine_get_stream_info (stream, XINE_STREAM_INFO_AUDIO_CHANNELS);
+		if (n > 0) {
+			add_uint32_info (metadata, g_strdup ("Audio:Channels"), n);
+		}
+
+		audio_codec = xine_get_meta_info (stream, XINE_META_INFO_AUDIOCODEC);
+		if (audio_codec) {
+			g_hash_table_insert (metadata, g_strdup ("Audio:Codec"), g_strdup (audio_codec));
+		}
+	}
+
+
+	/* Tags */
+
+	comment = xine_get_meta_info (stream, XINE_META_INFO_COMMENT);
+	if (comment) {
+		if (has_video) {
+			g_hash_table_insert (metadata, g_strdup ("Video:Comment"), g_strdup (comment));
+		} else if (has_audio) {
+			g_hash_table_insert (metadata, g_strdup ("Audio:Comment"), g_strdup (comment));
+		}
+	}
+
+	title = xine_get_meta_info (stream, XINE_META_INFO_TITLE);
+	if (title) {
+		if (has_video) {
+			g_hash_table_insert (metadata, g_strdup ("Video:Title"), g_strdup (title));
+		} else if (has_audio) {
+			g_hash_table_insert (metadata, g_strdup ("Audio:Title"), g_strdup (title));
+		}
+	}
+
+	author = xine_get_meta_info (stream, XINE_META_INFO_ARTIST);
+	if (author) {
+		if (has_video) {
+			g_hash_table_insert (metadata, g_strdup ("Video:Author"), g_strdup (author));
+		} else if (has_audio) {
+			g_hash_table_insert (metadata, g_strdup ("Audio:Author"), g_strdup (author));
+		}
+	}
+
+	album = xine_get_meta_info (stream, XINE_META_INFO_ALBUM);
+	if (album) {
+		g_hash_table_insert (metadata, g_strdup ("Audio:Album"), g_strdup (album));
+	}
+
+	year = xine_get_meta_info (stream, XINE_META_INFO_YEAR);
+	if (year) {
+		g_hash_table_insert (metadata, g_strdup ("Audio:Year"), g_strdup (year));
+	}
+
+	genre = xine_get_meta_info (stream, XINE_META_INFO_GENRE);
+	if (genre) {
+		g_hash_table_insert (metadata, g_strdup ("Audio:Genre"), g_strdup (genre));
+	}
+
+	track = xine_get_meta_info (stream, XINE_META_INFO_TRACK_NUMBER);
+	if (track) {
+		g_hash_table_insert (metadata, g_strdup ("Audio:Track"), g_strdup (track));
+	}
+
+	/* FIXME: "Video.Copyright" seems missing */
+	/* g_hash_table_insert (metadata, g_strdup ("Video.Copyright"), NULL); */
+
+
+	xine_dispose (stream);
+
+	xine_close_audio_driver (xine_base, audio_port);
+	xine_close_video_driver (xine_base, video_port);
+
+	xine_exit (xine_base);
+}
+
+
+TrackerExtractorData data[] = {
+	{ "audio/*", tracker_extract_xine },
+	{ "video/*", tracker_extract_xine },
+	{ NULL, NULL }
+};
+
+
+TrackerExtractorData *
+tracker_get_extractor_data (void)
+{
+	return data;
+}

Added: trunk/src/tracker-extract/tracker-extract-mp3.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-extract/tracker-extract-mp3.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,1111 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#ifndef G_OS_WIN32
+#include <sys/mman.h>
+#endif
+
+#include "tracker-extract.h"
+
+#define MAX_FILE_READ	  1024 * 1024 * 10
+#define MAX_MP3_SCAN_DEEP 16768
+
+typedef struct {
+	gchar *text;
+	gchar *type;
+} Matches;
+
+typedef struct {
+	gchar *title;
+	gchar *artist;
+	gchar *album;
+	gchar *year;
+	gchar *comment;
+	gchar *genre;
+} id3tag;
+
+enum {
+	MPEG_ERR,
+	MPEG_V1,
+	MPEG_V2,
+	MPEG_V25
+};
+
+enum {
+	LAYER_ERR,
+	LAYER_1,
+	LAYER_2,
+	LAYER_3
+};
+
+static void extract_mp3 (const gchar *filename,
+			 GHashTable  *metadata);
+
+static const char *const genre_names[] = {
+	"Blues",
+	"Classic Rock",
+	"Country",
+	"Dance",
+	"Disco",
+	"Funk",
+	"Grunge",
+	"Hip-Hop",
+	"Jazz",
+	"Metal",
+	"New Age",
+	"Oldies",
+	"Other",
+	"Pop",
+	"R&B",
+	"Rap",
+	"Reggae",
+	"Rock",
+	"Techno",
+	"Industrial",
+	"Alternative",
+	"Ska",
+	"Death Metal",
+	"Pranks",
+	"Soundtrack",
+	"Euro-Techno",
+	"Ambient",
+	"Trip-Hop",
+	"Vocal",
+	"Jazz+Funk",
+	"Fusion",
+	"Trance",
+	"Classical",
+	"Instrumental",
+	"Acid",
+	"House",
+	"Game",
+	"Sound Clip",
+	"Gospel",
+	"Noise",
+	"Alt. Rock",
+	"Bass",
+	"Soul",
+	"Punk",
+	"Space",
+	"Meditative",
+	"Instrumental Pop",
+	"Instrumental Rock",
+	"Ethnic",
+	"Gothic",
+	"Darkwave",
+	"Techno-Industrial",
+	"Electronic",
+	"Pop-Folk",
+	"Eurodance",
+	"Dream",
+	"Southern Rock",
+	"Comedy",
+	"Cult",
+	"Gangsta Rap",
+	"Top 40",
+	"Christian Rap",
+	"Pop/Funk",
+	"Jungle",
+	"Native American",
+	"Cabaret",
+	"New Wave",
+	"Psychedelic",
+	"Rave",
+	"Showtunes",
+	"Trailer",
+	"Lo-Fi",
+	"Tribal",
+	"Acid Punk",
+	"Acid Jazz",
+	"Polka",
+	"Retro",
+	"Musical",
+	"Rock & Roll",
+	"Hard Rock",
+	"Folk",
+	"Folk/Rock",
+	"National Folk",
+	"Swing",
+	"Fast-Fusion",
+	"Bebob",
+	"Latin",
+	"Revival",
+	"Celtic",
+	"Bluegrass",
+	"Avantgarde",
+	"Gothic Rock",
+	"Progressive Rock",
+	"Psychedelic Rock",
+	"Symphonic Rock",
+	"Slow Rock",
+	"Big Band",
+	"Chorus",
+	"Easy Listening",
+	"Acoustic",
+	"Humour",
+	"Speech",
+	"Chanson",
+	"Opera",
+	"Chamber Music",
+	"Sonata",
+	"Symphony",
+	"Booty Bass",
+	"Primus",
+	"Porn Groove",
+	"Satire",
+	"Slow Jam",
+	"Club",
+	"Tango",
+	"Samba",
+	"Folklore",
+	"Ballad",
+	"Power Ballad",
+	"Rhythmic Soul",
+	"Freestyle",
+	"Duet",
+	"Punk Rock",
+	"Drum Solo",
+	"A Cappella",
+	"Euro-House",
+	"Dance Hall",
+	"Goa",
+	"Drum & Bass",
+	"Club-House",
+	"Hardcore",
+	"Terror",
+	"Indie",
+	"BritPop",
+	"Negerpunk",
+	"Polsk Punk",
+	"Beat",
+	"Christian Gangsta Rap",
+	"Heavy Metal",
+	"Black Metal",
+	"Crossover",
+	"Contemporary Christian",
+	"Christian Rock",
+	"Merengue",
+	"Salsa",
+	"Thrash Metal",
+	"Anime",
+	"JPop",
+	"Synthpop"
+};
+
+static const guint max_frames_scan = 1024;
+
+static const guint sync_mask = 0xE0FF;
+static const guint mpeg_ver_mask = 0x1800;
+static const guint mpeg_layer_mask = 0x600;
+static const guint bitrate_mask = 0xF00000;
+static const guint freq_mask = 0xC0000;
+static const guint ch_mask = 0xC0000000;
+static const guint pad_mask = 0x20000;
+
+static guint bitrate_table[16][6] = {
+	{0  , 0  , 0  , 0  , 0	, 0},
+	{32 , 32 , 32 , 32 , 32 , 8},
+	{64 , 48 , 40 , 64 , 48 , 16},
+	{96 , 56 , 48 , 96 , 56 , 24},
+	{128, 64 , 56 , 128, 64 , 32},
+	{160, 80 , 64 , 160, 80 , 64},
+	{192, 96 , 80 , 192, 96 , 80},
+	{224, 112, 96 , 224, 112, 56},
+	{256, 128, 112, 256, 128, 64},
+	{288, 160, 128, 288, 160, 128},
+	{320, 192, 160, 320, 192, 160},
+	{352, 224, 192, 352, 224, 112},
+	{384, 256, 224, 384, 256, 128},
+	{416, 320, 256, 416, 320, 256},
+	{448, 384, 320, 448, 384, 320},
+	{-1,  -1,  -1,	-1,  -1,  -1}
+};
+
+static gint freq_table[4][3] = {
+	{44100, 22050, 11025},
+	{48000, 24000, 12000},
+	{32000, 16000, 8000}
+};
+
+static TrackerExtractorData data[] = {
+	{ "audio/mpeg", extract_mp3 },
+	{ "audio/x-mp3", extract_mp3 },
+	{ NULL, NULL }
+};
+
+static gchar *
+get_utf8 (const gchar *txt,
+	  gint	       size,
+	  gpointer     p1,
+	  gpointer     p2,
+	  gpointer     p3)
+{
+	if (!g_utf8_validate (txt, size, NULL)) {
+		return g_locale_to_utf8 (txt, size, NULL, NULL, NULL);
+	} else {
+		return g_strndup (txt, size);
+	}
+}
+
+static gboolean
+get_id3 (const gchar *data,
+	 size_t       size,
+	 id3tag      *id3)
+{
+	const gchar *pos;
+
+	if (size < 128) {
+		return FALSE;
+	}
+
+	pos = &data[size - 128];
+
+	if (strncmp ("TAG", pos, 3) != 0) {
+		return FALSE;
+	}
+
+	pos += 3;
+
+	id3->title = get_utf8 (pos, 30, NULL, NULL, NULL);
+	pos += 30;
+	id3->artist = get_utf8 (pos, 30, NULL, NULL, NULL);
+	pos += 30;
+	id3->album = get_utf8 (pos, 30, NULL, NULL, NULL);
+	pos += 30;
+	id3->year = get_utf8 (pos, 4, NULL, NULL, NULL);
+
+	pos += 4;
+	id3->comment = get_utf8 (pos, 30, NULL, NULL, NULL);
+	pos += 30;
+	id3->genre = "";
+
+	if ((guint) pos[0] < G_N_ELEMENTS (genre_names)) {
+		id3->genre = g_strdup (genre_names[(unsigned) pos[0]]);
+	}
+
+	return TRUE;
+}
+
+static void
+mp3_parse (const gchar *data,
+	   size_t	size,
+	   GHashTable  *metadata)
+{
+	guint header;
+	gint counter = 0;
+	gchar mpeg_ver = 0;
+	gchar layer_ver = 0;
+	gint idx_num = 0;
+	guint bitrate = 0;
+	guint avg_bps = 0;
+	gint vbr_flag = 0;
+	guint length = 0;
+	guint sample_rate = 0;
+	gint ch = 0;
+	guint frame_size;
+	guint frames = 0;
+	size_t pos = 0;
+
+	do {
+		/* Seek for frame start */
+		if (pos + sizeof(header) > size) {
+			return;
+		}
+
+		memcpy (&header, &data[pos], sizeof (header));
+
+		if ((header&sync_mask) == sync_mask) {
+			/* Found header sync */
+			break;
+		}
+
+		pos++;
+		counter++;
+	} while (counter < MAX_MP3_SCAN_DEEP);
+
+	if (counter >= MAX_MP3_SCAN_DEEP) {
+		/* Give up to find mp3 header */
+		return;
+	};
+
+	do {
+		frames++;
+		switch (header & mpeg_ver_mask) {
+		case 0x1000:
+			mpeg_ver = MPEG_ERR;
+			break;
+		case 0x800:
+			g_hash_table_insert (metadata,
+					     g_strdup ("Audio:Codec"),
+					     g_strdup ("MPEG"));
+			g_hash_table_insert (metadata,
+					     g_strdup ("Audio:CodecVersion"),
+					     g_strdup ("2"));
+			mpeg_ver = MPEG_V2;
+			break;
+		case 0x1800:
+			g_hash_table_insert (metadata,
+					     g_strdup ("Audio:Codec"),
+					     g_strdup ("MPEG"));
+			g_hash_table_insert (metadata,
+					     g_strdup ("Audio:CodecVersion"),
+					     g_strdup ("1"));
+			mpeg_ver = MPEG_V1;
+			break;
+		case 0:
+			g_hash_table_insert (metadata,
+					     g_strdup ("Audio:Codec"),
+					     g_strdup ("MPEG"));
+			g_hash_table_insert (metadata,
+					     g_strdup ("Audio:CodecVersion"),
+					     g_strdup ("2.5"));
+			mpeg_ver = MPEG_V25;
+			break;
+		}
+
+		switch (header&mpeg_layer_mask) {
+		case 0x400:
+			layer_ver = LAYER_2;
+			break;
+		case 0x200:
+			layer_ver = LAYER_3;
+			break;
+		case 0x600:
+			layer_ver = LAYER_1;
+			break;
+		case 0:
+			layer_ver = LAYER_ERR;
+		}
+
+		if (!layer_ver || !mpeg_ver) {
+			/* Unknown mpeg type */
+			return;
+		}
+
+		if (mpeg_ver<3) {
+			idx_num = (mpeg_ver - 1) * 3 + layer_ver - 1;
+		} else {
+			idx_num = 2 + layer_ver;
+		}
+
+		bitrate = 1000 * bitrate_table[(header & bitrate_mask) >> 20][idx_num];
+
+		if (bitrate < 0) {
+			frames--;
+			break;
+		}
+
+		sample_rate = freq_table[(header & freq_mask) >> 18][mpeg_ver - 1];
+		if (sample_rate < 0) {
+			/* Error in header */
+			frames--;
+			break;
+		}
+
+		if ((header & ch_mask) == ch_mask) {
+			ch = 1;
+			g_hash_table_insert (metadata,
+					     g_strdup ("Audio:Channels"),
+					     g_strdup ("1"));
+		} else {
+			ch=2; /*stereo non stereo select*/
+			g_hash_table_insert (metadata,
+					     g_strdup ("Audio:Channels"),
+					     g_strdup ("2"));
+		}
+
+		frame_size = 144 * bitrate / (sample_rate ? sample_rate : 1) + ((header & pad_mask) >> 17);
+		avg_bps += bitrate / 1000;
+
+		pos += frame_size - 4;
+
+		if (frames > max_frames_scan) {
+			/* Optimization */
+			break;
+		}
+
+		if (avg_bps / frames != bitrate / 1000) {
+			vbr_flag = 1;
+		}
+
+		if (pos + sizeof (header) > size) {
+			/* EOF */
+			break;
+		}
+
+		memcpy(&header, &data[pos], sizeof (header));
+	} while ((header & sync_mask) == sync_mask);
+
+	if (!frames) {
+		/* No valid frames */
+		return;
+	}
+
+	avg_bps /= frames;
+
+	if (max_frames_scan) {
+		/* If not all frames scaned */
+		length = size / (avg_bps ? avg_bps : bitrate ? bitrate : 0xFFFFFFFF) / 125;
+	} else{
+		length = 1152 * frames / (sample_rate ? sample_rate : 0xFFFFFFFF);
+	}
+
+	g_hash_table_insert (metadata,
+			     g_strdup ("Audio:Duration"),
+			     g_strdup_printf ("%d", length));
+	g_hash_table_insert (metadata,
+			     g_strdup ("Audio:Samplerate"),
+			     g_strdup_printf ("%d", sample_rate));
+	g_hash_table_insert (metadata,
+			     g_strdup ("Audio:Bitrate"),
+			     g_strdup_printf ("%d", avg_bps));
+}
+
+static void
+get_id3v24_tags (const gchar *data,
+		 size_t       size,
+		 GHashTable  *metadata)
+{
+	gint	unsync;
+	gint	extendedHdr;
+	gint	experimental;
+	gint	footer;
+	guint	tsize;
+	guint	pos;
+	guint	ehdrSize;
+	guint	padding;
+	Matches tmap[] = {
+		{"COMM", "Audio:Comment"},
+		{"TCOP", "File:Copyright"},
+		{"TDRC", "Audio:ReleaseDate"},
+		{"TCON", "Audio:Genre"},
+		{"TIT1", "Audio:Genre"},
+		{"TENC", "DC:Publishers"},
+		{"TEXT", "Audio:Lyrics"},
+		{"TPE1", "Audio:Artist"},
+		{"TPE2", "Audio:Artist"},
+		{"TPE3", "Audio:Performer"},
+		{"TOPE", "Audio:Artist"},
+		{"TPUB", "DC:Publishers"},
+		{"TOAL", "Audio:Album"},
+		{"TALB", "Audio:Album"},
+		{"TLAN", "File:Language"},
+		{"TIT2", "Audio:Title"},
+		{"TIT3", "Audio:Comment"},
+		{"WCOP", "File:License"},
+		{NULL, 0},
+	};
+
+	if ((size < 16) ||
+	    (data[0] != 0x49) ||
+	    (data[1] != 0x44) ||
+	    (data[2] != 0x33) ||
+	    (data[3] != 0x04) ||
+	    (data[4] != 0x00) ) {
+		return;
+	}
+
+	unsync = (data[5] & 0x80) > 0;
+	extendedHdr = (data[5] & 0x40) > 0;
+	experimental = (data[5] & 0x20) > 0;
+	footer = (data[5] & 0x10) > 0;
+	tsize = (((data[6] & 0x7F) << 21) |
+		 ((data[7] & 0x7F) << 14) |
+		 ((data[8] & 0x7F) << 7) |
+		 ((data[9] & 0x7F) << 0));
+
+	if ((tsize + 10 > size) || (experimental)) {
+		return;
+	}
+
+	pos = 10;
+	padding = 0;
+
+	if (extendedHdr) {
+		ehdrSize = (((data[10] & 0x7F) << 21) |
+			    ((data[11] & 0x7F) << 14) |
+			    ((data[12] & 0x7F) << 7) |
+			    ((data[13] & 0x7F) << 0));
+		pos += ehdrSize;
+	}
+
+	while (pos < tsize) {
+		size_t csize;
+		gint i;
+		unsigned short flags;
+
+		if (pos + 10 > tsize) {
+			return;
+		}
+
+		csize = (((data[pos+4] & 0x7F) << 21) |
+			 ((data[pos+5] & 0x7F) << 14) |
+			 ((data[pos+6] & 0x7F) << 7) |
+			 ((data[pos+7] & 0x7F) << 0));
+
+		if ((pos + 10 + csize > tsize) ||
+		    (csize > tsize) ||
+		    (csize == 0)) {
+			break;
+		}
+
+		flags = (data[pos + 8] << 8) + data[pos + 9];
+		if (((flags & 0x80) > 0) ||
+		    ((flags & 0x40) > 0)) {
+			pos += 10 + csize;
+			continue;
+		}
+
+		i = 0;
+		while (tmap[i].text != NULL) {
+			if (strncmp (tmap[i].text, (const char*) &data[pos], 4) == 0) {
+				gchar * word;
+
+				if ((flags & 0x20) > 0) {
+					/* The "group" identifier, skip a byte */
+					pos++;
+					csize--;
+				}
+
+				/* This byte describes the encoding
+				 * try to convert strings to UTF-8
+				 * if it fails, then forget it
+				 */
+				switch (data[pos + 10]) {
+				case 0x00:
+					word = get_utf8 ((const char*) &data[pos + 11],
+							 csize,
+							 NULL, NULL, NULL);
+					break;
+				case 0x01 :
+					word = get_utf8 ((const char*) &data[pos + 11],
+							 csize,
+							 NULL, NULL, NULL);
+					break;
+				case 0x02 :
+					word = get_utf8 ((const char*) &data[pos + 11],
+							 csize,
+							 NULL, NULL, NULL);
+					break;
+				case 0x03 :
+					word = malloc (csize + 1);
+					memcpy (word,
+						&data[pos + 11],
+						csize);
+					word[csize] = '\0';
+					break;
+
+				default:
+					/* Bad encoding byte,
+					 * try to convert from
+					 * iso-8859-1
+					 */
+					word = get_utf8 ((const char*) &data[pos + 11],
+							 csize,
+							 NULL, NULL, NULL);
+					break;
+				}
+
+				pos++;
+				csize--;
+
+				if (word != NULL && strlen (word) > 0) {
+					if (strcmp (tmap[i].text, "COMM") == 0) {
+						gchar *s;
+
+						s = g_strdup (word + strlen (word) + 1);
+						g_free (word);
+						word = s;
+					}
+
+					g_hash_table_insert (metadata,
+							     g_strdup (tmap[i].type),
+							     g_strdup (word));
+				} else {
+					g_free (word);
+				}
+
+				break;
+			}
+
+			i++;
+		}
+
+		pos += 10 + csize;
+	}
+}
+
+static void
+get_id3v23_tags (const gchar *data,
+		 size_t       size,
+		 GHashTable  *metadata)
+{
+	gint	unsync;
+	gint	extendedHdr;
+	gint	experimental;
+	guint	tsize;
+	guint	pos;
+	guint	ehdrSize;
+	guint	padding;
+	Matches tmap[] = {
+		{"COMM", "Audio:Comment"},
+		{"TCOP", "File:Copyright"},
+		{"TDAT", "Audio:ReleaseDate"},
+		{"TCON", "Audio:Genre"},
+		{"TIT1", "Audio:Genre"},
+		{"TENC", "DC:Publishers"},
+		{"TEXT", "Audio:Lyrics"},
+		{"TPE1", "Audio:Artist"},
+		{"TPE2", "Audio:Artist"},
+		{"TPE3", "Audio:Performer"},
+		{"TIME", "Audio:ReleaseDate"},
+		{"TOPE", "Audio:Artist"},
+		{"TPUB", "DC:Publishers"},
+		{"TOAL", "Audio:Album"},
+		{"TALB", "Audio:Album"},
+		{"TLAN", "File:Language"},
+		{"TIT2", "Audio:Title"},
+		{"WCOP", "File:License"},
+		{NULL, 0},
+	};
+
+	if ((size < 16) ||
+	    (data[0] != 0x49) ||
+	    (data[1] != 0x44) ||
+	    (data[2] != 0x33) ||
+	    (data[3] != 0x03) ||
+	    (data[4] != 0x00)) {
+		return;
+	}
+
+	unsync = (data[5] & 0x80) > 0;
+	extendedHdr = (data[5] & 0x40) > 0;
+	experimental = (data[5] & 0x20) > 0;
+	tsize = (((data[6] & 0x7F) << 21) |
+		 ((data[7] & 0x7F) << 14) |
+		 ((data[8] & 0x7F) << 7) |
+		 ((data[9] & 0x7F) << 0));
+
+	if ((tsize + 10 > size) || (experimental)) {
+		return;
+	}
+
+	pos = 10;
+	padding = 0;
+
+	if (extendedHdr) {
+		ehdrSize = (((data[10]) << 24) |
+			    ((data[11]) << 16) |
+			    ((data[12]) << 8) |
+			    ((data[12]) << 0));
+
+		padding	= (((data[15]) << 24) |
+			   ((data[16]) << 16) |
+			   ((data[17]) << 8) |
+			   ((data[18]) << 0));
+
+		pos += 4 + ehdrSize;
+
+		if (padding < tsize)
+			tsize -= padding;
+		else {
+			return;
+		}
+	}
+
+	while (pos < tsize) {
+		size_t csize;
+		gint i;
+		unsigned short flags;
+
+		if (pos + 10 > tsize) {
+			return;
+		}
+
+		csize = (data[pos + 4] << 24) +
+			(data[pos + 5] << 16) +
+			(data[pos + 6] << 8) +
+			data[pos + 7];
+
+		if ((pos + 10 + csize > tsize) ||
+		    (csize > tsize) ||
+		    (csize == 0)) {
+			break;
+		}
+
+		flags = (data[pos + 8] << 8) + data[pos + 9];
+
+		if (((flags & 0x80) > 0) || ((flags & 0x40) > 0)) {
+			pos += 10 + csize;
+			continue;
+		}
+
+		i = 0;
+		while (tmap[i].text != NULL) {
+			if (strncmp (tmap[i].text, (const gchar*) &data[pos], 4) == 0) {
+				gchar * word;
+
+				if ((flags & 0x20) > 0) {
+					/* The "group" identifier, skip a byte */
+					pos++;
+					csize--;
+				}
+
+				csize--;
+
+				/* This byte describes the encoding
+				 * try to convert strings to UTF-8 if
+				 * it fails, then forget it./
+				 */
+
+				switch (data[pos + 10]) {
+				case 0x00:
+					word = get_utf8 ((const gchar*) &data[pos + 11],
+							 csize,
+							 NULL, NULL, NULL);
+					break;
+				case 0x01 :
+					word = get_utf8 ((const gchar*) &data[pos + 11],
+							 csize,
+							 NULL, NULL, NULL);
+					break;
+				default:
+					/* Bad encoding byte, try to
+					 * convert from iso-8859-1
+					 */
+					word = get_utf8 ((const gchar*) &data[pos + 11],
+							 csize,
+							 NULL, NULL, NULL);
+					break;
+				}
+
+				pos++;
+
+				if (word != NULL && strlen(word) > 0) {
+					if (strcmp (tmap[i].text, "COMM") == 0) {
+						gchar *s;
+
+						s = g_strdup (word + strlen (word) + 1);
+						g_free (word);
+						word = s;
+					}
+
+					g_hash_table_insert (metadata,
+							     g_strdup (tmap[i].type),
+							     g_strdup (word));
+				} else {
+					g_free (word);
+				}
+
+				break;
+			}
+
+			i++;
+		}
+
+		pos += 10 + csize;
+	}
+}
+
+static void
+get_id3v2_tags (const gchar *data,
+		size_t	     size,
+		GHashTable  *metadata)
+{
+	gint	unsync;
+	guint	tsize;
+	guint	pos;
+	Matches tmap[] = {
+		{"TAL", "Audio:Title"},
+		{"TT1", "Audio:Artist"},
+		{"TT2", "Audio:Title"},
+		{"TT3", "Audio:Title"},
+		{"TXT", "Audio:Comment"},
+		{"TPB", "DC:Publishers"},
+		{"WAF", "DC:Location"},
+		{"WAR", "DC:Location"},
+		{"WAS", "DC:Location"},
+		{"WCP", "File:Copyright"},
+		{"WAF", "DC:Location"},
+		{"WCM", "File:License"},
+		{"TYE", "Audio:ReleaseDate"},
+		{"TLA", "File:Lanuguage"},
+		{"TP1", "Audio:Artist"},
+		{"TP2", "Audio:Artist"},
+		{"TP3", "Audio:Performer"},
+		{"TEN", "Audio:Performer"},
+		{"TCO", "Audio:Title"},
+		{"TCR", "File:Copyright"},
+		{"SLT", "Audio:Lyrics"},
+		{"TOA", "Audio:Artist"},
+		{"TOT", "Audio:Album"},
+		{"TOL", "Audio:Artist"},
+		{"COM", "Audio:Comment"},
+		{ NULL, 0},
+	};
+
+	if ((size < 16) ||
+	    (data[0] != 0x49) ||
+	    (data[1] != 0x44) ||
+	    (data[2] != 0x33) ||
+	    (data[3] != 0x02) ||
+	    (data[4] != 0x00)) {
+		return;
+	}
+
+	unsync = (data[5] & 0x80) > 0;
+	tsize = (((data[6] & 0x7F) << 21) |
+		 ((data[7] & 0x7F) << 14) |
+		 ((data[8] & 0x7F) << 07) |
+		 ((data[9] & 0x7F) << 00));
+
+	if (tsize + 10 > size)	{
+		return;
+	}
+
+	pos = 10;
+
+	while (pos < tsize) {
+		size_t csize;
+		gint i;
+
+		if (pos + 6 > tsize)  {
+			return;
+		}
+
+		csize = (data[pos+3] << 16) + (data[pos + 4] << 8) + data[pos + 5];
+		if ((pos + 6 + csize > tsize) ||
+		    (csize > tsize) ||
+		    (csize == 0)) {
+			break;
+		}
+
+		i = 0;
+
+		while (tmap[i].text != NULL) {
+			if (strncmp(tmap[i].text, (const char*) &data[pos], 3) == 0) {
+				gchar * word;
+
+				/* This byte describes the encoding
+				 * try to convert strings to UTF-8 if
+				 * it fails, then forget it.
+				 */
+
+				switch (data[pos + 6]) {
+				case 0x00:
+					word = get_utf8 ((const gchar*) &data[pos + 7],
+							 csize,
+							 NULL, NULL, NULL);
+					break;
+
+				default:
+					/* Bad encoding byte, try to
+					 * convert from iso-8859-1.
+					 */
+					word = get_utf8 ((const gchar*) &data[pos + 7],
+							 csize,
+							 NULL, NULL, NULL);
+					break;
+				}
+
+				pos++;
+				csize--;
+
+				if (word != NULL && strlen(word) > 0) {
+					if (strcmp (tmap[i].text, "COM") == 0) {
+						gchar *s;
+
+						s = g_strdup (word + strlen(word) + 1);
+						g_free (word);
+						word = s;
+					}
+
+					g_hash_table_insert (metadata,
+							     g_strdup (tmap[i].type),
+							     g_strdup (word));
+				} else {
+					g_free (word);
+				}
+
+				break;
+			}
+
+			i++;
+		}
+
+		pos += 6 + csize;
+	}
+}
+
+static void
+extract_mp3 (const gchar *filename,
+	     GHashTable  *metadata)
+{
+	gint	     file;
+	void	    *buffer;
+	struct stat  fstatbuf;
+	size_t	     size;
+	id3tag	     info;
+
+	info.title = NULL;
+	info.artist = NULL;
+	info.album = NULL;
+	info.year = NULL;
+	info.comment = NULL;
+	info.genre = NULL;
+
+#if defined(__linux__)
+	file = g_open (filename, (O_RDONLY | O_NOATIME), 0);
+#else
+	file = g_open (filename, O_RDONLY, 0);
+#endif
+
+	if (file == -1 || stat (filename, &fstatbuf) == -1) {
+		close (file);
+		return;
+	}
+
+	size = fstatbuf.st_size;
+	if (size == 0) {
+		close (file);
+		return;
+	}
+
+	if (size >  MAX_FILE_READ) {
+		size =	MAX_FILE_READ;
+	}
+
+#ifndef G_OS_WIN32
+	buffer = mmap (NULL, size, PROT_READ, MAP_PRIVATE, file, 0);
+#endif
+
+	if (buffer == NULL || buffer == (void*) -1) {
+		close(file);
+		return;
+	}
+
+	if (!get_id3 (buffer, size, &info)) {
+		/* Do nothing? */
+	}
+
+	if (info.title && strlen (info.title) > 0) {
+		g_hash_table_insert (metadata,
+				     g_strdup ("Audio:Title"),
+				     g_strdup (info.title));
+	}
+
+	if (info.artist && strlen (info.artist) > 0) {
+		g_hash_table_insert (metadata,
+				     g_strdup ("Audio:Artist"),
+				     g_strdup (info.artist));
+	}
+
+	if (info.album && strlen (info.album) > 0) {
+		g_hash_table_insert (metadata,
+				     g_strdup ("Audio:Album"),
+				     g_strdup (info.album));
+	}
+
+	if (info.year && strlen (info.year) > 0) {
+		g_hash_table_insert (metadata,
+				     g_strdup ("Audio:ReleaseDate"),
+				     g_strdup (info.year));
+	}
+
+	if (info.genre && strlen (info.genre) > 0) {
+		g_hash_table_insert (metadata,
+				     g_strdup ("Audio:Genre"),
+				     g_strdup (info.genre));
+	}
+
+	if (info.comment && strlen (info.comment) > 0) {
+		g_hash_table_insert (metadata,
+				     g_strdup ("Audio:Comment"),
+				     g_strdup (info.comment));
+	}
+
+	free (info.title);
+	free (info.year);
+	free (info.album);
+	free (info.artist);
+	free (info.comment);
+
+	/* Get other embedded tags */
+	get_id3v2_tags (buffer, size, metadata);
+	get_id3v23_tags (buffer, size, metadata);
+	get_id3v24_tags (buffer, size, metadata);
+
+	/* Get mp3 stream info */
+	mp3_parse (buffer, size, metadata);
+
+	/* Check that we have the minimum data. FIXME We should not need to do this */
+	if (!g_hash_table_lookup (metadata, "Audio:Title")) {
+		g_hash_table_insert (metadata,
+				     g_strdup ("Audio:Title"),
+				     g_strdup ("tracker:unknown"));
+	}
+
+	if (!g_hash_table_lookup (metadata, "Audio:Album")) {
+		g_hash_table_insert (metadata,
+				     g_strdup ("Audio:Album"),
+				     g_strdup ("tracker:unknown"));
+	}
+
+	if (!g_hash_table_lookup (metadata, "Audio:Artist")) {
+		g_hash_table_insert (metadata,
+				     g_strdup ("Audio:Artist"),
+				     g_strdup ("tracker:unknown"));
+	}
+
+	if (!g_hash_table_lookup (metadata, "Audio:Genre")) {
+		g_hash_table_insert (metadata,
+				     g_strdup ("Audio:Genre"),
+				     g_strdup ("tracker:unknown"));
+	}
+
+#ifndef G_OS_WIN32
+	munmap (buffer, size);
+#endif
+	close(file);
+}
+
+TrackerExtractorData *
+tracker_get_extractor_data (void)
+{
+	return data;
+}

Added: trunk/src/tracker-extract/tracker-extract-mplayer.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-extract/tracker-extract-mplayer.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,253 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Edward Duffy (eduffy gmail com)
+ * Copyright (C) 2006, Laurent Aguerreche (laurent aguerreche free fr)
+ * Copyright (C) 2008, Nokia
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <glib.h>
+
+#include <libtracker-common/tracker-os-dependant.h>
+
+#include "tracker-extract.h"
+
+static void extract_mplayer (const gchar *filename,
+			     GHashTable  *metadata);
+
+static TrackerExtractorData data[] = {
+	{ "audio/*", extract_mplayer },
+	{ "video/*", extract_mplayer },
+	{ NULL, NULL }
+};
+
+static gchar *video_tags[][2] = {
+	{ "ID_VIDEO_HEIGHT",	"Video:Height"		},
+	{ "ID_VIDEO_WIDTH",	"Video:Width"		},
+	{ "ID_VIDEO_FPS",	"Video:FrameRate"	},
+	{ "ID_VIDEO_CODEC",	"Video:Codec"		},
+	{ "ID_VIDEO_BITRATE",	"Video:Bitrate"		},
+	{ NULL,			NULL			}
+};
+
+static gchar *audio_tags[][2] = {
+	{ "ID_AUDIO_BITRATE",	"Audio:Bitrate"		},
+	{ "ID_AUDIO_RATE",	"Audio:Samplerate"	},
+	{ "ID_AUDIO_CODEC",	"Audio:Codec"		},
+	{ "ID_AUDIO_NCH",	"Audio:Channels"	},
+	{ NULL,			NULL			}
+};
+
+/* Some of "info_tags" tags can belong to Audio or/and video or none, so 3 cases :
+ * 1/ tag does not belong to audio nor video, it is a general tag ;
+ * 2/ tag only belongs to audio ;
+ * 3/ tag can belong to audio and video. If current media has video we will associate
+ *    tag to Video, otherwise to Audio if it has audio.
+ */
+static gchar *info_tags[][3] = {
+	{ "Comment",		"Audio:Comment",	"Video:Comment"	},
+	{ "Title",		"Audio:Title",		"Video:Title"	},
+	{ "Genre",		"Audio:Genre",		NULL		},
+	{ "Track",		"Audio:TrackNo",	NULL		},
+	{ "Artist",		"Audio:Performer",	"Video:Author"	},
+	{ "Album",		"Audio:Album",		NULL		},
+	{ "Year",		"Audio:ReleaseDate",	NULL		},
+	{ "copyright",		"File:Copyright",	NULL		},
+	{ NULL,			NULL,			NULL		}
+};
+
+static void
+copy_hash_table_entry (gpointer key,
+		       gpointer value,
+		       gpointer user_data)
+{
+	g_hash_table_insert (user_data,
+			     g_strdup (key),
+			     g_strdup (value));
+}
+
+static void
+extract_mplayer (const gchar *filename,
+		 GHashTable  *metadata)
+{
+	gchar *argv[10];
+	gchar *mplayer;
+
+	argv[0] = g_strdup ("mplayer");
+	argv[1] = g_strdup ("-identify");
+	argv[2] = g_strdup ("-frames");
+	argv[3] = g_strdup ("0");
+	argv[4] = g_strdup ("-vo");
+	argv[5] = g_strdup ("null");
+	argv[6] = g_strdup ("-ao");
+	argv[7] = g_strdup ("null");
+	argv[8] = g_strdup (filename);
+	argv[9] = NULL;
+
+	if (tracker_spawn (argv, 10, &mplayer, NULL)) {
+		GPatternSpec  *pattern_ID_AUDIO_ID;
+		GPatternSpec  *pattern_ID_VIDEO_ID;
+		GPatternSpec  *pattern_ID_AUDIO;
+		GPatternSpec  *pattern_ID_VIDEO;
+		GPatternSpec  *pattern_ID_CLIP_INFO_NAME;
+		GPatternSpec  *pattern_ID_CLIP_INFO_VALUE;
+		GPatternSpec  *pattern_ID_LENGTH;
+		GHashTable    *tmp_metadata_audio;
+		GHashTable    *tmp_metadata_video;
+		gboolean       has_audio, has_video;
+		gchar	      *duration;
+		gchar	     **lines, **line;
+
+		pattern_ID_AUDIO_ID = g_pattern_spec_new ("ID_AUDIO_ID=*");
+		pattern_ID_VIDEO_ID = g_pattern_spec_new ("ID_VIDEO_ID=*");
+		pattern_ID_AUDIO = g_pattern_spec_new ("ID_AUDIO*=*");
+		pattern_ID_VIDEO = g_pattern_spec_new ("ID_VIDEO*=*");
+		pattern_ID_CLIP_INFO_NAME = g_pattern_spec_new ("ID_CLIP_INFO_NAME*=*");
+		pattern_ID_CLIP_INFO_VALUE = g_pattern_spec_new ("ID_CLIP_INFO_VALUE*=*");
+		pattern_ID_LENGTH = g_pattern_spec_new ("ID_LENGTH=*");
+
+		tmp_metadata_audio = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+		tmp_metadata_video = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+		has_audio = has_video = FALSE;
+
+		duration = NULL;
+
+		lines = g_strsplit (mplayer, "\n", -1);
+
+		for (line = lines; *line; ++line) {
+			if (g_pattern_match_string (pattern_ID_AUDIO_ID, *line)) {
+				has_audio = TRUE;
+			}
+
+			else if (g_pattern_match_string (pattern_ID_VIDEO_ID, *line)) {
+				has_video = TRUE;
+			}
+
+			else if (g_pattern_match_string (pattern_ID_AUDIO, *line)) {
+				gint i;
+
+				for (i = 0; audio_tags[i][0]; i++) {
+					if (g_str_has_prefix (*line, audio_tags[i][0])) {
+						g_hash_table_insert (metadata,
+								     g_strdup (audio_tags[i][1]),
+								     g_strdup ((*line) + strlen (audio_tags[i][0]) + 1));
+						break;
+					}
+				}
+			}
+
+			else if (g_pattern_match_string (pattern_ID_VIDEO, *line)) {
+				gint i;
+
+				for (i = 0; video_tags[i][0]; i++) {
+					if (g_str_has_prefix (*line, video_tags[i][0])) {
+						g_hash_table_insert (metadata,
+								     g_strdup (video_tags[i][1]),
+								     g_strdup ((*line) + strlen (video_tags[i][0]) + 1));
+						break;
+					}
+				}
+			}
+
+			else if (g_pattern_match_string (pattern_ID_CLIP_INFO_NAME, *line)) {
+				const char *next_line;
+
+				next_line = *(line + 1);
+
+				if (next_line) {
+					if (g_pattern_match_string (pattern_ID_CLIP_INFO_VALUE, next_line)) {
+						gint i;
+
+						for (i = 0; info_tags[i][0]; i++) {
+							if (g_str_has_suffix (*line, info_tags[i][0])) {
+								gchar *equal_char_pos, *data;
+
+								equal_char_pos = strchr (next_line, '=');
+
+								data = g_strdup (equal_char_pos + 1);
+
+								if (data) {
+									if (data[0] != '\0') {
+										g_hash_table_insert (tmp_metadata_audio,
+												     g_strdup (info_tags[i][1]),
+												     data);
+
+										if (info_tags[i][2]) {
+											g_hash_table_insert (tmp_metadata_video,
+													     g_strdup (info_tags[i][2]),
+													     g_strdup (data));
+										}
+									} else {
+										g_free (data);
+									}
+								}
+
+								break;
+							}
+						}
+
+						line++;
+					}
+				}
+			}
+
+			else if (g_pattern_match_string (pattern_ID_LENGTH, *line)) {
+				gchar *equal_char_pos;
+
+				equal_char_pos = strchr (*line, '=');
+
+				duration = g_strdup (equal_char_pos + 1);
+			}
+		}
+
+		g_pattern_spec_free (pattern_ID_AUDIO_ID);
+		g_pattern_spec_free (pattern_ID_VIDEO_ID);
+		g_pattern_spec_free (pattern_ID_AUDIO);
+		g_pattern_spec_free (pattern_ID_VIDEO);
+		g_pattern_spec_free (pattern_ID_CLIP_INFO_NAME);
+		g_pattern_spec_free (pattern_ID_CLIP_INFO_VALUE);
+		g_pattern_spec_free (pattern_ID_LENGTH);
+
+		if (has_video) {
+			g_hash_table_foreach (tmp_metadata_video, copy_hash_table_entry, metadata);
+
+			if (duration) {
+				g_hash_table_insert (metadata, g_strdup ("Video:Duration"), duration);
+			}
+		} else if (has_audio) {
+			g_hash_table_foreach (tmp_metadata_audio, copy_hash_table_entry, metadata);
+
+			if (duration) {
+				g_hash_table_insert (metadata, g_strdup ("Audio:Duration"), duration);
+			}
+		}
+
+		g_hash_table_destroy (tmp_metadata_audio);
+		g_hash_table_destroy (tmp_metadata_video);
+	}
+}
+
+TrackerExtractorData *
+tracker_get_extractor_data (void)
+{
+	return data;
+}

Added: trunk/src/tracker-extract/tracker-extract-msoffice.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-extract/tracker-extract-msoffice.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,227 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Edward Duffy (eduffy gmail com)
+ * Copyright (C) 2006, Laurent Aguerreche (laurent aguerreche free fr)
+ * Copyright (C) 2008, Nokia
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <glib.h>
+
+#include <gsf/gsf.h>
+#include <gsf/gsf-doc-meta-data.h>
+#include <gsf/gsf-infile.h>
+#include <gsf/gsf-infile-msole.h>
+#include <gsf/gsf-input-stdio.h>
+#include <gsf/gsf-msole-utils.h>
+#include <gsf/gsf-utils.h>
+
+#include <libtracker-common/tracker-utils.h>
+
+#include "tracker-extract.h"
+
+static void extract_msoffice (const gchar *filename,
+			      GHashTable  *metadata);
+
+static TrackerExtractorData data[] = {
+	{ "application/msword",	  extract_msoffice },
+	{ "application/vnd.ms-*", extract_msoffice },
+	{ NULL, NULL }
+};
+
+static void
+add_gvalue_in_hash_table (GHashTable   *table,
+			  const gchar  *key,
+			  GValue const *val)
+{
+	g_return_if_fail (table != NULL);
+	g_return_if_fail (key != NULL);
+
+	if (val) {
+		gchar *s = g_strdup_value_contents (val);
+
+		if (s) {
+			if (!tracker_is_empty_string (s)) {
+				gchar *str_val;
+
+				/* Some fun: strings are always
+				 * written "str" with double quotes
+				 * around, but not numbers!
+				 */
+				if (s[0] == '"') {
+					size_t len;
+
+					len = strlen (s);
+
+					if (s[len - 1] == '"') {
+						str_val = (len > 2 ? g_strndup (s + 1, len - 2) : NULL);
+					} else {
+						/* We have a string
+						 * that begins with a
+						 * double quote but
+						 * which finishes by
+						 * something different...
+						 * We copy the string
+						 * from the
+						 * beginning.
+						 */
+						str_val = g_strdup (s);
+					}
+				} else {
+					/* Here, we probably have a number */
+					str_val = g_strdup (s);
+				}
+
+				if (str_val) {
+					g_hash_table_insert (table, g_strdup (key), str_val);
+				}
+			}
+
+			g_free (s);
+		}
+	}
+}
+
+static void
+metadata_cb (gpointer key,
+	     gpointer value,
+	     gpointer user_data)
+{
+	gchar	     *name;
+	GsfDocProp   *property;
+	GHashTable   *metadata;
+	GValue const *val;
+
+	name = key;
+	property = value;
+	metadata = user_data;
+	val = gsf_doc_prop_get_val (property);
+
+	if (strcmp (name, "dc:title") == 0) {
+		add_gvalue_in_hash_table (metadata, "Doc:Title", val);
+	} else if (strcmp (name, "dc:subject") == 0) {
+		add_gvalue_in_hash_table (metadata, "Doc:Subject", val);
+	} else if (strcmp (name, "dc:creator") == 0) {
+		add_gvalue_in_hash_table (metadata, "Doc:Author", val);
+	} else if (strcmp (name, "dc:keywords") == 0) {
+		add_gvalue_in_hash_table (metadata, "Doc:Keywords", val);
+	} else if (strcmp (name, "dc:description") == 0) {
+		add_gvalue_in_hash_table (metadata, "Doc:Comments", val);
+	} else if (strcmp (name, "gsf:page-count") == 0) {
+		add_gvalue_in_hash_table (metadata, "Doc:PageCount", val);
+	} else if (strcmp (name, "gsf:word-count") == 0) {
+		add_gvalue_in_hash_table (metadata, "Doc:WordCount", val);
+	} else if (strcmp (name, "meta:creation-date") == 0) {
+		add_gvalue_in_hash_table (metadata, "Doc:Created", val);
+	} else if (strcmp (name, "meta:generator") == 0) {
+		add_gvalue_in_hash_table (metadata, "File:Other", val);
+	}
+}
+
+static void
+doc_metadata_cb (gpointer key,
+		 gpointer value,
+		 gpointer user_data)
+{
+	gchar	     *name;
+	GsfDocProp   *property;
+	GHashTable   *metadata;
+	GValue const *val;
+
+	name = key;
+	property = value;
+	metadata = user_data;
+	val = gsf_doc_prop_get_val (property);
+
+	if (strcmp (name, "CreativeCommons_LicenseURL") == 0) {
+		add_gvalue_in_hash_table (metadata, "File:License", val);
+	}
+}
+
+static void
+extract_msoffice (const gchar *filename,
+		  GHashTable  *metadata)
+{
+	GsfInput  *input;
+	GsfInfile *infile;
+	GsfInput  *stream;
+
+	gsf_init ();
+
+	input = gsf_input_stdio_new (filename, NULL);
+
+	if (!input) {
+		gsf_shutdown ();
+		return;
+	}
+
+	infile = gsf_infile_msole_new (input, NULL);
+	g_object_unref (G_OBJECT (input));
+
+	if (!infile) {
+		gsf_shutdown ();
+		return;
+	}
+
+	stream = gsf_infile_child_by_name (infile, "\05SummaryInformation");
+	if (stream) {
+		GsfDocMetaData *md;
+
+		md = gsf_doc_meta_data_new ();
+
+		if (gsf_msole_metadata_read (stream, md)) {
+			gsf_shutdown ();
+			return;
+		}
+
+		gsf_doc_meta_data_foreach (md, metadata_cb, metadata);
+
+		g_object_unref (G_OBJECT (md));
+		g_object_unref (G_OBJECT (stream));
+	}
+
+	stream = gsf_infile_child_by_name (infile, "\05DocumentSummaryInformation");
+	if (stream) {
+		GsfDocMetaData *md;
+
+		md = gsf_doc_meta_data_new ();
+
+		if (gsf_msole_metadata_read (stream, md)) {
+			gsf_shutdown ();
+			return;
+		}
+
+		gsf_doc_meta_data_foreach (md, doc_metadata_cb, metadata);
+
+		g_object_unref (G_OBJECT (md));
+		g_object_unref (G_OBJECT (stream));
+	}
+
+	g_object_unref (G_OBJECT (infile));
+
+	gsf_shutdown ();
+}
+
+TrackerExtractorData *
+tracker_get_extractor_data (void)
+{
+	return data;
+}

Added: trunk/src/tracker-extract/tracker-extract-oasis.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-extract/tracker-extract-oasis.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,244 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include <libtracker-common/tracker-os-dependant.h>
+
+#include "tracker-extract.h"
+
+typedef enum {
+	READ_TITLE,
+	READ_SUBJECT,
+	READ_AUTHOR,
+	READ_KEYWORDS,
+	READ_COMMENTS,
+	READ_STATS,
+	READ_CREATED,
+	READ_FILE_OTHER
+} tag_type;
+
+typedef struct {
+	GHashTable *metadata;
+	tag_type current;
+} ODTParseInfo;
+
+static void start_element_handler (GMarkupParseContext	*context,
+				   const gchar		*element_name,
+				   const gchar	       **attribute_names,
+				   const gchar	       **attribute_values,
+				   gpointer		 user_data,
+				   GError	       **error);
+static void end_element_handler   (GMarkupParseContext	*context,
+				   const gchar		*element_name,
+				   gpointer		 user_data,
+				   GError	       **error);
+static void text_handler	  (GMarkupParseContext	*context,
+				   const gchar		*text,
+				   gsize		 text_len,
+				   gpointer		 user_data,
+				   GError	       **error);
+static void extract_oasis	  (const gchar		*filename,
+				   GHashTable		*metadata);
+
+static TrackerExtractorData data[] = {
+	{ "application/vnd.oasis.opendocument.*", extract_oasis },
+	{ NULL, NULL }
+};
+
+static void
+extract_oasis (const gchar *filename,
+	       GHashTable  *metadata)
+{
+	gchar	      *argv[5];
+	gchar	      *xml;
+	ODTParseInfo   info = {
+		metadata,
+		-1
+	};
+
+	argv[0] = g_strdup ("unzip");
+	argv[1] = g_strdup ("-p");
+	argv[2] = g_strdup (filename);
+	argv[3] = g_strdup ("meta.xml");
+	argv[4] = NULL;
+
+	if (tracker_spawn (argv, 10, &xml, NULL)) {
+		GMarkupParseContext *context;
+		GMarkupParser	     parser = {
+			start_element_handler,
+			end_element_handler,
+			text_handler,
+			NULL,
+			NULL
+		};
+
+		context = g_markup_parse_context_new (&parser, 0, &info, NULL);
+		g_markup_parse_context_parse (context, xml, -1, NULL);
+
+		g_markup_parse_context_free (context);
+		g_free (xml);
+	}
+
+	g_free (argv[3]);
+	g_free (argv[2]);
+	g_free (argv[1]);
+	g_free (argv[0]);
+}
+
+void
+start_element_handler (GMarkupParseContext  *context,
+		       const gchar	    *element_name,
+		       const gchar	   **attribute_names,
+		       const gchar	   **attribute_values,
+		       gpointer		     user_data,
+		       GError		   **error)
+{
+	ODTParseInfo *data = user_data;
+
+	if (strcmp (element_name, "dc:title") == 0) {
+		data->current = READ_TITLE;
+	}
+	else if (strcmp (element_name, "dc:subject") == 0) {
+		data->current = READ_SUBJECT;
+	}
+	else if (strcmp (element_name, "dc:creator") == 0) {
+		data->current = READ_AUTHOR;
+	}
+	else if (strcmp (element_name, "meta:keyword") == 0) {
+		data->current = READ_KEYWORDS;
+	}
+	else if (strcmp (element_name, "dc:description") == 0) {
+		data->current = READ_COMMENTS;
+	}
+	else if (strcmp (element_name, "meta:document-statistic") == 0) {
+		GHashTable *metadata;
+		const gchar **a, **v;
+
+		metadata = data->metadata;
+
+		for (a = attribute_names, v = attribute_values; *a; ++a, ++v) {
+			if (strcmp (*a, "meta:word-count") == 0) {
+				g_hash_table_insert (metadata,
+						     g_strdup ("Doc:WordCount"),
+						     g_strdup (*v));
+			}
+			else if (strcmp (*a, "meta:page-count") == 0) {
+				g_hash_table_insert (metadata,
+						     g_strdup ("Doc:PageCount"),
+						     g_strdup (*v));
+			}
+		}
+
+		data->current = READ_STATS;
+	}
+	else if (strcmp (element_name, "meta:creation-date") == 0) {
+		data->current = READ_CREATED;
+	}
+	else if (strcmp (element_name, "meta:generator") == 0) {
+		data->current = READ_FILE_OTHER;
+	}
+	else {
+		data->current = -1;
+	}
+}
+
+void
+end_element_handler (GMarkupParseContext  *context,
+		     const gchar	  *element_name,
+		     gpointer		   user_data,
+		     GError		 **error)
+{
+	((ODTParseInfo*) user_data)->current = -1;
+}
+
+void
+text_handler (GMarkupParseContext  *context,
+	      const gchar	   *text,
+	      gsize		    text_len,
+	      gpointer		    user_data,
+	      GError		  **error)
+{
+	ODTParseInfo *data;
+	GHashTable   *metadata;
+
+	data = user_data;
+	metadata = data->metadata;
+
+	switch (data->current) {
+	case READ_TITLE:
+		g_hash_table_insert (metadata,
+				     g_strdup ("Doc:Title"),
+				     g_strdup (text));
+		break;
+	case READ_SUBJECT:
+		g_hash_table_insert (metadata,
+				     g_strdup ("Doc:Subject"),
+				     g_strdup (text));
+		break;
+	case READ_AUTHOR:
+		g_hash_table_insert (metadata,
+				     g_strdup ("Doc:Author"),
+				     g_strdup (text));
+		break;
+	case READ_KEYWORDS: {
+		gchar *keywords;
+
+		if ((keywords = g_hash_table_lookup (metadata, "Doc:Keywords"))) {
+			g_hash_table_replace (metadata,
+					      g_strdup ("Doc:Keywords"),
+					      g_strconcat (keywords, ",", text, NULL));
+		} else {
+			g_hash_table_insert (metadata,
+					     g_strdup ("Doc:Keywords"),
+					     g_strdup (text));
+		}
+	}
+		break;
+	case READ_COMMENTS:
+		g_hash_table_insert (metadata,
+				     g_strdup ("Doc:Comments"),
+				     g_strdup (text));
+		break;
+	case READ_CREATED:
+		g_hash_table_insert (metadata,
+				     g_strdup ("Doc:Created"),
+				     g_strdup (text));
+		break;
+	case READ_FILE_OTHER:
+		g_hash_table_insert (metadata,
+				     g_strdup ("File:Other"),
+				     g_strdup (text));
+		break;
+
+	default:
+		break;
+	}
+}
+
+TrackerExtractorData *
+tracker_get_extractor_data (void)
+{
+	return data;
+}

Added: trunk/src/tracker-extract/tracker-extract-pdf.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-extract/tracker-extract-pdf.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,121 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <poppler.h>
+
+#include <glib.h>
+
+#include "tracker-extract.h"
+#include "tracker-xmp.h"
+
+#include <libtracker-common/tracker-utils.h>
+
+static void extract_pdf (const gchar *filename,
+			 GHashTable  *metadata);
+
+static TrackerExtractorData data[] = {
+	{ "application/pdf", extract_pdf },
+	{ NULL, NULL }
+};
+
+static void
+extract_pdf (const gchar *filename,
+	     GHashTable  *metadata)
+{
+	PopplerDocument *document;
+	gchar		*tmp;
+	gchar		*title		= NULL;
+	gchar		*author		= NULL;
+	gchar		*subject	= NULL;
+	gchar		*keywords	= NULL;
+	gchar		*metadata_xml	= NULL;
+	GTime		 creation_date;
+	GError		*error		= NULL;
+
+	g_type_init ();
+
+	tmp = g_strconcat ("file://", filename, NULL);
+	document = poppler_document_new_from_file (tmp, NULL, &error);
+	g_free (tmp);
+
+	if (document == NULL || error) {
+		return;
+	}
+
+	g_object_get (document,
+		      "title", &title,
+		      "author", &author,
+		      "subject", &subject,
+		      "keywords", &keywords,
+		      "creation-date", &creation_date,
+		      NULL);
+
+	/* metadata property not present in older poppler versions */
+	if (g_object_class_find_property (G_OBJECT_GET_CLASS (document), "metadata")) {
+		g_object_get (document, "metadata", &metadata_xml, NULL);
+	}
+
+	if (!tracker_is_empty_string (title)) {
+		g_hash_table_insert (metadata,
+				     g_strdup ("Doc:Title"),
+				     g_strdup (title));
+	}
+	if (!tracker_is_empty_string (author)) {
+		g_hash_table_insert (metadata,
+				     g_strdup ("Doc:Author"),
+				     g_strdup (author));
+	}
+	if (!tracker_is_empty_string (subject)) {
+		g_hash_table_insert (metadata,
+				     g_strdup ("Doc:Subject"),
+				     g_strdup (subject));
+	}
+	if (!tracker_is_empty_string (keywords)) {
+		g_hash_table_insert (metadata,
+				     g_strdup ("Doc:Keywords"),
+				     g_strdup (keywords));
+	}
+
+	g_hash_table_insert (metadata,
+			     g_strdup ("Doc:PageCount"),
+			     g_strdup_printf ("%d", poppler_document_get_n_pages (document)));
+
+	if ( metadata_xml ) {
+		tracker_read_xmp (metadata_xml, strlen (metadata_xml), metadata);
+	}
+
+	g_free (title);
+	g_free (author);
+	g_free (subject);
+	g_free (keywords);
+	g_free (metadata_xml);
+
+	g_object_unref (document);
+}
+
+TrackerExtractorData *
+tracker_get_extractor_data (void)
+{
+	return data;
+}

Added: trunk/src/tracker-extract/tracker-extract-png.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-extract/tracker-extract-png.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,201 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <png.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include "tracker-extract.h"
+#include "tracker-xmp.h"
+
+#define RFC1123_DATE_FORMAT "%d %B %Y %H:%M:%S %z"
+
+typedef gchar * (*PostProcessor) (gchar *);
+
+typedef struct {
+	gchar	      *name;
+	gchar	      *type;
+	PostProcessor  post;
+} TagProcessors;
+
+static gchar *rfc1123_to_iso8601_date (gchar	   *rfc_date);
+static void   extract_png	      (const gchar *filename,
+				       GHashTable  *metadata);
+
+static TagProcessors tag_processors[] = {
+	{ "Author",		"Image:Creator",      NULL},
+	{ "Creator",		"Image:Creator",      NULL},
+	{ "Description",	"Image:Description",  NULL},
+	{ "Comment",		"Image:Comments",     NULL},
+	{ "Copyright",		"File:Copyright",     NULL},
+	{ "Creation Time",	"Image:Date",	      rfc1123_to_iso8601_date},
+	{ "Title",		"Image:Title",	      NULL},
+	{ "Software",		"Image:Software",     NULL},
+	{ "Disclaimer",		"File:License",       NULL},
+	{ NULL,			NULL,		      NULL},
+};
+
+static TrackerExtractorData data[] = {
+	{ "image/png", extract_png },
+	{ NULL, NULL }
+};
+
+static gchar *
+rfc1123_to_iso8601_date (gchar *rfc_date)
+{
+	/* From: ex. RFC1123 date: "22 May 1997 18:07:10 -0600"
+	 * To  : ex. ISO8601 date: "2007-05-22T18:07:10-0600"
+	 */
+	return tracker_generic_date_to_iso8601 (rfc_date, RFC1123_DATE_FORMAT);
+}
+
+static void
+extract_png (const gchar *filename,
+	     GHashTable  *metadata)
+{
+	gint	     fd_png;
+	FILE	    *png;
+	png_structp  png_ptr;
+	png_infop    info_ptr;
+	png_uint_32  width, height;
+	gint	     num_text;
+	png_textp    text_ptr;
+	gint	     bit_depth, color_type;
+	gint	     interlace_type, compression_type, filter_type;
+
+#if defined(__linux__)
+	if ((fd_png = g_open (filename, (O_RDONLY | O_NOATIME))) == -1) {
+#else
+	if ((fd_png = g_open (filename, O_RDONLY)) == -1) {
+#endif
+		return;
+	}
+
+	if ((png = fdopen (fd_png, "r"))) {
+		png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING,
+						  NULL,
+						  NULL,
+						  NULL);
+		if (!png_ptr) {
+			fclose (png);
+			return;
+		}
+
+		info_ptr = png_create_info_struct (png_ptr);
+		if (!info_ptr) {
+			png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
+			fclose (png);
+			return;
+		}
+
+		png_init_io (png_ptr, png);
+		png_read_info (png_ptr, info_ptr);
+
+		if (png_get_text (png_ptr, info_ptr, &text_ptr, &num_text) > 0) {
+			gint i;
+			gint j;
+
+			for (i = 0; i < num_text; i++) {
+				if (!text_ptr[i].key) {
+					continue;
+				}
+
+#if defined(HAVE_EXEMPI) && defined(PNG_iTXt_SUPPORTED)
+				if (strcmp ("XML:com.adobe.xmp", text_ptr[i].key) == 0) {
+					tracker_read_xmp (text_ptr[i].text,
+							  text_ptr[i].itxt_length,
+							  metadata);
+					continue;
+				}
+#endif
+
+				for (j = 0; tag_processors[j].type; j++) {
+					if (strcasecmp (tag_processors[j].name, text_ptr[i].key) != 0) {
+						continue;
+					}
+
+					if (text_ptr[i].text && text_ptr[i].text[0] != '\0') {
+						if (tag_processors[j].post) {
+							gchar *str;
+
+							str = (*tag_processors[j].post) (text_ptr[i].text);
+							if (str) {
+								g_hash_table_insert (metadata,
+										     g_strdup (tag_processors[j].type),
+										     str);
+							}
+						} else {
+							g_hash_table_insert (metadata,
+									     g_strdup (tag_processors[j].type),
+									     g_strdup (text_ptr[i].text));
+						}
+
+						break;
+					}
+				}
+			}
+		}
+
+		/* Read size from header. We want native have higher
+		 * priority than EXIF etc.
+		 */
+		if (png_get_IHDR (png_ptr,
+				  info_ptr,
+				  &width,
+				  &height,
+				  &bit_depth,
+				  &color_type,
+				  &interlace_type,
+				  &compression_type,
+				  &filter_type)) {
+			g_hash_table_insert (metadata,
+					     g_strdup ("Image:Width"),
+					     g_strdup_printf ("%ld", width));
+			g_hash_table_insert (metadata,
+					     g_strdup ("Image:Height"),
+					     g_strdup_printf ("%ld", height));
+		}
+
+		png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
+		fclose (png);
+	} else {
+		close (fd_png);
+	}
+}
+
+TrackerExtractorData *
+tracker_get_extractor_data (void)
+{
+	return data;
+}

Added: trunk/src/tracker-extract/tracker-extract-ps.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-extract/tracker-extract-ps.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,353 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include "config.h"
+
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include <libtracker-common/tracker-os-dependant.h>
+
+#include "tracker-extract.h"
+
+static void extract_ps_gz (const gchar *filename,
+			   GHashTable  *metadata);
+static void extract_ps	  (const gchar *filename,
+			   GHashTable  *metadata);
+
+static TrackerExtractorData data[] = {
+	{ "application/x-gzpostscript",	extract_ps_gz },
+	{ "application/postscript",	extract_ps    },
+	{ NULL, NULL }
+};
+
+#ifndef HAVE_GETLINE
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <errno.h>
+
+#undef getdelim
+#undef getline
+
+#define GROWBY 80
+
+static ssize_t
+igetdelim (gchar  **linebuf,
+	   size_t  *linebufsz,
+	   gint     delimiter,
+	   FILE    *file)
+{
+	gint ch;
+	gint idx;
+
+	if ((file == NULL || linebuf == NULL || *linebuf == NULL || *linebufsz == 0) &&
+	    !(*linebuf == NULL && *linebufsz == 0)) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	if (*linebuf == NULL && *linebufsz == 0) {
+		*linebuf = g_malloc (GROWBY);
+
+		if (!*linebuf) {
+			errno = ENOMEM;
+			return -1;
+		}
+
+		*linebufsz += GROWBY;
+	}
+
+	idx = 0;
+
+	while ((ch = fgetc (file)) != EOF) {
+		/* Grow the line buffer as necessary */
+		while (idx > *linebufsz - 2) {
+			*linebuf = g_realloc (*linebuf, *linebufsz += GROWBY);
+
+			if (!*linebuf) {
+				errno = ENOMEM;
+				return -1;
+			}
+		}
+		(*linebuf)[idx++] = (gchar) ch;
+
+		if ((gchar) ch == delimiter) {
+			break;
+		}
+	}
+
+	if (idx != 0) {
+		(*linebuf)[idx] = 0;
+	} else if ( ch == EOF ) {
+		return -1;
+	}
+
+	return idx;
+}
+
+static gint
+getline (gchar **s,
+	 guint	*lim,
+	 FILE	*stream)
+{
+	return igetdelim (s, lim, '\n', stream);
+}
+
+#endif /* HAVE_GETLINE */
+
+static gchar *
+hour_day_str_day (const gchar *date)
+{
+	/* From: ex. date: "(18:07 Tuesday 22 May 2007)"
+	 * To  : ex. ISO8601 date: "2007-05-22T18:07:10-0600"
+	 */
+	return tracker_generic_date_to_iso8601 (date, "(%H:%M %A %d %B %Y)");
+}
+
+static gchar *
+day_str_month_day (const gchar *date)
+{
+	/* From: ex. date: "Tue May 22 18:07:10 2007"
+	 * To  : ex. ISO8601 date: "2007-05-22T18:07:10-0600"
+	 */
+	return tracker_generic_date_to_iso8601 (date, "%A %B %d %H:%M:%S %Y");
+}
+
+static gchar *
+day_month_year_date (const gchar *date)
+{
+	/* From: ex. date: "22 May 1997 18:07:10 -0600"
+	 * To  : ex. ISO8601 date: "2007-05-22T18:07:10-0600"
+	 */
+	return tracker_generic_date_to_iso8601 (date, "%d %B %Y %H:%M:%S %z");
+}
+
+static gchar *
+hour_month_day_date (const gchar *date)
+{
+	/* From: ex. date: "6:07 PM May 22, 2007"
+	 * To  : ex. ISO8601 date: "2007-05-22T18:07:10-0600"
+	 */
+	return tracker_generic_date_to_iso8601 (date, "%I:%M %p %B %d, %Y");
+}
+
+static gchar *
+date_to_iso8601 (const gchar *date)
+{
+	if (date && date[1] && date[2]) {
+		if (date[0] == '(') {
+			/* we have probably a date like
+			   "(18:07 Tuesday 22 May 2007)" */
+			return hour_day_str_day (date);
+		} else if (g_ascii_isalpha (date[0])) {
+			/* we have probably a date like
+			   "Tue May 22 18:07:10 2007" */
+			return day_str_month_day (date);
+
+		} else if (date[1] == ' ' || date[2] == ' ') {
+			/* we have probably a date like
+			   "22 May 1997 18:07:10 -0600" */
+			return day_month_year_date (date);
+
+		} else if (date[1] == ':' || date[2] == ':') {
+			/* we have probably a date like
+			   "6:07 PM May 22, 2007" */
+			return hour_month_day_date (date);
+		}
+	}
+
+	return NULL;
+}
+
+static void
+extract_ps (const gchar *filename,
+	    GHashTable	*metadata)
+{
+	gint fd;
+	FILE *f;
+
+#if defined(__linux__)
+	if ((fd = g_open (filename, (O_RDONLY | O_NOATIME))) == -1) {
+#else
+	if ((fd = g_open (filename, O_RDONLY)) == -1) {
+#endif
+		return;
+	}
+
+	if ((f = fdopen (fd, "r"))) {
+		gchar  *line;
+		gsize	length;
+		gssize	read_char;
+
+		line = NULL;
+		length = 0;
+
+		while ((read_char = getline (&line, &length, f)) != -1) {
+			gboolean pageno_atend	 = FALSE;
+			gboolean header_finished = FALSE;
+
+			line[read_char - 1] = '\0';  /* overwrite '\n' char */
+
+			if (!header_finished && strncmp (line, "%%Copyright:", 12) == 0) {
+				g_hash_table_insert (metadata,
+						     g_strdup ("File:Other"),
+						     g_strdup (line + 13));
+
+			} else if (!header_finished && strncmp (line, "%%Title:", 8) == 0) {
+				g_hash_table_insert (metadata,
+						     g_strdup ("Doc:Title"),
+						     g_strdup (line + 9));
+
+			} else if (!header_finished && strncmp (line, "%%Creator:", 10) == 0) {
+				g_hash_table_insert (metadata,
+						     g_strdup ("Doc:Author"),
+						     g_strdup (line + 11));
+
+			} else if (!header_finished && strncmp (line, "%%CreationDate:", 15) == 0) {
+				gchar *date;
+
+				date = date_to_iso8601 (line + 16);
+
+				if (date) {
+					g_hash_table_insert (metadata,
+							     g_strdup ("Doc:Created"),
+							     date);
+				}
+
+			} else if (strncmp (line, "%%Pages:", 8) == 0) {
+				if (strcmp (line + 9, "(atend)") == 0) {
+					pageno_atend = TRUE;
+				} else {
+					g_hash_table_insert (metadata,
+							     g_strdup ("Doc:PageCount"),
+							     g_strdup (line + 9));
+				}
+			} else if (strncmp (line, "%%EndComments", 14) == 0) {
+				header_finished = TRUE;
+
+				if (!pageno_atend) {
+					break;
+				}
+			}
+
+			g_free (line);
+			line = NULL;
+			length = 0;
+		}
+
+		if (line) {
+			g_free (line);
+		}
+
+		fclose (f);
+	} else {
+		close (fd);
+	}
+}
+
+static void
+extract_ps_gz (const gchar *filename,
+	       GHashTable  *metadata)
+{
+	FILE	    *fz;
+	GError	    *error = NULL;
+	gchar	    *gunzipped;
+	gint	     fdz;
+	gint	     fd;
+	gboolean     stat;
+	const gchar *argv[4];
+
+	fd = g_file_open_tmp ("tracker-extract-ps-gunzipped.XXXXXX",
+			      &gunzipped,
+			      &error);
+
+	if (error) {
+		g_error_free (error);
+		return;
+	}
+
+	argv[0] = "gunzip";
+	argv[1] = "-c";
+	argv[2] = filename;
+	argv[3] = NULL;
+
+	stat = g_spawn_async_with_pipes (g_get_tmp_dir (),
+					 (gchar **) argv,
+					 NULL,
+					 G_SPAWN_SEARCH_PATH | G_SPAWN_STDERR_TO_DEV_NULL,
+					 tracker_spawn_child_func,
+					 GINT_TO_POINTER (10),
+					 NULL,
+					 NULL,
+					 &fdz,
+					 NULL,
+					 &error);
+
+	if (!stat) {
+		g_unlink (gunzipped);
+		g_clear_error (&error);
+		return;
+	}
+
+	if ((fz = fdopen (fdz, "r"))) {
+		FILE *f;
+
+		if ((f = fdopen (fd, "w"))) {
+			unsigned char buf[8192];
+			size_t b, accum;
+			size_t max;
+
+			/* 20 MiB should be enough! */
+			accum = 0;
+			max = 20u << 20;
+
+			while ((b = fread (buf, 1, 8192, fz)) && accum <= max) {
+				accum += b;
+				fwrite (buf, 1, b, f);
+			}
+
+			fclose (f);
+		}
+
+		fclose (fz);
+	}
+
+	extract_ps (gunzipped, metadata);
+	g_unlink (gunzipped);
+}
+
+TrackerExtractorData *
+tracker_get_extractor_data (void)
+{
+	return data;
+}

Added: trunk/src/tracker-extract/tracker-extract-tiff.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-extract/tracker-extract-tiff.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,244 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* Tracker Extract - extracts embedded metadata from files
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "tracker-extract.h"
+
+#include <glib.h>
+
+#include <tiff.h>
+#include <tiffio.h>
+
+#include "tracker-xmp.h"
+
+#define XMP_NAMESPACE_LENGTH 29
+
+typedef gchar * (*PostProcessor) (gchar *);
+
+typedef enum {
+	TIFF_TAGTYPE_UNDEFINED = 0,
+	TIFF_TAGTYPE_STRING,
+	TIFF_TAGTYPE_UINT16,
+	TIFF_TAGTYPE_UINT32,
+	TIFF_TAGTYPE_DOUBLE
+} TagType;
+
+typedef struct {
+	guint	       tag;
+	gchar	      *name;
+	TagType        type;
+	PostProcessor  post;
+} TiffTag;
+
+#define EXIF_DATE_FORMAT "%Y:%m:%d %H:%M:%S"
+
+static gchar *
+date_to_iso8601 (gchar *exif_date)
+{
+	/* ex; date "2007:04:15 15:35:58"
+	   To
+	   ex. "2007-04-15T17:35:58+0200 where +0200 is localtime
+	*/
+	return tracker_generic_date_to_iso8601 (exif_date, EXIF_DATE_FORMAT);
+}
+
+
+/* FIXME We are missing some */
+TiffTag tags[] = {
+	{ TIFFTAG_ARTIST, "Image:Creator", TIFF_TAGTYPE_STRING, NULL },
+	{ TIFFTAG_COPYRIGHT, "File:Copyright", TIFF_TAGTYPE_STRING, NULL },
+	{ TIFFTAG_DATETIME, "Image:Date", TIFF_TAGTYPE_STRING, NULL },
+	{ TIFFTAG_DOCUMENTNAME, "Image:Title", TIFF_TAGTYPE_STRING, NULL },
+	{ TIFFTAG_IMAGEDESCRIPTION, "Image:Comments", TIFF_TAGTYPE_STRING, NULL },
+	{ TIFFTAG_IMAGEWIDTH, "Image:Width", TIFF_TAGTYPE_UINT32, NULL },
+	{ TIFFTAG_IMAGELENGTH, "Image:Height", TIFF_TAGTYPE_UINT32, NULL },
+	{ TIFFTAG_MAKE, "Image:CameraMake", TIFF_TAGTYPE_STRING, NULL },
+	{ TIFFTAG_MODEL, "Image:CameraModel", TIFF_TAGTYPE_STRING, NULL },
+	{ TIFFTAG_ORIENTATION, "Image:Orientation", TIFF_TAGTYPE_UINT16, NULL },
+	{ TIFFTAG_SOFTWARE, "Image:Software", TIFF_TAGTYPE_STRING, NULL },
+	{ -1, NULL, TIFF_TAGTYPE_UNDEFINED, NULL }
+};
+
+TiffTag exiftags[] = {
+	{EXIFTAG_EXPOSURETIME, "Image:ExposureTime", TIFF_TAGTYPE_DOUBLE, NULL},
+	{EXIFTAG_FNUMBER, "Image:FNumber", TIFF_TAGTYPE_DOUBLE, NULL},
+	{EXIFTAG_EXPOSUREPROGRAM, "Image:ExposureProgram", TIFF_TAGTYPE_UINT16 ,NULL },
+	{EXIFTAG_ISOSPEEDRATINGS, "Image:ISOSpeed",TIFF_TAGTYPE_UINT32, NULL},
+	{EXIFTAG_DATETIMEORIGINAL, "Image:Date", TIFF_TAGTYPE_STRING,date_to_iso8601},
+	{EXIFTAG_METERINGMODE, "Image:MeteringMode", TIFF_TAGTYPE_UINT16, NULL},
+	{EXIFTAG_FLASH, "Image:Flash", TIFF_TAGTYPE_UINT16, NULL},
+	{EXIFTAG_FOCALLENGTH, "Image:FocalLength", TIFF_TAGTYPE_UINT16, NULL},
+	{EXIFTAG_PIXELXDIMENSION, "Image:Width",TIFF_TAGTYPE_UINT32, NULL},
+	{EXIFTAG_PIXELYDIMENSION, "Image:Height", TIFF_TAGTYPE_UINT32,NULL},
+	{EXIFTAG_WHITEBALANCE, "Image:WhiteBalance", TIFF_TAGTYPE_UINT16,NULL},
+	{ -1, NULL, TIFF_TAGTYPE_UNDEFINED, NULL }
+};
+
+
+static void
+tracker_extract_tiff (const gchar *filename, GHashTable *metadata)
+{
+	TIFF	 *image;
+	long	  exifOffset;
+
+	TiffTag  *tag;
+
+	gchar buffer[1024];
+	guint16 varui16 = 0;
+	guint32 varui32 = 0;
+	float vardouble;
+
+#ifdef HAVE_EXEMPI
+	gchar	 *xmpOffset;
+	uint32	  size;
+#endif /* HAVE_EXEMPI */
+
+
+	if((image = TIFFOpen(filename, "r")) == NULL){
+		g_error("Could not open image\n");
+		return;
+	}
+
+	/* FIXME There are problems between XMP data embedded with different tools
+	   due to bugs in the original spec (type) */
+
+#ifdef HAVE_EXEMPI
+
+	if (TIFFGetField(image, TIFFTAG_XMLPACKET, &size, &xmpOffset)) {
+		tracker_read_xmp (xmpOffset,
+				  size,
+				  metadata);
+	}
+
+#endif /* HAVE_EXEMPI */
+
+	if (TIFFGetField(image, TIFFTAG_EXIFIFD, &exifOffset)) {
+
+		if (TIFFReadEXIFDirectory(image, exifOffset)) {
+
+			for (tag = exiftags; tag->name; ++tag) {
+
+				switch (tag->type) {
+				case TIFF_TAGTYPE_STRING:
+					if (!TIFFGetField(image, tag->tag, &buffer)) {
+						continue;
+					}
+					break;
+				case TIFF_TAGTYPE_UINT16:
+					if (!TIFFGetField(image, tag->tag, &varui32)) {
+						continue;
+					}
+
+					sprintf(buffer,"%i",varui16);
+					break;
+				case TIFF_TAGTYPE_UINT32:
+					if (!TIFFGetField(image, tag->tag, &varui32)) {
+						continue;
+					}
+
+					sprintf(buffer,"%i",varui32);
+					break;
+				case TIFF_TAGTYPE_DOUBLE:
+					if (!TIFFGetField(image, tag->tag, &vardouble)) {
+						continue;
+					}
+
+					sprintf(buffer,"%f",vardouble);
+					break;
+				default:
+					continue;
+					break;
+				}
+
+				if (tag->post) {
+					g_hash_table_insert (metadata, g_strdup (tag->name),
+							     g_strdup ((*tag->post) (buffer)));
+				} else {
+					g_hash_table_insert (metadata, g_strdup (tag->name),
+							     g_strdup (buffer));
+				}
+			}
+
+		}
+	}
+
+	/* We want to give native tags priority over XMP/Exif */
+	for (tag = tags; tag->name; ++tag) {
+		switch (tag->type) {
+			case TIFF_TAGTYPE_STRING:
+				if (!TIFFGetField(image, tag->tag, &buffer)) {
+					continue;
+				}
+				break;
+			case TIFF_TAGTYPE_UINT16:
+				if (!TIFFGetField(image, tag->tag, &varui32)) {
+					continue;
+				}
+
+				sprintf(buffer,"%i",varui16);
+				break;
+			case TIFF_TAGTYPE_UINT32:
+				if (!TIFFGetField(image, tag->tag, &varui32)) {
+					continue;
+				}
+
+				sprintf(buffer,"%i",varui32);
+				break;
+			case TIFF_TAGTYPE_DOUBLE:
+				if (!TIFFGetField(image, tag->tag, &vardouble)) {
+					continue;
+				}
+
+				sprintf(buffer,"%f",vardouble);
+				break;
+			default:
+				continue;
+				break;
+			}
+
+		if (tag->post) {
+			g_hash_table_insert (metadata, g_strdup (tag->name),
+					     g_strdup ((*tag->post) (buffer)));
+		} else {
+			g_hash_table_insert (metadata, g_strdup (tag->name),
+					     g_strdup (buffer));
+		}
+	}
+
+	TIFFClose(image);
+
+}
+
+
+
+
+TrackerExtractorData data[] = {
+	{ "image/tiff", tracker_extract_tiff },
+	{ NULL, NULL }
+};
+
+
+TrackerExtractorData *
+tracker_get_extractor_data (void)
+{
+	return data;
+}

Added: trunk/src/tracker-extract/tracker-extract-totem.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-extract/tracker-extract-totem.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,89 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Edward Duffy (eduffy gmail com)
+ * Copyright (C) 2008, Nokia
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <string.h>
+
+#include <glib.h>
+
+#include <libtracker-common/tracker-os-dependant.h>
+
+#include "tracker-extract.h"
+
+static gchar *tags[][2] = {
+	{ "TOTEM_INFO_VIDEO_HEIGHT",		"Video:Height"		},
+	{ "TOTEM_INFO_VIDEO_WIDTH",		"Video:Width"		},
+	{ "TOTEM_INFO_FPS",			"Video:FrameRate"	},
+	{ "TOTEM_INFO_VIDEO_CODEC",		"Video:Codec"		},
+	{ "TOTEM_INFO_VIDEO_BITRATE",		"Video:Bitrate"		},
+	{ "TOTEM_INFO_TITLE",			"Video:Title"		},
+	{ "TOTEM_INFO_AUTHOR",			"Video:Author"		},
+	{ "TOTEM_INFO_AUDIO_BITRATE",		"Audio:Bitrate"		},
+	{ "TOTEM_INFO_AUDIO_SAMPLE_RATE",	"Audio:Samplerate"	},
+	{ "TOTEM_INFO_AUDIO_CODEC",		"Audio:Codec"		},
+	{ "TOTEM_INFO_AUDIO_CHANNELS",		"Audio:Channels"	},
+	{ NULL,					NULL			}
+};
+
+static void extract_totem (const gchar *filename,
+			   GHashTable  *metadata);
+
+static TrackerExtractorData data[] = {
+	{ "audio/*", extract_totem },
+	{ "video/*", extract_totem },
+	{ NULL, NULL }
+};
+
+static void
+extract_totem (const gchar *filename,
+	       GHashTable  *metadata)
+{
+	gchar *argv[3];
+	gchar *totem;
+
+	argv[0] = g_strdup ("totem-video-indexer");
+	argv[1] = g_strdup (filename);
+	argv[2] = NULL;
+
+	if (tracker_spawn (argv, 10, &totem, NULL)) {
+		gchar **lines, **line;
+
+		lines = g_strsplit (totem, "\n", -1);
+
+		for (line = lines; *line; ++line) {
+			gint i;
+
+			for (i = 0; tags[i][0]; i++) {
+				if (g_str_has_prefix (*line, tags[i][0])) {
+					g_hash_table_insert (metadata,
+							     g_strdup (tags[i][1]),
+							     g_strdup ((*line) + strlen (tags[i][0]) + 1));
+					break;
+				}
+			}
+		}
+	}
+}
+
+TrackerExtractorData *
+tracker_get_extractor_data (void)
+{
+	return data;
+}

Added: trunk/src/tracker-extract/tracker-extract-vorbis.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-extract/tracker-extract-vorbis.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,204 @@
+/* Tracker Extract - extracts embedded metadata from files
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#ifdef HAVE_VORBIS
+
+#include <stdio.h>
+#include <string.h>
+#include <glib.h>
+#include <vorbis/vorbisfile.h>
+
+#include "tracker-extract.h"
+
+/*#include "tracker-utils.h"*/
+
+static struct {
+	char * name;
+	char *meta_name;
+	gboolean writable;
+} tags[] = {
+	 {"title", "Audio.Title", FALSE},
+	 {"artist", "Audio.Artist", FALSE},
+	 {"album", "Audio.Album", FALSE},
+	 {"albumartist", "Audio.AlbumArtist", FALSE},
+	 {"trackcount", "Audio.AlbumTrackCount", FALSE},
+	 {"tracknumber", "Audio.TrackNo", FALSE},
+	 {"DiscNo", "Audio.DiscNo", FALSE},
+	 {"Performer", "Audio.Performer", FALSE},
+	 {"TrackGain", "Audio.TrackGain", FALSE},
+	 {"TrackPeakGain", "Audio.TrackPeakGain", FALSE},
+	 {"AlbumGain", "Audio.AlbumGain", FALSE},
+	 {"AlbumPeakGain", "Audio.AlbumPeakGain", FALSE},
+	 {"date", "Audio.ReleaseDate", FALSE},
+	 {"comment", "Audio.Comment", FALSE},
+	 {"genre", "Audio.Genre", FALSE},
+	 {"Codec", "Audio.Codec", FALSE},
+	 {"CodecVersion", "Audio.CodecVersion", FALSE},
+	 {"Samplerate", "Audio.Samplerate", FALSE},
+	 {"Channels", "Audio.Channels", FALSE},
+	 {"MBAlbumID", "Audio.MBAlbumID", FALSE},
+	 {"MBArtistID", "Audio.MBArtistID", FALSE},
+	 {"MBAlbumArtistID", "Audio.MBAlbumArtistID", FALSE},
+	 {"MBTrackID", "Audio.MBTrackID", FALSE},
+	 {"Lyrics", "Audio.Lyrics", FALSE},
+	 {"Copyright", "File.Copyright", FALSE},
+	 {"License", "File.License", FALSE},
+	 {"Organization", "File.Organization", FALSE},
+	 {"Location", "File.Location", FALSE},
+	 {"Publisher", "File.Publisher", FALSE},
+	 {NULL, NULL, FALSE},
+};
+
+
+static char*
+get_comment (vorbis_comment *vc, char *label)
+{
+	char *tag;
+	char *utf_tag;
+
+	if (vc && (tag = vorbis_comment_query (vc, label, 0)) != NULL) {
+
+		utf_tag = g_locale_to_utf8 (tag, -1, NULL, NULL, NULL);
+
+		/*g_free (tag);*/
+
+		return utf_tag;
+
+	} else {
+		return NULL;
+	}
+
+}
+
+
+gboolean
+tracker_metadata_ogg_is_writable (const char *meta)
+{
+	int i;
+
+	i = 0;
+	while (tags[i].name != NULL) {
+
+		if (strcmp (tags[i].meta_name, meta) == 0) {
+			return tags[i].writable;
+		}
+
+		i++;
+	}
+
+	return FALSE;
+
+}
+
+
+gboolean
+tracker_metadata_ogg_write (const char *meta_name, const char *value)
+{
+	/* to do */
+	return FALSE;
+}
+
+
+void
+tracker_extract_vorbis (const char *filename, GHashTable *metadata)
+{
+	gint	       fd;
+	FILE	       *oggFile;
+	OggVorbis_File vf;
+	gint	       i;
+
+#if defined(__linux__)
+	if ((fd = g_open (filename, (O_RDONLY | O_NOATIME))) == -1) {
+#else
+	if ((fd = g_open (filename, O_RDONLY)) == -1) {
+#endif
+		return;
+	}
+
+	oggFile = fdopen (fd, "r");
+
+	if (!oggFile) {
+		close (fd);
+		return;
+	}
+
+	if ( ov_open (oggFile, &vf, NULL, 0) < 0 ) {
+		fclose (oggFile);
+		return;
+	}
+
+	char *tmpComment;
+
+	vorbis_comment *comment;
+
+	if ((comment  = ov_comment (&vf, -1)) == NULL) {
+		ov_clear (&vf);
+		return;
+	}
+
+	i = 0;
+	while (tags[i].name != NULL) {
+		tmpComment = get_comment (comment, tags[i].name);
+
+		if (tmpComment) {
+			g_hash_table_insert (metadata, g_strdup (tags[i].meta_name), tmpComment);
+		}
+
+		i++;
+	}
+
+	vorbis_comment_clear(comment);
+
+	/* Bitrate */
+
+	vorbis_info *vi;
+	unsigned int bitrate;
+	char *str_bitrate;
+
+	if ( ( vi = ov_info(&vf, 0)) != NULL ) {
+		bitrate = vi->bitrate_nominal/1000;
+		str_bitrate = g_strdup_printf ("%d", bitrate);
+		g_hash_table_insert (metadata, g_strdup ("Audio.Bitrate"), str_bitrate);
+		g_hash_table_insert (metadata, g_strdup ("Audio.CodecVersion"), g_strdup_printf ("%d", vi->version));
+		g_hash_table_insert (metadata, g_strdup ("Audio.Channels"), g_strdup_printf ("%d", vi->channels));
+		g_hash_table_insert (metadata, g_strdup ("Audio.Samplerate"), g_strdup_printf ("%ld", vi->rate));
+	}
+
+
+
+	/* Duration */
+
+	int time;
+	char *str_time;
+	if ( ( time = ov_time_total(&vf, -1) ) != OV_EINVAL ) {
+		str_time = g_strdup_printf ("%d", time);
+		g_hash_table_insert (metadata, g_strdup ("Audio.Duration"), str_time);
+	}
+
+	g_hash_table_insert (metadata, g_strdup ("Audio.Codec"), g_strdup ("vorbis"));
+
+	ov_clear(&vf);
+
+}
+
+#else
+#warning "Not building ogg/vorbis metadata extractor"
+#endif	/* HAVE_VORBIS */

Added: trunk/src/tracker-extract/tracker-extract-xmp.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-extract/tracker-extract-xmp.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,50 @@
+/* Tracker Extract - extracts embedded metadata from files
+ * Copyright (C) 2007, Jason Kivlighn (jkivlighn gmail com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+
+#include "tracker-extract.h"
+#include "tracker-xmp.h"
+
+
+static void
+tracker_extract_xmp (const gchar* filename, GHashTable *metadata)
+{
+	gchar *contents;
+	gsize length;
+	GError *error;
+
+	if ( g_file_get_contents ( filename, &contents, &length, &error ) )
+		tracker_read_xmp (contents, length, metadata);
+}
+
+
+TrackerExtractorData data[] = {
+	{ "application/rdf+xml", tracker_extract_xmp },
+	{ NULL, NULL }
+};
+
+
+TrackerExtractorData *
+tracker_get_extractor_data (void)
+{
+	return data;
+}

Added: trunk/src/tracker-extract/tracker-extract.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-extract/tracker-extract.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,398 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#define _XOPEN_SOURCE
+#include <time.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <locale.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <gmodule.h>
+
+#ifndef G_OS_WIN32
+#include <sys/resource.h>
+#endif
+
+#include <libtracker-common/tracker-os-dependant.h>
+#include <libtracker-common/tracker-type-utils.h>
+#include <libtracker-common/tracker-utils.h>
+
+#include "tracker-extract.h"
+
+#define MAX_MEM       128
+#define MAX_MEM_AMD64 512
+
+#define ISO8601_FORMAT "%Y-%m-%dT%H:%M:%S%z"
+
+#define DISABLE_DEBUG
+
+#ifdef G_HAVE_ISO_VARARGS
+#  ifdef DISABLE_DEBUG
+#    define debug(...)
+#  else
+#    define debug(...) debug_impl (__VA_ARGS__)
+#  endif
+#elif defined(G_HAVE_GNUC_VARARGS)
+#  if DISABLE_DEBUG
+#    define debug(fmt...)
+#  else
+#    define debug(fmt...) debug_impl(fmt)
+#  endif
+#else
+#  if DISABLE_DEBUG
+#    define debug(x)
+#  else
+#    define debug debug_impl
+#  endif
+#endif
+
+static GArray *extractors = NULL;
+static guint   shutdown_timeout_id = 0;
+
+gchar *
+tracker_generic_date_to_iso8601 (const gchar *date,
+				 const gchar *format)
+{
+	gchar *result;
+	struct tm date_tm;
+
+	memset (&date_tm, 0, sizeof (struct tm));
+
+	if (strptime (date, format, &date_tm) == NULL) {
+		return NULL;
+	}
+
+	result = g_malloc (sizeof (char)*25);
+
+	strftime (result, 25, ISO8601_FORMAT , &date_tm);
+
+	return result;
+}
+
+#ifndef DISABLE_DEBUG
+
+static void
+debug_impl (const gchar *msg, ...)
+{
+	va_list args;
+
+	va_start (args, msg);
+	g_vfprintf (stderr, msg, args);
+	va_end (args);
+
+	g_fprintf (stderr, "\n");
+}
+
+#endif /* DISABLE_DEBUG */
+
+static void
+initialize_extractors (void)
+{
+	GDir	    *dir;
+	GError	    *error;
+	const gchar *name;
+	GArray	    *generic_extractors;
+
+	if (extractors != NULL) {
+		return;
+	}
+
+	if (!g_module_supported ()) {
+		g_error ("Modules are not supported for this platform");
+		return;
+	}
+
+	error = NULL;
+
+	extractors = g_array_sized_new (FALSE,
+					TRUE,
+					sizeof (TrackerExtractorData),
+					10);
+
+	/* This array is going to be used to store
+	 * temporarily extractors with mimetypes such as "audio / *"
+	 */
+	generic_extractors = g_array_sized_new (FALSE,
+						TRUE,
+						sizeof (TrackerExtractorData),
+						10);
+
+	dir = g_dir_open (MODULES_DIR, 0, &error);
+
+	if (!dir) {
+		g_error ("Error opening modules directory: %s", error->message);
+		g_error_free (error);
+		g_array_free (extractors, TRUE);
+		extractors = NULL;
+		g_array_free (generic_extractors, TRUE);
+		return;
+	}
+
+	while ((name = g_dir_read_name (dir)) != NULL) {
+		GModule			 *module;
+		gchar			 *module_path;
+		TrackerExtractorDataFunc func;
+		TrackerExtractorData	 *data;
+
+		if (!g_str_has_suffix (name, "." G_MODULE_SUFFIX)) {
+			continue;
+		}
+
+		module_path = g_build_filename (MODULES_DIR, name, NULL);
+
+		module = g_module_open (module_path, G_MODULE_BIND_LOCAL);
+
+		if (!module) {
+			g_warning ("Could not load module '%s': %s", name, g_module_error ());
+			g_free (module_path);
+			continue;
+		}
+
+		g_module_make_resident (module);
+
+		if (g_module_symbol (module, "tracker_get_extractor_data", (gpointer *) &func)) {
+			data = (func) ();
+
+			while (data->mime) {
+				if (strchr (data->mime, '*') != NULL) {
+					g_array_append_val (generic_extractors, *data);
+				} else {
+					g_array_append_val (extractors, *data);
+				}
+
+				data++;
+			}
+		}
+
+		g_free (module_path);
+	}
+
+	/* Append the generic extractors at the end of
+	 * the list, so the specific ones are used first
+	 */
+	g_array_append_vals (extractors,
+			     generic_extractors->data,
+			     generic_extractors->len);
+	g_array_free (generic_extractors, TRUE);
+}
+
+static GHashTable *
+tracker_get_file_metadata (const gchar *uri,
+			   const gchar *mime)
+{
+	GHashTable *meta_table;
+	gchar	   *uri_in_locale;
+
+	if (!uri) {
+		return NULL;
+	}
+
+	debug ("Extractor - Getting metadata from file:'%s' with mime:'%s'",
+	       uri,
+	       mime);
+
+	uri_in_locale = g_filename_from_utf8 (uri, -1, NULL, NULL, NULL);
+
+	if (!uri_in_locale) {
+		g_warning ("Could not convert uri:'%s' from UTF-8 to locale", uri);
+		return NULL;
+	}
+
+	if (!g_file_test (uri_in_locale, G_FILE_TEST_EXISTS)) {
+		g_warning ("File does not exist '%s'", uri_in_locale);
+		g_free (uri_in_locale);
+		return NULL;
+	}
+
+	meta_table = g_hash_table_new_full (g_str_hash,
+					    g_str_equal,
+					    g_free,
+					    g_free);
+
+	if (mime) {
+		guint i;
+		TrackerExtractorData *data;
+
+		for (i = 0; i < extractors->len; i++) {
+			data = &g_array_index (extractors, TrackerExtractorData, i);
+
+			if (g_pattern_match_simple (data->mime, mime)) {
+				(*data->extractor) (uri_in_locale, meta_table);
+
+				if (g_hash_table_size (meta_table) == 0) {
+					continue;
+				}
+
+				g_free (uri_in_locale);
+
+				debug ("Extractor - Found %d metadata items",
+				       g_hash_table_size (meta_table));
+
+				return meta_table;
+			}
+		}
+
+		debug ("Extractor - Could not find any extractors to handle metadata type.");
+	} else {
+		debug ("Extractor - No mime available, not extracting data");
+	}
+
+	g_free (uri_in_locale);
+
+	return NULL;
+}
+
+static void
+print_meta_table_data (gpointer key,
+		       gpointer value,
+		       gpointer user_data)
+{
+	gchar *value_utf8;
+
+	g_return_if_fail (key != NULL);
+	g_return_if_fail (value != NULL);
+
+	value_utf8 = g_locale_to_utf8 (value, -1, NULL, NULL, NULL);
+
+	if (value_utf8) {
+		if (value_utf8[0] != '\0') {
+			/* Replace any embedded semicolons or "=" as we use them for delimiters */
+			value_utf8 = g_strdelimit (value_utf8, ";", ',');
+			value_utf8 = g_strdelimit (value_utf8, "=", '-');
+			value_utf8 = g_strstrip (value_utf8);
+
+			debug ("Extractor - Found '%s' = '%s'",
+			       (gchar*) key,
+			       value_utf8);
+
+			g_print ("%s=%s;\n", (gchar*) key, value_utf8);
+		}
+
+		g_free (value_utf8);
+	}
+}
+
+static gboolean
+shutdown_app_timeout (gpointer user_data)
+{
+	GMainLoop *main_loop;
+
+	debug ("Extractor - Timed out, shutting down");
+
+	main_loop = (GMainLoop *) user_data;
+	g_main_loop_quit (main_loop);
+
+	return FALSE;
+}
+
+static void
+reset_shutdown_timeout (GMainLoop *main_loop)
+{
+	debug ("Extractor - Resetting timeout");
+
+	if (shutdown_timeout_id != 0) {
+		g_source_remove (shutdown_timeout_id);
+	}
+
+	shutdown_timeout_id = g_timeout_add (30000, shutdown_app_timeout, main_loop);
+}
+
+static gboolean
+process_input_cb (GIOChannel   *channel,
+		  GIOCondition	condition,
+		  gpointer	user_data)
+{
+	GHashTable *meta;
+	gchar *filename, *mimetype;
+
+	debug ("Extractor - Processing input");
+
+	reset_shutdown_timeout ((GMainLoop *) user_data);
+
+	g_io_channel_read_line (channel, &filename, NULL, NULL, NULL);
+	g_io_channel_read_line (channel, &mimetype, NULL, NULL, NULL);
+
+	g_strstrip (filename);
+	g_strstrip (mimetype);
+
+	if (mimetype && *mimetype) {
+		meta = tracker_get_file_metadata (filename, mimetype);
+	} else {
+		meta = tracker_get_file_metadata (filename, NULL);
+	}
+
+	if (meta) {
+		g_hash_table_foreach (meta, print_meta_table_data, NULL);
+		g_hash_table_destroy (meta);
+	}
+
+	/* Add an empty line so the indexer
+	 * knows when to stop reading
+	 */
+	g_print ("\n");
+
+	g_free (filename);
+	g_free (mimetype);
+
+	return TRUE;
+}
+
+int
+main (int argc, char *argv[])
+{
+	GMainLoop  *main_loop;
+	GIOChannel *input;
+
+	debug ("Extractor - Initializing...");
+	tracker_memory_setrlimits ();
+
+	if (!g_thread_supported ()) {
+		g_thread_init (NULL);
+	}
+
+	g_set_application_name ("tracker-extract");
+
+	setlocale (LC_ALL, "");
+
+	initialize_extractors ();
+	main_loop = g_main_loop_new (NULL, FALSE);
+
+	input = g_io_channel_unix_new (STDIN_FILENO);
+	g_io_add_watch (input, G_IO_IN, process_input_cb, main_loop);
+
+	reset_shutdown_timeout (main_loop);
+
+	debug ("Extractor - Waiting for work");
+	g_main_loop_run (main_loop);
+
+	debug ("Extractor - Finished");
+
+	return EXIT_SUCCESS;
+}

Added: trunk/src/tracker-extract/tracker-extract.h
==============================================================================
--- (empty file)
+++ trunk/src/tracker-extract/tracker-extract.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,46 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKER_EXTRACT_H__
+#define __TRACKER_EXTRACT_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct TrackerExtractorData TrackerExtractorData;
+
+typedef TrackerExtractorData * (* TrackerExtractorDataFunc) (void);
+
+struct TrackerExtractorData {
+	const gchar *mime;
+
+	void (* extractor) (const gchar *filename,
+			    GHashTable	*metadata);
+};
+
+gchar *		      tracker_generic_date_to_iso8601 (const gchar  *date,
+						       const gchar  *format);
+TrackerExtractorData *tracker_get_extractor_data      (void);
+
+G_END_DECLS
+
+#endif /* __TRACKER_EXTRACT_H__ */

Added: trunk/src/tracker-extract/tracker-xmp.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-extract/tracker-xmp.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,271 @@
+/* Tracker Xmp - Xmp helper functions
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <locale.h>
+#include <string.h>
+#include <glib.h>
+
+#include "config.h"
+#include "tracker-xmp.h"
+
+#ifdef HAVE_EXEMPI
+
+#include <exempi/xmp.h>
+#include <exempi/xmpconsts.h>
+
+static void tracker_xmp_iter	    (XmpPtr	     xmp,
+				     XmpIteratorPtr  iter,
+				     GHashTable     *metadata,
+				     gboolean	     append);
+
+static void tracker_xmp_iter_simple (GHashTable  *metadata,
+				     const gchar *schema,
+				     const gchar *path,
+				     const gchar *value,
+				     gboolean	  append);
+
+
+static void
+tracker_append_string_to_hash_table (GHashTable *metadata, const gchar *key, const gchar *value, gboolean append)
+{
+	gchar *new_value;
+
+	if (append) {
+		gchar *orig;
+		if (g_hash_table_lookup_extended (metadata, key, NULL, (gpointer)&orig )) {
+			new_value = g_strconcat (orig, " ", value, NULL);
+		} else {
+			new_value = g_strdup (value);
+		}
+	} else {
+		new_value = g_strdup (value);
+	}
+
+	g_hash_table_insert (metadata, g_strdup (key), new_value);
+}
+
+
+/* We have an array, now recursively iterate over it's children.  Set 'append' to true so that all values of the array are added
+   under one entry. */
+static void
+tracker_xmp_iter_array (XmpPtr xmp, GHashTable *metadata, const gchar *schema, const gchar *path)
+{
+		XmpIteratorPtr iter = xmp_iterator_new (xmp, schema, path, XMP_ITER_JUSTCHILDREN);
+		tracker_xmp_iter (xmp, iter, metadata, TRUE);
+		xmp_iterator_free (iter);
+}
+
+
+/* We have an array, now recursively iterate over it's children.  Set 'append' to false so that only one item is used. */
+static void
+tracker_xmp_iter_alt_text (XmpPtr xmp, GHashTable *metadata, const gchar *schema, const gchar *path)
+{
+		XmpIteratorPtr iter = xmp_iterator_new (xmp, schema, path, XMP_ITER_JUSTCHILDREN);
+		tracker_xmp_iter (xmp, iter, metadata, FALSE);
+		xmp_iterator_free (iter);
+}
+
+
+/* We have a simple element, but need to iterate over the qualifiers */
+static void
+tracker_xmp_iter_simple_qual (XmpPtr xmp, GHashTable *metadata,
+			      const gchar *schema, const gchar *path, const gchar *value, gboolean append)
+{
+	XmpIteratorPtr iter = xmp_iterator_new (xmp, schema, path, XMP_ITER_JUSTCHILDREN | XMP_ITER_JUSTLEAFNAME);
+
+	XmpStringPtr the_path = xmp_string_new ();
+	XmpStringPtr the_prop = xmp_string_new ();
+
+	gchar *locale = setlocale (LC_ALL, NULL);
+	gchar *sep = strchr (locale,'.');
+	if (sep) {
+		locale[sep - locale] = '\0';
+	}
+	sep = strchr (locale, '_');
+	if (sep) {
+		locale[sep - locale] = '-';
+	}
+
+	gboolean ignore_element = FALSE;
+
+	while (xmp_iterator_next (iter, NULL, the_path, the_prop, NULL)) {
+		const gchar *qual_path = xmp_string_cstr (the_path);
+		const gchar *qual_value = xmp_string_cstr (the_prop);
+
+		if (strcmp (qual_path, "xml:lang") == 0) {
+			/* is this a language we should ignore? */
+			if (strcmp (qual_value, "x-default") != 0 && strcmp (qual_value, "x-repair") != 0 && strcmp (qual_value, locale) != 0) {
+				ignore_element = TRUE;
+				break;
+			}
+		}
+	}
+
+	if (!ignore_element) {
+		tracker_xmp_iter_simple (metadata, schema, path, value, append);
+	}
+
+	xmp_string_free (the_prop);
+	xmp_string_free (the_path);
+
+	xmp_iterator_free (iter);
+}
+
+
+/* We have a simple element. Add any metadata we know about to the hash table  */
+static void
+tracker_xmp_iter_simple (GHashTable *metadata,
+			 const gchar *schema, const gchar *path, const gchar *value, gboolean append)
+{
+	gchar *name = g_strdup (strchr (path, ':') + 1);
+	const gchar *index = strrchr (name, '[');
+	if (index) {
+		name[index-name] = '\0';
+	}
+
+	/* Dublin Core */
+	if (strcmp (schema, NS_DC) == 0) {
+		if (strcmp (name, "title") == 0) {
+			tracker_append_string_to_hash_table (metadata, "Image:Title", value, append);
+		}
+		else if (strcmp (name, "rights") == 0) {
+			tracker_append_string_to_hash_table (metadata, "File:Copyright", value, append);
+		}
+		else if (strcmp (name, "creator") == 0) {
+			tracker_append_string_to_hash_table (metadata, "Image:Creator", value, append);
+		}
+		else if (strcmp (name, "description") == 0) {
+			tracker_append_string_to_hash_table (metadata, "Image:Description", value, append);
+		}
+		else if (strcmp (name, "date") == 0) {
+			tracker_append_string_to_hash_table (metadata, "Image:Date", value, append);
+		}
+		else if (strcmp (name, "keywords") == 0) {
+			tracker_append_string_to_hash_table (metadata, "Image:Keywords", value, append);
+		}
+		else if (strcmp (name, "subject") == 0) {
+			tracker_append_string_to_hash_table (metadata, "DC:Subject", value, append);
+		}
+		else if (strcmp (name, "publisher") == 0) {
+			tracker_append_string_to_hash_table (metadata, "DC:Publisher", value, append);
+		}
+		else if (strcmp (name, "contributor") == 0) {
+			tracker_append_string_to_hash_table (metadata, "DC:Contributor", value, append);
+		}
+		else if (strcmp (name, "type") == 0) {
+			tracker_append_string_to_hash_table (metadata, "DC:Type", value, append);
+		}
+		else if (strcmp (name, "format") == 0) {
+			tracker_append_string_to_hash_table (metadata, "DC:Format", value, append);
+		}
+		else if (strcmp (name, "identifier") == 0) {
+			tracker_append_string_to_hash_table (metadata, "DC:Identifier", value, append);
+		}
+		else if (strcmp (name, "source") == 0) {
+			tracker_append_string_to_hash_table (metadata, "DC:Source", value, append);
+		}
+		else if (strcmp (name, "language") == 0) {
+			tracker_append_string_to_hash_table (metadata, "DC:Language", value, append);
+		}
+		else if (strcmp (name, "relation") == 0) {
+			tracker_append_string_to_hash_table (metadata, "DC:Relation", value, append);
+		}
+		else if (strcmp (name, "coverage") == 0) {
+			tracker_append_string_to_hash_table (metadata, "DC:Coverage", value, append);
+		}
+
+	}
+	/* Creative Commons */
+	else if (strcmp (schema, NS_CC) == 0) {
+		if (strcmp (name, "license") == 0) {
+			tracker_append_string_to_hash_table (metadata, "File:License", value, append);
+		}
+	}
+	/* Exif basic scheme */
+	else if (strcmp (schema, NS_EXIF) == 0) {
+		if (strcmp (name, "title") == 0) {
+			tracker_append_string_to_hash_table (metadata, "Image:Title", value, append);
+		}
+	}
+
+	g_free (name);
+}
+
+
+/* Iterate over the XMP, dispatching to the appropriate element type (simple, simple w/qualifiers, or an array) handler */
+void
+tracker_xmp_iter (XmpPtr xmp, XmpIteratorPtr iter, GHashTable *metadata, gboolean append)
+{
+	XmpStringPtr the_schema = xmp_string_new ();
+	XmpStringPtr the_path = xmp_string_new ();
+	XmpStringPtr the_prop = xmp_string_new ();
+
+	uint32_t opt;
+	while (xmp_iterator_next (iter, the_schema, the_path, the_prop, &opt)) {
+		const gchar *schema = xmp_string_cstr (the_schema);
+		const gchar *path = xmp_string_cstr (the_path);
+		const gchar *value = xmp_string_cstr (the_prop);
+
+		if (XMP_IS_PROP_SIMPLE (opt)) {
+			if (strcmp (path,"") != 0) {
+				if (XMP_HAS_PROP_QUALIFIERS (opt)) {
+					tracker_xmp_iter_simple_qual (xmp, metadata, schema, path, value, append);
+				} else {
+					tracker_xmp_iter_simple (metadata, schema, path, value, append);
+				}
+			}
+		}
+		else if (XMP_IS_PROP_ARRAY (opt)) {
+			if (XMP_IS_ARRAY_ALTTEXT (opt)) {
+				tracker_xmp_iter_alt_text (xmp, metadata, schema, path);
+				xmp_iterator_skip (iter, XMP_ITER_SKIPSUBTREE);
+			} else {
+				tracker_xmp_iter_array (xmp, metadata, schema, path);
+				xmp_iterator_skip (iter, XMP_ITER_SKIPSUBTREE);
+			}
+		}
+	}
+
+	xmp_string_free (the_prop);
+	xmp_string_free (the_path);
+	xmp_string_free (the_schema);
+}
+
+#endif /* HAVE_EXEMPI */
+
+
+void
+tracker_read_xmp (const gchar *buffer, size_t len, GHashTable *metadata)
+{
+#ifdef HAVE_EXEMPI
+	xmp_init ();
+
+	XmpPtr xmp = xmp_new_empty ();
+	xmp_parse (xmp, buffer, len);
+	if (xmp != NULL) {
+		XmpIteratorPtr iter = xmp_iterator_new (xmp, NULL, NULL, XMP_ITER_PROPERTIES);
+		tracker_xmp_iter (xmp, iter, metadata, FALSE);
+		xmp_iterator_free (iter);
+
+		xmp_free (xmp);
+	}
+
+	xmp_terminate ();
+#endif
+}

Added: trunk/src/tracker-extract/tracker-xmp.h
==============================================================================
--- (empty file)
+++ trunk/src/tracker-extract/tracker-xmp.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,27 @@
+/* Tracker Xmp - Xmp helper functions
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef _TRACKER_XMP_H_
+#define _TRACKER_XMP_H_
+
+#include <glib.h>
+
+void tracker_read_xmp (const gchar *buffer, size_t len, GHashTable *metadata);
+
+#endif /* _TRACKER_XMP_H_ */

Added: trunk/src/tracker-fts/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/tracker-fts/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,28 @@
+include $(top_srcdir)/Makefile.decl
+
+module_flags = -module -avoid-version -no-undefined
+tracker_fts_modulesdir = $(libdir)
+
+INCLUDES =								\
+	-g								\
+	-I$(top_srcdir)/src						\
+	$(GLIB2_CFLAGS)							\
+	$(SQLITE3_CFLAGS)						\
+	$(PANGO_CFLAGS)
+
+tracker_fts_modules_LTLIBRARIES = tracker-fts.la
+		
+
+tracker_fts_la_SOURCES = 						\
+			tracker-fts.c					\
+			tracker-fts-hash.c				
+						
+			
+tracker_fts_la_LDFLAGS = $(module_flags)
+
+tracker_fts_la_LIBADD =							\
+	$(top_builddir)/src/libtracker-common/libtracker-common.la 	\
+	$(SQLITE3_LIBS)							\
+	$(GTHREAD_LIBS)							\
+	$(PANGO_LIBS)							\
+	$(GLIB2_LIBS)							

Added: trunk/src/tracker-fts/tracker-fts-hash.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-fts/tracker-fts-hash.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,373 @@
+/*
+** 2001 September 22
+**
+** 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.
+**
+*************************************************************************
+** This is the implementation of generic hash-tables used in SQLite.
+** We've modified it slightly to serve as a standalone hash table
+** implementation for the full-text indexing module.
+*/
+
+/*
+** 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).
+*/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sqlite3.h>
+#include "tracker-fts-hash.h"
+
+/*
+** Malloc and Free functions
+*/
+static void *fts3HashMalloc(int n){
+  void *p = sqlite3_malloc(n);
+  if( p ){
+    memset(p, 0, n);
+  }
+  return p;
+}
+static void fts3HashFree(void *p){
+  sqlite3_free(p);
+}
+
+/* Turn bulk memory into a hash table object by initializing the
+** fields of the Hash structure.
+**
+** "pNew" is a pointer to the hash table that is to be initialized.
+** keyClass is one of the constants
+** FTS3_HASH_BINARY or FTS3_HASH_STRING.  The value of keyClass
+** determines what kind of key the hash table will use.  "copyKey" is
+** true if the hash table should make its own private copy of keys and
+** false if it should just use the supplied pointer.
+*/
+void sqlite3Fts3HashInit(fts3Hash *pNew, int keyClass, int copyKey){
+  assert( pNew!=0 );
+  assert( keyClass>=FTS3_HASH_STRING && keyClass<=FTS3_HASH_BINARY );
+  pNew->keyClass = keyClass;
+  pNew->copyKey = copyKey;
+  pNew->first = 0;
+  pNew->count = 0;
+  pNew->htsize = 0;
+  pNew->ht = 0;
+}
+
+/* Remove all entries from a hash table.  Reclaim all memory.
+** Call this routine to delete a hash table or to reset a hash table
+** to the empty state.
+*/
+void sqlite3Fts3HashClear(fts3Hash *pH){
+  fts3HashElem *elem;	      /* For looping over all elements of the table */
+
+  assert( pH!=0 );
+  elem = pH->first;
+  pH->first = 0;
+  fts3HashFree(pH->ht);
+  pH->ht = 0;
+  pH->htsize = 0;
+  while( elem ){
+    fts3HashElem *next_elem = elem->next;
+    if( pH->copyKey && elem->pKey ){
+      fts3HashFree(elem->pKey);
+    }
+    fts3HashFree(elem);
+    elem = next_elem;
+  }
+  pH->count = 0;
+}
+
+/*
+** Hash and comparison functions when the mode is FTS3_HASH_STRING
+*/
+static int fts3StrHash(const void *pKey, int nKey){
+  const char *z = (const char *)pKey;
+  int h = 0;
+  if( nKey<=0 ) nKey = (int) strlen(z);
+  while( nKey > 0  ){
+    h = (h<<3) ^ h ^ *z++;
+    nKey--;
+  }
+  return h & 0x7fffffff;
+}
+static int fts3StrCompare(const void *pKey1, int n1, const void *pKey2, int n2){
+  if( n1!=n2 ) return 1;
+  return strncmp((const char*)pKey1,(const char*)pKey2,n1);
+}
+
+/*
+** Hash and comparison functions when the mode is FTS3_HASH_BINARY
+*/
+static int fts3BinHash(const void *pKey, int nKey){
+  int h = 0;
+  const char *z = (const char *)pKey;
+  while( nKey-- > 0 ){
+    h = (h<<3) ^ h ^ *(z++);
+  }
+  return h & 0x7fffffff;
+}
+static int fts3BinCompare(const void *pKey1, int n1, const void *pKey2, int n2){
+  if( n1!=n2 ) return 1;
+  return memcmp(pKey1,pKey2,n1);
+}
+
+/*
+** Return a pointer to the appropriate hash function given the key class.
+**
+** The C syntax in this function definition may be unfamilar to some
+** programmers, so we provide the following additional explanation:
+**
+** The name of the function is "ftsHashFunction".  The function takes a
+** single parameter "keyClass".  The return value of ftsHashFunction()
+** is a pointer to another function.  Specifically, the return value
+** of ftsHashFunction() is a pointer to a function that takes two parameters
+** with types "const void*" and "int" and returns an "int".
+*/
+static int (*ftsHashFunction(int keyClass))(const void*,int){
+  if( keyClass==FTS3_HASH_STRING ){
+    return &fts3StrHash;
+  }else{
+    assert( keyClass==FTS3_HASH_BINARY );
+    return &fts3BinHash;
+  }
+}
+
+/*
+** Return a pointer to the appropriate hash function given the key class.
+**
+** For help in interpreted the obscure C code in the function definition,
+** see the header comment on the previous function.
+*/
+static int (*ftsCompareFunction(int keyClass))(const void*,int,const void*,int){
+  if( keyClass==FTS3_HASH_STRING ){
+    return &fts3StrCompare;
+  }else{
+    assert( keyClass==FTS3_HASH_BINARY );
+    return &fts3BinCompare;
+  }
+}
+
+/* Link an element into the hash table
+*/
+static void fts3HashInsertElement(
+  fts3Hash *pH,		   /* The complete hash table */
+  struct _fts3ht *pEntry,  /* The entry into which pNew is inserted */
+  fts3HashElem *pNew	   /* The element to be inserted */
+){
+  fts3HashElem *pHead;	   /* First element already in pEntry */
+  pHead = pEntry->chain;
+  if( pHead ){
+    pNew->next = pHead;
+    pNew->prev = pHead->prev;
+    if( pHead->prev ){ pHead->prev->next = pNew; }
+    else	     { pH->first = pNew; }
+    pHead->prev = pNew;
+  }else{
+    pNew->next = pH->first;
+    if( pH->first ){ pH->first->prev = pNew; }
+    pNew->prev = 0;
+    pH->first = pNew;
+  }
+  pEntry->count++;
+  pEntry->chain = pNew;
+}
+
+
+/* Resize the hash table so that it cantains "new_size" buckets.
+** "new_size" must be a power of 2.  The hash table might fail
+** to resize if sqliteMalloc() fails.
+*/
+static void fts3Rehash(fts3Hash *pH, int new_size){
+  struct _fts3ht *new_ht;	   /* The new hash table */
+  fts3HashElem *elem, *next_elem;  /* For looping over existing elements */
+  int (*xHash)(const void*,int);   /* The hash function */
+
+  assert( (new_size & (new_size-1))==0 );
+  new_ht = (struct _fts3ht *)fts3HashMalloc( new_size*sizeof(struct _fts3ht) );
+  if( new_ht==0 ) return;
+  fts3HashFree(pH->ht);
+  pH->ht = new_ht;
+  pH->htsize = new_size;
+  xHash = ftsHashFunction(pH->keyClass);
+  for(elem=pH->first, pH->first=0; elem; elem = next_elem){
+    int h = (*xHash)(elem->pKey, elem->nKey) & (new_size-1);
+    next_elem = elem->next;
+    fts3HashInsertElement(pH, &new_ht[h], elem);
+  }
+}
+
+/* This function (for internal use only) locates an element in an
+** hash table that matches the given key.  The hash for this key has
+** already been computed and is passed as the 4th parameter.
+*/
+static fts3HashElem *fts3FindElementByHash(
+  const fts3Hash *pH, /* The pH to be searched */
+  const void *pKey,   /* The key we are searching for */
+  int nKey,
+  int h		      /* The hash for this key. */
+){
+  fts3HashElem *elem;		 /* Used to loop thru the element list */
+  int count;			 /* Number of elements left to test */
+  int (*xCompare)(const void*,int,const void*,int);  /* comparison function */
+
+  if( pH->ht ){
+    struct _fts3ht *pEntry = &pH->ht[h];
+    elem = pEntry->chain;
+    count = pEntry->count;
+    xCompare = ftsCompareFunction(pH->keyClass);
+    while( count-- && elem ){
+      if( (*xCompare)(elem->pKey,elem->nKey,pKey,nKey)==0 ){
+	return elem;
+      }
+      elem = elem->next;
+    }
+  }
+  return 0;
+}
+
+/* Remove a single entry from the hash table given a pointer to that
+** element and a hash on the element's key.
+*/
+static void fts3RemoveElementByHash(
+  fts3Hash *pH,		/* The pH containing "elem" */
+  fts3HashElem* elem,	/* The element to be removed from the pH */
+  int h			/* Hash value for the element */
+){
+  struct _fts3ht *pEntry;
+  if( elem->prev ){
+    elem->prev->next = elem->next;
+  }else{
+    pH->first = elem->next;
+  }
+  if( elem->next ){
+    elem->next->prev = elem->prev;
+  }
+  pEntry = &pH->ht[h];
+  if( pEntry->chain==elem ){
+    pEntry->chain = elem->next;
+  }
+  pEntry->count--;
+  if( pEntry->count<=0 ){
+    pEntry->chain = 0;
+  }
+  if( pH->copyKey && elem->pKey ){
+    fts3HashFree(elem->pKey);
+  }
+  fts3HashFree( elem );
+  pH->count--;
+  if( pH->count<=0 ){
+    assert( pH->first==0 );
+    assert( pH->count==0 );
+    fts3HashClear(pH);
+  }
+}
+
+/* Attempt to locate an element of the hash table pH with a key
+** that matches pKey,nKey.  Return the data for this element if it is
+** found, or NULL if there is no match.
+*/
+void *sqlite3Fts3HashFind(const fts3Hash *pH, const void *pKey, int nKey){
+  int h;		 /* A hash on key */
+  fts3HashElem *elem;	 /* The element that matches key */
+  int (*xHash)(const void*,int);  /* The hash function */
+
+  if( pH==0 || pH->ht==0 ) return 0;
+  xHash = ftsHashFunction(pH->keyClass);
+  assert( xHash!=0 );
+  h = (*xHash)(pKey,nKey);
+  assert( (pH->htsize & (pH->htsize-1))==0 );
+  elem = fts3FindElementByHash(pH,pKey,nKey, h & (pH->htsize-1));
+  return elem ? elem->data : 0;
+}
+
+/* Insert an element into the hash table pH.  The key is pKey,nKey
+** and the data is "data".
+**
+** If no element exists with a matching key, then a new
+** element is created.	A copy of the key is made if the copyKey
+** flag is set.  NULL is returned.
+**
+** If another element already exists with the same key, then the
+** new data replaces the old data and the old data is returned.
+** The key is not copied in this instance.  If a malloc fails, then
+** the new data is returned and the hash table is unchanged.
+**
+** If the "data" parameter to this function is NULL, then the
+** element corresponding to "key" is removed from the hash table.
+*/
+void *sqlite3Fts3HashInsert(
+  fts3Hash *pH,        /* The hash table to insert into */
+  const void *pKey,    /* The key */
+  int nKey,	       /* Number of bytes in the key */
+  void *data	       /* The data */
+){
+  int hraw;		    /* Raw hash value of the key */
+  int h;		    /* the hash of the key modulo hash table size */
+  fts3HashElem *elem;	    /* Used to loop thru the element list */
+  fts3HashElem *new_elem;   /* New element added to the pH */
+  int (*xHash)(const void*,int);  /* The hash function */
+
+  assert( pH!=0 );
+  xHash = ftsHashFunction(pH->keyClass);
+  assert( xHash!=0 );
+  hraw = (*xHash)(pKey, nKey);
+  assert( (pH->htsize & (pH->htsize-1))==0 );
+  h = hraw & (pH->htsize-1);
+  elem = fts3FindElementByHash(pH,pKey,nKey,h);
+  if( elem ){
+    void *old_data = elem->data;
+    if( data==0 ){
+      fts3RemoveElementByHash(pH,elem,h);
+    }else{
+      elem->data = data;
+    }
+    return old_data;
+  }
+  if( data==0 ) return 0;
+  new_elem = (fts3HashElem*)fts3HashMalloc( sizeof(fts3HashElem) );
+  if( new_elem==0 ) return data;
+  if( pH->copyKey && pKey!=0 ){
+    new_elem->pKey = fts3HashMalloc( nKey );
+    if( new_elem->pKey==0 ){
+      fts3HashFree(new_elem);
+      return data;
+    }
+    memcpy((void*)new_elem->pKey, pKey, nKey);
+  }else{
+    new_elem->pKey = (void*)pKey;
+  }
+  new_elem->nKey = nKey;
+  pH->count++;
+  if( pH->htsize==0 ){
+    fts3Rehash(pH,8);
+    if( pH->htsize==0 ){
+      pH->count = 0;
+      fts3HashFree(new_elem);
+      return data;
+    }
+  }
+  if( pH->count > pH->htsize ){
+    fts3Rehash(pH,pH->htsize*2);
+  }
+  assert( pH->htsize>0 );
+  assert( (pH->htsize & (pH->htsize-1))==0 );
+  h = hraw & (pH->htsize-1);
+  fts3HashInsertElement(pH, &pH->ht[h], new_elem);
+  new_elem->data = data;
+  return 0;
+}
+
+

Added: trunk/src/tracker-fts/tracker-fts-hash.h
==============================================================================
--- (empty file)
+++ trunk/src/tracker-fts/tracker-fts-hash.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,110 @@
+/*
+** 2001 September 22
+**
+** 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.
+**
+*************************************************************************
+** This is the header file for the generic hash-table implemenation
+** used in SQLite.  We've modified it slightly to serve as a standalone
+** hash table implementation for the full-text indexing module.
+**
+*/
+#ifndef _FTS3_HASH_H_
+#define _FTS3_HASH_H_
+
+/* Forward declarations of structures. */
+typedef struct fts3Hash fts3Hash;
+typedef struct fts3HashElem fts3HashElem;
+
+/* A complete hash table is an instance of the following structure.
+** The internals of this structure are intended to be opaque -- client
+** code should not attempt to access or modify the fields of this structure
+** directly.  Change this structure only by using the routines below.
+** However, many of the "procedures" and "functions" for modifying and
+** accessing this structure are really macros, so we can't really make
+** this structure opaque.
+*/
+struct fts3Hash {
+  char keyClass;	  /* HASH_INT, _POINTER, _STRING, _BINARY */
+  char copyKey;		  /* True if copy of key made on insert */
+  int count;		  /* Number of entries in this table */
+  fts3HashElem *first;	  /* The first element of the array */
+  int htsize;		  /* Number of buckets in the hash table */
+  struct _fts3ht {	  /* the hash table */
+    int count;		     /* Number of entries with this hash */
+    fts3HashElem *chain;     /* Pointer to first entry with this hash */
+  } *ht;
+};
+
+/* Each element in the hash table is an instance of the following
+** structure.  All elements are stored on a single doubly-linked list.
+**
+** Again, this structure is intended to be opaque, but it can't really
+** be opaque because it is used by macros.
+*/
+struct fts3HashElem {
+  fts3HashElem *next, *prev; /* Next and previous elements in the table */
+  void *data;		     /* Data associated with this element */
+  void *pKey; int nKey;      /* Key associated with this element */
+};
+
+/*
+** There are 2 different modes of operation for a hash table:
+**
+**   FTS3_HASH_STRING	     pKey points to a string that is nKey bytes long
+**			     (including the null-terminator, if any).  Case
+**			     is respected in comparisons.
+**
+**   FTS3_HASH_BINARY	     pKey points to binary data nKey bytes long.
+**			     memcmp() is used to compare keys.
+**
+** A copy of the key is made if the copyKey parameter to fts3HashInit is 1.
+*/
+#define FTS3_HASH_STRING    1
+#define FTS3_HASH_BINARY    2
+
+/*
+** Access routines.  To delete, insert a NULL pointer.
+*/
+void sqlite3Fts3HashInit(fts3Hash*, int keytype, int copyKey);
+void *sqlite3Fts3HashInsert(fts3Hash*, const void *pKey, int nKey, void *pData);
+void *sqlite3Fts3HashFind(const fts3Hash*, const void *pKey, int nKey);
+void sqlite3Fts3HashClear(fts3Hash*);
+
+/*
+** Shorthand for the functions above
+*/
+#define fts3HashInit   sqlite3Fts3HashInit
+#define fts3HashInsert sqlite3Fts3HashInsert
+#define fts3HashFind   sqlite3Fts3HashFind
+#define fts3HashClear  sqlite3Fts3HashClear
+
+/*
+** Macros for looping over all elements of a hash table.  The idiom is
+** like this:
+**
+**   fts3Hash h;
+**   fts3HashElem *p;
+**   ...
+**   for(p=fts3HashFirst(&h); p; p=fts3HashNext(p)){
+**     SomeStructure *pData = fts3HashData(p);
+**     // do something with pData
+**   }
+*/
+#define fts3HashFirst(H)  ((H)->first)
+#define fts3HashNext(E)   ((E)->next)
+#define fts3HashData(E)   ((E)->data)
+#define fts3HashKey(E)	  ((E)->pKey)
+#define fts3HashKeysize(E) ((E)->nKey)
+
+/*
+** Number of entries in a hash table
+*/
+#define fts3HashCount(H)  ((H)->count)
+
+#endif /* _FTS3_HASH_H_ */

Added: trunk/src/tracker-fts/tracker-fts.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-fts/tracker-fts.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,7230 @@
+/*
+** 2006 Oct 10
+**
+** 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.
+**
+******************************************************************************
+**
+** This is an SQLite module implementing full-text search.
+*/
+
+
+// gcc	-shared -o tracker-fts *.c
+//gcc -Wall -fPIC -c *.c
+
+//gcc -shared -Wl,-soname,libtracker-fts.so.1 -o libtracker-fts.so.1.0	 *.o
+
+/*
+** 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).
+*/
+
+/* TODO(shess) Consider exporting this comment to an HTML file or the
+** wiki.
+*/
+/* The full-text index is stored in a series of b+tree (-like)
+** structures called segments which map terms to doclists.  The
+** structures are like b+trees in layout, but are constructed from the
+** bottom up in optimal fashion and are not updatable.	Since trees
+** are built from the bottom up, things will be described from the
+** bottom up.
+**
+**
+**** Varints ****
+** The basic unit of encoding is a variable-length integer called a
+** varint.  We encode variable-length integers in little-endian order
+** using seven bits * per byte as follows:
+**
+** KEY:
+**	   A = 0xxxxxxx    7 bits of data and one flag bit
+**	   B = 1xxxxxxx    7 bits of data and one flag bit
+**
+**  7 bits - A
+** 14 bits - BA
+** 21 bits - BBA
+** and so on.
+**
+** This is identical to how sqlite encodes varints (see util.c).
+**
+**
+**** Document lists ****
+** A doclist (document list) holds a docid-sorted list of hits for a
+** given term.	Doclists hold docids, and can optionally associate
+** token positions and offsets with docids.
+**
+** A DL_POSITIONS_OFFSETS doclist is stored like this:
+**
+** array {
+**   varint docid;
+**   array {		    (position list for column 0)
+**     varint position;     (delta from previous position plus POS_BASE)
+**     varint startOffset;  (delta from previous startOffset)
+**     varint endOffset;    (delta from startOffset)
+**   }
+**   array {
+**     varint POS_COLUMN;   (marks start of position list for new column)
+**     varint column;	    (index of new column)
+**     array {
+**	 varint position;   (delta from previous position plus POS_BASE)
+**	 varint startOffset;(delta from previous startOffset)
+**	 varint endOffset;  (delta from startOffset)
+**     }
+**   }
+**   varint POS_END;	    (marks end of positions for this document.
+** }
+**
+** Here, array { X } means zero or more occurrences of X, adjacent in
+** memory.  A "position" is an index of a token in the token stream
+** generated by the tokenizer, while an "offset" is a byte offset,
+** both based at 0.  Note that POS_END and POS_COLUMN occur in the
+** same logical place as the position element, and act as sentinals
+** ending a position list array.
+**
+** A DL_POSITIONS doclist omits the startOffset and endOffset
+** information.  A DL_DOCIDS doclist omits both the position and
+** offset information, becoming an array of varint-encoded docids.
+**
+** On-disk data is stored as type DL_DEFAULT, so we don't serialize
+** the type.  Due to how deletion is implemented in the segmentation
+** system, on-disk doclists MUST store at least positions.
+**
+**
+**** Segment leaf nodes ****
+** Segment leaf nodes store terms and doclists, ordered by term.  Leaf
+** nodes are written using LeafWriter, and read using LeafReader (to
+** iterate through a single leaf node's data) and LeavesReader (to
+** iterate through a segment's entire leaf layer).  Leaf nodes have
+** the format:
+**
+** varint iHeight;	       (height from leaf level, always 0)
+** varint nTerm;	       (length of first term)
+** char pTerm[nTerm];	       (content of first term)
+** varint nDoclist;	       (length of term's associated doclist)
+** char pDoclist[nDoclist];    (content of doclist)
+** array {
+**			       (further terms are delta-encoded)
+**   varint nPrefix;	       (length of prefix shared with previous term)
+**   varint nSuffix;	       (length of unshared suffix)
+**   char pTermSuffix[nSuffix];(unshared suffix of next term)
+**   varint nDoclist;	       (length of term's associated doclist)
+**   char pDoclist[nDoclist];  (content of doclist)
+** }
+**
+** Here, array { X } means zero or more occurrences of X, adjacent in
+** memory.
+**
+** Leaf nodes are broken into blocks which are stored contiguously in
+** the %_segments table in sorted order.  This means that when the end
+** of a node is reached, the next term is in the node with the next
+** greater node id.
+**
+** New data is spilled to a new leaf node when the current node
+** exceeds LEAF_MAX bytes (default 2048).  New data which itself is
+** larger than STANDALONE_MIN (default 1024) is placed in a standalone
+** node (a leaf node with a single term and doclist).  The goal of
+** these settings is to pack together groups of small doclists while
+** making it efficient to directly access large doclists.  The
+** assumption is that large doclists represent terms which are more
+** likely to be query targets.
+**
+** TODO(shess) It may be useful for blocking decisions to be more
+** dynamic.  For instance, it may make more sense to have a 2.5k leaf
+** node rather than splitting into 2k and .5k nodes.  My intuition is
+** that this might extend through 2x or 4x the pagesize.
+**
+**
+**** Segment interior nodes ****
+** Segment interior nodes store blockids for subtree nodes and terms
+** to describe what data is stored by the each subtree.  Interior
+** nodes are written using InteriorWriter, and read using
+** InteriorReader.  InteriorWriters are created as needed when
+** SegmentWriter creates new leaf nodes, or when an interior node
+** itself grows too big and must be split.  The format of interior
+** nodes:
+**
+** varint iHeight;	     (height from leaf level, always >0)
+** varint iBlockid;	     (block id of node's leftmost subtree)
+** optional {
+**   varint nTerm;	     (length of first term)
+**   char pTerm[nTerm];      (content of first term)
+**   array {
+**				  (further terms are delta-encoded)
+**     varint nPrefix;		  (length of shared prefix with previous term)
+**     varint nSuffix;		  (length of unshared suffix)
+**     char pTermSuffix[nSuffix]; (unshared suffix of next term)
+**   }
+** }
+**
+** Here, optional { X } means an optional element, while array { X }
+** means zero or more occurrences of X, adjacent in memory.
+**
+** An interior node encodes n terms separating n+1 subtrees.  The
+** subtree blocks are contiguous, so only the first subtree's blockid
+** is encoded.	The subtree at iBlockid will contain all terms less
+** than the first term encoded (or all terms if no term is encoded).
+** Otherwise, for terms greater than or equal to pTerm[i] but less
+** than pTerm[i+1], the subtree for that term will be rooted at
+** iBlockid+i.	Interior nodes only store enough term data to
+** distinguish adjacent children (if the rightmost term of the left
+** child is "something", and the leftmost term of the right child is
+** "wicked", only "w" is stored).
+**
+** New data is spilled to a new interior node at the same height when
+** the current node exceeds INTERIOR_MAX bytes (default 2048).
+** INTERIOR_MIN_TERMS (default 7) keeps large terms from monopolizing
+** interior nodes and making the tree too skinny.  The interior nodes
+** at a given height are naturally tracked by interior nodes at
+** height+1, and so on.
+**
+**
+**** Segment directory ****
+** The segment directory in table %_segdir stores meta-information for
+** merging and deleting segments, and also the root node of the
+** segment's tree.
+**
+** The root node is the top node of the segment's tree after encoding
+** the entire segment, restricted to ROOT_MAX bytes (default 1024).
+** This could be either a leaf node or an interior node.  If the top
+** node requires more than ROOT_MAX bytes, it is flushed to %_segments
+** and a new root interior node is generated (which should always fit
+** within ROOT_MAX because it only needs space for 2 varints, the
+** height and the blockid of the previous root).
+**
+** The meta-information in the segment directory is:
+**   level		 - segment level (see below)
+**   idx		 - index within level
+**			 - (level,idx uniquely identify a segment)
+**   start_block	 - first leaf node
+**   leaves_end_block	 - last leaf node
+**   end_block		 - last block (including interior nodes)
+**   root		 - contents of root node
+**
+** If the root node is a leaf node, then start_block,
+** leaves_end_block, and end_block are all 0.
+**
+**
+**** Segment merging ****
+** To amortize update costs, segments are groups into levels and
+** merged in matches.  Each increase in level represents exponentially
+** more documents.
+**
+** New documents (actually, document updates) are tokenized and
+** written individually (using LeafWriter) to a level 0 segment, with
+** incrementing idx.  When idx reaches MERGE_COUNT (default 16), all
+** level 0 segments are merged into a single level 1 segment.  Level 1
+** is populated like level 0, and eventually MERGE_COUNT level 1
+** segments are merged to a single level 2 segment (representing
+** MERGE_COUNT^2 updates), and so on.
+**
+** A segment merge traverses all segments at a given level in
+** parallel, performing a straightforward sorted merge.  Since segment
+** leaf nodes are written in to the %_segments table in order, this
+** merge traverses the underlying sqlite disk structures efficiently.
+** After the merge, all segment blocks from the merged level are
+** deleted.
+**
+** MERGE_COUNT controls how often we merge segments.  16 seems to be
+** somewhat of a sweet spot for insertion performance.	32 and 64 show
+** very similar performance numbers to 16 on insertion, though they're
+** a tiny bit slower (perhaps due to more overhead in merge-time
+** sorting).  8 is about 20% slower than 16, 4 about 50% slower than
+** 16, 2 about 66% slower than 16.
+**
+** At query time, high MERGE_COUNT increases the number of segments
+** which need to be scanned and merged.  For instance, with 100k docs
+** inserted:
+**
+**    MERGE_COUNT   segments
+**	 16	      25
+**	  8	      12
+**	  4	      10
+**	  2	       6
+**
+** This appears to have only a moderate impact on queries for very
+** frequent terms (which are somewhat dominated by segment merge
+** costs), and infrequent and non-existent terms still seem to be fast
+** even with many segments.
+**
+** TODO(shess) That said, it would be nice to have a better query-side
+** argument for MERGE_COUNT of 16.  Also, it is possible/likely that
+** optimizations to things like doclist merging will swing the sweet
+** spot around.
+**
+**
+**
+**** Handling of deletions and updates ****
+** Since we're using a segmented structure, with no docid-oriented
+** index into the term index, we clearly cannot simply update the term
+** index when a document is deleted or updated.  For deletions, we
+** write an empty doclist (varint(docid) varint(POS_END)), for updates
+** we simply write the new doclist.  Segment merges overwrite older
+** data for a particular docid with newer data, so deletes or updates
+** will eventually overtake the earlier data and knock it out.	The
+** query logic likewise merges doclists so that newer data knocks out
+** older data.
+**
+** TODO(shess) Provide a VACUUM type operation to clear out all
+** deletions and duplications.	This would basically be a forced merge
+** into a single segment.
+*/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <sqlite3ext.h>
+
+#include <libtracker-common/tracker-config.h>
+#include <libtracker-common/tracker-language.h>
+#include <libtracker-common/tracker-parser.h>
+
+
+#include "tracker-fts.h"
+#include "tracker-fts-hash.h"
+
+SQLITE_EXTENSION_INIT1
+
+
+
+/* TODO(shess) MAN, this thing needs some refactoring.	At minimum, it
+** would be nice to order the file better, perhaps something along the
+** lines of:
+**
+**  - utility functions
+**  - table setup functions
+**  - table update functions
+**  - table query functions
+**
+** Put the query functions last because they're likely to reference
+** typedefs or functions from the table update section.
+*/
+
+#if 0
+# define FTSTRACE(A)  printf A; fflush(stdout)
+#else
+# define FTSTRACE(A)
+#endif
+
+/*
+** Default span for NEAR operators.
+*/
+#define SQLITE_FTS3_DEFAULT_NEAR_PARAM 10
+
+/* It is not safe to call isspace(), tolower(), or isalnum() on
+** hi-bit-set characters.  This is the same solution used in the
+** tokenizer.
+*/
+/* TODO(shess) The snippet-generation code should be using the
+** tokenizer-generated tokens rather than doing its own local
+** tokenization.
+*/
+/* TODO(shess) Is __isascii() a portable version of (c&0x80)==0? */
+static int safe_isspace(char c){
+  return (c&0x80)==0 ? isspace(c) : 0;
+}
+static int safe_tolower(char c){
+  return (c&0x80)==0 ? tolower(c) : c;
+}
+static int safe_isalnum(char c){
+  return (c&0x80)==0 ? isalnum(c) : 0;
+}
+
+int sqlite3_extension_init(
+  sqlite3 *db,
+  char **pzErrMsg,
+  const sqlite3_api_routines *pApi
+);
+
+
+typedef enum DocListType {
+  DL_DOCIDS,		  /* docids only */
+  DL_POSITIONS,		  /* docids + positions */
+  DL_POSITIONS_OFFSETS	  /* docids + positions + offsets */
+} DocListType;
+
+/*
+** By default, only positions and not offsets are stored in the doclists.
+** To change this so that offsets are stored too, compile with
+**
+**	    -DDL_DEFAULT=DL_POSITIONS_OFFSETS
+**
+** If DL_DEFAULT is set to DL_DOCIDS, your table can only be inserted
+** into (no deletes or updates).
+*/
+#ifndef DL_DEFAULT
+# define DL_DEFAULT DL_POSITIONS
+#endif
+
+enum {
+  POS_END = 0,	      /* end of this position list */
+  POS_COLUMN,	      /* followed by new column number */
+  POS_BASE
+};
+
+/* MERGE_COUNT controls how often we merge segments (see comment at
+** top of file).
+*/
+#define MERGE_COUNT 16
+
+/* utility functions */
+
+/* CLEAR() and SCRAMBLE() abstract memset() on a pointer to a single
+** record to prevent errors of the form:
+**
+** my_function(SomeType *b){
+**   memset(b, '\0', sizeof(b));  // sizeof(b)!=sizeof(*b)
+** }
+*/
+/* TODO(shess) Obvious candidates for a header file. */
+#define CLEAR(b) memset(b, '\0', sizeof(*(b)))
+
+#ifndef NDEBUG
+#  define SCRAMBLE(b) memset(b, 0x55, sizeof(*(b)))
+#else
+#  define SCRAMBLE(b)
+#endif
+
+/* We may need up to VARINT_MAX bytes to store an encoded 64-bit integer. */
+#define VARINT_MAX 10
+
+/* Write a 64-bit variable-length integer to memory starting at p[0].
+ * The length of data written will be between 1 and VARINT_MAX bytes.
+ * The number of bytes written is returned. */
+static int fts3PutVarint(char *p, sqlite_int64 v){
+  unsigned char *q = (unsigned char *) p;
+  sqlite_uint64 vu = v;
+  do{
+    *q++ = (unsigned char) ((vu & 0x7f) | 0x80);
+    vu >>= 7;
+  }while( vu!=0 );
+  q[-1] &= 0x7f;  /* turn off high bit in final byte */
+  assert( q - (unsigned char *)p <= VARINT_MAX );
+  return (int) (q - (unsigned char *)p);
+}
+
+/* Read a 64-bit variable-length integer from memory starting at p[0].
+ * Return the number of bytes read, or 0 on error.
+ * The value is stored in *v. */
+static int fts3GetVarint(const char *p, sqlite_int64 *v){
+  const unsigned char *q = (const unsigned char *) p;
+  sqlite_uint64 x = 0, y = 1;
+  while( (*q & 0x80) == 0x80 ){
+    x += y * (*q++ & 0x7f);
+    y <<= 7;
+    if( q - (unsigned char *)p >= VARINT_MAX ){  /* bad data */
+      assert( 0 );
+      return 0;
+    }
+  }
+  x += y * (*q++);
+  *v = (sqlite_int64) x;
+  return (int) (q - (unsigned char *)p);
+}
+
+static int fts3GetVarint32(const char *p, int *pi){
+ sqlite_int64 i;
+ int ret = fts3GetVarint(p, &i);
+ *pi = (int) i;
+ assert( *pi==i );
+ return ret;
+}
+
+/*******************************************************************/
+/* DataBuffer is used to collect data into a buffer in piecemeal
+** fashion.  It implements the usual distinction between amount of
+** data currently stored (nData) and buffer capacity (nCapacity).
+**
+** dataBufferInit - create a buffer with given initial capacity.
+** dataBufferReset - forget buffer's data, retaining capacity.
+** dataBufferDestroy - free buffer's data.
+** dataBufferSwap - swap contents of two buffers.
+** dataBufferExpand - expand capacity without adding data.
+** dataBufferAppend - append data.
+** dataBufferAppend2 - append two pieces of data at once.
+** dataBufferReplace - replace buffer's data.
+*/
+typedef struct DataBuffer {
+  char *pData;		/* Pointer to malloc'ed buffer. */
+  int nCapacity;	/* Size of pData buffer. */
+  int nData;		/* End of data loaded into pData. */
+} DataBuffer;
+
+static void dataBufferInit(DataBuffer *pBuffer, int nCapacity){
+  assert( nCapacity>=0 );
+  pBuffer->nData = 0;
+  pBuffer->nCapacity = nCapacity;
+  pBuffer->pData = nCapacity==0 ? NULL : sqlite3_malloc(nCapacity);
+}
+static void dataBufferReset(DataBuffer *pBuffer){
+  pBuffer->nData = 0;
+}
+static void dataBufferDestroy(DataBuffer *pBuffer){
+  if( pBuffer->pData!=NULL ) sqlite3_free(pBuffer->pData);
+  SCRAMBLE(pBuffer);
+}
+static void dataBufferSwap(DataBuffer *pBuffer1, DataBuffer *pBuffer2){
+  DataBuffer tmp = *pBuffer1;
+  *pBuffer1 = *pBuffer2;
+  *pBuffer2 = tmp;
+}
+static void dataBufferExpand(DataBuffer *pBuffer, int nAddCapacity){
+  assert( nAddCapacity>0 );
+  /* TODO(shess) Consider expanding more aggressively.	Note that the
+  ** underlying malloc implementation may take care of such things for
+  ** us already.
+  */
+  if( pBuffer->nData+nAddCapacity>pBuffer->nCapacity ){
+    pBuffer->nCapacity = pBuffer->nData+nAddCapacity;
+    pBuffer->pData = sqlite3_realloc(pBuffer->pData, pBuffer->nCapacity);
+  }
+}
+static void dataBufferAppend(DataBuffer *pBuffer,
+			     const char *pSource, int nSource){
+  assert( nSource>0 && pSource!=NULL );
+  dataBufferExpand(pBuffer, nSource);
+  memcpy(pBuffer->pData+pBuffer->nData, pSource, nSource);
+  pBuffer->nData += nSource;
+}
+static void dataBufferAppend2(DataBuffer *pBuffer,
+			      const char *pSource1, int nSource1,
+			      const char *pSource2, int nSource2){
+  assert( nSource1>0 && pSource1!=NULL );
+  assert( nSource2>0 && pSource2!=NULL );
+  dataBufferExpand(pBuffer, nSource1+nSource2);
+  memcpy(pBuffer->pData+pBuffer->nData, pSource1, nSource1);
+  memcpy(pBuffer->pData+pBuffer->nData+nSource1, pSource2, nSource2);
+  pBuffer->nData += nSource1+nSource2;
+}
+static void dataBufferReplace(DataBuffer *pBuffer,
+			      const char *pSource, int nSource){
+  dataBufferReset(pBuffer);
+  dataBufferAppend(pBuffer, pSource, nSource);
+}
+
+/* StringBuffer is a null-terminated version of DataBuffer. */
+typedef struct StringBuffer {
+  DataBuffer b;		   /* Includes null terminator. */
+} StringBuffer;
+
+static void initStringBuffer(StringBuffer *sb){
+  dataBufferInit(&sb->b, 100);
+  dataBufferReplace(&sb->b, "", 1);
+}
+static int stringBufferLength(StringBuffer *sb){
+  return sb->b.nData-1;
+}
+static char *stringBufferData(StringBuffer *sb){
+  return sb->b.pData;
+}
+static void stringBufferDestroy(StringBuffer *sb){
+  dataBufferDestroy(&sb->b);
+}
+
+static void nappend(StringBuffer *sb, const char *zFrom, int nFrom){
+  assert( sb->b.nData>0 );
+  if( nFrom>0 ){
+    sb->b.nData--;
+    dataBufferAppend2(&sb->b, zFrom, nFrom, "", 1);
+  }
+}
+static void append(StringBuffer *sb, const char *zFrom){
+  nappend(sb, zFrom, strlen(zFrom));
+}
+
+/* Append a list of strings separated by commas. */
+static void appendList(StringBuffer *sb, int nString, char **azString){
+  int i;
+  for(i=0; i<nString; ++i){
+    if( i>0 ) append(sb, ", ");
+    append(sb, azString[i]);
+  }
+}
+
+static int endsInWhiteSpace(StringBuffer *p){
+  return stringBufferLength(p)>0 &&
+    safe_isspace(stringBufferData(p)[stringBufferLength(p)-1]);
+}
+
+/* If the StringBuffer ends in something other than white space, add a
+** single space character to the end.
+*/
+static void appendWhiteSpace(StringBuffer *p){
+  if( stringBufferLength(p)==0 ) return;
+  if( !endsInWhiteSpace(p) ) append(p, " ");
+}
+
+/* Remove white space from the end of the StringBuffer */
+static void trimWhiteSpace(StringBuffer *p){
+  while( endsInWhiteSpace(p) ){
+    p->b.pData[--p->b.nData-1] = '\0';
+  }
+}
+
+/*******************************************************************/
+/* DLReader is used to read document elements from a doclist.  The
+** current docid is cached, so dlrDocid() is fast.  DLReader does not
+** own the doclist buffer.
+**
+** dlrAtEnd - true if there's no more data to read.
+** dlrDocid - docid of current document.
+** dlrDocData - doclist data for current document (including docid).
+** dlrDocDataBytes - length of same.
+** dlrAllDataBytes - length of all remaining data.
+** dlrPosData - position data for current document.
+** dlrPosDataLen - length of pos data for current document (incl POS_END).
+** dlrStep - step to current document.
+** dlrInit - initial for doclist of given type against given data.
+** dlrDestroy - clean up.
+**
+** Expected usage is something like:
+**
+**   DLReader reader;
+**   dlrInit(&reader, pData, nData);
+**   while( !dlrAtEnd(&reader) ){
+**     // calls to dlrDocid() and kin.
+**     dlrStep(&reader);
+**   }
+**   dlrDestroy(&reader);
+*/
+typedef struct DLReader {
+  DocListType iType;
+  const char *pData;
+  int nData;
+
+  sqlite_int64 iDocid;
+  int nElement;
+} DLReader;
+
+static int dlrAtEnd(DLReader *pReader){
+  assert( pReader->nData>=0 );
+  return pReader->nData==0;
+}
+static sqlite_int64 dlrDocid(DLReader *pReader){
+  assert( !dlrAtEnd(pReader) );
+  return pReader->iDocid;
+}
+static const char *dlrDocData(DLReader *pReader){
+  assert( !dlrAtEnd(pReader) );
+  return pReader->pData;
+}
+static int dlrDocDataBytes(DLReader *pReader){
+  assert( !dlrAtEnd(pReader) );
+  return pReader->nElement;
+}
+static int dlrAllDataBytes(DLReader *pReader){
+  assert( !dlrAtEnd(pReader) );
+  return pReader->nData;
+}
+/* TODO(shess) Consider adding a field to track iDocid varint length
+** to make these two functions faster.	This might matter (a tiny bit)
+** for queries.
+*/
+static const char *dlrPosData(DLReader *pReader){
+  sqlite_int64 iDummy;
+  int n = fts3GetVarint(pReader->pData, &iDummy);
+  assert( !dlrAtEnd(pReader) );
+  return pReader->pData+n;
+}
+static int dlrPosDataLen(DLReader *pReader){
+  sqlite_int64 iDummy;
+  int n = fts3GetVarint(pReader->pData, &iDummy);
+  assert( !dlrAtEnd(pReader) );
+  return pReader->nElement-n;
+}
+static void dlrStep(DLReader *pReader){
+  assert( !dlrAtEnd(pReader) );
+
+  /* Skip past current doclist element. */
+  assert( pReader->nElement<=pReader->nData );
+  pReader->pData += pReader->nElement;
+  pReader->nData -= pReader->nElement;
+
+  /* If there is more data, read the next doclist element. */
+  if( pReader->nData!=0 ){
+    sqlite_int64 iDocidDelta;
+    int iDummy, n = fts3GetVarint(pReader->pData, &iDocidDelta);
+    pReader->iDocid += iDocidDelta;
+    if( pReader->iType>=DL_POSITIONS ){
+      assert( n<pReader->nData );
+      while( 1 ){
+	n += fts3GetVarint32(pReader->pData+n, &iDummy);
+	assert( n<=pReader->nData );
+	if( iDummy==POS_END ) break;
+	if( iDummy==POS_COLUMN ){
+	  n += fts3GetVarint32(pReader->pData+n, &iDummy);
+	  assert( n<pReader->nData );
+	}else if( pReader->iType==DL_POSITIONS_OFFSETS ){
+	  n += fts3GetVarint32(pReader->pData+n, &iDummy);
+	  n += fts3GetVarint32(pReader->pData+n, &iDummy);
+	  assert( n<pReader->nData );
+	}
+      }
+    }
+    pReader->nElement = n;
+    assert( pReader->nElement<=pReader->nData );
+  }
+}
+static void dlrInit(DLReader *pReader, DocListType iType,
+		    const char *pData, int nData){
+  assert( pData!=NULL && nData!=0 );
+  pReader->iType = iType;
+  pReader->pData = pData;
+  pReader->nData = nData;
+  pReader->nElement = 0;
+  pReader->iDocid = 0;
+
+  /* Load the first element's data.  There must be a first element. */
+  dlrStep(pReader);
+}
+static void dlrDestroy(DLReader *pReader){
+  SCRAMBLE(pReader);
+}
+
+#ifndef NDEBUG
+/* Verify that the doclist can be validly decoded.  Also returns the
+** last docid found because it is convenient in other assertions for
+** DLWriter.
+*/
+static void docListValidate(DocListType iType, const char *pData, int nData,
+			    sqlite_int64 *pLastDocid){
+  sqlite_int64 iPrevDocid = 0;
+  assert( nData>0 );
+  assert( pData!=0 );
+  assert( pData+nData>pData );
+  while( nData!=0 ){
+    sqlite_int64 iDocidDelta;
+    int n = fts3GetVarint(pData, &iDocidDelta);
+    iPrevDocid += iDocidDelta;
+    if( iType>DL_DOCIDS ){
+      int iDummy;
+      while( 1 ){
+	n += fts3GetVarint32(pData+n, &iDummy);
+	if( iDummy==POS_END ) break;
+	if( iDummy==POS_COLUMN ){
+	  n += fts3GetVarint32(pData+n, &iDummy);
+	}else if( iType>DL_POSITIONS ){
+	  n += fts3GetVarint32(pData+n, &iDummy);
+	  n += fts3GetVarint32(pData+n, &iDummy);
+	}
+	assert( n<=nData );
+      }
+    }
+    assert( n<=nData );
+    pData += n;
+    nData -= n;
+  }
+  if( pLastDocid ) *pLastDocid = iPrevDocid;
+}
+#define ASSERT_VALID_DOCLIST(i, p, n, o) docListValidate(i, p, n, o)
+#else
+#define ASSERT_VALID_DOCLIST(i, p, n, o) assert( 1 )
+#endif
+
+/*******************************************************************/
+/* DLWriter is used to write doclist data to a DataBuffer.  DLWriter
+** always appends to the buffer and does not own it.
+**
+** dlwInit - initialize to write a given type doclistto a buffer.
+** dlwDestroy - clear the writer's memory.  Does not free buffer.
+** dlwAppend - append raw doclist data to buffer.
+** dlwCopy - copy next doclist from reader to writer.
+** dlwAdd - construct doclist element and append to buffer.
+**    Only apply dlwAdd() to DL_DOCIDS doclists (else use PLWriter).
+*/
+typedef struct DLWriter {
+  DocListType iType;
+  DataBuffer *b;
+  sqlite_int64 iPrevDocid;
+#ifndef NDEBUG
+  int has_iPrevDocid;
+#endif
+} DLWriter;
+
+static void dlwInit(DLWriter *pWriter, DocListType iType, DataBuffer *b){
+  pWriter->b = b;
+  pWriter->iType = iType;
+  pWriter->iPrevDocid = 0;
+#ifndef NDEBUG
+  pWriter->has_iPrevDocid = 0;
+#endif
+}
+static void dlwDestroy(DLWriter *pWriter){
+  SCRAMBLE(pWriter);
+}
+/* iFirstDocid is the first docid in the doclist in pData.  It is
+** needed because pData may point within a larger doclist, in which
+** case the first item would be delta-encoded.
+**
+** iLastDocid is the final docid in the doclist in pData.  It is
+** needed to create the new iPrevDocid for future delta-encoding.  The
+** code could decode the passed doclist to recreate iLastDocid, but
+** the only current user (docListMerge) already has decoded this
+** information.
+*/
+/* TODO(shess) This has become just a helper for docListMerge.
+** Consider a refactor to make this cleaner.
+*/
+static void dlwAppend(DLWriter *pWriter,
+		      const char *pData, int nData,
+		      sqlite_int64 iFirstDocid, sqlite_int64 iLastDocid){
+  sqlite_int64 iDocid = 0;
+  char c[VARINT_MAX];
+  int nFirstOld, nFirstNew;	/* Old and new varint len of first docid. */
+#ifndef NDEBUG
+  sqlite_int64 iLastDocidDelta;
+#endif
+
+  /* Recode the initial docid as delta from iPrevDocid. */
+  nFirstOld = fts3GetVarint(pData, &iDocid);
+  assert( nFirstOld<nData || (nFirstOld==nData && pWriter->iType==DL_DOCIDS) );
+  nFirstNew = fts3PutVarint(c, iFirstDocid-pWriter->iPrevDocid);
+
+  /* Verify that the incoming doclist is valid AND that it ends with
+  ** the expected docid.  This is essential because we'll trust this
+  ** docid in future delta-encoding.
+  */
+  ASSERT_VALID_DOCLIST(pWriter->iType, pData, nData, &iLastDocidDelta);
+  assert( iLastDocid==iFirstDocid-iDocid+iLastDocidDelta );
+
+  /* Append recoded initial docid and everything else.	Rest of docids
+  ** should have been delta-encoded from previous initial docid.
+  */
+  if( nFirstOld<nData ){
+    dataBufferAppend2(pWriter->b, c, nFirstNew,
+		      pData+nFirstOld, nData-nFirstOld);
+  }else{
+    dataBufferAppend(pWriter->b, c, nFirstNew);
+  }
+  pWriter->iPrevDocid = iLastDocid;
+}
+static void dlwCopy(DLWriter *pWriter, DLReader *pReader){
+  dlwAppend(pWriter, dlrDocData(pReader), dlrDocDataBytes(pReader),
+	    dlrDocid(pReader), dlrDocid(pReader));
+}
+static void dlwAdd(DLWriter *pWriter, sqlite_int64 iDocid){
+  char c[VARINT_MAX];
+  int n = fts3PutVarint(c, iDocid-pWriter->iPrevDocid);
+
+  /* Docids must ascend. */
+  assert( !pWriter->has_iPrevDocid || iDocid>pWriter->iPrevDocid );
+  assert( pWriter->iType==DL_DOCIDS );
+
+  dataBufferAppend(pWriter->b, c, n);
+  pWriter->iPrevDocid = iDocid;
+#ifndef NDEBUG
+  pWriter->has_iPrevDocid = 1;
+#endif
+}
+
+/*******************************************************************/
+/* PLReader is used to read data from a document's position list.  As
+** the caller steps through the list, data is cached so that varints
+** only need to be decoded once.
+**
+** plrInit, plrDestroy - create/destroy a reader.
+** plrColumn, plrPosition, plrStartOffset, plrEndOffset - accessors
+** plrAtEnd - at end of stream, only call plrDestroy once true.
+** plrStep - step to the next element.
+*/
+typedef struct PLReader {
+  /* These refer to the next position's data.  nData will reach 0 when
+  ** reading the last position, so plrStep() signals EOF by setting
+  ** pData to NULL.
+  */
+  const char *pData;
+  int nData;
+
+  DocListType iType;
+  int iColumn;	       /* the last column read */
+  int iPosition;       /* the last position read */
+  int iStartOffset;    /* the last start offset read */
+  int iEndOffset;      /* the last end offset read */
+} PLReader;
+
+static int plrAtEnd(PLReader *pReader){
+  return pReader->pData==NULL;
+}
+static int plrColumn(PLReader *pReader){
+  assert( !plrAtEnd(pReader) );
+  return pReader->iColumn;
+}
+static int plrPosition(PLReader *pReader){
+  assert( !plrAtEnd(pReader) );
+  return pReader->iPosition;
+}
+static int plrStartOffset(PLReader *pReader){
+  assert( !plrAtEnd(pReader) );
+  return pReader->iStartOffset;
+}
+static int plrEndOffset(PLReader *pReader){
+  assert( !plrAtEnd(pReader) );
+  return pReader->iEndOffset;
+}
+static void plrStep(PLReader *pReader){
+  int i, n;
+
+  assert( !plrAtEnd(pReader) );
+
+  if( pReader->nData==0 ){
+    pReader->pData = NULL;
+    return;
+  }
+
+  n = fts3GetVarint32(pReader->pData, &i);
+  if( i==POS_COLUMN ){
+    n += fts3GetVarint32(pReader->pData+n, &pReader->iColumn);
+    pReader->iPosition = 0;
+    pReader->iStartOffset = 0;
+    n += fts3GetVarint32(pReader->pData+n, &i);
+  }
+  /* Should never see adjacent column changes. */
+  assert( i!=POS_COLUMN );
+
+  if( i==POS_END ){
+    pReader->nData = 0;
+    pReader->pData = NULL;
+    return;
+  }
+
+  pReader->iPosition += i-POS_BASE;
+  if( pReader->iType==DL_POSITIONS_OFFSETS ){
+    n += fts3GetVarint32(pReader->pData+n, &i);
+    pReader->iStartOffset += i;
+    n += fts3GetVarint32(pReader->pData+n, &i);
+    pReader->iEndOffset = pReader->iStartOffset+i;
+  }
+  assert( n<=pReader->nData );
+  pReader->pData += n;
+  pReader->nData -= n;
+}
+
+static void plrInit(PLReader *pReader, DLReader *pDLReader){
+  pReader->pData = dlrPosData(pDLReader);
+  pReader->nData = dlrPosDataLen(pDLReader);
+  pReader->iType = pDLReader->iType;
+  pReader->iColumn = 0;
+  pReader->iPosition = 0;
+  pReader->iStartOffset = 0;
+  pReader->iEndOffset = 0;
+  plrStep(pReader);
+}
+static void plrDestroy(PLReader *pReader){
+  SCRAMBLE(pReader);
+}
+
+/*******************************************************************/
+/* PLWriter is used in constructing a document's position list.  As a
+** convenience, if iType is DL_DOCIDS, PLWriter becomes a no-op.
+** PLWriter writes to the associated DLWriter's buffer.
+**
+** plwInit - init for writing a document's poslist.
+** plwDestroy - clear a writer.
+** plwAdd - append position and offset information.
+** plwCopy - copy next position's data from reader to writer.
+** plwTerminate - add any necessary doclist terminator.
+**
+** Calling plwAdd() after plwTerminate() may result in a corrupt
+** doclist.
+*/
+/* TODO(shess) Until we've written the second item, we can cache the
+** first item's information.  Then we'd have three states:
+**
+** - initialized with docid, no positions.
+** - docid and one position.
+** - docid and multiple positions.
+**
+** Only the last state needs to actually write to dlw->b, which would
+** be an improvement in the DLCollector case.
+*/
+typedef struct PLWriter {
+  DLWriter *dlw;
+
+  int iColumn;	  /* the last column written */
+  int iPos;	  /* the last position written */
+  int iOffset;	  /* the last start offset written */
+} PLWriter;
+
+/* TODO(shess) In the case where the parent is reading these values
+** from a PLReader, we could optimize to a copy if that PLReader has
+** the same type as pWriter.
+*/
+static void plwAdd(PLWriter *pWriter, int iColumn, int iPos,
+		   int iStartOffset, int iEndOffset){
+  /* Worst-case space for POS_COLUMN, iColumn, iPosDelta,
+  ** iStartOffsetDelta, and iEndOffsetDelta.
+  */
+  char c[5*VARINT_MAX];
+  int n = 0;
+
+  /* Ban plwAdd() after plwTerminate(). */
+  assert( pWriter->iPos!=-1 );
+
+  if( pWriter->dlw->iType==DL_DOCIDS ) return;
+
+  if( iColumn!=pWriter->iColumn ){
+    n += fts3PutVarint(c+n, POS_COLUMN);
+    n += fts3PutVarint(c+n, iColumn);
+    pWriter->iColumn = iColumn;
+    pWriter->iPos = 0;
+    pWriter->iOffset = 0;
+  }
+  assert( iPos>=pWriter->iPos );
+  n += fts3PutVarint(c+n, POS_BASE+(iPos-pWriter->iPos));
+  pWriter->iPos = iPos;
+  if( pWriter->dlw->iType==DL_POSITIONS_OFFSETS ){
+    assert( iStartOffset>=pWriter->iOffset );
+    n += fts3PutVarint(c+n, iStartOffset-pWriter->iOffset);
+    pWriter->iOffset = iStartOffset;
+    assert( iEndOffset>=iStartOffset );
+    n += fts3PutVarint(c+n, iEndOffset-iStartOffset);
+  }
+  dataBufferAppend(pWriter->dlw->b, c, n);
+}
+static void plwCopy(PLWriter *pWriter, PLReader *pReader){
+  plwAdd(pWriter, plrColumn(pReader), plrPosition(pReader),
+	 plrStartOffset(pReader), plrEndOffset(pReader));
+}
+static void plwInit(PLWriter *pWriter, DLWriter *dlw, sqlite_int64 iDocid){
+  char c[VARINT_MAX];
+  int n;
+
+  pWriter->dlw = dlw;
+
+  /* Docids must ascend. */
+  assert( !pWriter->dlw->has_iPrevDocid || iDocid>pWriter->dlw->iPrevDocid );
+  n = fts3PutVarint(c, iDocid-pWriter->dlw->iPrevDocid);
+  dataBufferAppend(pWriter->dlw->b, c, n);
+  pWriter->dlw->iPrevDocid = iDocid;
+#ifndef NDEBUG
+  pWriter->dlw->has_iPrevDocid = 1;
+#endif
+
+  pWriter->iColumn = 0;
+  pWriter->iPos = 0;
+  pWriter->iOffset = 0;
+}
+/* TODO(shess) Should plwDestroy() also terminate the doclist?	But
+** then plwDestroy() would no longer be just a destructor, it would
+** also be doing work, which isn't consistent with the overall idiom.
+** Another option would be for plwAdd() to always append any necessary
+** terminator, so that the output is always correct.  But that would
+** add incremental work to the common case with the only benefit being
+** API elegance.  Punt for now.
+*/
+static void plwTerminate(PLWriter *pWriter){
+  if( pWriter->dlw->iType>DL_DOCIDS ){
+    char c[VARINT_MAX];
+    int n = fts3PutVarint(c, POS_END);
+    dataBufferAppend(pWriter->dlw->b, c, n);
+  }
+#ifndef NDEBUG
+  /* Mark as terminated for assert in plwAdd(). */
+  pWriter->iPos = -1;
+#endif
+}
+static void plwDestroy(PLWriter *pWriter){
+  SCRAMBLE(pWriter);
+}
+
+/*******************************************************************/
+/* DLCollector wraps PLWriter and DLWriter to provide a
+** dynamically-allocated doclist area to use during tokenization.
+**
+** dlcNew - malloc up and initialize a collector.
+** dlcDelete - destroy a collector and all contained items.
+** dlcAddPos - append position and offset information.
+** dlcAddDoclist - add the collected doclist to the given buffer.
+** dlcNext - terminate the current document and open another.
+*/
+typedef struct DLCollector {
+  DataBuffer b;
+  DLWriter dlw;
+  PLWriter plw;
+} DLCollector;
+
+/* TODO(shess) This could also be done by calling plwTerminate() and
+** dataBufferAppend().	I tried that, expecting nominal performance
+** differences, but it seemed to pretty reliably be worth 1% to code
+** it this way.  I suspect it is the incremental malloc overhead (some
+** percentage of the plwTerminate() calls will cause a realloc), so
+** this might be worth revisiting if the DataBuffer implementation
+** changes.
+*/
+static void dlcAddDoclist(DLCollector *pCollector, DataBuffer *b){
+  if( pCollector->dlw.iType>DL_DOCIDS ){
+    char c[VARINT_MAX];
+    int n = fts3PutVarint(c, POS_END);
+    dataBufferAppend2(b, pCollector->b.pData, pCollector->b.nData, c, n);
+  }else{
+    dataBufferAppend(b, pCollector->b.pData, pCollector->b.nData);
+  }
+}
+static void dlcNext(DLCollector *pCollector, sqlite_int64 iDocid){
+  plwTerminate(&pCollector->plw);
+  plwDestroy(&pCollector->plw);
+  plwInit(&pCollector->plw, &pCollector->dlw, iDocid);
+}
+static void dlcAddPos(DLCollector *pCollector, int iColumn, int iPos,
+		      int iStartOffset, int iEndOffset){
+  plwAdd(&pCollector->plw, iColumn, iPos, iStartOffset, iEndOffset);
+}
+
+static DLCollector *dlcNew(sqlite_int64 iDocid, DocListType iType){
+  DLCollector *pCollector = sqlite3_malloc(sizeof(DLCollector));
+  dataBufferInit(&pCollector->b, 0);
+  dlwInit(&pCollector->dlw, iType, &pCollector->b);
+  plwInit(&pCollector->plw, &pCollector->dlw, iDocid);
+  return pCollector;
+}
+static void dlcDelete(DLCollector *pCollector){
+  plwDestroy(&pCollector->plw);
+  dlwDestroy(&pCollector->dlw);
+  dataBufferDestroy(&pCollector->b);
+  SCRAMBLE(pCollector);
+  sqlite3_free(pCollector);
+}
+
+
+/* Copy the doclist data of iType in pData/nData into *out, trimming
+** unnecessary data as we go.  Only columns matching iColumn are
+** copied, all columns copied if iColumn is -1.  Elements with no
+** matching columns are dropped.  The output is an iOutType doclist.
+*/
+/* NOTE(shess) This code is only valid after all doclists are merged.
+** If this is run before merges, then doclist items which represent
+** deletion will be trimmed, and will thus not effect a deletion
+** during the merge.
+*/
+static void docListTrim(DocListType iType, const char *pData, int nData,
+			int iColumn, DocListType iOutType, DataBuffer *out){
+  DLReader dlReader;
+  DLWriter dlWriter;
+
+  assert( iOutType<=iType );
+
+  dlrInit(&dlReader, iType, pData, nData);
+  dlwInit(&dlWriter, iOutType, out);
+
+  while( !dlrAtEnd(&dlReader) ){
+    PLReader plReader;
+    PLWriter plWriter;
+    int match = 0;
+
+    plrInit(&plReader, &dlReader);
+
+    while( !plrAtEnd(&plReader) ){
+      if( iColumn==-1 || plrColumn(&plReader)==iColumn ){
+	if( !match ){
+	  plwInit(&plWriter, &dlWriter, dlrDocid(&dlReader));
+	  match = 1;
+	}
+	plwAdd(&plWriter, plrColumn(&plReader), plrPosition(&plReader),
+	       plrStartOffset(&plReader), plrEndOffset(&plReader));
+      }
+      plrStep(&plReader);
+    }
+    if( match ){
+      plwTerminate(&plWriter);
+      plwDestroy(&plWriter);
+    }
+
+    plrDestroy(&plReader);
+    dlrStep(&dlReader);
+  }
+  dlwDestroy(&dlWriter);
+  dlrDestroy(&dlReader);
+}
+
+/* Used by docListMerge() to keep doclists in the ascending order by
+** docid, then ascending order by age (so the newest comes first).
+*/
+typedef struct OrderedDLReader {
+  DLReader *pReader;
+
+  /* TODO(shess) If we assume that docListMerge pReaders is ordered by
+  ** age (which we do), then we could use pReader comparisons to break
+  ** ties.
+  */
+  int idx;
+} OrderedDLReader;
+
+/* Order eof to end, then by docid asc, idx desc. */
+static int orderedDLReaderCmp(OrderedDLReader *r1, OrderedDLReader *r2){
+  if( dlrAtEnd(r1->pReader) ){
+    if( dlrAtEnd(r2->pReader) ) return 0;  /* Both atEnd(). */
+    return 1;				   /* Only r1 atEnd(). */
+  }
+  if( dlrAtEnd(r2->pReader) ) return -1;   /* Only r2 atEnd(). */
+
+  if( dlrDocid(r1->pReader)<dlrDocid(r2->pReader) ) return -1;
+  if( dlrDocid(r1->pReader)>dlrDocid(r2->pReader) ) return 1;
+
+  /* Descending on idx. */
+  return r2->idx-r1->idx;
+}
+
+/* Bubble p[0] to appropriate place in p[1..n-1].  Assumes that
+** p[1..n-1] is already sorted.
+*/
+/* TODO(shess) Is this frequent enough to warrant a binary search?
+** Before implementing that, instrument the code to check.  In most
+** current usage, I expect that p[0] will be less than p[1] a very
+** high proportion of the time.
+*/
+static void orderedDLReaderReorder(OrderedDLReader *p, int n){
+  while( n>1 && orderedDLReaderCmp(p, p+1)>0 ){
+    OrderedDLReader tmp = p[0];
+    p[0] = p[1];
+    p[1] = tmp;
+    n--;
+    p++;
+  }
+}
+
+/* Given an array of doclist readers, merge their doclist elements
+** into out in sorted order (by docid), dropping elements from older
+** readers when there is a duplicate docid.  pReaders is assumed to be
+** ordered by age, oldest first.
+*/
+/* TODO(shess) nReaders must be <= MERGE_COUNT.  This should probably
+** be fixed.
+*/
+static void docListMerge(DataBuffer *out,
+			 DLReader *pReaders, int nReaders){
+  OrderedDLReader readers[MERGE_COUNT];
+  DLWriter writer;
+  int i, n;
+  const char *pStart = 0;
+  int nStart = 0;
+  sqlite_int64 iFirstDocid = 0, iLastDocid = 0;
+
+  assert( nReaders>0 );
+  if( nReaders==1 ){
+    dataBufferAppend(out, dlrDocData(pReaders), dlrAllDataBytes(pReaders));
+    return;
+  }
+
+  assert( nReaders<=MERGE_COUNT );
+  n = 0;
+  for(i=0; i<nReaders; i++){
+    assert( pReaders[i].iType==pReaders[0].iType );
+    readers[i].pReader = pReaders+i;
+    readers[i].idx = i;
+    n += dlrAllDataBytes(&pReaders[i]);
+  }
+  /* Conservatively size output to sum of inputs.  Output should end
+  ** up strictly smaller than input.
+  */
+  dataBufferExpand(out, n);
+
+  /* Get the readers into sorted order. */
+  while( i-->0 ){
+    orderedDLReaderReorder(readers+i, nReaders-i);
+  }
+
+  dlwInit(&writer, pReaders[0].iType, out);
+  while( !dlrAtEnd(readers[0].pReader) ){
+    sqlite_int64 iDocid = dlrDocid(readers[0].pReader);
+
+    /* If this is a continuation of the current buffer to copy, extend
+    ** that buffer.  memcpy() seems to be more efficient if it has a
+    ** lots of data to copy.
+    */
+    if( dlrDocData(readers[0].pReader)==pStart+nStart ){
+      nStart += dlrDocDataBytes(readers[0].pReader);
+    }else{
+      if( pStart!=0 ){
+	dlwAppend(&writer, pStart, nStart, iFirstDocid, iLastDocid);
+      }
+      pStart = dlrDocData(readers[0].pReader);
+      nStart = dlrDocDataBytes(readers[0].pReader);
+      iFirstDocid = iDocid;
+    }
+    iLastDocid = iDocid;
+    dlrStep(readers[0].pReader);
+
+    /* Drop all of the older elements with the same docid. */
+    for(i=1; i<nReaders &&
+	     !dlrAtEnd(readers[i].pReader) &&
+	     dlrDocid(readers[i].pReader)==iDocid; i++){
+      dlrStep(readers[i].pReader);
+    }
+
+    /* Get the readers back into order. */
+    while( i-->0 ){
+      orderedDLReaderReorder(readers+i, nReaders-i);
+    }
+  }
+
+  /* Copy over any remaining elements. */
+  if( nStart>0 ) dlwAppend(&writer, pStart, nStart, iFirstDocid, iLastDocid);
+  dlwDestroy(&writer);
+}
+
+/* Helper function for posListUnion().	Compares the current position
+** between left and right, returning as standard C idiom of <0 if
+** left<right, >0 if left>right, and 0 if left==right.	"End" always
+** compares greater.
+*/
+static int posListCmp(PLReader *pLeft, PLReader *pRight){
+  assert( pLeft->iType==pRight->iType );
+  if( pLeft->iType==DL_DOCIDS ) return 0;
+
+  if( plrAtEnd(pLeft) ) return plrAtEnd(pRight) ? 0 : 1;
+  if( plrAtEnd(pRight) ) return -1;
+
+  if( plrColumn(pLeft)<plrColumn(pRight) ) return -1;
+  if( plrColumn(pLeft)>plrColumn(pRight) ) return 1;
+
+  if( plrPosition(pLeft)<plrPosition(pRight) ) return -1;
+  if( plrPosition(pLeft)>plrPosition(pRight) ) return 1;
+  if( pLeft->iType==DL_POSITIONS ) return 0;
+
+  if( plrStartOffset(pLeft)<plrStartOffset(pRight) ) return -1;
+  if( plrStartOffset(pLeft)>plrStartOffset(pRight) ) return 1;
+
+  if( plrEndOffset(pLeft)<plrEndOffset(pRight) ) return -1;
+  if( plrEndOffset(pLeft)>plrEndOffset(pRight) ) return 1;
+
+  return 0;
+}
+
+/* Write the union of position lists in pLeft and pRight to pOut.
+** "Union" in this case meaning "All unique position tuples".  Should
+** work with any doclist type, though both inputs and the output
+** should be the same type.
+*/
+static void posListUnion(DLReader *pLeft, DLReader *pRight, DLWriter *pOut){
+  PLReader left, right;
+  PLWriter writer;
+
+  assert( dlrDocid(pLeft)==dlrDocid(pRight) );
+  assert( pLeft->iType==pRight->iType );
+  assert( pLeft->iType==pOut->iType );
+
+  plrInit(&left, pLeft);
+  plrInit(&right, pRight);
+  plwInit(&writer, pOut, dlrDocid(pLeft));
+
+  while( !plrAtEnd(&left) || !plrAtEnd(&right) ){
+    int c = posListCmp(&left, &right);
+    if( c<0 ){
+      plwCopy(&writer, &left);
+      plrStep(&left);
+    }else if( c>0 ){
+      plwCopy(&writer, &right);
+      plrStep(&right);
+    }else{
+      plwCopy(&writer, &left);
+      plrStep(&left);
+      plrStep(&right);
+    }
+  }
+
+  plwTerminate(&writer);
+  plwDestroy(&writer);
+  plrDestroy(&left);
+  plrDestroy(&right);
+}
+
+/* Write the union of doclists in pLeft and pRight to pOut.  For
+** docids in common between the inputs, the union of the position
+** lists is written.  Inputs and outputs are always type DL_DEFAULT.
+*/
+static void docListUnion(
+  const char *pLeft, int nLeft,
+  const char *pRight, int nRight,
+  DataBuffer *pOut	/* Write the combined doclist here */
+){
+  DLReader left, right;
+  DLWriter writer;
+
+  if( nLeft==0 ){
+    if( nRight!=0) dataBufferAppend(pOut, pRight, nRight);
+    return;
+  }
+  if( nRight==0 ){
+    dataBufferAppend(pOut, pLeft, nLeft);
+    return;
+  }
+
+  dlrInit(&left, DL_DEFAULT, pLeft, nLeft);
+  dlrInit(&right, DL_DEFAULT, pRight, nRight);
+  dlwInit(&writer, DL_DEFAULT, pOut);
+
+  while( !dlrAtEnd(&left) || !dlrAtEnd(&right) ){
+    if( dlrAtEnd(&right) ){
+      dlwCopy(&writer, &left);
+      dlrStep(&left);
+    }else if( dlrAtEnd(&left) ){
+      dlwCopy(&writer, &right);
+      dlrStep(&right);
+    }else if( dlrDocid(&left)<dlrDocid(&right) ){
+      dlwCopy(&writer, &left);
+      dlrStep(&left);
+    }else if( dlrDocid(&left)>dlrDocid(&right) ){
+      dlwCopy(&writer, &right);
+      dlrStep(&right);
+    }else{
+      posListUnion(&left, &right, &writer);
+      dlrStep(&left);
+      dlrStep(&right);
+    }
+  }
+
+  dlrDestroy(&left);
+  dlrDestroy(&right);
+  dlwDestroy(&writer);
+}
+
+/*
+** This function is used as part of the implementation of phrase and
+** NEAR matching.
+**
+** pLeft and pRight are DLReaders positioned to the same docid in
+** lists of type DL_POSITION. This function writes an entry to the
+** DLWriter pOut for each position in pRight that is less than
+** (nNear+1) greater (but not equal to or smaller) than a position
+** in pLeft. For example, if nNear is 0, and the positions contained
+** by pLeft and pRight are:
+**
+**    pLeft:  5 10 15 20
+**    pRight: 6  9 17 21
+**
+** then the docid is added to pOut. If pOut is of type DL_POSITIONS,
+** then a positionids "6" and "21" are also added to pOut.
+**
+** If boolean argument isSaveLeft is true, then positionids are copied
+** from pLeft instead of pRight. In the example above, the positions "5"
+** and "20" would be added instead of "6" and "21".
+*/
+static void posListPhraseMerge(
+  DLReader *pLeft,
+  DLReader *pRight,
+  int nNear,
+  int isSaveLeft,
+  DLWriter *pOut
+){
+  PLReader left, right;
+  PLWriter writer;
+  int match = 0;
+
+  assert( dlrDocid(pLeft)==dlrDocid(pRight) );
+  assert( pOut->iType!=DL_POSITIONS_OFFSETS );
+
+  plrInit(&left, pLeft);
+  plrInit(&right, pRight);
+
+  while( !plrAtEnd(&left) && !plrAtEnd(&right) ){
+    if( plrColumn(&left)<plrColumn(&right) ){
+      plrStep(&left);
+    }else if( plrColumn(&left)>plrColumn(&right) ){
+      plrStep(&right);
+    }else if( plrPosition(&left)>=plrPosition(&right) ){
+      plrStep(&right);
+    }else{
+      if( (plrPosition(&right)-plrPosition(&left))<=(nNear+1) ){
+	if( !match ){
+	  plwInit(&writer, pOut, dlrDocid(pLeft));
+	  match = 1;
+	}
+	if( !isSaveLeft ){
+	  plwAdd(&writer, plrColumn(&right), plrPosition(&right), 0, 0);
+	}else{
+	  plwAdd(&writer, plrColumn(&left), plrPosition(&left), 0, 0);
+	}
+	plrStep(&right);
+      }else{
+	plrStep(&left);
+      }
+    }
+  }
+
+  if( match ){
+    plwTerminate(&writer);
+    plwDestroy(&writer);
+  }
+
+  plrDestroy(&left);
+  plrDestroy(&right);
+}
+
+/*
+** Compare the values pointed to by the PLReaders passed as arguments.
+** Return -1 if the value pointed to by pLeft is considered less than
+** the value pointed to by pRight, +1 if it is considered greater
+** than it, or 0 if it is equal. i.e.
+**
+**     (*pLeft - *pRight)
+**
+** A PLReader that is in the EOF condition is considered greater than
+** any other. If neither argument is in EOF state, the return value of
+** plrColumn() is used. If the plrColumn() values are equal, the
+** comparison is on the basis of plrPosition().
+*/
+static int plrCompare(PLReader *pLeft, PLReader *pRight){
+  assert(!plrAtEnd(pLeft) || !plrAtEnd(pRight));
+
+  if( plrAtEnd(pRight) || plrAtEnd(pLeft) ){
+    return (plrAtEnd(pRight) ? -1 : 1);
+  }
+  if( plrColumn(pLeft)!=plrColumn(pRight) ){
+    return ((plrColumn(pLeft)<plrColumn(pRight)) ? -1 : 1);
+  }
+  if( plrPosition(pLeft)!=plrPosition(pRight) ){
+    return ((plrPosition(pLeft)<plrPosition(pRight)) ? -1 : 1);
+  }
+  return 0;
+}
+
+/* We have two doclists with positions:  pLeft and pRight. Depending
+** on the value of the nNear parameter, perform either a phrase
+** intersection (if nNear==0) or a NEAR intersection (if nNear>0)
+** and write the results into pOut.
+**
+** A phrase intersection means that two documents only match
+** if pLeft.iPos+1==pRight.iPos.
+**
+** A NEAR intersection means that two documents only match if
+** (abs(pLeft.iPos-pRight.iPos)<nNear).
+**
+** If a NEAR intersection is requested, then the nPhrase argument should
+** be passed the number of tokens in the two operands to the NEAR operator
+** combined. For example:
+**
+**	 Query syntax		    nPhrase
+**	------------------------------------
+**	 "A B C" NEAR "D E"	    5
+**	 A NEAR B		    2
+**
+** iType controls the type of data written to pOut.  If iType is
+** DL_POSITIONS, the positions are those from pRight.
+*/
+static void docListPhraseMerge(
+  const char *pLeft, int nLeft,
+  const char *pRight, int nRight,
+  int nNear,		/* 0 for a phrase merge, non-zero for a NEAR merge */
+  int nPhrase,		/* Number of tokens in left+right operands to NEAR */
+  DocListType iType,	/* Type of doclist to write to pOut */
+  DataBuffer *pOut	/* Write the combined doclist here */
+){
+  DLReader left, right;
+  DLWriter writer;
+
+  if( nLeft==0 || nRight==0 ) return;
+
+  assert( iType!=DL_POSITIONS_OFFSETS );
+
+  dlrInit(&left, DL_POSITIONS, pLeft, nLeft);
+  dlrInit(&right, DL_POSITIONS, pRight, nRight);
+  dlwInit(&writer, iType, pOut);
+
+  while( !dlrAtEnd(&left) && !dlrAtEnd(&right) ){
+    if( dlrDocid(&left)<dlrDocid(&right) ){
+      dlrStep(&left);
+    }else if( dlrDocid(&right)<dlrDocid(&left) ){
+      dlrStep(&right);
+    }else{
+      if( nNear==0 ){
+	posListPhraseMerge(&left, &right, 0, 0, &writer);
+      }else{
+	/* This case occurs when two terms (simple terms or phrases) are
+	 * connected by a NEAR operator, span (nNear+1). i.e.
+	 *
+	 *     '"terrible company" NEAR widget'
+	 */
+	DataBuffer one = {0, 0, 0};
+	DataBuffer two = {0, 0, 0};
+
+	DLWriter dlwriter2;
+	DLReader dr1 = {0, 0, 0, 0, 0};
+	DLReader dr2 = {0, 0, 0, 0, 0};
+
+	dlwInit(&dlwriter2, iType, &one);
+	posListPhraseMerge(&right, &left, nNear-3+nPhrase, 1, &dlwriter2);
+	dlwInit(&dlwriter2, iType, &two);
+	posListPhraseMerge(&left, &right, nNear-1, 0, &dlwriter2);
+
+	if( one.nData) dlrInit(&dr1, iType, one.pData, one.nData);
+	if( two.nData) dlrInit(&dr2, iType, two.pData, two.nData);
+
+	if( !dlrAtEnd(&dr1) || !dlrAtEnd(&dr2) ){
+	  PLReader pr1 = {0};
+	  PLReader pr2 = {0};
+
+	  PLWriter plwriter;
+	  plwInit(&plwriter, &writer, dlrDocid(dlrAtEnd(&dr1)?&dr2:&dr1));
+
+	  if( one.nData ) plrInit(&pr1, &dr1);
+	  if( two.nData ) plrInit(&pr2, &dr2);
+	  while( !plrAtEnd(&pr1) || !plrAtEnd(&pr2) ){
+	    int iCompare = plrCompare(&pr1, &pr2);
+	    switch( iCompare ){
+	      case -1:
+		plwCopy(&plwriter, &pr1);
+		plrStep(&pr1);
+		break;
+	      case 1:
+		plwCopy(&plwriter, &pr2);
+		plrStep(&pr2);
+		break;
+	      case 0:
+		plwCopy(&plwriter, &pr1);
+		plrStep(&pr1);
+		plrStep(&pr2);
+		break;
+	    }
+	  }
+	  plwTerminate(&plwriter);
+	}
+	dataBufferDestroy(&one);
+	dataBufferDestroy(&two);
+      }
+      dlrStep(&left);
+      dlrStep(&right);
+    }
+  }
+
+  dlrDestroy(&left);
+  dlrDestroy(&right);
+  dlwDestroy(&writer);
+}
+
+/* We have two DL_DOCIDS doclists:  pLeft and pRight.
+** Write the intersection of these two doclists into pOut as a
+** DL_DOCIDS doclist.
+*/
+static void docListAndMerge(
+  const char *pLeft, int nLeft,
+  const char *pRight, int nRight,
+  DataBuffer *pOut	/* Write the combined doclist here */
+){
+  DLReader left, right;
+  DLWriter writer;
+
+  if( nLeft==0 || nRight==0 ) return;
+
+  dlrInit(&left, DL_DOCIDS, pLeft, nLeft);
+  dlrInit(&right, DL_DOCIDS, pRight, nRight);
+  dlwInit(&writer, DL_DOCIDS, pOut);
+
+  while( !dlrAtEnd(&left) && !dlrAtEnd(&right) ){
+    if( dlrDocid(&left)<dlrDocid(&right) ){
+      dlrStep(&left);
+    }else if( dlrDocid(&right)<dlrDocid(&left) ){
+      dlrStep(&right);
+    }else{
+      dlwAdd(&writer, dlrDocid(&left));
+      dlrStep(&left);
+      dlrStep(&right);
+    }
+  }
+
+  dlrDestroy(&left);
+  dlrDestroy(&right);
+  dlwDestroy(&writer);
+}
+
+/* We have two DL_DOCIDS doclists:  pLeft and pRight.
+** Write the union of these two doclists into pOut as a
+** DL_DOCIDS doclist.
+*/
+static void docListOrMerge(
+  const char *pLeft, int nLeft,
+  const char *pRight, int nRight,
+  DataBuffer *pOut	/* Write the combined doclist here */
+){
+  DLReader left, right;
+  DLWriter writer;
+
+  if( nLeft==0 ){
+    if( nRight!=0 ) dataBufferAppend(pOut, pRight, nRight);
+    return;
+  }
+  if( nRight==0 ){
+    dataBufferAppend(pOut, pLeft, nLeft);
+    return;
+  }
+
+  dlrInit(&left, DL_DOCIDS, pLeft, nLeft);
+  dlrInit(&right, DL_DOCIDS, pRight, nRight);
+  dlwInit(&writer, DL_DOCIDS, pOut);
+
+  while( !dlrAtEnd(&left) || !dlrAtEnd(&right) ){
+    if( dlrAtEnd(&right) ){
+      dlwAdd(&writer, dlrDocid(&left));
+      dlrStep(&left);
+    }else if( dlrAtEnd(&left) ){
+      dlwAdd(&writer, dlrDocid(&right));
+      dlrStep(&right);
+    }else if( dlrDocid(&left)<dlrDocid(&right) ){
+      dlwAdd(&writer, dlrDocid(&left));
+      dlrStep(&left);
+    }else if( dlrDocid(&right)<dlrDocid(&left) ){
+      dlwAdd(&writer, dlrDocid(&right));
+      dlrStep(&right);
+    }else{
+      dlwAdd(&writer, dlrDocid(&left));
+      dlrStep(&left);
+      dlrStep(&right);
+    }
+  }
+
+  dlrDestroy(&left);
+  dlrDestroy(&right);
+  dlwDestroy(&writer);
+}
+
+/* We have two DL_DOCIDS doclists:  pLeft and pRight.
+** Write into pOut as DL_DOCIDS doclist containing all documents that
+** occur in pLeft but not in pRight.
+*/
+static void docListExceptMerge(
+  const char *pLeft, int nLeft,
+  const char *pRight, int nRight,
+  DataBuffer *pOut	/* Write the combined doclist here */
+){
+  DLReader left, right;
+  DLWriter writer;
+
+  if( nLeft==0 ) return;
+  if( nRight==0 ){
+    dataBufferAppend(pOut, pLeft, nLeft);
+    return;
+  }
+
+  dlrInit(&left, DL_DOCIDS, pLeft, nLeft);
+  dlrInit(&right, DL_DOCIDS, pRight, nRight);
+  dlwInit(&writer, DL_DOCIDS, pOut);
+
+  while( !dlrAtEnd(&left) ){
+    while( !dlrAtEnd(&right) && dlrDocid(&right)<dlrDocid(&left) ){
+      dlrStep(&right);
+    }
+    if( dlrAtEnd(&right) || dlrDocid(&left)<dlrDocid(&right) ){
+      dlwAdd(&writer, dlrDocid(&left));
+    }
+    dlrStep(&left);
+  }
+
+  dlrDestroy(&left);
+  dlrDestroy(&right);
+  dlwDestroy(&writer);
+}
+
+static char *string_dup_n(const char *s, int n){
+  char *str = sqlite3_malloc(n + 1);
+  memcpy(str, s, n);
+  str[n] = '\0';
+  return str;
+}
+
+/* Duplicate a string; the caller must free() the returned string.
+ * (We don't use strdup() since it is not part of the standard C library and
+ * may not be available everywhere.) */
+static char *string_dup(const char *s){
+  return string_dup_n(s, strlen(s));
+}
+
+/* Format a string, replacing each occurrence of the % character with
+ * zDb.zName.  This may be more convenient than sqlite_mprintf()
+ * when one string is used repeatedly in a format string.
+ * The caller must free() the returned string. */
+static char *string_format(const char *zFormat,
+			   const char *zDb, const char *zName){
+  const char *p;
+  size_t len = 0;
+  size_t nDb = strlen(zDb);
+  size_t nName = strlen(zName);
+  size_t nFullTableName = nDb+1+nName;
+  char *result;
+  char *r;
+
+  /* first compute length needed */
+  for(p = zFormat ; *p ; ++p){
+    len += (*p=='%' ? nFullTableName : 1);
+  }
+  len += 1;  /* for null terminator */
+
+  r = result = sqlite3_malloc(len);
+  for(p = zFormat; *p; ++p){
+    if( *p=='%' ){
+      memcpy(r, zDb, nDb);
+      r += nDb;
+      *r++ = '.';
+      memcpy(r, zName, nName);
+      r += nName;
+    } else {
+      *r++ = *p;
+    }
+  }
+  *r++ = '\0';
+  assert( r == result + len );
+  return result;
+}
+
+static int sql_exec(sqlite3 *db, const char *zDb, const char *zName,
+		    const char *zFormat){
+  char *zCommand = string_format(zFormat, zDb, zName);
+  int rc;
+  FTSTRACE(("FTS3 sql: %s\n", zCommand));
+  rc = sqlite3_exec(db, zCommand, NULL, 0, NULL);
+  sqlite3_free(zCommand);
+  return rc;
+}
+
+static int sql_prepare(sqlite3 *db, const char *zDb, const char *zName,
+		       sqlite3_stmt **ppStmt, const char *zFormat){
+  char *zCommand = string_format(zFormat, zDb, zName);
+  int rc;
+  FTSTRACE(("FTS3 prepare: %s\n", zCommand));
+  rc = sqlite3_prepare_v2(db, zCommand, -1, ppStmt, NULL);
+  sqlite3_free(zCommand);
+  return rc;
+}
+
+/* end utility functions */
+
+/* Forward reference */
+typedef struct fulltext_vtab fulltext_vtab;
+
+/* A single term in a query is represented by an instances of
+** the following structure. Each word which may match against
+** document content is a term. Operators, like NEAR or OR, are
+** not terms. Query terms are organized as a flat list stored
+** in the Query.pTerms array.
+**
+** If the QueryTerm.nPhrase variable is non-zero, then the QueryTerm
+** is the first in a contiguous string of terms that are either part
+** of the same phrase, or connected by the NEAR operator.
+**
+** If the QueryTerm.nNear variable is non-zero, then the token is followed
+** by a NEAR operator with span set to (nNear-1). For example, the
+** following query:
+**
+** The QueryTerm.iPhrase variable stores the index of the token within
+** its phrase, indexed starting at 1, or 1 if the token is not part
+** of any phrase.
+**
+** For example, the data structure used to represent the following query:
+**
+**     ... MATCH 'sqlite NEAR/5 google NEAR/2 "search engine"'
+**
+** is:
+**
+**     {nPhrase=4, iPhrase=1, nNear=6, pTerm="sqlite"},
+**     {nPhrase=0, iPhrase=1, nNear=3, pTerm="google"},
+**     {nPhrase=0, iPhrase=1, nNear=0, pTerm="search"},
+**     {nPhrase=0, iPhrase=2, nNear=0, pTerm="engine"},
+**
+** compiling the FTS3 syntax to Query structures is done by the parseQuery()
+** function.
+*/
+typedef struct QueryTerm {
+  short int nPhrase; /* How many following terms are part of the same phrase */
+  short int iPhrase; /* This is the i-th term of a phrase. */
+  short int iColumn; /* Column of the index that must match this term */
+  signed char nNear; /* term followed by a NEAR operator with span=(nNear-1) */
+  signed char isOr;  /* this term is preceded by "OR" */
+  signed char isNot; /* this term is preceded by "-" */
+  signed char isPrefix; /* this term is followed by "*" */
+  char *pTerm;	     /* text of the term.  '\000' terminated.  malloced */
+  int nTerm;	     /* Number of bytes in pTerm[] */
+} QueryTerm;
+
+
+/* A query string is parsed into a Query structure.
+ *
+ * We could, in theory, allow query strings to be complicated
+ * nested expressions with precedence determined by parentheses.
+ * But none of the major search engines do this.  (Perhaps the
+ * feeling is that an parenthesized expression is two complex of
+ * an idea for the average user to grasp.)  Taking our lead from
+ * the major search engines, we will allow queries to be a list
+ * of terms (with an implied AND operator) or phrases in double-quotes,
+ * with a single optional "-" before each non-phrase term to designate
+ * negation and an optional OR connector.
+ *
+ * OR binds more tightly than the implied AND, which is what the
+ * major search engines seem to do.  So, for example:
+ *
+ *    [one two OR three]     ==>    one AND (two OR three)
+ *    [one OR two three]     ==>    (one OR two) AND three
+ *
+ * A "-" before a term matches all entries that lack that term.
+ * The "-" must occur immediately before the term with in intervening
+ * space.  This is how the search engines do it.
+ *
+ * A NOT term cannot be the right-hand operand of an OR.  If this
+ * occurs in the query string, the NOT is ignored:
+ *
+ *    [one OR -two]	     ==>    one OR two
+ *
+ */
+typedef struct Query {
+  fulltext_vtab *pFts;	/* The full text index */
+  int nTerms;		/* Number of terms in the query */
+  QueryTerm *pTerms;	/* Array of terms.  Space obtained from malloc() */
+  int nextIsOr;		/* Set the isOr flag on the next inserted term */
+  int nextIsNear;	/* Set the isOr flag on the next inserted term */
+  int nextColumn;	/* Next word parsed must be in this column */
+  int dfltColumn;	/* The default column */
+} Query;
+
+
+/*
+** An instance of the following structure keeps track of generated
+** matching-word offset information and snippets.
+*/
+typedef struct Snippet {
+  int nMatch;	  /* Total number of matches */
+  int nAlloc;	  /* Space allocated for aMatch[] */
+  struct snippetMatch { /* One entry for each matching term */
+    char snStatus;	 /* Status flag for use while constructing snippets */
+    short int iCol;	 /* The column that contains the match */
+    short int iTerm;	 /* The index in Query.pTerms[] of the matching term */
+    int iToken;		 /* The index of the matching document token */
+    short int nByte;	 /* Number of bytes in the term */
+    int iStart;		 /* The offset to the first character of the term */
+  } *aMatch;	  /* Points to space obtained from malloc */
+  char *zOffset;  /* Text rendering of aMatch[] */
+  int nOffset;	  /* strlen(zOffset) */
+  char *zSnippet; /* Snippet text */
+  int nSnippet;   /* strlen(zSnippet) */
+} Snippet;
+
+
+typedef enum QueryType {
+  QUERY_GENERIC,   /* table scan */
+  QUERY_DOCID,	   /* lookup by docid */
+  QUERY_FULLTEXT   /* QUERY_FULLTEXT + [i] is a full-text search for column i*/
+} QueryType;
+
+typedef enum fulltext_statement {
+  CONTENT_INSERT_STMT,
+  CONTENT_SELECT_STMT,
+  CONTENT_UPDATE_STMT,
+  CONTENT_DELETE_STMT,
+  CONTENT_EXISTS_STMT,
+
+  BLOCK_INSERT_STMT,
+  BLOCK_SELECT_STMT,
+  BLOCK_DELETE_STMT,
+  BLOCK_DELETE_ALL_STMT,
+
+  SEGDIR_MAX_INDEX_STMT,
+  SEGDIR_SET_STMT,
+  SEGDIR_SELECT_LEVEL_STMT,
+  SEGDIR_SPAN_STMT,
+  SEGDIR_DELETE_STMT,
+  SEGDIR_SELECT_SEGMENT_STMT,
+  SEGDIR_SELECT_ALL_STMT,
+  SEGDIR_DELETE_ALL_STMT,
+  SEGDIR_COUNT_STMT,
+
+  MAX_STMT		       /* Always at end! */
+} fulltext_statement;
+
+/* These must exactly match the enum above. */
+/* TODO(shess): Is there some risk that a statement will be used in two
+** cursors at once, e.g.  if a query joins a virtual table to itself?
+** If so perhaps we should move some of these to the cursor object.
+*/
+static const char *const fulltext_zStatement[MAX_STMT] = {
+  /* CONTENT_INSERT */ NULL,  /* generated in contentInsertStatement() */
+  /* CONTENT_SELECT */ NULL,  /* generated in contentSelectStatement() */
+  /* CONTENT_UPDATE */ NULL,  /* generated in contentUpdateStatement() */
+  /* CONTENT_DELETE */ "delete from %_content where docid = ?",
+  /* CONTENT_EXISTS */ "select docid from %_content limit 1",
+
+  /* BLOCK_INSERT */
+  "insert into %_segments (blockid, block) values (null, ?)",
+  /* BLOCK_SELECT */ "select block from %_segments where blockid = ?",
+  /* BLOCK_DELETE */ "delete from %_segments where blockid between ? and ?",
+  /* BLOCK_DELETE_ALL */ "delete from %_segments",
+
+  /* SEGDIR_MAX_INDEX */ "select max(idx) from %_segdir where level = ?",
+  /* SEGDIR_SET */ "insert into %_segdir values (?, ?, ?, ?, ?, ?)",
+  /* SEGDIR_SELECT_LEVEL */
+  "select start_block, leaves_end_block, root from %_segdir "
+  " where level = ? order by idx",
+  /* SEGDIR_SPAN */
+  "select min(start_block), max(end_block) from %_segdir "
+  " where level = ? and start_block <> 0",
+  /* SEGDIR_DELETE */ "delete from %_segdir where level = ?",
+
+  /* NOTE(shess): The first three results of the following two
+  ** statements must match.
+  */
+  /* SEGDIR_SELECT_SEGMENT */
+  "select start_block, leaves_end_block, root from %_segdir "
+  " where level = ? and idx = ?",
+  /* SEGDIR_SELECT_ALL */
+  "select start_block, leaves_end_block, root from %_segdir "
+  " order by level desc, idx asc",
+  /* SEGDIR_DELETE_ALL */ "delete from %_segdir",
+  /* SEGDIR_COUNT */ "select count(*), ifnull(max(level),0) from %_segdir",
+};
+
+/*
+** A connection to a fulltext index is an instance of the following
+** structure.  The xCreate and xConnect methods create an instance
+** of this structure and xDestroy and xDisconnect free that instance.
+** All other methods receive a pointer to the structure as one of their
+** arguments.
+*/
+struct fulltext_vtab {
+  sqlite3_vtab base;		   /* Base class used by SQLite core */
+  sqlite3 *db;			   /* The database connection */
+  const char *zDb;		   /* logical database name */
+  const char *zName;		   /* virtual table name */
+  int nColumn;			   /* number of columns in virtual table */
+  char **azColumn;		   /* column names.  malloced */
+  char **azContentColumn;	   /* column names in content table; malloced */
+  TrackerParser *parser;	   /* tokenizer for inserts and queries */
+  int max_words;
+
+  /* Precompiled statements which we keep as long as the table is
+  ** open.
+  */
+  sqlite3_stmt *pFulltextStatements[MAX_STMT];
+
+  /* Precompiled statements used for segment merges.  We run a
+  ** separate select across the leaf level of each tree being merged.
+  */
+  sqlite3_stmt *pLeafSelectStmts[MERGE_COUNT];
+  /* The statement used to prepare pLeafSelectStmts. */
+#define LEAF_SELECT \
+  "select block from %_segments where blockid between ? and ? order by blockid"
+
+  /* These buffer pending index updates during transactions.
+  ** nPendingData estimates the memory size of the pending data.  It
+  ** doesn't include the hash-bucket overhead, nor any malloc
+  ** overhead.	When nPendingData exceeds kPendingThreshold, the
+  ** buffer is flushed even before the transaction closes.
+  ** pendingTerms stores the data, and is only valid when nPendingData
+  ** is >=0 (nPendingData<0 means pendingTerms has not been
+  ** initialized).  iPrevDocid is the last docid written, used to make
+  ** certain we're inserting in sorted order.
+  */
+  int nPendingData;
+#define kPendingThreshold (1*1024*1024)
+  sqlite_int64 iPrevDocid;
+  fts3Hash pendingTerms;
+};
+
+/*
+** When the core wants to do a query, it create a cursor using a
+** call to xOpen.  This structure is an instance of a cursor.  It
+** is destroyed by xClose.
+*/
+typedef struct fulltext_cursor {
+  sqlite3_vtab_cursor base;	   /* Base class used by SQLite core */
+  QueryType iCursorType;	   /* Copy of sqlite3_index_info.idxNum */
+  sqlite3_stmt *pStmt;		   /* Prepared statement in use by the cursor */
+  int eof;			   /* True if at End Of Results */
+  Query q;			   /* Parsed query string */
+  Snippet snippet;		   /* Cached snippet for the current row */
+  int iColumn;			   /* Column being searched */
+  DataBuffer result;		   /* Doclist results from fulltextQuery */
+  DLReader reader;		   /* Result reader if result not empty */
+} fulltext_cursor;
+
+static struct fulltext_vtab *cursor_vtab(fulltext_cursor *c){
+  return (fulltext_vtab *) c->base.pVtab;
+}
+
+static const sqlite3_module fts3Module;   /* forward declaration */
+
+/* Return a dynamically generated statement of the form
+ *   insert into %_content (docid, ...) values (?, ...)
+ */
+static const char *contentInsertStatement(fulltext_vtab *v){
+  StringBuffer sb;
+  int i;
+
+  initStringBuffer(&sb);
+  append(&sb, "insert into %_content (docid, ");
+  appendList(&sb, v->nColumn, v->azContentColumn);
+  append(&sb, ") values (?");
+  for(i=0; i<v->nColumn; ++i)
+    append(&sb, ", ?");
+  append(&sb, ")");
+  return stringBufferData(&sb);
+}
+
+/* Return a dynamically generated statement of the form
+ *   select <content columns> from %_content where docid = ?
+ */
+static const char *contentSelectStatement(fulltext_vtab *v){
+  StringBuffer sb;
+  initStringBuffer(&sb);
+  append(&sb, "SELECT ");
+  appendList(&sb, v->nColumn, v->azContentColumn);
+  append(&sb, " FROM %_content WHERE docid = ?");
+  return stringBufferData(&sb);
+}
+
+/* Return a dynamically generated statement of the form
+ *   update %_content set [col_0] = ?, [col_1] = ?, ...
+ *		      where docid = ?
+ */
+static const char *contentUpdateStatement(fulltext_vtab *v){
+  StringBuffer sb;
+  int i;
+
+  initStringBuffer(&sb);
+  append(&sb, "update %_content set ");
+  for(i=0; i<v->nColumn; ++i) {
+    if( i>0 ){
+      append(&sb, ", ");
+    }
+    append(&sb, v->azContentColumn[i]);
+    append(&sb, " = ?");
+  }
+  append(&sb, " where docid = ?");
+  return stringBufferData(&sb);
+}
+
+/* Puts a freshly-prepared statement determined by iStmt in *ppStmt.
+** If the indicated statement has never been prepared, it is prepared
+** and cached, otherwise the cached version is reset.
+*/
+static int sql_get_statement(fulltext_vtab *v, fulltext_statement iStmt,
+			     sqlite3_stmt **ppStmt){
+  assert( iStmt<MAX_STMT );
+  if( v->pFulltextStatements[iStmt]==NULL ){
+    const char *zStmt;
+    int rc;
+    switch( iStmt ){
+      case CONTENT_INSERT_STMT:
+	zStmt = contentInsertStatement(v); break;
+      case CONTENT_SELECT_STMT:
+	zStmt = contentSelectStatement(v); break;
+      case CONTENT_UPDATE_STMT:
+	zStmt = contentUpdateStatement(v); break;
+      default:
+	zStmt = fulltext_zStatement[iStmt];
+    }
+    rc = sql_prepare(v->db, v->zDb, v->zName, &v->pFulltextStatements[iStmt],
+			 zStmt);
+    if( zStmt != fulltext_zStatement[iStmt]) sqlite3_free((void *) zStmt);
+    if( rc!=SQLITE_OK ) return rc;
+  } else {
+    int rc = sqlite3_reset(v->pFulltextStatements[iStmt]);
+    if( rc!=SQLITE_OK ) return rc;
+  }
+
+  *ppStmt = v->pFulltextStatements[iStmt];
+  return SQLITE_OK;
+}
+
+/* Like sqlite3_step(), but convert SQLITE_DONE to SQLITE_OK and
+** SQLITE_ROW to SQLITE_ERROR.	Useful for statements like UPDATE,
+** where we expect no results.
+*/
+static int sql_single_step(sqlite3_stmt *s){
+  int rc = sqlite3_step(s);
+  return (rc==SQLITE_DONE) ? SQLITE_OK : rc;
+}
+
+/* Like sql_get_statement(), but for special replicated LEAF_SELECT
+** statements.	idx -1 is a special case for an uncached version of
+** the statement (used in the optimize implementation).
+*/
+/* TODO(shess) Write version for generic statements and then share
+** that between the cached-statement functions.
+*/
+static int sql_get_leaf_statement(fulltext_vtab *v, int idx,
+				  sqlite3_stmt **ppStmt){
+  assert( idx>=-1 && idx<MERGE_COUNT );
+  if( idx==-1 ){
+    return sql_prepare(v->db, v->zDb, v->zName, ppStmt, LEAF_SELECT);
+  }else if( v->pLeafSelectStmts[idx]==NULL ){
+    int rc = sql_prepare(v->db, v->zDb, v->zName, &v->pLeafSelectStmts[idx],
+			 LEAF_SELECT);
+    if( rc!=SQLITE_OK ) return rc;
+  }else{
+    int rc = sqlite3_reset(v->pLeafSelectStmts[idx]);
+    if( rc!=SQLITE_OK ) return rc;
+  }
+
+  *ppStmt = v->pLeafSelectStmts[idx];
+  return SQLITE_OK;
+}
+
+/* insert into %_content (docid, ...) values ([docid], [pValues])
+** If the docid contains SQL NULL, then a unique docid will be
+** generated.
+*/
+static int content_insert(fulltext_vtab *v, sqlite3_value *docid,
+			  sqlite3_value **pValues){
+  sqlite3_stmt *s;
+  int i;
+  int rc = sql_get_statement(v, CONTENT_INSERT_STMT, &s);
+  if( rc!=SQLITE_OK ) return rc;
+
+  rc = sqlite3_bind_value(s, 1, docid);
+  if( rc!=SQLITE_OK ) return rc;
+
+  for(i=0; i<v->nColumn; ++i){
+    rc = sqlite3_bind_value(s, 2+i, pValues[i]);
+    if( rc!=SQLITE_OK ) return rc;
+  }
+
+  return sql_single_step(s);
+}
+
+/* update %_content set col0 = pValues[0], col1 = pValues[1], ...
+ *		    where docid = [iDocid] */
+static int content_update(fulltext_vtab *v, sqlite3_value **pValues,
+			  sqlite_int64 iDocid){
+  sqlite3_stmt *s;
+  int i;
+  int rc = sql_get_statement(v, CONTENT_UPDATE_STMT, &s);
+  if( rc!=SQLITE_OK ) return rc;
+
+  for(i=0; i<v->nColumn; ++i){
+    rc = sqlite3_bind_value(s, 1+i, pValues[i]);
+    if( rc!=SQLITE_OK ) return rc;
+  }
+
+  rc = sqlite3_bind_int64(s, 1+v->nColumn, iDocid);
+  if( rc!=SQLITE_OK ) return rc;
+
+  return sql_single_step(s);
+}
+
+static void freeStringArray(int nString, const char **pString){
+  int i;
+
+  for (i=0 ; i < nString ; ++i) {
+    if( pString[i]!=NULL ) sqlite3_free((void *) pString[i]);
+  }
+  sqlite3_free((void *) pString);
+}
+
+/* select * from %_content where docid = [iDocid]
+ * The caller must delete the returned array and all strings in it.
+ * null fields will be NULL in the returned array.
+ *
+ * TODO: Perhaps we should return pointer/length strings here for consistency
+ * with other code which uses pointer/length. */
+static int content_select(fulltext_vtab *v, sqlite_int64 iDocid,
+			  const char ***pValues){
+  sqlite3_stmt *s;
+  const char **values;
+  int i;
+  int rc;
+
+  *pValues = NULL;
+
+  rc = sql_get_statement(v, CONTENT_SELECT_STMT, &s);
+  if( rc!=SQLITE_OK ) return rc;
+
+  rc = sqlite3_bind_int64(s, 1, iDocid);
+  if( rc!=SQLITE_OK ) return rc;
+
+  rc = sqlite3_step(s);
+  if( rc!=SQLITE_ROW ) return rc;
+
+  values = (const char **) sqlite3_malloc(v->nColumn * sizeof(const char *));
+  for(i=0; i<v->nColumn; ++i){
+    if( sqlite3_column_type(s, i)==SQLITE_NULL ){
+      values[i] = NULL;
+    }else{
+      values[i] = string_dup((char*)sqlite3_column_text(s, i));
+    }
+  }
+
+  /* We expect only one row.  We must execute another sqlite3_step()
+   * to complete the iteration; otherwise the table will remain locked. */
+  rc = sqlite3_step(s);
+  if( rc==SQLITE_DONE ){
+    *pValues = values;
+    return SQLITE_OK;
+  }
+
+  freeStringArray(v->nColumn, values);
+  return rc;
+}
+
+/* delete from %_content where docid = [iDocid ] */
+static int content_delete(fulltext_vtab *v, sqlite_int64 iDocid){
+  sqlite3_stmt *s;
+  int rc = sql_get_statement(v, CONTENT_DELETE_STMT, &s);
+  if( rc!=SQLITE_OK ) return rc;
+
+  rc = sqlite3_bind_int64(s, 1, iDocid);
+  if( rc!=SQLITE_OK ) return rc;
+
+  return sql_single_step(s);
+}
+
+/* Returns SQLITE_ROW if any rows exist in %_content, SQLITE_DONE if
+** no rows exist, and any error in case of failure.
+*/
+static int content_exists(fulltext_vtab *v){
+  sqlite3_stmt *s;
+  int rc = sql_get_statement(v, CONTENT_EXISTS_STMT, &s);
+  if( rc!=SQLITE_OK ) return rc;
+
+  rc = sqlite3_step(s);
+  if( rc!=SQLITE_ROW ) return rc;
+
+  /* We expect only one row.  We must execute another sqlite3_step()
+   * to complete the iteration; otherwise the table will remain locked. */
+  rc = sqlite3_step(s);
+  if( rc==SQLITE_DONE ) return SQLITE_ROW;
+  if( rc==SQLITE_ROW ) return SQLITE_ERROR;
+  return rc;
+}
+
+/* insert into %_segments values ([pData])
+**   returns assigned blockid in *piBlockid
+*/
+static int block_insert(fulltext_vtab *v, const char *pData, int nData,
+			sqlite_int64 *piBlockid){
+  sqlite3_stmt *s;
+  int rc = sql_get_statement(v, BLOCK_INSERT_STMT, &s);
+  if( rc!=SQLITE_OK ) return rc;
+
+  rc = sqlite3_bind_blob(s, 1, pData, nData, SQLITE_STATIC);
+  if( rc!=SQLITE_OK ) return rc;
+
+  rc = sqlite3_step(s);
+  if( rc==SQLITE_ROW ) return SQLITE_ERROR;
+  if( rc!=SQLITE_DONE ) return rc;
+
+  /* blockid column is an alias for rowid. */
+  *piBlockid = sqlite3_last_insert_rowid(v->db);
+  return SQLITE_OK;
+}
+
+/* delete from %_segments
+**   where blockid between [iStartBlockid] and [iEndBlockid]
+**
+** Deletes the range of blocks, inclusive, used to delete the blocks
+** which form a segment.
+*/
+static int block_delete(fulltext_vtab *v,
+			sqlite_int64 iStartBlockid, sqlite_int64 iEndBlockid){
+  sqlite3_stmt *s;
+  int rc = sql_get_statement(v, BLOCK_DELETE_STMT, &s);
+  if( rc!=SQLITE_OK ) return rc;
+
+  rc = sqlite3_bind_int64(s, 1, iStartBlockid);
+  if( rc!=SQLITE_OK ) return rc;
+
+  rc = sqlite3_bind_int64(s, 2, iEndBlockid);
+  if( rc!=SQLITE_OK ) return rc;
+
+  return sql_single_step(s);
+}
+
+/* Returns SQLITE_ROW with *pidx set to the maximum segment idx found
+** at iLevel.  Returns SQLITE_DONE if there are no segments at
+** iLevel.  Otherwise returns an error.
+*/
+static int segdir_max_index(fulltext_vtab *v, int iLevel, int *pidx){
+  sqlite3_stmt *s;
+  int rc = sql_get_statement(v, SEGDIR_MAX_INDEX_STMT, &s);
+  if( rc!=SQLITE_OK ) return rc;
+
+  rc = sqlite3_bind_int(s, 1, iLevel);
+  if( rc!=SQLITE_OK ) return rc;
+
+  rc = sqlite3_step(s);
+  /* Should always get at least one row due to how max() works. */
+  if( rc==SQLITE_DONE ) return SQLITE_DONE;
+  if( rc!=SQLITE_ROW ) return rc;
+
+  /* NULL means that there were no inputs to max(). */
+  if( SQLITE_NULL==sqlite3_column_type(s, 0) ){
+    rc = sqlite3_step(s);
+    if( rc==SQLITE_ROW ) return SQLITE_ERROR;
+    return rc;
+  }
+
+  *pidx = sqlite3_column_int(s, 0);
+
+  /* We expect only one row.  We must execute another sqlite3_step()
+   * to complete the iteration; otherwise the table will remain locked. */
+  rc = sqlite3_step(s);
+  if( rc==SQLITE_ROW ) return SQLITE_ERROR;
+  if( rc!=SQLITE_DONE ) return rc;
+  return SQLITE_ROW;
+}
+
+/* insert into %_segdir values (
+**   [iLevel], [idx],
+**   [iStartBlockid], [iLeavesEndBlockid], [iEndBlockid],
+**   [pRootData]
+** )
+*/
+static int segdir_set(fulltext_vtab *v, int iLevel, int idx,
+		      sqlite_int64 iStartBlockid,
+		      sqlite_int64 iLeavesEndBlockid,
+		      sqlite_int64 iEndBlockid,
+		      const char *pRootData, int nRootData){
+  sqlite3_stmt *s;
+  int rc = sql_get_statement(v, SEGDIR_SET_STMT, &s);
+  if( rc!=SQLITE_OK ) return rc;
+
+  rc = sqlite3_bind_int(s, 1, iLevel);
+  if( rc!=SQLITE_OK ) return rc;
+
+  rc = sqlite3_bind_int(s, 2, idx);
+  if( rc!=SQLITE_OK ) return rc;
+
+  rc = sqlite3_bind_int64(s, 3, iStartBlockid);
+  if( rc!=SQLITE_OK ) return rc;
+
+  rc = sqlite3_bind_int64(s, 4, iLeavesEndBlockid);
+  if( rc!=SQLITE_OK ) return rc;
+
+  rc = sqlite3_bind_int64(s, 5, iEndBlockid);
+  if( rc!=SQLITE_OK ) return rc;
+
+  rc = sqlite3_bind_blob(s, 6, pRootData, nRootData, SQLITE_STATIC);
+  if( rc!=SQLITE_OK ) return rc;
+
+  return sql_single_step(s);
+}
+
+/* Queries %_segdir for the block span of the segments in level
+** iLevel.  Returns SQLITE_DONE if there are no blocks for iLevel,
+** SQLITE_ROW if there are blocks, else an error.
+*/
+static int segdir_span(fulltext_vtab *v, int iLevel,
+		       sqlite_int64 *piStartBlockid,
+		       sqlite_int64 *piEndBlockid){
+  sqlite3_stmt *s;
+  int rc = sql_get_statement(v, SEGDIR_SPAN_STMT, &s);
+  if( rc!=SQLITE_OK ) return rc;
+
+  rc = sqlite3_bind_int(s, 1, iLevel);
+  if( rc!=SQLITE_OK ) return rc;
+
+  rc = sqlite3_step(s);
+  if( rc==SQLITE_DONE ) return SQLITE_DONE;  /* Should never happen */
+  if( rc!=SQLITE_ROW ) return rc;
+
+  /* This happens if all segments at this level are entirely inline. */
+  if( SQLITE_NULL==sqlite3_column_type(s, 0) ){
+    /* We expect only one row.	We must execute another sqlite3_step()
+     * to complete the iteration; otherwise the table will remain locked. */
+    int rc2 = sqlite3_step(s);
+    if( rc2==SQLITE_ROW ) return SQLITE_ERROR;
+    return rc2;
+  }
+
+  *piStartBlockid = sqlite3_column_int64(s, 0);
+  *piEndBlockid = sqlite3_column_int64(s, 1);
+
+  /* We expect only one row.  We must execute another sqlite3_step()
+   * to complete the iteration; otherwise the table will remain locked. */
+  rc = sqlite3_step(s);
+  if( rc==SQLITE_ROW ) return SQLITE_ERROR;
+  if( rc!=SQLITE_DONE ) return rc;
+  return SQLITE_ROW;
+}
+
+/* Delete the segment blocks and segment directory records for all
+** segments at iLevel.
+*/
+static int segdir_delete(fulltext_vtab *v, int iLevel){
+  sqlite3_stmt *s;
+  sqlite_int64 iStartBlockid, iEndBlockid;
+  int rc = segdir_span(v, iLevel, &iStartBlockid, &iEndBlockid);
+  if( rc!=SQLITE_ROW && rc!=SQLITE_DONE ) return rc;
+
+  if( rc==SQLITE_ROW ){
+    rc = block_delete(v, iStartBlockid, iEndBlockid);
+    if( rc!=SQLITE_OK ) return rc;
+  }
+
+  /* Delete the segment directory itself. */
+  rc = sql_get_statement(v, SEGDIR_DELETE_STMT, &s);
+  if( rc!=SQLITE_OK ) return rc;
+
+  rc = sqlite3_bind_int64(s, 1, iLevel);
+  if( rc!=SQLITE_OK ) return rc;
+
+  return sql_single_step(s);
+}
+
+/* Delete entire fts index, SQLITE_OK on success, relevant error on
+** failure.
+*/
+static int segdir_delete_all(fulltext_vtab *v){
+  sqlite3_stmt *s;
+  int rc = sql_get_statement(v, SEGDIR_DELETE_ALL_STMT, &s);
+  if( rc!=SQLITE_OK ) return rc;
+
+  rc = sql_single_step(s);
+  if( rc!=SQLITE_OK ) return rc;
+
+  rc = sql_get_statement(v, BLOCK_DELETE_ALL_STMT, &s);
+  if( rc!=SQLITE_OK ) return rc;
+
+  return sql_single_step(s);
+}
+
+/* Returns SQLITE_OK with *pnSegments set to the number of entries in
+** %_segdir and *piMaxLevel set to the highest level which has a
+** segment.  Otherwise returns the SQLite error which caused failure.
+*/
+static int segdir_count(fulltext_vtab *v, int *pnSegments, int *piMaxLevel){
+  sqlite3_stmt *s;
+  int rc = sql_get_statement(v, SEGDIR_COUNT_STMT, &s);
+  if( rc!=SQLITE_OK ) return rc;
+
+  rc = sqlite3_step(s);
+  /* TODO(shess): This case should not be possible?  Should stronger
+  ** measures be taken if it happens?
+  */
+  if( rc==SQLITE_DONE ){
+    *pnSegments = 0;
+    *piMaxLevel = 0;
+    return SQLITE_OK;
+  }
+  if( rc!=SQLITE_ROW ) return rc;
+
+  *pnSegments = sqlite3_column_int(s, 0);
+  *piMaxLevel = sqlite3_column_int(s, 1);
+
+  /* We expect only one row.  We must execute another sqlite3_step()
+   * to complete the iteration; otherwise the table will remain locked. */
+  rc = sqlite3_step(s);
+  if( rc==SQLITE_DONE ) return SQLITE_OK;
+  if( rc==SQLITE_ROW ) return SQLITE_ERROR;
+  return rc;
+}
+
+/* TODO(shess) clearPendingTerms() is far down the file because
+** writeZeroSegment() is far down the file because LeafWriter is far
+** down the file.  Consider refactoring the code to move the non-vtab
+** code above the vtab code so that we don't need this forward
+** reference.
+*/
+static int clearPendingTerms(fulltext_vtab *v);
+
+/*
+** Free the memory used to contain a fulltext_vtab structure.
+*/
+static void fulltext_vtab_destroy(fulltext_vtab *v){
+  int iStmt, i;
+
+  FTSTRACE(("FTS3 Destroy %p\n", v));
+  for( iStmt=0; iStmt<MAX_STMT; iStmt++ ){
+    if( v->pFulltextStatements[iStmt]!=NULL ){
+      sqlite3_finalize(v->pFulltextStatements[iStmt]);
+      v->pFulltextStatements[iStmt] = NULL;
+    }
+  }
+
+  for( i=0; i<MERGE_COUNT; i++ ){
+    if( v->pLeafSelectStmts[i]!=NULL ){
+      sqlite3_finalize(v->pLeafSelectStmts[i]);
+      v->pLeafSelectStmts[i] = NULL;
+    }
+  }
+
+  if( v->parser!=NULL ){
+    tracker_parser_free (v->parser);
+    v->parser = NULL;
+  }
+
+  clearPendingTerms(v);
+
+  sqlite3_free(v->azColumn);
+  for(i = 0; i < v->nColumn; ++i) {
+    sqlite3_free(v->azContentColumn[i]);
+  }
+  sqlite3_free(v->azContentColumn);
+  sqlite3_free(v);
+}
+
+/*
+** Token types for parsing the arguments to xConnect or xCreate.
+*/
+#define TOKEN_EOF	  0    /* End of file */
+#define TOKEN_SPACE	  1    /* Any kind of whitespace */
+#define TOKEN_ID	  2    /* An identifier */
+#define TOKEN_STRING	  3    /* A string literal */
+#define TOKEN_PUNCT	  4    /* A single punctuation character */
+
+/*
+** If X is a character that can be used in an identifier then
+** ftsIdChar(X) will be true.  Otherwise it is false.
+**
+** For ASCII, any character with the high-order bit set is
+** allowed in an identifier.  For 7-bit characters,
+** isFtsIdChar[X] must be 1.
+**
+** Ticket #1066.  the SQL standard does not allow '$' in the
+** middle of identfiers.  But many SQL implementations do.
+** SQLite will allow '$' in identifiers for compatibility.
+** But the feature is undocumented.
+*/
+static const char isFtsIdChar[] = {
+/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */
+    0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 2x */
+    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 */
+};
+#define ftsIdChar(C)  (((c=C)&0x80)!=0 || (c>0x1f && isFtsIdChar[c-0x20]))
+
+
+/*
+** Return the length of the token that begins at z[0].
+** Store the token type in *tokenType before returning.
+*/
+static int ftsGetToken(const char *z, int *tokenType){
+  int i, c;
+  switch( *z ){
+    case 0: {
+      *tokenType = TOKEN_EOF;
+      return 0;
+    }
+    case ' ': case '\t': case '\n': case '\f': case '\r': {
+      for(i=1; safe_isspace(z[i]); i++){}
+      *tokenType = TOKEN_SPACE;
+      return i;
+    }
+    case '`':
+    case '\'':
+    case '"': {
+      int delim = z[0];
+      for(i=1; (c=z[i])!=0; i++){
+	if( c==delim ){
+	  if( z[i+1]==delim ){
+	    i++;
+	  }else{
+	    break;
+	  }
+	}
+      }
+      *tokenType = TOKEN_STRING;
+      return i + (c!=0);
+    }
+    case '[': {
+      for(i=1, c=z[0]; c!=']' && (c=z[i])!=0; i++){}
+      *tokenType = TOKEN_ID;
+      return i;
+    }
+    default: {
+      if( !ftsIdChar(*z) ){
+	break;
+      }
+      for(i=1; ftsIdChar(z[i]); i++){}
+      *tokenType = TOKEN_ID;
+      return i;
+    }
+  }
+  *tokenType = TOKEN_PUNCT;
+  return 1;
+}
+
+/*
+** A token extracted from a string is an instance of the following
+** structure.
+*/
+typedef struct FtsToken {
+  const char *z;       /* Pointer to token text.  Not '\000' terminated */
+  short int n;	       /* Length of the token text in bytes. */
+} FtsToken;
+
+/*
+** Given a input string (which is really one of the argv[] parameters
+** passed into xConnect or xCreate) split the string up into tokens.
+** Return an array of pointers to '\000' terminated strings, one string
+** for each non-whitespace token.
+**
+** The returned array is terminated by a single NULL pointer.
+**
+** Space to hold the returned array is obtained from a single
+** malloc and should be freed by passing the return value to free().
+** The individual strings within the token list are all a part of
+** the single memory allocation and will all be freed at once.
+*/
+static char **tokenizeString(const char *z, int *pnToken){
+  int nToken = 0;
+  FtsToken *aToken = sqlite3_malloc( strlen(z) * sizeof(aToken[0]) );
+  int n = 1;
+  int e, i;
+  int totalSize = 0;
+  char **azToken;
+  char *zCopy;
+  while( n>0 ){
+    n = ftsGetToken(z, &e);
+    if( e!=TOKEN_SPACE ){
+      aToken[nToken].z = z;
+      aToken[nToken].n = n;
+      nToken++;
+      totalSize += n+1;
+    }
+    z += n;
+  }
+  azToken = (char**)sqlite3_malloc( nToken*sizeof(char*) + totalSize );
+  zCopy = (char*)&azToken[nToken];
+  nToken--;
+  for(i=0; i<nToken; i++){
+    azToken[i] = zCopy;
+    n = aToken[i].n;
+    memcpy(zCopy, aToken[i].z, n);
+    zCopy[n] = 0;
+    zCopy += n+1;
+  }
+  azToken[nToken] = 0;
+  sqlite3_free(aToken);
+  *pnToken = nToken;
+  return azToken;
+}
+
+/*
+** Convert an SQL-style quoted string into a normal string by removing
+** the quote characters.  The conversion is done in-place.  If the
+** input does not begin with a quote character, then this routine
+** is a no-op.
+**
+** Examples:
+**
+**     "abc"   becomes	 abc
+**     'xyz'   becomes	 xyz
+**     [pqr]   becomes	 pqr
+**     `mno`   becomes	 mno
+*/
+static void dequoteString(char *z){
+  int quote;
+  int i, j;
+  if( z==0 ) return;
+  quote = z[0];
+  switch( quote ){
+    case '\'':	break;
+    case '"':	break;
+    case '`':	break;		      /* For MySQL compatibility */
+    case '[':	quote = ']';  break;  /* For MS SqlServer compatibility */
+    default:	return;
+  }
+  for(i=1, j=0; z[i]; i++){
+    if( z[i]==quote ){
+      if( z[i+1]==quote ){
+	z[j++] = quote;
+	i++;
+      }else{
+	z[j++] = 0;
+	break;
+      }
+    }else{
+      z[j++] = z[i];
+    }
+  }
+}
+
+/*
+** The input azIn is a NULL-terminated list of tokens.	Remove the first
+** token and all punctuation tokens.  Remove the quotes from
+** around string literal tokens.
+**
+** Example:
+**
+**     input:	   tokenize chinese ( 'simplifed' , 'mixed' )
+**     output:	   chinese simplifed mixed
+**
+** Another example:
+**
+**     input:	   delimiters ( '[' , ']' , '...' )
+**     output:	   [ ] ...
+*/
+static void tokenListToIdList(char **azIn){
+  int i, j;
+  if( azIn ){
+    for(i=0, j=-1; azIn[i]; i++){
+      if( safe_isalnum(azIn[i][0]) || azIn[i][1] ){
+	dequoteString(azIn[i]);
+	if( j>=0 ){
+	  azIn[j] = azIn[i];
+	}
+	j++;
+      }
+    }
+    azIn[j] = 0;
+  }
+}
+
+
+/*
+** Find the first alphanumeric token in the string zIn.  Null-terminate
+** this token.	Remove any quotation marks.  And return a pointer to
+** the result.
+*/
+static char *firstToken(char *zIn, char **pzTail){
+  int n, ttype;
+  while(1){
+    n = ftsGetToken(zIn, &ttype);
+    if( ttype==TOKEN_SPACE ){
+      zIn += n;
+    }else if( ttype==TOKEN_EOF ){
+      *pzTail = zIn;
+      return 0;
+    }else{
+      zIn[n] = 0;
+      *pzTail = &zIn[1];
+      dequoteString(zIn);
+      return zIn;
+    }
+  }
+  /*NOTREACHED*/
+}
+
+/* Return true if...
+**
+**   *	s begins with the string t, ignoring case
+**   *	s is longer than t
+**   *	The first character of s beyond t is not a alphanumeric
+**
+** Ignore leading space in *s.
+**
+** To put it another way, return true if the first token of
+** s[] is t[].
+*/
+static int startsWith(const char *s, const char *t){
+  while( safe_isspace(*s) ){ s++; }
+  while( *t ){
+    if( safe_tolower(*s++)!=safe_tolower(*t++) ) return 0;
+  }
+  return *s!='_' && !safe_isalnum(*s);
+}
+
+/*
+** An instance of this structure defines the "spec" of a
+** full text index.  This structure is populated by parseSpec
+** and use by fulltextConnect and fulltextCreate.
+*/
+typedef struct TableSpec {
+  const char *zDb;	   /* Logical database name */
+  const char *zName;	   /* Name of the full-text index */
+  int nColumn;		   /* Number of columns to be indexed */
+  char **azColumn;	   /* Original names of columns to be indexed */
+  char **azContentColumn;  /* Column names for %_content */
+  char **azTokenizer;	   /* Name of tokenizer and its arguments */
+} TableSpec;
+
+/*
+** Reclaim all of the memory used by a TableSpec
+*/
+static void clearTableSpec(TableSpec *p) {
+  sqlite3_free(p->azColumn);
+  sqlite3_free(p->azContentColumn);
+  sqlite3_free(p->azTokenizer);
+}
+
+/* Parse a CREATE VIRTUAL TABLE statement, which looks like this:
+ *
+ * CREATE VIRTUAL TABLE email
+ *	  USING fts3(subject, body, tokenize mytokenizer(myarg))
+ *
+ * We return parsed information in a TableSpec structure.
+ *
+ */
+static int parseSpec(TableSpec *pSpec, int argc, const char *const*argv,
+		     char**pzErr){
+  int i, n;
+  char *z, *zDummy;
+  char **azArg;
+  const char *zTokenizer = 0;	 /* argv[] entry describing the tokenizer */
+
+  assert( argc>=3 );
+  /* Current interface:
+  ** argv[0] - module name
+  ** argv[1] - database name
+  ** argv[2] - table name
+  ** argv[3..] - columns, optionally followed by tokenizer specification
+  **		 and snippet delimiters specification.
+  */
+
+  /* Make a copy of the complete argv[][] array in a single allocation.
+  ** The argv[][] array is read-only and transient.  We can write to the
+  ** copy in order to modify things and the copy is persistent.
+  */
+  CLEAR(pSpec);
+  for(i=n=0; i<argc; i++){
+    n += strlen(argv[i]) + 1;
+  }
+  azArg = sqlite3_malloc( sizeof(char*)*argc + n );
+  if( azArg==0 ){
+    return SQLITE_NOMEM;
+  }
+  z = (char*)&azArg[argc];
+  for(i=0; i<argc; i++){
+    azArg[i] = z;
+    strcpy(z, argv[i]);
+    z += strlen(z)+1;
+  }
+
+  /* Identify the column names and the tokenizer and delimiter arguments
+  ** in the argv[][] array.
+  */
+  pSpec->zDb = azArg[1];
+  pSpec->zName = azArg[2];
+  pSpec->nColumn = 0;
+  pSpec->azColumn = azArg;
+  zTokenizer = "tokenize simple";
+  for(i=3; i<argc; ++i){
+    if( startsWith(azArg[i],"tokenize") ){
+      zTokenizer = azArg[i];
+    }else{
+      z = azArg[pSpec->nColumn] = firstToken(azArg[i], &zDummy);
+      pSpec->nColumn++;
+    }
+  }
+  if( pSpec->nColumn==0 ){
+    azArg[0] = "content";
+    pSpec->nColumn = 1;
+  }
+
+  /*
+  ** Construct the list of content column names.
+  **
+  ** Each content column name will be of the form cNNAAAA
+  ** where NN is the column number and AAAA is the sanitized
+  ** column name.  "sanitized" means that special characters are
+  ** converted to "_".	The cNN prefix guarantees that all column
+  ** names are unique.
+  **
+  ** The AAAA suffix is not strictly necessary.  It is included
+  ** for the convenience of people who might examine the generated
+  ** %_content table and wonder what the columns are used for.
+  */
+  pSpec->azContentColumn = sqlite3_malloc( pSpec->nColumn * sizeof(char *) );
+  if( pSpec->azContentColumn==0 ){
+    clearTableSpec(pSpec);
+    return SQLITE_NOMEM;
+  }
+  for(i=0; i<pSpec->nColumn; i++){
+    char *p;
+    pSpec->azContentColumn[i] = sqlite3_mprintf("c%d%s", i, azArg[i]);
+    for (p = pSpec->azContentColumn[i]; *p ; ++p) {
+      if( !safe_isalnum(*p) ) *p = '_';
+    }
+  }
+
+  /*
+  ** Parse the tokenizer specification string.
+  */
+  pSpec->azTokenizer = tokenizeString(zTokenizer, &n);
+  tokenListToIdList(pSpec->azTokenizer);
+
+  return SQLITE_OK;
+}
+
+/*
+** Generate a CREATE TABLE statement that describes the schema of
+** the virtual table.  Return a pointer to this schema string.
+**
+** Space is obtained from sqlite3_mprintf() and should be freed
+** using sqlite3_free().
+*/
+static char *fulltextSchema(
+  int nColumn,			/* Number of columns */
+  const char *const* azColumn,	/* List of columns */
+  const char *zTableName	/* Name of the table */
+){
+  int i;
+  char *zSchema, *zNext;
+  const char *zSep = "(";
+  zSchema = sqlite3_mprintf("CREATE TABLE x");
+  for(i=0; i<nColumn; i++){
+    zNext = sqlite3_mprintf("%s%s%Q", zSchema, zSep, azColumn[i]);
+    sqlite3_free(zSchema);
+    zSchema = zNext;
+    zSep = ",";
+  }
+  zNext = sqlite3_mprintf("%s,%Q HIDDEN", zSchema, zTableName);
+  sqlite3_free(zSchema);
+  zSchema = zNext;
+  zNext = sqlite3_mprintf("%s,docid HIDDEN)", zSchema);
+  sqlite3_free(zSchema);
+  return zNext;
+}
+
+/*
+** Build a new sqlite3_vtab structure that will describe the
+** fulltext index defined by spec.
+*/
+static int constructVtab(
+  sqlite3 *db,		    /* The SQLite database connection */
+  TableSpec *spec,	    /* Parsed spec information from parseSpec() */
+  sqlite3_vtab **ppVTab,    /* Write the resulting vtab structure here */
+  char **pzErr		    /* Write any error message here */
+){
+  int rc;
+  fulltext_vtab *v = 0;
+  char *schema;
+
+
+  v = (fulltext_vtab *) sqlite3_malloc(sizeof(fulltext_vtab));
+  if( v==0 ) return SQLITE_NOMEM;
+  CLEAR(v);
+  /* sqlite will initialize v->base */
+  v->db = db;
+  v->zDb = spec->zDb;	    /* Freed when azColumn is freed */
+  v->zName = spec->zName;   /* Freed when azColumn is freed */
+  v->nColumn = spec->nColumn;
+  v->azContentColumn = spec->azContentColumn;
+  spec->azContentColumn = 0;
+  v->azColumn = spec->azColumn;
+  spec->azColumn = 0;
+
+/* comment out tokenizer stuff
+  if( spec->azTokenizer==0 ){
+    return SQLITE_NOMEM;
+  }
+
+  zTok = spec->azTokenizer[0];
+  if( !zTok ){
+    zTok = "simple";
+  }
+  nTok = strlen(zTok)+1;
+
+  m = (sqlite3_tokenizer_module *)sqlite3Fts3HashFind(pHash, zTok, nTok);
+  if( !m ){
+    *pzErr = sqlite3_mprintf("unknown tokenizer: %s", spec->azTokenizer[0]);
+    rc = SQLITE_ERROR;
+    goto err;
+  }
+
+  for(n=0; spec->azTokenizer[n]; n++){}
+  if( n ){
+    rc = m->xCreate(n-1, (const char*const*)&spec->azTokenizer[1],
+		    &v->pTokenizer);
+  }else{
+    rc = m->xCreate(0, 0, &v->pTokenizer);
+  }
+  if( rc!=SQLITE_OK ) goto err;
+  */
+
+
+  /* set up our parser */
+
+  TrackerConfig *config = tracker_config_new ();
+
+  TrackerLanguage *language = tracker_language_new (config);
+
+  int min_len = tracker_config_get_min_word_length (config);
+  int max_len = tracker_config_get_max_word_length (config);
+  v->max_words = tracker_config_get_max_words_to_index (config);
+
+  v->parser =	tracker_parser_new (language, max_len, min_len);
+
+  g_object_unref (language);
+
+
+  /* TODO: verify the existence of backing tables foo_content, foo_term */
+
+  schema = fulltextSchema(v->nColumn, (const char*const*)v->azColumn,
+			  spec->zName);
+  rc = sqlite3_declare_vtab(db, schema);
+  sqlite3_free(schema);
+  if( rc!=SQLITE_OK ) goto err;
+
+  memset(v->pFulltextStatements, 0, sizeof(v->pFulltextStatements));
+
+  /* Indicate that the buffer is not live. */
+  v->nPendingData = -1;
+
+  *ppVTab = &v->base;
+  FTSTRACE(("FTS3 Connect %p\n", v));
+
+  return rc;
+
+err:
+  fulltext_vtab_destroy(v);
+  return rc;
+}
+
+static int fulltextConnect(
+  sqlite3 *db,
+  void *pAux,
+  int argc, const char *const*argv,
+  sqlite3_vtab **ppVTab,
+  char **pzErr
+){
+  TableSpec spec;
+  int rc = parseSpec(&spec, argc, argv, pzErr);
+  if( rc!=SQLITE_OK ) return rc;
+
+  rc = constructVtab(db,  &spec, ppVTab, pzErr);
+  clearTableSpec(&spec);
+  return rc;
+}
+
+/* The %_content table holds the text of each document, with
+** the docid column exposed as the SQLite rowid for the table.
+*/
+/* TODO(shess) This comment needs elaboration to match the updated
+** code.  Work it into the top-of-file comment at that time.
+*/
+static int fulltextCreate(sqlite3 *db, void *pAux,
+			  int argc, const char * const *argv,
+			  sqlite3_vtab **ppVTab, char **pzErr){
+  int rc;
+  TableSpec spec;
+  StringBuffer schema;
+  FTSTRACE(("FTS3 Create\n"));
+
+  rc = parseSpec(&spec, argc, argv, pzErr);
+  if( rc!=SQLITE_OK ) return rc;
+
+  initStringBuffer(&schema);
+  append(&schema, "CREATE TABLE %_content(");
+  append(&schema, "  docid INTEGER PRIMARY KEY,");
+  appendList(&schema, spec.nColumn, spec.azContentColumn);
+  append(&schema, ")");
+  rc = sql_exec(db, spec.zDb, spec.zName, stringBufferData(&schema));
+  stringBufferDestroy(&schema);
+  if( rc!=SQLITE_OK ) goto out;
+
+  rc = sql_exec(db, spec.zDb, spec.zName,
+		"create table %_segments("
+		"  blockid INTEGER PRIMARY KEY,"
+		"  block blob"
+		");"
+		);
+  if( rc!=SQLITE_OK ) goto out;
+
+  rc = sql_exec(db, spec.zDb, spec.zName,
+		"create table %_segdir("
+		"  level integer,"
+		"  idx integer,"
+		"  start_block integer,"
+		"  leaves_end_block integer,"
+		"  end_block integer,"
+		"  root blob,"
+		"  primary key(level, idx)"
+		");");
+  if( rc!=SQLITE_OK ) goto out;
+
+  rc = constructVtab(db, &spec, ppVTab, pzErr);
+
+out:
+  clearTableSpec(&spec);
+  return rc;
+}
+
+/* Decide how to handle an SQL query. */
+static int fulltextBestIndex(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
+  fulltext_vtab *v = (fulltext_vtab *)pVTab;
+  int i;
+  FTSTRACE(("FTS3 BestIndex\n"));
+
+  for(i=0; i<pInfo->nConstraint; ++i){
+    const struct sqlite3_index_constraint *pConstraint;
+    pConstraint = &pInfo->aConstraint[i];
+    if( pConstraint->usable ) {
+      if( (pConstraint->iColumn==-1 || pConstraint->iColumn==v->nColumn+1) &&
+	  pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){
+	pInfo->idxNum = QUERY_DOCID;	  /* lookup by docid */
+	FTSTRACE(("FTS3 QUERY_DOCID\n"));
+      } else if( pConstraint->iColumn>=0 && pConstraint->iColumn<=v->nColumn &&
+		 pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH ){
+	/* full-text search */
+	pInfo->idxNum = QUERY_FULLTEXT + pConstraint->iColumn;
+	FTSTRACE(("FTS3 QUERY_FULLTEXT %d\n", pConstraint->iColumn));
+      } else continue;
+
+      pInfo->aConstraintUsage[i].argvIndex = 1;
+      pInfo->aConstraintUsage[i].omit = 1;
+
+      /* An arbitrary value for now.
+       * TODO: Perhaps docid matches should be considered cheaper than
+       * full-text searches. */
+      pInfo->estimatedCost = 1.0;
+
+      return SQLITE_OK;
+    }
+  }
+  pInfo->idxNum = QUERY_GENERIC;
+  return SQLITE_OK;
+}
+
+static int fulltextDisconnect(sqlite3_vtab *pVTab){
+  FTSTRACE(("FTS3 Disconnect %p\n", pVTab));
+  fulltext_vtab_destroy((fulltext_vtab *)pVTab);
+  return SQLITE_OK;
+}
+
+static int fulltextDestroy(sqlite3_vtab *pVTab){
+  fulltext_vtab *v = (fulltext_vtab *)pVTab;
+  int rc;
+
+  FTSTRACE(("FTS3 Destroy %p\n", pVTab));
+  rc = sql_exec(v->db, v->zDb, v->zName,
+		"drop table if exists %_content;"
+		"drop table if exists %_segments;"
+		"drop table if exists %_segdir;"
+		);
+  if( rc!=SQLITE_OK ) return rc;
+
+  fulltext_vtab_destroy((fulltext_vtab *)pVTab);
+  return SQLITE_OK;
+}
+
+static int fulltextOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
+  fulltext_cursor *c;
+
+  c = (fulltext_cursor *) sqlite3_malloc(sizeof(fulltext_cursor));
+  if( c ){
+    memset(c, 0, sizeof(fulltext_cursor));
+    /* sqlite will initialize c->base */
+    *ppCursor = &c->base;
+    FTSTRACE(("FTS3 Open %p: %p\n", pVTab, c));
+    return SQLITE_OK;
+  }else{
+    return SQLITE_NOMEM;
+  }
+}
+
+
+/* Free all of the dynamically allocated memory held by *q
+*/
+static void queryClear(Query *q){
+  int i;
+  for(i = 0; i < q->nTerms; ++i){
+    sqlite3_free(q->pTerms[i].pTerm);
+  }
+  sqlite3_free(q->pTerms);
+  CLEAR(q);
+}
+
+/* Free all of the dynamically allocated memory held by the
+** Snippet
+*/
+static void snippetClear(Snippet *p){
+  sqlite3_free(p->aMatch);
+  sqlite3_free(p->zOffset);
+  sqlite3_free(p->zSnippet);
+  CLEAR(p);
+}
+/*
+** Append a single entry to the p->aMatch[] log.
+*/
+static void snippetAppendMatch(
+  Snippet *p,		    /* Append the entry to this snippet */
+  int iCol, int iTerm,	    /* The column and query term */
+  int iToken,		    /* Matching token in document */
+  int iStart, int nByte     /* Offset and size of the match */
+){
+  int i;
+  struct snippetMatch *pMatch;
+  if( p->nMatch+1>=p->nAlloc ){
+    p->nAlloc = p->nAlloc*2 + 10;
+    p->aMatch = sqlite3_realloc(p->aMatch, p->nAlloc*sizeof(p->aMatch[0]) );
+    if( p->aMatch==0 ){
+      p->nMatch = 0;
+      p->nAlloc = 0;
+      return;
+    }
+  }
+  i = p->nMatch++;
+  pMatch = &p->aMatch[i];
+  pMatch->iCol = iCol;
+  pMatch->iTerm = iTerm;
+  pMatch->iToken = iToken;
+  pMatch->iStart = iStart;
+  pMatch->nByte = nByte;
+}
+
+/*
+** Sizing information for the circular buffer used in snippetOffsetsOfColumn()
+*/
+#define FTS3_ROTOR_SZ	(32)
+#define FTS3_ROTOR_MASK (FTS3_ROTOR_SZ-1)
+
+/*
+** Add entries to pSnippet->aMatch[] for every match that occurs against
+** document zDoc[0..nDoc-1] which is stored in column iColumn.
+*/
+static void snippetOffsetsOfColumn(
+  Query *pQuery,
+  Snippet *pSnippet,
+  int iColumn,
+  const char *zDoc,
+  int nDoc
+){
+
+  fulltext_vtab *pVtab;		       /* The full text index */
+  int nColumn;			       /* Number of columns in the index */
+  const QueryTerm *aTerm;	       /* Query string terms */
+  int nTerm;			       /* Number of query string terms */
+  int i, j;			       /* Loop counters */
+  unsigned int match, prevMatch;       /* Phrase search bitmasks */
+  const char *zToken;		       /* Next token from the tokenizer */
+  int nToken;			       /* Size of zToken */
+  int iBegin, iEnd, iPos;	       /* Offsets of beginning and end */
+  gboolean new_paragraph, stop_word;
+
+  /* The following variables keep a circular buffer of the last
+  ** few tokens */
+  unsigned int iRotor = 0;	       /* Index of current token */
+  int iRotorBegin[FTS3_ROTOR_SZ];      /* Beginning offset of token */
+  int iRotorLen[FTS3_ROTOR_SZ];        /* Length of token */
+
+  pVtab = pQuery->pFts;
+  nColumn = pVtab->nColumn;
+
+  tracker_parser_reset (pVtab->parser, zDoc, nDoc, FALSE, TRUE, TRUE, FALSE);
+
+  aTerm = pQuery->pTerms;
+  nTerm = pQuery->nTerms;
+
+  if( nTerm>=FTS3_ROTOR_SZ ){
+    nTerm = FTS3_ROTOR_SZ - 1;
+  }
+
+  prevMatch = 0;
+
+  while(1){
+//    rc = pTModule->xNext(pTCursor, &zToken, &nToken, &iBegin, &iEnd, &iPos);
+
+    zToken = tracker_parser_next (pVtab->parser,
+				  &iPos,
+				  &iBegin,
+				  &iEnd,
+				  &new_paragraph,
+				  &stop_word,
+				  &nToken);
+
+    if (!zToken) break;
+
+    if (stop_word) {
+      continue;
+    }
+
+    iRotorBegin[iRotor&FTS3_ROTOR_MASK] = iBegin;
+    iRotorLen[iRotor&FTS3_ROTOR_MASK] = iEnd-iBegin;
+    match = 0;
+    for(i=0; i<nTerm; i++){
+      int iCol;
+      iCol = aTerm[i].iColumn;
+      if( iCol>=0 && iCol<nColumn && iCol!=iColumn ) continue;
+      if( aTerm[i].nTerm>nToken ) continue;
+      if( !aTerm[i].isPrefix && aTerm[i].nTerm<nToken ) continue;
+      assert( aTerm[i].nTerm<=nToken );
+      if( memcmp(aTerm[i].pTerm, zToken, aTerm[i].nTerm) ) continue;
+      if( aTerm[i].iPhrase>1 && (prevMatch & (1<<i))==0 ) continue;
+      match |= 1<<i;
+      if( i==nTerm-1 || aTerm[i+1].iPhrase==1 ){
+	for(j=aTerm[i].iPhrase-1; j>=0; j--){
+	  int k = (iRotor-j) & FTS3_ROTOR_MASK;
+	  snippetAppendMatch(pSnippet, iColumn, i-j, iPos-j,
+		iRotorBegin[k], iRotorLen[k]);
+	}
+      }
+    }
+    prevMatch = match<<1;
+    iRotor++;
+  }
+//  pTModule->xClose(pTCursor);
+}
+
+/*
+** Remove entries from the pSnippet structure to account for the NEAR
+** operator. When this is called, pSnippet contains the list of token
+** offsets produced by treating all NEAR operators as AND operators.
+** This function removes any entries that should not be present after
+** accounting for the NEAR restriction. For example, if the queried
+** document is:
+**
+**     "A B C D E A"
+**
+** and the query is:
+**
+**     A NEAR/0 E
+**
+** then when this function is called the Snippet contains token offsets
+** 0, 4 and 5. This function removes the "0" entry (because the first A
+** is not near enough to an E).
+*/
+static void trimSnippetOffsetsForNear(Query *pQuery, Snippet *pSnippet){
+  int ii;
+  int iDir = 1;
+
+  while(iDir>-2) {
+    assert( iDir==1 || iDir==-1 );
+    for(ii=0; ii<pSnippet->nMatch; ii++){
+      int jj;
+      int nNear;
+      struct snippetMatch *pMatch = &pSnippet->aMatch[ii];
+      QueryTerm *pQueryTerm = &pQuery->pTerms[pMatch->iTerm];
+
+      if( (pMatch->iTerm+iDir)<0
+       || (pMatch->iTerm+iDir)>=pQuery->nTerms
+      ){
+	continue;
+      }
+
+      nNear = pQueryTerm->nNear;
+      if( iDir<0 ){
+	nNear = pQueryTerm[-1].nNear;
+      }
+
+      if( pMatch->iTerm>=0 && nNear ){
+	int isOk = 0;
+	int iNextTerm = pMatch->iTerm+iDir;
+	int iPrevTerm = iNextTerm;
+
+	int iEndToken;
+	int iStartToken;
+
+	if( iDir<0 ){
+	  int nPhrase = 1;
+	  iStartToken = pMatch->iToken;
+	  while( (pMatch->iTerm+nPhrase)<pQuery->nTerms
+	      && pQuery->pTerms[pMatch->iTerm+nPhrase].iPhrase>1
+	  ){
+	    nPhrase++;
+	  }
+	  iEndToken = iStartToken + nPhrase - 1;
+	}else{
+	  iEndToken   = pMatch->iToken;
+	  iStartToken = pMatch->iToken+1-pQueryTerm->iPhrase;
+	}
+
+	while( pQuery->pTerms[iNextTerm].iPhrase>1 ){
+	  iNextTerm--;
+	}
+	while( (iPrevTerm+1)<pQuery->nTerms &&
+	       pQuery->pTerms[iPrevTerm+1].iPhrase>1
+	){
+	  iPrevTerm++;
+	}
+
+	for(jj=0; isOk==0 && jj<pSnippet->nMatch; jj++){
+	  struct snippetMatch *p = &pSnippet->aMatch[jj];
+	  if( p->iCol==pMatch->iCol && ((
+	       p->iTerm==iNextTerm &&
+	       p->iToken>iEndToken &&
+	       p->iToken<=iEndToken+nNear
+	  ) || (
+	       p->iTerm==iPrevTerm &&
+	       p->iToken<iStartToken &&
+	       p->iToken>=iStartToken-nNear
+	  ))){
+	    isOk = 1;
+	  }
+	}
+	if( !isOk ){
+	  for(jj=1-pQueryTerm->iPhrase; jj<=0; jj++){
+	    pMatch[jj].iTerm = -1;
+	  }
+	  ii = -1;
+	  iDir = 1;
+	}
+      }
+    }
+    iDir -= 2;
+  }
+}
+
+/*
+** Compute all offsets for the current row of the query.
+** If the offsets have already been computed, this routine is a no-op.
+*/
+static void snippetAllOffsets(fulltext_cursor *p){
+  int nColumn;
+  int iColumn, i;
+  int iFirst, iLast;
+  fulltext_vtab *pFts;
+
+  if( p->snippet.nMatch ) return;
+  if( p->q.nTerms==0 ) return;
+  pFts = p->q.pFts;
+  nColumn = pFts->nColumn;
+  iColumn = (p->iCursorType - QUERY_FULLTEXT);
+  if( iColumn<0 || iColumn>=nColumn ){
+    iFirst = 0;
+    iLast = nColumn-1;
+  }else{
+    iFirst = iColumn;
+    iLast = iColumn;
+  }
+  for(i=iFirst; i<=iLast; i++){
+    const char *zDoc;
+    int nDoc;
+    zDoc = (const char*)sqlite3_column_text(p->pStmt, i+1);
+    nDoc = sqlite3_column_bytes(p->pStmt, i+1);
+    snippetOffsetsOfColumn(&p->q, &p->snippet, i, zDoc, nDoc);
+  }
+
+  trimSnippetOffsetsForNear(&p->q, &p->snippet);
+}
+
+/*
+** Convert the information in the aMatch[] array of the snippet
+** into the string zOffset[0..nOffset-1].
+*/
+static void snippetOffsetText(Snippet *p){
+  int i;
+  int cnt = 0;
+  StringBuffer sb;
+  char zBuf[200];
+  if( p->zOffset ) return;
+  initStringBuffer(&sb);
+  for(i=0; i<p->nMatch; i++){
+    struct snippetMatch *pMatch = &p->aMatch[i];
+    if( pMatch->iTerm>=0 ){
+      /* If snippetMatch.iTerm is less than 0, then the match was
+      ** discarded as part of processing the NEAR operator (see the
+      ** trimSnippetOffsetsForNear() function for details). Ignore
+      ** it in this case
+      */
+      zBuf[0] = ' ';
+      sqlite3_snprintf(sizeof(zBuf)-1, &zBuf[cnt>0], "%d %d %d %d",
+	  pMatch->iCol, pMatch->iTerm, pMatch->iStart, pMatch->nByte);
+      append(&sb, zBuf);
+      cnt++;
+    }
+  }
+  p->zOffset = stringBufferData(&sb);
+  p->nOffset = stringBufferLength(&sb);
+}
+
+/*
+** zDoc[0..nDoc-1] is phrase of text.  aMatch[0..nMatch-1] are a set
+** of matching words some of which might be in zDoc.  zDoc is column
+** number iCol.
+**
+** iBreak is suggested spot in zDoc where we could begin or end an
+** excerpt.  Return a value similar to iBreak but possibly adjusted
+** to be a little left or right so that the break point is better.
+*/
+static int wordBoundary(
+  int iBreak,			/* The suggested break point */
+  const char *zDoc,		/* Document text */
+  int nDoc,			/* Number of bytes in zDoc[] */
+  struct snippetMatch *aMatch,	/* Matching words */
+  int nMatch,			/* Number of entries in aMatch[] */
+  int iCol			/* The column number for zDoc[] */
+){
+  int i;
+  if( iBreak<=10 ){
+    return 0;
+  }
+  if( iBreak>=nDoc-10 ){
+    return nDoc;
+  }
+  for(i=0; i<nMatch && aMatch[i].iCol<iCol; i++){}
+  while( i<nMatch && aMatch[i].iStart+aMatch[i].nByte<iBreak ){ i++; }
+  if( i<nMatch ){
+    if( aMatch[i].iStart<iBreak+10 ){
+      return aMatch[i].iStart;
+    }
+    if( i>0 && aMatch[i-1].iStart+aMatch[i-1].nByte>=iBreak ){
+      return aMatch[i-1].iStart;
+    }
+  }
+  for(i=1; i<=10; i++){
+    if( safe_isspace(zDoc[iBreak-i]) ){
+      return iBreak - i + 1;
+    }
+    if( safe_isspace(zDoc[iBreak+i]) ){
+      return iBreak + i + 1;
+    }
+  }
+  return iBreak;
+}
+
+
+
+/*
+** Allowed values for Snippet.aMatch[].snStatus
+*/
+#define SNIPPET_IGNORE	0   /* It is ok to omit this match from the snippet */
+#define SNIPPET_DESIRED 1   /* We want to include this match in the snippet */
+
+/*
+** Generate the text of a snippet.
+*/
+static void snippetText(
+  fulltext_cursor *pCursor,   /* The cursor we need the snippet for */
+  const char *zStartMark,     /* Markup to appear before each match */
+  const char *zEndMark,       /* Markup to appear after each match */
+  const char *zEllipsis       /* Ellipsis mark */
+){
+  int i, j;
+  struct snippetMatch *aMatch;
+  int nMatch;
+  int nDesired;
+  StringBuffer sb;
+  int tailCol;
+  int tailOffset;
+  int iCol;
+  int nDoc;
+  const char *zDoc;
+  int iStart, iEnd;
+  int tailEllipsis = 0;
+  int iMatch;
+
+
+  sqlite3_free(pCursor->snippet.zSnippet);
+  pCursor->snippet.zSnippet = 0;
+  aMatch = pCursor->snippet.aMatch;
+  nMatch = pCursor->snippet.nMatch;
+  initStringBuffer(&sb);
+
+  for(i=0; i<nMatch; i++){
+    aMatch[i].snStatus = SNIPPET_IGNORE;
+  }
+  nDesired = 0;
+  for(i=0; i<pCursor->q.nTerms; i++){
+    for(j=0; j<nMatch; j++){
+      if( aMatch[j].iTerm==i ){
+	aMatch[j].snStatus = SNIPPET_DESIRED;
+	nDesired++;
+	break;
+      }
+    }
+  }
+
+  iMatch = 0;
+  tailCol = -1;
+  tailOffset = 0;
+  for(i=0; i<nMatch && nDesired>0; i++){
+    if( aMatch[i].snStatus!=SNIPPET_DESIRED ) continue;
+    nDesired--;
+    iCol = aMatch[i].iCol;
+    zDoc = (const char*)sqlite3_column_text(pCursor->pStmt, iCol+1);
+    nDoc = sqlite3_column_bytes(pCursor->pStmt, iCol+1);
+    iStart = aMatch[i].iStart - 40;
+    iStart = wordBoundary(iStart, zDoc, nDoc, aMatch, nMatch, iCol);
+    if( iStart<=10 ){
+      iStart = 0;
+    }
+    if( iCol==tailCol && iStart<=tailOffset+20 ){
+      iStart = tailOffset;
+    }
+    if( (iCol!=tailCol && tailCol>=0) || iStart!=tailOffset ){
+      trimWhiteSpace(&sb);
+      appendWhiteSpace(&sb);
+      append(&sb, zEllipsis);
+      appendWhiteSpace(&sb);
+    }
+    iEnd = aMatch[i].iStart + aMatch[i].nByte + 40;
+    iEnd = wordBoundary(iEnd, zDoc, nDoc, aMatch, nMatch, iCol);
+    if( iEnd>=nDoc-10 ){
+      iEnd = nDoc;
+      tailEllipsis = 0;
+    }else{
+      tailEllipsis = 1;
+    }
+    while( iMatch<nMatch && aMatch[iMatch].iCol<iCol ){ iMatch++; }
+    while( iStart<iEnd ){
+      while( iMatch<nMatch && aMatch[iMatch].iStart<iStart
+	     && aMatch[iMatch].iCol<=iCol ){
+	iMatch++;
+      }
+      if( iMatch<nMatch && aMatch[iMatch].iStart<iEnd
+	     && aMatch[iMatch].iCol==iCol ){
+	nappend(&sb, &zDoc[iStart], aMatch[iMatch].iStart - iStart);
+	iStart = aMatch[iMatch].iStart;
+	append(&sb, zStartMark);
+	nappend(&sb, &zDoc[iStart], aMatch[iMatch].nByte);
+	append(&sb, zEndMark);
+	iStart += aMatch[iMatch].nByte;
+	for(j=iMatch+1; j<nMatch; j++){
+	  if( aMatch[j].iTerm==aMatch[iMatch].iTerm
+	      && aMatch[j].snStatus==SNIPPET_DESIRED ){
+	    nDesired--;
+	    aMatch[j].snStatus = SNIPPET_IGNORE;
+	  }
+	}
+      }else{
+	nappend(&sb, &zDoc[iStart], iEnd - iStart);
+	iStart = iEnd;
+      }
+    }
+    tailCol = iCol;
+    tailOffset = iEnd;
+  }
+  trimWhiteSpace(&sb);
+  if( tailEllipsis ){
+    appendWhiteSpace(&sb);
+    append(&sb, zEllipsis);
+  }
+  pCursor->snippet.zSnippet = stringBufferData(&sb);
+  pCursor->snippet.nSnippet = stringBufferLength(&sb);
+}
+
+
+/*
+** Close the cursor.  For additional information see the documentation
+** on the xClose method of the virtual table interface.
+*/
+static int fulltextClose(sqlite3_vtab_cursor *pCursor){
+  fulltext_cursor *c = (fulltext_cursor *) pCursor;
+  FTSTRACE(("FTS3 Close %p\n", c));
+  sqlite3_finalize(c->pStmt);
+  queryClear(&c->q);
+  snippetClear(&c->snippet);
+  if( c->result.nData!=0 ) dlrDestroy(&c->reader);
+  dataBufferDestroy(&c->result);
+  sqlite3_free(c);
+  return SQLITE_OK;
+}
+
+static int fulltextNext(sqlite3_vtab_cursor *pCursor){
+  fulltext_cursor *c = (fulltext_cursor *) pCursor;
+  int rc;
+
+  FTSTRACE(("FTS3 Next %p\n", pCursor));
+  snippetClear(&c->snippet);
+  if( c->iCursorType < QUERY_FULLTEXT ){
+    /* TODO(shess) Handle SQLITE_SCHEMA AND SQLITE_BUSY. */
+    rc = sqlite3_step(c->pStmt);
+    switch( rc ){
+      case SQLITE_ROW:
+	c->eof = 0;
+	return SQLITE_OK;
+      case SQLITE_DONE:
+	c->eof = 1;
+	return SQLITE_OK;
+      default:
+	c->eof = 1;
+	return rc;
+    }
+  } else {  /* full-text query */
+    rc = sqlite3_reset(c->pStmt);
+    if( rc!=SQLITE_OK ) return rc;
+
+    if( c->result.nData==0 || dlrAtEnd(&c->reader) ){
+      c->eof = 1;
+      return SQLITE_OK;
+    }
+    rc = sqlite3_bind_int64(c->pStmt, 1, dlrDocid(&c->reader));
+    dlrStep(&c->reader);
+    if( rc!=SQLITE_OK ) return rc;
+    /* TODO(shess) Handle SQLITE_SCHEMA AND SQLITE_BUSY. */
+    rc = sqlite3_step(c->pStmt);
+    if( rc==SQLITE_ROW ){   /* the case we expect */
+      c->eof = 0;
+      return SQLITE_OK;
+    }
+    /* an error occurred; abort */
+    return rc==SQLITE_DONE ? SQLITE_ERROR : rc;
+  }
+}
+
+
+/* TODO(shess) If we pushed LeafReader to the top of the file, or to
+** another file, term_select() could be pushed above
+** docListOfTerm().
+*/
+static int termSelect(fulltext_vtab *v, int iColumn,
+		      const char *pTerm, int nTerm, int isPrefix,
+		      DocListType iType, DataBuffer *out);
+
+/* Return a DocList corresponding to the query term *pTerm.  If *pTerm
+** is the first term of a phrase query, go ahead and evaluate the phrase
+** query and return the doclist for the entire phrase query.
+**
+** The resulting DL_DOCIDS doclist is stored in pResult, which is
+** overwritten.
+*/
+static int docListOfTerm(
+  fulltext_vtab *v,    /* The full text index */
+  int iColumn,	       /* column to restrict to.  No restriction if >=nColumn */
+  QueryTerm *pQTerm,   /* Term we are looking for, or 1st term of a phrase */
+  DataBuffer *pResult  /* Write the result here */
+){
+  DataBuffer left, right, new;
+  int i, rc;
+
+  /* No phrase search if no position info. */
+  assert( pQTerm->nPhrase==0 || DL_DEFAULT!=DL_DOCIDS );
+
+  /* This code should never be called with buffered updates. */
+  assert( v->nPendingData<0 );
+
+  dataBufferInit(&left, 0);
+  rc = termSelect(v, iColumn, pQTerm->pTerm, pQTerm->nTerm, pQTerm->isPrefix,
+		  (0<pQTerm->nPhrase ? DL_POSITIONS : DL_DOCIDS), &left);
+  if( rc ) return rc;
+  for(i=1; i<=pQTerm->nPhrase && left.nData>0; i++){
+    /* If this token is connected to the next by a NEAR operator, and
+    ** the next token is the start of a phrase, then set nPhraseRight
+    ** to the number of tokens in the phrase. Otherwise leave it at 1.
+    */
+    int nPhraseRight = 1;
+    while( (i+nPhraseRight)<=pQTerm->nPhrase
+	&& pQTerm[i+nPhraseRight].nNear==0
+    ){
+      nPhraseRight++;
+    }
+
+    dataBufferInit(&right, 0);
+    rc = termSelect(v, iColumn, pQTerm[i].pTerm, pQTerm[i].nTerm,
+		    pQTerm[i].isPrefix, DL_POSITIONS, &right);
+    if( rc ){
+      dataBufferDestroy(&left);
+      return rc;
+    }
+    dataBufferInit(&new, 0);
+    docListPhraseMerge(left.pData, left.nData, right.pData, right.nData,
+		       pQTerm[i-1].nNear, pQTerm[i-1].iPhrase + nPhraseRight,
+		       ((i<pQTerm->nPhrase) ? DL_POSITIONS : DL_DOCIDS),
+		       &new);
+    dataBufferDestroy(&left);
+    dataBufferDestroy(&right);
+    left = new;
+  }
+  *pResult = left;
+  return SQLITE_OK;
+}
+
+/* Add a new term pTerm[0..nTerm-1] to the query *q.
+*/
+static void queryAdd(Query *q, const char *pTerm, int nTerm){
+  QueryTerm *t;
+  ++q->nTerms;
+  q->pTerms = sqlite3_realloc(q->pTerms, q->nTerms * sizeof(q->pTerms[0]));
+  if( q->pTerms==0 ){
+    q->nTerms = 0;
+    return;
+  }
+  t = &q->pTerms[q->nTerms - 1];
+  CLEAR(t);
+  t->pTerm = sqlite3_malloc(nTerm+1);
+  memcpy(t->pTerm, pTerm, nTerm);
+  t->pTerm[nTerm] = 0;
+  t->nTerm = nTerm;
+  t->isOr = q->nextIsOr;
+  t->isPrefix = 0;
+  q->nextIsOr = 0;
+  t->iColumn = q->nextColumn;
+  q->nextColumn = q->dfltColumn;
+}
+
+/*
+** Check to see if the string zToken[0...nToken-1] matches any
+** column name in the virtual table.   If it does,
+** return the zero-indexed column number.  If not, return -1.
+*/
+static int checkColumnSpecifier(
+  fulltext_vtab *pVtab,    /* The virtual table */
+  const char *zToken,	   /* Text of the token */
+  int nToken		   /* Number of characters in the token */
+){
+  int i;
+  for(i=0; i<pVtab->nColumn; i++){
+    if( memcmp(pVtab->azColumn[i], zToken, nToken)==0
+	&& pVtab->azColumn[i][nToken]==0 ){
+      return i;
+    }
+  }
+  return -1;
+}
+
+/*
+** Parse the text at pSegment[0..nSegment-1].  Add additional terms
+** to the query being assemblied in pQuery.
+**
+** inPhrase is true if pSegment[0..nSegement-1] is contained within
+** double-quotes.  If inPhrase is true, then the first term
+** is marked with the number of terms in the phrase less one and
+** OR and "-" syntax is ignored.  If inPhrase is false, then every
+** term found is marked with nPhrase=0 and OR and "-" syntax is significant.
+*/
+static int tokenizeSegment(
+  TrackerParser *parser,		  /* The tokenizer to use */
+  const char *pSegment, int nSegment,	  /* Query expression being parsed */
+  int inPhrase,				  /* True if within "..." */
+  Query *pQuery				  /* Append results here */
+){
+  int firstIndex = pQuery->nTerms;
+  int iCol;
+  int nTerm = 1;
+
+  tracker_parser_reset (parser, pSegment, nSegment, FALSE, TRUE, TRUE, TRUE);
+
+  while( 1 ){
+    const char *pToken;
+    int nToken, iBegin, iEnd, iPos, stop_word, new_paragraph;
+
+
+    pToken = tracker_parser_next (parser, &iPos,
+				     &iBegin,
+				     &iEnd,
+				     &new_paragraph,
+				     &stop_word,
+				     &nToken);
+    if (!pToken) {
+      break;
+     }
+
+//   printf("token being indexed  is %s, pos is %d, begin is %d, end is %d and length is %d\n", pToken, iPos, iBegin, iEnd, nToken);
+
+    if( !inPhrase &&
+	pSegment[iEnd]==':') {
+
+	int len = iEnd - iBegin;
+	char *field = g_strndup (pSegment + iBegin, len);
+
+    //	  printf ("field is %s\n", field);
+
+	if ((iCol = checkColumnSpecifier(pQuery->pFts, field, len))>=0 ){
+	   pQuery->nextColumn = iCol;
+	   g_free (field);
+	   continue;
+	}
+    }
+    if( !inPhrase && pQuery->nTerms>0 && nToken==2
+     && pToken[0] == 'o' && pToken[1] == 'r'
+    ){
+      pQuery->nextIsOr = 1;
+      continue;
+    }
+    if( !inPhrase && pQuery->nTerms>0 && !pQuery->nextIsOr && nToken==4
+      && pToken[0]=='n'
+      && pToken[1]=='e'
+      && pToken[2]=='a'
+      && pToken[3]=='r'
+    ){
+      QueryTerm *pTerm = &pQuery->pTerms[pQuery->nTerms-1];
+      if( (iBegin+6)<nSegment
+       && pSegment[iBegin+4] == '/'
+       && pSegment[iBegin+5]>='0' && pSegment[iBegin+5]<='9'
+      ){
+	pTerm->nNear = (pSegment[iBegin+5] - '0');
+	nToken += 2;
+	if( pSegment[iBegin+6]>='0' && pSegment[iBegin+6]<=9 ){
+	  pTerm->nNear = pTerm->nNear * 10 + (pSegment[iBegin+6] - '0');
+	  iEnd++;
+	}
+	pToken = tracker_parser_next (parser, &iPos,
+				     &iBegin,
+				     &iEnd,
+				     &new_paragraph,
+				     &stop_word,
+				     &nToken);
+	if (!pToken) {
+	  break;
+	}
+
+
+      } else {
+	pTerm->nNear = SQLITE_FTS3_DEFAULT_NEAR_PARAM;
+      }
+      pTerm->nNear++;
+      continue;
+    }
+
+    if (stop_word != 0) {
+	continue;
+    }
+
+    queryAdd(pQuery, pToken, nToken);
+    if( !inPhrase && iBegin>0) {
+
+   //  printf("first char is %c, prev char is %c\n", pSegment[iBegin], pSegment[iBegin-1]);
+
+      if (pSegment[iBegin-1]=='-' ){
+	pQuery->pTerms[pQuery->nTerms-1].isNot = 1;
+      }
+    }
+    if( iEnd<nSegment && pSegment[iEnd]=='*' ){
+      pQuery->pTerms[pQuery->nTerms-1].isPrefix = 1;
+    }
+    pQuery->pTerms[pQuery->nTerms-1].iPhrase = nTerm;
+    if( inPhrase ){
+      nTerm++;
+    }
+  }
+
+  if( inPhrase && pQuery->nTerms>firstIndex ){
+    pQuery->pTerms[firstIndex].nPhrase = pQuery->nTerms - firstIndex - 1;
+  }
+
+  return SQLITE_OK;
+}
+
+/* Parse a query string, yielding a Query object pQuery.
+**
+** The calling function will need to queryClear() to clean up
+** the dynamically allocated memory held by pQuery.
+*/
+static int parseQuery(
+  fulltext_vtab *v,	   /* The fulltext index */
+  const char *zInput,	   /* Input text of the query string */
+  int nInput,		   /* Size of the input text */
+  int dfltColumn,	   /* Default column of the index to match against */
+  Query *pQuery		   /* Write the parse results here. */
+){
+  int iInput, inPhrase = 0;
+  int ii;
+  QueryTerm *aTerm;
+
+  if( zInput==0 ) nInput = 0;
+  if( nInput<0 ) nInput = strlen(zInput);
+  pQuery->nTerms = 0;
+  pQuery->pTerms = NULL;
+  pQuery->nextIsOr = 0;
+  pQuery->nextColumn = dfltColumn;
+  pQuery->dfltColumn = dfltColumn;
+  pQuery->pFts = v;
+
+  for(iInput=0; iInput<nInput; ++iInput){
+    int i;
+    for(i=iInput; i<nInput && zInput[i]!='"'; ++i){}
+    if( i>iInput ){
+      tokenizeSegment(v->parser, zInput+iInput, i-iInput, inPhrase,
+		       pQuery);
+    }
+    iInput = i;
+    if( i<nInput ){
+      assert( zInput[i]=='"' );
+      inPhrase = !inPhrase;
+    }
+  }
+
+  if( inPhrase ){
+    /* unmatched quote */
+    queryClear(pQuery);
+    return SQLITE_ERROR;
+  }
+
+  /* Modify the values of the QueryTerm.nPhrase variables to account for
+  ** the NEAR operator. For the purposes of QueryTerm.nPhrase, phrases
+  ** and tokens connected by the NEAR operator are handled as a single
+  ** phrase. See comments above the QueryTerm structure for details.
+  */
+  aTerm = pQuery->pTerms;
+  for(ii=0; ii<pQuery->nTerms; ii++){
+    if( aTerm[ii].nNear || aTerm[ii].nPhrase ){
+      while (aTerm[ii+aTerm[ii].nPhrase].nNear) {
+	aTerm[ii].nPhrase += (1 + aTerm[ii+aTerm[ii].nPhrase+1].nPhrase);
+      }
+    }
+  }
+
+  return SQLITE_OK;
+}
+
+/* TODO(shess) Refactor the code to remove this forward decl. */
+static int flushPendingTerms(fulltext_vtab *v);
+
+/* Perform a full-text query using the search expression in
+** zInput[0..nInput-1].  Return a list of matching documents
+** in pResult.
+**
+** Queries must match column iColumn.  Or if iColumn>=nColumn
+** they are allowed to match against any column.
+*/
+static int fulltextQuery(
+  fulltext_vtab *v,	 /* The full text index */
+  int iColumn,		 /* Match against this column by default */
+  const char *zInput,	 /* The query string */
+  int nInput,		 /* Number of bytes in zInput[] */
+  DataBuffer *pResult,	 /* Write the result doclist here */
+  Query *pQuery		 /* Put parsed query string here */
+){
+  int i, iNext, rc;
+  DataBuffer left, right, or, new;
+  int nNot = 0;
+  QueryTerm *aTerm;
+
+  /* TODO(shess) Instead of flushing pendingTerms, we could query for
+  ** the relevant term and merge the doclist into what we receive from
+  ** the database.  Wait and see if this is a common issue, first.
+  **
+  ** A good reason not to flush is to not generate update-related
+  ** error codes from here.
+  */
+
+  /* Flush any buffered updates before executing the query. */
+  rc = flushPendingTerms(v);
+  if( rc!=SQLITE_OK ) return rc;
+
+  /* TODO(shess) I think that the queryClear() calls below are not
+  ** necessary, because fulltextClose() already clears the query.
+  */
+  rc = parseQuery(v, zInput, nInput, iColumn, pQuery);
+  if( rc!=SQLITE_OK ) return rc;
+
+  /* Empty or NULL queries return no results. */
+  if( pQuery->nTerms==0 ){
+    dataBufferInit(pResult, 0);
+    return SQLITE_OK;
+  }
+
+  /* Merge AND terms. */
+  /* TODO(shess) I think we can early-exit if( i>nNot && left.nData==0 ). */
+  aTerm = pQuery->pTerms;
+  for(i = 0; i<pQuery->nTerms; i=iNext){
+    if( aTerm[i].isNot ){
+      /* Handle all NOT terms in a separate pass */
+      nNot++;
+      iNext = i + aTerm[i].nPhrase+1;
+      continue;
+    }
+    iNext = i + aTerm[i].nPhrase + 1;
+    rc = docListOfTerm(v, aTerm[i].iColumn, &aTerm[i], &right);
+    if( rc ){
+      if( i!=nNot ) dataBufferDestroy(&left);
+      queryClear(pQuery);
+      return rc;
+    }
+    while( iNext<pQuery->nTerms && aTerm[iNext].isOr ){
+      rc = docListOfTerm(v, aTerm[iNext].iColumn, &aTerm[iNext], &or);
+      iNext += aTerm[iNext].nPhrase + 1;
+      if( rc ){
+	if( i!=nNot ) dataBufferDestroy(&left);
+	dataBufferDestroy(&right);
+	queryClear(pQuery);
+	return rc;
+      }
+      dataBufferInit(&new, 0);
+      docListOrMerge(right.pData, right.nData, or.pData, or.nData, &new);
+      dataBufferDestroy(&right);
+      dataBufferDestroy(&or);
+      right = new;
+    }
+    if( i==nNot ){	     /* first term processed. */
+      left = right;
+    }else{
+      dataBufferInit(&new, 0);
+      docListAndMerge(left.pData, left.nData, right.pData, right.nData, &new);
+      dataBufferDestroy(&right);
+      dataBufferDestroy(&left);
+      left = new;
+    }
+  }
+
+  if( nNot==pQuery->nTerms ){
+    /* We do not yet know how to handle a query of only NOT terms */
+    return SQLITE_ERROR;
+  }
+
+  /* Do the EXCEPT terms */
+  for(i=0; i<pQuery->nTerms;  i += aTerm[i].nPhrase + 1){
+    if( !aTerm[i].isNot ) continue;
+    rc = docListOfTerm(v, aTerm[i].iColumn, &aTerm[i], &right);
+    if( rc ){
+      queryClear(pQuery);
+      dataBufferDestroy(&left);
+      return rc;
+    }
+    dataBufferInit(&new, 0);
+    docListExceptMerge(left.pData, left.nData, right.pData, right.nData, &new);
+    dataBufferDestroy(&right);
+    dataBufferDestroy(&left);
+    left = new;
+  }
+
+  *pResult = left;
+  return rc;
+}
+
+/*
+** This is the xFilter interface for the virtual table.  See
+** the virtual table xFilter method documentation for additional
+** information.
+**
+** If idxNum==QUERY_GENERIC then do a full table scan against
+** the %_content table.
+**
+** If idxNum==QUERY_DOCID then do a docid lookup for a single entry
+** in the %_content table.
+**
+** If idxNum>=QUERY_FULLTEXT then use the full text index.  The
+** column on the left-hand side of the MATCH operator is column
+** number idxNum-QUERY_FULLTEXT, 0 indexed.  argv[0] is the right-hand
+** side of the MATCH operator.
+*/
+/* TODO(shess) Upgrade the cursor initialization and destruction to
+** account for fulltextFilter() being called multiple times on the
+** same cursor.  The current solution is very fragile.	Apply fix to
+** fts3 as appropriate.
+*/
+static int fulltextFilter(
+  sqlite3_vtab_cursor *pCursor,     /* The cursor used for this query */
+  int idxNum, const char *idxStr,   /* Which indexing scheme to use */
+  int argc, sqlite3_value **argv    /* Arguments for the indexing scheme */
+){
+  fulltext_cursor *c = (fulltext_cursor *) pCursor;
+  fulltext_vtab *v = cursor_vtab(c);
+  int rc;
+  StringBuffer sb;
+
+  FTSTRACE(("FTS3 Filter %p\n",pCursor));
+
+  initStringBuffer(&sb);
+  append(&sb, "SELECT docid, ");
+  appendList(&sb, v->nColumn, v->azContentColumn);
+  append(&sb, " FROM %_content");
+  if( idxNum!=QUERY_GENERIC ) append(&sb, " WHERE docid = ?");
+  sqlite3_finalize(c->pStmt);
+  rc = sql_prepare(v->db, v->zDb, v->zName, &c->pStmt, stringBufferData(&sb));
+  stringBufferDestroy(&sb);
+  if( rc!=SQLITE_OK ) return rc;
+
+  c->iCursorType = idxNum;
+  switch( idxNum ){
+    case QUERY_GENERIC:
+      break;
+
+    case QUERY_DOCID:
+      rc = sqlite3_bind_int64(c->pStmt, 1, sqlite3_value_int64(argv[0]));
+      if( rc!=SQLITE_OK ) return rc;
+      break;
+
+    default:   /* full-text search */
+    {
+      const char *zQuery = (const char *)sqlite3_value_text(argv[0]);
+      assert( idxNum<=QUERY_FULLTEXT+v->nColumn);
+      assert( argc==1 );
+      queryClear(&c->q);
+      if( c->result.nData!=0 ){
+	/* This case happens if the same cursor is used repeatedly. */
+	dlrDestroy(&c->reader);
+	dataBufferReset(&c->result);
+      }else{
+	dataBufferInit(&c->result, 0);
+      }
+      rc = fulltextQuery(v, idxNum-QUERY_FULLTEXT, zQuery, -1, &c->result, &c->q);
+      if( rc!=SQLITE_OK ) return rc;
+      if( c->result.nData!=0 ){
+	dlrInit(&c->reader, DL_DOCIDS, c->result.pData, c->result.nData);
+      }
+      break;
+    }
+  }
+
+  return fulltextNext(pCursor);
+}
+
+/* This is the xEof method of the virtual table.  The SQLite core
+** calls this routine to find out if it has reached the end of
+** a query's results set.
+*/
+static int fulltextEof(sqlite3_vtab_cursor *pCursor){
+  fulltext_cursor *c = (fulltext_cursor *) pCursor;
+  return c->eof;
+}
+
+/* This is the xColumn method of the virtual table.  The SQLite
+** core calls this method during a query when it needs the value
+** of a column from the virtual table.	This method needs to use
+** one of the sqlite3_result_*() routines to store the requested
+** value back in the pContext.
+*/
+static int fulltextColumn(sqlite3_vtab_cursor *pCursor,
+			  sqlite3_context *pContext, int idxCol){
+  fulltext_cursor *c = (fulltext_cursor *) pCursor;
+  fulltext_vtab *v = cursor_vtab(c);
+
+  if( idxCol<v->nColumn ){
+    sqlite3_value *pVal = sqlite3_column_value(c->pStmt, idxCol+1);
+    sqlite3_result_value(pContext, pVal);
+  }else if( idxCol==v->nColumn ){
+    /* The extra column whose name is the same as the table.
+    ** Return a blob which is a pointer to the cursor
+    */
+    sqlite3_result_blob(pContext, &c, sizeof(c), SQLITE_TRANSIENT);
+  }else if( idxCol==v->nColumn+1 ){
+    /* The docid column, which is an alias for rowid. */
+    sqlite3_value *pVal = sqlite3_column_value(c->pStmt, 0);
+    sqlite3_result_value(pContext, pVal);
+  }
+  return SQLITE_OK;
+}
+
+/* This is the xRowid method.  The SQLite core calls this routine to
+** retrieve the rowid for the current row of the result set.  fts3
+** exposes %_content.docid as the rowid for the virtual table.	The
+** rowid should be written to *pRowid.
+*/
+static int fulltextRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
+  fulltext_cursor *c = (fulltext_cursor *) pCursor;
+
+  *pRowid = sqlite3_column_int64(c->pStmt, 0);
+  return SQLITE_OK;
+}
+
+/* Add all terms in [zText] to pendingTerms table.  If [iColumn] > 0,
+** we also store positions and offsets in the hash table using that
+** column number.
+*/
+static int buildTerms(fulltext_vtab *v, sqlite_int64 iDocid,
+		      const char *zText, int iColumn){
+  const char *pToken;
+  int nTokenBytes;
+  int iStartOffset, iEndOffset, iPosition, stop_word, new_paragraph;
+  int rc;
+  TrackerParser *parser = v->parser;
+
+  if (!zText) return SQLITE_OK;
+
+  tracker_parser_reset (parser, zText, strlen (zText), FALSE, TRUE, TRUE, FALSE);
+
+  while( 1 ){
+
+    pToken = tracker_parser_next (parser, &iPosition,
+				     &iStartOffset,
+				     &iEndOffset,
+				     &new_paragraph,
+				     &stop_word,
+				     &nTokenBytes);
+   if (!pToken) {
+	break;
+   }
+
+  // printf("token being indexed  is %s, begin is %d, end is %d and length is %d\n", pToken, iStartOffset, iEndOffset, nTokenBytes);
+
+   if (stop_word) {
+	continue;
+   }
+
+
+
+
+    DLCollector *p;
+    int nData;			 /* Size of doclist before our update. */
+
+    /* Positions can't be negative; we use -1 as a terminator
+     * internally.  Token can't be NULL or empty. */
+    if( iPosition<0 || pToken == NULL || nTokenBytes == 0 ){
+      rc = SQLITE_ERROR;
+      break;
+    }
+
+    p = fts3HashFind(&v->pendingTerms, pToken, nTokenBytes);
+    if( p==NULL ){
+      nData = 0;
+      p = dlcNew(iDocid, DL_DEFAULT);
+      fts3HashInsert(&v->pendingTerms, pToken, nTokenBytes, p);
+
+      /* Overhead for our hash table entry, the key, and the value. */
+      v->nPendingData += sizeof(struct fts3HashElem)+sizeof(*p)+nTokenBytes;
+    }else{
+      nData = p->b.nData;
+      if( p->dlw.iPrevDocid!=iDocid ) dlcNext(p, iDocid);
+    }
+    if( iColumn>=0 ){
+      dlcAddPos(p, iColumn, iPosition, iStartOffset, iEndOffset);
+    }
+
+    /* Accumulate data added by dlcNew or dlcNext, and dlcAddPos. */
+    v->nPendingData += p->b.nData-nData;
+  }
+
+  /* TODO(shess) Check return?	Should this be able to cause errors at
+  ** this point?  Actually, same question about sqlite3_finalize(),
+  ** though one could argue that failure there means that the data is
+  ** not durable.  *ponder*
+  */
+
+  return SQLITE_OK;
+
+}
+
+/* Add doclists for all terms in [pValues] to pendingTerms table. */
+static int insertTerms(fulltext_vtab *v, sqlite_int64 iDocid,
+		       sqlite3_value **pValues){
+  int i;
+  for(i = 0; i < v->nColumn ; ++i){
+    char *zText = (char*)sqlite3_value_text(pValues[i]);
+    int rc = buildTerms(v, iDocid, zText, i);
+    if( rc!=SQLITE_OK ) return rc;
+  }
+  return SQLITE_OK;
+}
+
+/* Add empty doclists for all terms in the given row's content to
+** pendingTerms.
+*/
+static int deleteTerms(fulltext_vtab *v, sqlite_int64 iDocid){
+  const char **pValues;
+  int i, rc;
+
+  /* TODO(shess) Should we allow such tables at all? */
+  if( DL_DEFAULT==DL_DOCIDS ) return SQLITE_ERROR;
+
+  rc = content_select(v, iDocid, &pValues);
+  if( rc!=SQLITE_OK ) return rc;
+
+  for(i = 0 ; i < v->nColumn; ++i) {
+    rc = buildTerms(v, iDocid, pValues[i], -1);
+    if( rc!=SQLITE_OK ) break;
+  }
+
+  freeStringArray(v->nColumn, pValues);
+  return SQLITE_OK;
+}
+
+/* TODO(shess) Refactor the code to remove this forward decl. */
+static int initPendingTerms(fulltext_vtab *v, sqlite_int64 iDocid);
+
+/* Insert a row into the %_content table; set *piDocid to be the ID of the
+** new row.  Add doclists for terms to pendingTerms.
+*/
+static int index_insert(fulltext_vtab *v, sqlite3_value *pRequestDocid,
+			sqlite3_value **pValues, sqlite_int64 *piDocid){
+  int rc;
+
+  rc = content_insert(v, pRequestDocid, pValues);  /* execute an SQL INSERT */
+  if( rc!=SQLITE_OK ) return rc;
+
+  /* docid column is an alias for rowid. */
+  *piDocid = sqlite3_last_insert_rowid(v->db);
+  rc = initPendingTerms(v, *piDocid);
+  if( rc!=SQLITE_OK ) return rc;
+
+  return insertTerms(v, *piDocid, pValues);
+}
+
+/* Delete a row from the %_content table; add empty doclists for terms
+** to pendingTerms.
+*/
+static int index_delete(fulltext_vtab *v, sqlite_int64 iRow){
+  int rc = initPendingTerms(v, iRow);
+  if( rc!=SQLITE_OK ) return rc;
+
+  rc = deleteTerms(v, iRow);
+  if( rc!=SQLITE_OK ) return rc;
+
+  return content_delete(v, iRow);  /* execute an SQL DELETE */
+}
+
+/* Update a row in the %_content table; add delete doclists to
+** pendingTerms for old terms not in the new data, add insert doclists
+** to pendingTerms for terms in the new data.
+*/
+static int index_update(fulltext_vtab *v, sqlite_int64 iRow,
+			sqlite3_value **pValues){
+  int rc = initPendingTerms(v, iRow);
+  if( rc!=SQLITE_OK ) return rc;
+
+  /* Generate an empty doclist for each term that previously appeared in this
+   * row. */
+  rc = deleteTerms(v, iRow);
+  if( rc!=SQLITE_OK ) return rc;
+
+  rc = content_update(v, pValues, iRow);  /* execute an SQL UPDATE */
+  if( rc!=SQLITE_OK ) return rc;
+
+  /* Now add positions for terms which appear in the updated row. */
+  return insertTerms(v, iRow, pValues);
+}
+
+/*******************************************************************/
+/* InteriorWriter is used to collect terms and block references into
+** interior nodes in %_segments.  See commentary at top of file for
+** format.
+*/
+
+/* How large interior nodes can grow. */
+#define INTERIOR_MAX 2048
+
+/* Minimum number of terms per interior node (except the root). This
+** prevents large terms from making the tree too skinny - must be >0
+** so that the tree always makes progress.  Note that the min tree
+** fanout will be INTERIOR_MIN_TERMS+1.
+*/
+#define INTERIOR_MIN_TERMS 7
+#if INTERIOR_MIN_TERMS<1
+# error INTERIOR_MIN_TERMS must be greater than 0.
+#endif
+
+/* ROOT_MAX controls how much data is stored inline in the segment
+** directory.
+*/
+/* TODO(shess) Push ROOT_MAX down to whoever is writing things.  It's
+** only here so that interiorWriterRootInfo() and leafWriterRootInfo()
+** can both see it, but if the caller passed it in, we wouldn't even
+** need a define.
+*/
+#define ROOT_MAX 1024
+#if ROOT_MAX<VARINT_MAX*2
+# error ROOT_MAX must have enough space for a header.
+#endif
+
+/* InteriorBlock stores a linked-list of interior blocks while a lower
+** layer is being constructed.
+*/
+typedef struct InteriorBlock {
+  DataBuffer term;	     /* Leftmost term in block's subtree. */
+  DataBuffer data;	     /* Accumulated data for the block. */
+  struct InteriorBlock *next;
+} InteriorBlock;
+
+static InteriorBlock *interiorBlockNew(int iHeight, sqlite_int64 iChildBlock,
+				       const char *pTerm, int nTerm){
+  InteriorBlock *block = sqlite3_malloc(sizeof(InteriorBlock));
+  char c[VARINT_MAX+VARINT_MAX];
+  int n;
+
+  if( block ){
+    memset(block, 0, sizeof(*block));
+    dataBufferInit(&block->term, 0);
+    dataBufferReplace(&block->term, pTerm, nTerm);
+
+    n = fts3PutVarint(c, iHeight);
+    n += fts3PutVarint(c+n, iChildBlock);
+    dataBufferInit(&block->data, INTERIOR_MAX);
+    dataBufferReplace(&block->data, c, n);
+  }
+  return block;
+}
+
+#ifndef NDEBUG
+/* Verify that the data is readable as an interior node. */
+static void interiorBlockValidate(InteriorBlock *pBlock){
+  const char *pData = pBlock->data.pData;
+  int nData = pBlock->data.nData;
+  int n, iDummy;
+  sqlite_int64 iBlockid;
+
+  assert( nData>0 );
+  assert( pData!=0 );
+  assert( pData+nData>pData );
+
+  /* Must lead with height of node as a varint(n), n>0 */
+  n = fts3GetVarint32(pData, &iDummy);
+  assert( n>0 );
+  assert( iDummy>0 );
+  assert( n<nData );
+  pData += n;
+  nData -= n;
+
+  /* Must contain iBlockid. */
+  n = fts3GetVarint(pData, &iBlockid);
+  assert( n>0 );
+  assert( n<=nData );
+  pData += n;
+  nData -= n;
+
+  /* Zero or more terms of positive length */
+  if( nData!=0 ){
+    /* First term is not delta-encoded. */
+    n = fts3GetVarint32(pData, &iDummy);
+    assert( n>0 );
+    assert( iDummy>0 );
+    assert( n+iDummy>0);
+    assert( n+iDummy<=nData );
+    pData += n+iDummy;
+    nData -= n+iDummy;
+
+    /* Following terms delta-encoded. */
+    while( nData!=0 ){
+      /* Length of shared prefix. */
+      n = fts3GetVarint32(pData, &iDummy);
+      assert( n>0 );
+      assert( iDummy>=0 );
+      assert( n<nData );
+      pData += n;
+      nData -= n;
+
+      /* Length and data of distinct suffix. */
+      n = fts3GetVarint32(pData, &iDummy);
+      assert( n>0 );
+      assert( iDummy>0 );
+      assert( n+iDummy>0);
+      assert( n+iDummy<=nData );
+      pData += n+iDummy;
+      nData -= n+iDummy;
+    }
+  }
+}
+#define ASSERT_VALID_INTERIOR_BLOCK(x) interiorBlockValidate(x)
+#else
+#define ASSERT_VALID_INTERIOR_BLOCK(x) assert( 1 )
+#endif
+
+typedef struct InteriorWriter {
+  int iHeight;			 /* from 0 at leaves. */
+  InteriorBlock *first, *last;
+  struct InteriorWriter *parentWriter;
+
+  DataBuffer term;		 /* Last term written to block "last". */
+  sqlite_int64 iOpeningChildBlock; /* First child block in block "last". */
+#ifndef NDEBUG
+  sqlite_int64 iLastChildBlock;  /* for consistency checks. */
+#endif
+} InteriorWriter;
+
+/* Initialize an interior node where pTerm[nTerm] marks the leftmost
+** term in the tree.  iChildBlock is the leftmost child block at the
+** next level down the tree.
+*/
+static void interiorWriterInit(int iHeight, const char *pTerm, int nTerm,
+			       sqlite_int64 iChildBlock,
+			       InteriorWriter *pWriter){
+  InteriorBlock *block;
+  assert( iHeight>0 );
+  CLEAR(pWriter);
+
+  pWriter->iHeight = iHeight;
+  pWriter->iOpeningChildBlock = iChildBlock;
+#ifndef NDEBUG
+  pWriter->iLastChildBlock = iChildBlock;
+#endif
+  block = interiorBlockNew(iHeight, iChildBlock, pTerm, nTerm);
+  pWriter->last = pWriter->first = block;
+  ASSERT_VALID_INTERIOR_BLOCK(pWriter->last);
+  dataBufferInit(&pWriter->term, 0);
+}
+
+/* Append the child node rooted at iChildBlock to the interior node,
+** with pTerm[nTerm] as the leftmost term in iChildBlock's subtree.
+*/
+static void interiorWriterAppend(InteriorWriter *pWriter,
+				 const char *pTerm, int nTerm,
+				 sqlite_int64 iChildBlock){
+  char c[VARINT_MAX+VARINT_MAX];
+  int n, nPrefix = 0;
+
+  ASSERT_VALID_INTERIOR_BLOCK(pWriter->last);
+
+  /* The first term written into an interior node is actually
+  ** associated with the second child added (the first child was added
+  ** in interiorWriterInit, or in the if clause at the bottom of this
+  ** function).  That term gets encoded straight up, with nPrefix left
+  ** at 0.
+  */
+  if( pWriter->term.nData==0 ){
+    n = fts3PutVarint(c, nTerm);
+  }else{
+    while( nPrefix<pWriter->term.nData &&
+	   pTerm[nPrefix]==pWriter->term.pData[nPrefix] ){
+      nPrefix++;
+    }
+
+    n = fts3PutVarint(c, nPrefix);
+    n += fts3PutVarint(c+n, nTerm-nPrefix);
+  }
+
+#ifndef NDEBUG
+  pWriter->iLastChildBlock++;
+#endif
+  assert( pWriter->iLastChildBlock==iChildBlock );
+
+  /* Overflow to a new block if the new term makes the current block
+  ** too big, and the current block already has enough terms.
+  */
+  if( pWriter->last->data.nData+n+nTerm-nPrefix>INTERIOR_MAX &&
+      iChildBlock-pWriter->iOpeningChildBlock>INTERIOR_MIN_TERMS ){
+    pWriter->last->next = interiorBlockNew(pWriter->iHeight, iChildBlock,
+					   pTerm, nTerm);
+    pWriter->last = pWriter->last->next;
+    pWriter->iOpeningChildBlock = iChildBlock;
+    dataBufferReset(&pWriter->term);
+  }else{
+    dataBufferAppend2(&pWriter->last->data, c, n,
+		      pTerm+nPrefix, nTerm-nPrefix);
+    dataBufferReplace(&pWriter->term, pTerm, nTerm);
+  }
+  ASSERT_VALID_INTERIOR_BLOCK(pWriter->last);
+}
+
+/* Free the space used by pWriter, including the linked-list of
+** InteriorBlocks, and parentWriter, if present.
+*/
+static int interiorWriterDestroy(InteriorWriter *pWriter){
+  InteriorBlock *block = pWriter->first;
+
+  while( block!=NULL ){
+    InteriorBlock *b = block;
+    block = block->next;
+    dataBufferDestroy(&b->term);
+    dataBufferDestroy(&b->data);
+    sqlite3_free(b);
+  }
+  if( pWriter->parentWriter!=NULL ){
+    interiorWriterDestroy(pWriter->parentWriter);
+    sqlite3_free(pWriter->parentWriter);
+  }
+  dataBufferDestroy(&pWriter->term);
+  SCRAMBLE(pWriter);
+  return SQLITE_OK;
+}
+
+/* If pWriter can fit entirely in ROOT_MAX, return it as the root info
+** directly, leaving *piEndBlockid unchanged.  Otherwise, flush
+** pWriter to %_segments, building a new layer of interior nodes, and
+** recursively ask for their root into.
+*/
+static int interiorWriterRootInfo(fulltext_vtab *v, InteriorWriter *pWriter,
+				  char **ppRootInfo, int *pnRootInfo,
+				  sqlite_int64 *piEndBlockid){
+  InteriorBlock *block = pWriter->first;
+  sqlite_int64 iBlockid = 0;
+  int rc;
+
+  /* If we can fit the segment inline */
+  if( block==pWriter->last && block->data.nData<ROOT_MAX ){
+    *ppRootInfo = block->data.pData;
+    *pnRootInfo = block->data.nData;
+    return SQLITE_OK;
+  }
+
+  /* Flush the first block to %_segments, and create a new level of
+  ** interior node.
+  */
+  ASSERT_VALID_INTERIOR_BLOCK(block);
+  rc = block_insert(v, block->data.pData, block->data.nData, &iBlockid);
+  if( rc!=SQLITE_OK ) return rc;
+  *piEndBlockid = iBlockid;
+
+  pWriter->parentWriter = sqlite3_malloc(sizeof(*pWriter->parentWriter));
+  interiorWriterInit(pWriter->iHeight+1,
+		     block->term.pData, block->term.nData,
+		     iBlockid, pWriter->parentWriter);
+
+  /* Flush additional blocks and append to the higher interior
+  ** node.
+  */
+  for(block=block->next; block!=NULL; block=block->next){
+    ASSERT_VALID_INTERIOR_BLOCK(block);
+    rc = block_insert(v, block->data.pData, block->data.nData, &iBlockid);
+    if( rc!=SQLITE_OK ) return rc;
+    *piEndBlockid = iBlockid;
+
+    interiorWriterAppend(pWriter->parentWriter,
+			 block->term.pData, block->term.nData, iBlockid);
+  }
+
+  /* Parent node gets the chance to be the root. */
+  return interiorWriterRootInfo(v, pWriter->parentWriter,
+				ppRootInfo, pnRootInfo, piEndBlockid);
+}
+
+/****************************************************************/
+/* InteriorReader is used to read off the data from an interior node
+** (see comment at top of file for the format).
+*/
+typedef struct InteriorReader {
+  const char *pData;
+  int nData;
+
+  DataBuffer term;	    /* previous term, for decoding term delta. */
+
+  sqlite_int64 iBlockid;
+} InteriorReader;
+
+static void interiorReaderDestroy(InteriorReader *pReader){
+  dataBufferDestroy(&pReader->term);
+  SCRAMBLE(pReader);
+}
+
+/* TODO(shess) The assertions are great, but what if we're in NDEBUG
+** and the blob is empty or otherwise contains suspect data?
+*/
+static void interiorReaderInit(const char *pData, int nData,
+			       InteriorReader *pReader){
+  int n, nTerm;
+
+  /* Require at least the leading flag byte */
+  assert( nData>0 );
+  assert( pData[0]!='\0' );
+
+  CLEAR(pReader);
+
+  /* Decode the base blockid, and set the cursor to the first term. */
+  n = fts3GetVarint(pData+1, &pReader->iBlockid);
+  assert( 1+n<=nData );
+  pReader->pData = pData+1+n;
+  pReader->nData = nData-(1+n);
+
+  /* A single-child interior node (such as when a leaf node was too
+  ** large for the segment directory) won't have any terms.
+  ** Otherwise, decode the first term.
+  */
+  if( pReader->nData==0 ){
+    dataBufferInit(&pReader->term, 0);
+  }else{
+    n = fts3GetVarint32(pReader->pData, &nTerm);
+    dataBufferInit(&pReader->term, nTerm);
+    dataBufferReplace(&pReader->term, pReader->pData+n, nTerm);
+    assert( n+nTerm<=pReader->nData );
+    pReader->pData += n+nTerm;
+    pReader->nData -= n+nTerm;
+  }
+}
+
+static int interiorReaderAtEnd(InteriorReader *pReader){
+  return pReader->term.nData==0;
+}
+
+static sqlite_int64 interiorReaderCurrentBlockid(InteriorReader *pReader){
+  return pReader->iBlockid;
+}
+
+static int interiorReaderTermBytes(InteriorReader *pReader){
+  assert( !interiorReaderAtEnd(pReader) );
+  return pReader->term.nData;
+}
+static const char *interiorReaderTerm(InteriorReader *pReader){
+  assert( !interiorReaderAtEnd(pReader) );
+  return pReader->term.pData;
+}
+
+/* Step forward to the next term in the node. */
+static void interiorReaderStep(InteriorReader *pReader){
+  assert( !interiorReaderAtEnd(pReader) );
+
+  /* If the last term has been read, signal eof, else construct the
+  ** next term.
+  */
+  if( pReader->nData==0 ){
+    dataBufferReset(&pReader->term);
+  }else{
+    int n, nPrefix, nSuffix;
+
+    n = fts3GetVarint32(pReader->pData, &nPrefix);
+    n += fts3GetVarint32(pReader->pData+n, &nSuffix);
+
+    /* Truncate the current term and append suffix data. */
+    pReader->term.nData = nPrefix;
+    dataBufferAppend(&pReader->term, pReader->pData+n, nSuffix);
+
+    assert( n+nSuffix<=pReader->nData );
+    pReader->pData += n+nSuffix;
+    pReader->nData -= n+nSuffix;
+  }
+  pReader->iBlockid++;
+}
+
+/* Compare the current term to pTerm[nTerm], returning strcmp-style
+** results.  If isPrefix, equality means equal through nTerm bytes.
+*/
+static int interiorReaderTermCmp(InteriorReader *pReader,
+				 const char *pTerm, int nTerm, int isPrefix){
+  const char *pReaderTerm = interiorReaderTerm(pReader);
+  int nReaderTerm = interiorReaderTermBytes(pReader);
+  int c, n = nReaderTerm<nTerm ? nReaderTerm : nTerm;
+
+  if( n==0 ){
+    if( nReaderTerm>0 ) return -1;
+    if( nTerm>0 ) return 1;
+    return 0;
+  }
+
+  c = memcmp(pReaderTerm, pTerm, n);
+  if( c!=0 ) return c;
+  if( isPrefix && n==nTerm ) return 0;
+  return nReaderTerm - nTerm;
+}
+
+/****************************************************************/
+/* LeafWriter is used to collect terms and associated doclist data
+** into leaf blocks in %_segments (see top of file for format info).
+** Expected usage is:
+**
+** LeafWriter writer;
+** leafWriterInit(0, 0, &writer);
+** while( sorted_terms_left_to_process ){
+**   // data is doclist data for that term.
+**   rc = leafWriterStep(v, &writer, pTerm, nTerm, pData, nData);
+**   if( rc!=SQLITE_OK ) goto err;
+** }
+** rc = leafWriterFinalize(v, &writer);
+**err:
+** leafWriterDestroy(&writer);
+** return rc;
+**
+** leafWriterStep() may write a collected leaf out to %_segments.
+** leafWriterFinalize() finishes writing any buffered data and stores
+** a root node in %_segdir.  leafWriterDestroy() frees all buffers and
+** InteriorWriters allocated as part of writing this segment.
+**
+** TODO(shess) Document leafWriterStepMerge().
+*/
+
+/* Put terms with data this big in their own block. */
+#define STANDALONE_MIN 1024
+
+/* Keep leaf blocks below this size. */
+#define LEAF_MAX 2048
+
+typedef struct LeafWriter {
+  int iLevel;
+  int idx;
+  sqlite_int64 iStartBlockid;	  /* needed to create the root info */
+  sqlite_int64 iEndBlockid;	  /* when we're done writing. */
+
+  DataBuffer term;		  /* previous encoded term */
+  DataBuffer data;		  /* encoding buffer */
+
+  /* bytes of first term in the current node which distinguishes that
+  ** term from the last term of the previous node.
+  */
+  int nTermDistinct;
+
+  InteriorWriter parentWriter;	  /* if we overflow */
+  int has_parent;
+} LeafWriter;
+
+static void leafWriterInit(int iLevel, int idx, LeafWriter *pWriter){
+  CLEAR(pWriter);
+  pWriter->iLevel = iLevel;
+  pWriter->idx = idx;
+
+  dataBufferInit(&pWriter->term, 32);
+
+  /* Start out with a reasonably sized block, though it can grow. */
+  dataBufferInit(&pWriter->data, LEAF_MAX);
+}
+
+#ifndef NDEBUG
+/* Verify that the data is readable as a leaf node. */
+static void leafNodeValidate(const char *pData, int nData){
+  int n, iDummy;
+
+  if( nData==0 ) return;
+  assert( nData>0 );
+  assert( pData!=0 );
+  assert( pData+nData>pData );
+
+  /* Must lead with a varint(0) */
+  n = fts3GetVarint32(pData, &iDummy);
+  assert( iDummy==0 );
+  assert( n>0 );
+  assert( n<nData );
+  pData += n;
+  nData -= n;
+
+  /* Leading term length and data must fit in buffer. */
+  n = fts3GetVarint32(pData, &iDummy);
+  assert( n>0 );
+  assert( iDummy>0 );
+  assert( n+iDummy>0 );
+  assert( n+iDummy<nData );
+  pData += n+iDummy;
+  nData -= n+iDummy;
+
+  /* Leading term's doclist length and data must fit. */
+  n = fts3GetVarint32(pData, &iDummy);
+  assert( n>0 );
+  assert( iDummy>0 );
+  assert( n+iDummy>0 );
+  assert( n+iDummy<=nData );
+  ASSERT_VALID_DOCLIST(DL_DEFAULT, pData+n, iDummy, NULL);
+  pData += n+iDummy;
+  nData -= n+iDummy;
+
+  /* Verify that trailing terms and doclists also are readable. */
+  while( nData!=0 ){
+    n = fts3GetVarint32(pData, &iDummy);
+    assert( n>0 );
+    assert( iDummy>=0 );
+    assert( n<nData );
+    pData += n;
+    nData -= n;
+    n = fts3GetVarint32(pData, &iDummy);
+    assert( n>0 );
+    assert( iDummy>0 );
+    assert( n+iDummy>0 );
+    assert( n+iDummy<nData );
+    pData += n+iDummy;
+    nData -= n+iDummy;
+
+    n = fts3GetVarint32(pData, &iDummy);
+    assert( n>0 );
+    assert( iDummy>0 );
+    assert( n+iDummy>0 );
+    assert( n+iDummy<=nData );
+    ASSERT_VALID_DOCLIST(DL_DEFAULT, pData+n, iDummy, NULL);
+    pData += n+iDummy;
+    nData -= n+iDummy;
+  }
+}
+#define ASSERT_VALID_LEAF_NODE(p, n) leafNodeValidate(p, n)
+#else
+#define ASSERT_VALID_LEAF_NODE(p, n) assert( 1 )
+#endif
+
+/* Flush the current leaf node to %_segments, and adding the resulting
+** blockid and the starting term to the interior node which will
+** contain it.
+*/
+static int leafWriterInternalFlush(fulltext_vtab *v, LeafWriter *pWriter,
+				   int iData, int nData){
+  sqlite_int64 iBlockid = 0;
+  const char *pStartingTerm;
+  int nStartingTerm, rc, n;
+
+  /* Must have the leading varint(0) flag, plus at least some
+  ** valid-looking data.
+  */
+  assert( nData>2 );
+  assert( iData>=0 );
+  assert( iData+nData<=pWriter->data.nData );
+  ASSERT_VALID_LEAF_NODE(pWriter->data.pData+iData, nData);
+
+  rc = block_insert(v, pWriter->data.pData+iData, nData, &iBlockid);
+  if( rc!=SQLITE_OK ) return rc;
+  assert( iBlockid!=0 );
+
+  /* Reconstruct the first term in the leaf for purposes of building
+  ** the interior node.
+  */
+  n = fts3GetVarint32(pWriter->data.pData+iData+1, &nStartingTerm);
+  pStartingTerm = pWriter->data.pData+iData+1+n;
+  assert( pWriter->data.nData>iData+1+n+nStartingTerm );
+  assert( pWriter->nTermDistinct>0 );
+  assert( pWriter->nTermDistinct<=nStartingTerm );
+  nStartingTerm = pWriter->nTermDistinct;
+
+  if( pWriter->has_parent ){
+    interiorWriterAppend(&pWriter->parentWriter,
+			 pStartingTerm, nStartingTerm, iBlockid);
+  }else{
+    interiorWriterInit(1, pStartingTerm, nStartingTerm, iBlockid,
+		       &pWriter->parentWriter);
+    pWriter->has_parent = 1;
+  }
+
+  /* Track the span of this segment's leaf nodes. */
+  if( pWriter->iEndBlockid==0 ){
+    pWriter->iEndBlockid = pWriter->iStartBlockid = iBlockid;
+  }else{
+    pWriter->iEndBlockid++;
+    assert( iBlockid==pWriter->iEndBlockid );
+  }
+
+  return SQLITE_OK;
+}
+static int leafWriterFlush(fulltext_vtab *v, LeafWriter *pWriter){
+  int rc = leafWriterInternalFlush(v, pWriter, 0, pWriter->data.nData);
+  if( rc!=SQLITE_OK ) return rc;
+
+  /* Re-initialize the output buffer. */
+  dataBufferReset(&pWriter->data);
+
+  return SQLITE_OK;
+}
+
+/* Fetch the root info for the segment.  If the entire leaf fits
+** within ROOT_MAX, then it will be returned directly, otherwise it
+** will be flushed and the root info will be returned from the
+** interior node.  *piEndBlockid is set to the blockid of the last
+** interior or leaf node written to disk (0 if none are written at
+** all).
+*/
+static int leafWriterRootInfo(fulltext_vtab *v, LeafWriter *pWriter,
+			      char **ppRootInfo, int *pnRootInfo,
+			      sqlite_int64 *piEndBlockid){
+  /* we can fit the segment entirely inline */
+  if( !pWriter->has_parent && pWriter->data.nData<ROOT_MAX ){
+    *ppRootInfo = pWriter->data.pData;
+    *pnRootInfo = pWriter->data.nData;
+    *piEndBlockid = 0;
+    return SQLITE_OK;
+  }
+
+  /* Flush remaining leaf data. */
+  if( pWriter->data.nData>0 ){
+    int rc = leafWriterFlush(v, pWriter);
+    if( rc!=SQLITE_OK ) return rc;
+  }
+
+  /* We must have flushed a leaf at some point. */
+  assert( pWriter->has_parent );
+
+  /* Tenatively set the end leaf blockid as the end blockid.  If the
+  ** interior node can be returned inline, this will be the final
+  ** blockid, otherwise it will be overwritten by
+  ** interiorWriterRootInfo().
+  */
+  *piEndBlockid = pWriter->iEndBlockid;
+
+  return interiorWriterRootInfo(v, &pWriter->parentWriter,
+				ppRootInfo, pnRootInfo, piEndBlockid);
+}
+
+/* Collect the rootInfo data and store it into the segment directory.
+** This has the effect of flushing the segment's leaf data to
+** %_segments, and also flushing any interior nodes to %_segments.
+*/
+static int leafWriterFinalize(fulltext_vtab *v, LeafWriter *pWriter){
+  sqlite_int64 iEndBlockid;
+  char *pRootInfo;
+  int rc, nRootInfo;
+
+  rc = leafWriterRootInfo(v, pWriter, &pRootInfo, &nRootInfo, &iEndBlockid);
+  if( rc!=SQLITE_OK ) return rc;
+
+  /* Don't bother storing an entirely empty segment. */
+  if( iEndBlockid==0 && nRootInfo==0 ) return SQLITE_OK;
+
+  return segdir_set(v, pWriter->iLevel, pWriter->idx,
+		    pWriter->iStartBlockid, pWriter->iEndBlockid,
+		    iEndBlockid, pRootInfo, nRootInfo);
+}
+
+static void leafWriterDestroy(LeafWriter *pWriter){
+  if( pWriter->has_parent ) interiorWriterDestroy(&pWriter->parentWriter);
+  dataBufferDestroy(&pWriter->term);
+  dataBufferDestroy(&pWriter->data);
+}
+
+/* Encode a term into the leafWriter, delta-encoding as appropriate.
+** Returns the length of the new term which distinguishes it from the
+** previous term, which can be used to set nTermDistinct when a node
+** boundary is crossed.
+*/
+static int leafWriterEncodeTerm(LeafWriter *pWriter,
+				const char *pTerm, int nTerm){
+  char c[VARINT_MAX+VARINT_MAX];
+  int n, nPrefix = 0;
+
+  assert( nTerm>0 );
+  while( nPrefix<pWriter->term.nData &&
+	 pTerm[nPrefix]==pWriter->term.pData[nPrefix] ){
+    nPrefix++;
+    /* Failing this implies that the terms weren't in order. */
+    assert( nPrefix<nTerm );
+  }
+
+  if( pWriter->data.nData==0 ){
+    /* Encode the node header and leading term as:
+    **	varint(0)
+    **	varint(nTerm)
+    **	char pTerm[nTerm]
+    */
+    n = fts3PutVarint(c, '\0');
+    n += fts3PutVarint(c+n, nTerm);
+    dataBufferAppend2(&pWriter->data, c, n, pTerm, nTerm);
+  }else{
+    /* Delta-encode the term as:
+    **	varint(nPrefix)
+    **	varint(nSuffix)
+    **	char pTermSuffix[nSuffix]
+    */
+    n = fts3PutVarint(c, nPrefix);
+    n += fts3PutVarint(c+n, nTerm-nPrefix);
+    dataBufferAppend2(&pWriter->data, c, n, pTerm+nPrefix, nTerm-nPrefix);
+  }
+  dataBufferReplace(&pWriter->term, pTerm, nTerm);
+
+  return nPrefix+1;
+}
+
+/* Used to avoid a memmove when a large amount of doclist data is in
+** the buffer.	This constructs a node and term header before
+** iDoclistData and flushes the resulting complete node using
+** leafWriterInternalFlush().
+*/
+static int leafWriterInlineFlush(fulltext_vtab *v, LeafWriter *pWriter,
+				 const char *pTerm, int nTerm,
+				 int iDoclistData){
+  char c[VARINT_MAX+VARINT_MAX];
+  int iData, n = fts3PutVarint(c, 0);
+  n += fts3PutVarint(c+n, nTerm);
+
+  /* There should always be room for the header.  Even if pTerm shared
+  ** a substantial prefix with the previous term, the entire prefix
+  ** could be constructed from earlier data in the doclist, so there
+  ** should be room.
+  */
+  assert( iDoclistData>=n+nTerm );
+
+  iData = iDoclistData-(n+nTerm);
+  memcpy(pWriter->data.pData+iData, c, n);
+  memcpy(pWriter->data.pData+iData+n, pTerm, nTerm);
+
+  return leafWriterInternalFlush(v, pWriter, iData, pWriter->data.nData-iData);
+}
+
+/* Push pTerm[nTerm] along with the doclist data to the leaf layer of
+** %_segments.
+*/
+static int leafWriterStepMerge(fulltext_vtab *v, LeafWriter *pWriter,
+			       const char *pTerm, int nTerm,
+			       DLReader *pReaders, int nReaders){
+  char c[VARINT_MAX+VARINT_MAX];
+  int iTermData = pWriter->data.nData, iDoclistData;
+  int i, nData, n, nActualData, nActual, rc, nTermDistinct;
+
+  ASSERT_VALID_LEAF_NODE(pWriter->data.pData, pWriter->data.nData);
+  nTermDistinct = leafWriterEncodeTerm(pWriter, pTerm, nTerm);
+
+  /* Remember nTermDistinct if opening a new node. */
+  if( iTermData==0 ) pWriter->nTermDistinct = nTermDistinct;
+
+  iDoclistData = pWriter->data.nData;
+
+  /* Estimate the length of the merged doclist so we can leave space
+  ** to encode it.
+  */
+  for(i=0, nData=0; i<nReaders; i++){
+    nData += dlrAllDataBytes(&pReaders[i]);
+  }
+  n = fts3PutVarint(c, nData);
+  dataBufferAppend(&pWriter->data, c, n);
+
+  docListMerge(&pWriter->data, pReaders, nReaders);
+  ASSERT_VALID_DOCLIST(DL_DEFAULT,
+		       pWriter->data.pData+iDoclistData+n,
+		       pWriter->data.nData-iDoclistData-n, NULL);
+
+  /* The actual amount of doclist data at this point could be smaller
+  ** than the length we encoded.  Additionally, the space required to
+  ** encode this length could be smaller.  For small doclists, this is
+  ** not a big deal, we can just use memmove() to adjust things.
+  */
+  nActualData = pWriter->data.nData-(iDoclistData+n);
+  nActual = fts3PutVarint(c, nActualData);
+  assert( nActualData<=nData );
+  assert( nActual<=n );
+
+  /* If the new doclist is big enough for force a standalone leaf
+  ** node, we can immediately flush it inline without doing the
+  ** memmove().
+  */
+  /* TODO(shess) This test matches leafWriterStep(), which does this
+  ** test before it knows the cost to varint-encode the term and
+  ** doclist lengths.  At some point, change to
+  ** pWriter->data.nData-iTermData>STANDALONE_MIN.
+  */
+  if( nTerm+nActualData>STANDALONE_MIN ){
+    /* Push leaf node from before this term. */
+    if( iTermData>0 ){
+      rc = leafWriterInternalFlush(v, pWriter, 0, iTermData);
+      if( rc!=SQLITE_OK ) return rc;
+
+      pWriter->nTermDistinct = nTermDistinct;
+    }
+
+    /* Fix the encoded doclist length. */
+    iDoclistData += n - nActual;
+    memcpy(pWriter->data.pData+iDoclistData, c, nActual);
+
+    /* Push the standalone leaf node. */
+    rc = leafWriterInlineFlush(v, pWriter, pTerm, nTerm, iDoclistData);
+    if( rc!=SQLITE_OK ) return rc;
+
+    /* Leave the node empty. */
+    dataBufferReset(&pWriter->data);
+
+    return rc;
+  }
+
+  /* At this point, we know that the doclist was small, so do the
+  ** memmove if indicated.
+  */
+  if( nActual<n ){
+    memmove(pWriter->data.pData+iDoclistData+nActual,
+	    pWriter->data.pData+iDoclistData+n,
+	    pWriter->data.nData-(iDoclistData+n));
+    pWriter->data.nData -= n-nActual;
+  }
+
+  /* Replace written length with actual length. */
+  memcpy(pWriter->data.pData+iDoclistData, c, nActual);
+
+  /* If the node is too large, break things up. */
+  /* TODO(shess) This test matches leafWriterStep(), which does this
+  ** test before it knows the cost to varint-encode the term and
+  ** doclist lengths.  At some point, change to
+  ** pWriter->data.nData>LEAF_MAX.
+  */
+  if( iTermData+nTerm+nActualData>LEAF_MAX ){
+    /* Flush out the leading data as a node */
+    rc = leafWriterInternalFlush(v, pWriter, 0, iTermData);
+    if( rc!=SQLITE_OK ) return rc;
+
+    pWriter->nTermDistinct = nTermDistinct;
+
+    /* Rebuild header using the current term */
+    n = fts3PutVarint(pWriter->data.pData, 0);
+    n += fts3PutVarint(pWriter->data.pData+n, nTerm);
+    memcpy(pWriter->data.pData+n, pTerm, nTerm);
+    n += nTerm;
+
+    /* There should always be room, because the previous encoding
+    ** included all data necessary to construct the term.
+    */
+    assert( n<iDoclistData );
+    /* So long as STANDALONE_MIN is half or less of LEAF_MAX, the
+    ** following memcpy() is safe (as opposed to needing a memmove).
+    */
+    assert( 2*STANDALONE_MIN<=LEAF_MAX );
+    assert( n+pWriter->data.nData-iDoclistData<iDoclistData );
+    memcpy(pWriter->data.pData+n,
+	   pWriter->data.pData+iDoclistData,
+	   pWriter->data.nData-iDoclistData);
+    pWriter->data.nData -= iDoclistData-n;
+  }
+  ASSERT_VALID_LEAF_NODE(pWriter->data.pData, pWriter->data.nData);
+
+  return SQLITE_OK;
+}
+
+/* Push pTerm[nTerm] along with the doclist data to the leaf layer of
+** %_segments.
+*/
+/* TODO(shess) Revise writeZeroSegment() so that doclists are
+** constructed directly in pWriter->data.
+*/
+static int leafWriterStep(fulltext_vtab *v, LeafWriter *pWriter,
+			  const char *pTerm, int nTerm,
+			  const char *pData, int nData){
+  int rc;
+  DLReader reader;
+
+  dlrInit(&reader, DL_DEFAULT, pData, nData);
+  rc = leafWriterStepMerge(v, pWriter, pTerm, nTerm, &reader, 1);
+  dlrDestroy(&reader);
+
+  return rc;
+}
+
+
+/****************************************************************/
+/* LeafReader is used to iterate over an individual leaf node. */
+typedef struct LeafReader {
+  DataBuffer term;	    /* copy of current term. */
+
+  const char *pData;	    /* data for current term. */
+  int nData;
+} LeafReader;
+
+static void leafReaderDestroy(LeafReader *pReader){
+  dataBufferDestroy(&pReader->term);
+  SCRAMBLE(pReader);
+}
+
+static int leafReaderAtEnd(LeafReader *pReader){
+  return pReader->nData<=0;
+}
+
+/* Access the current term. */
+static int leafReaderTermBytes(LeafReader *pReader){
+  return pReader->term.nData;
+}
+static const char *leafReaderTerm(LeafReader *pReader){
+  assert( pReader->term.nData>0 );
+  return pReader->term.pData;
+}
+
+/* Access the doclist data for the current term. */
+static int leafReaderDataBytes(LeafReader *pReader){
+  int nData;
+  assert( pReader->term.nData>0 );
+  fts3GetVarint32(pReader->pData, &nData);
+  return nData;
+}
+static const char *leafReaderData(LeafReader *pReader){
+  int n, nData;
+  assert( pReader->term.nData>0 );
+  n = fts3GetVarint32(pReader->pData, &nData);
+  return pReader->pData+n;
+}
+
+static void leafReaderInit(const char *pData, int nData,
+			   LeafReader *pReader){
+  int nTerm, n;
+
+  assert( nData>0 );
+  assert( pData[0]=='\0' );
+
+  CLEAR(pReader);
+
+  /* Read the first term, skipping the header byte. */
+  n = fts3GetVarint32(pData+1, &nTerm);
+  dataBufferInit(&pReader->term, nTerm);
+  dataBufferReplace(&pReader->term, pData+1+n, nTerm);
+
+  /* Position after the first term. */
+  assert( 1+n+nTerm<nData );
+  pReader->pData = pData+1+n+nTerm;
+  pReader->nData = nData-1-n-nTerm;
+}
+
+/* Step the reader forward to the next term. */
+static void leafReaderStep(LeafReader *pReader){
+  int n, nData, nPrefix, nSuffix;
+  assert( !leafReaderAtEnd(pReader) );
+
+  /* Skip previous entry's data block. */
+  n = fts3GetVarint32(pReader->pData, &nData);
+  assert( n+nData<=pReader->nData );
+  pReader->pData += n+nData;
+  pReader->nData -= n+nData;
+
+  if( !leafReaderAtEnd(pReader) ){
+    /* Construct the new term using a prefix from the old term plus a
+    ** suffix from the leaf data.
+    */
+    n = fts3GetVarint32(pReader->pData, &nPrefix);
+    n += fts3GetVarint32(pReader->pData+n, &nSuffix);
+    assert( n+nSuffix<pReader->nData );
+    pReader->term.nData = nPrefix;
+    dataBufferAppend(&pReader->term, pReader->pData+n, nSuffix);
+
+    pReader->pData += n+nSuffix;
+    pReader->nData -= n+nSuffix;
+  }
+}
+
+/* strcmp-style comparison of pReader's current term against pTerm.
+** If isPrefix, equality means equal through nTerm bytes.
+*/
+static int leafReaderTermCmp(LeafReader *pReader,
+			     const char *pTerm, int nTerm, int isPrefix){
+  int c, n = pReader->term.nData<nTerm ? pReader->term.nData : nTerm;
+  if( n==0 ){
+    if( pReader->term.nData>0 ) return -1;
+    if(nTerm>0 ) return 1;
+    return 0;
+  }
+
+  c = memcmp(pReader->term.pData, pTerm, n);
+  if( c!=0 ) return c;
+  if( isPrefix && n==nTerm ) return 0;
+  return pReader->term.nData - nTerm;
+}
+
+
+/****************************************************************/
+/* LeavesReader wraps LeafReader to allow iterating over the entire
+** leaf layer of the tree.
+*/
+typedef struct LeavesReader {
+  int idx;		    /* Index within the segment. */
+
+  sqlite3_stmt *pStmt;	    /* Statement we're streaming leaves from. */
+  int eof;		    /* we've seen SQLITE_DONE from pStmt. */
+
+  LeafReader leafReader;    /* reader for the current leaf. */
+  DataBuffer rootData;	    /* root data for inline. */
+} LeavesReader;
+
+/* Access the current term. */
+static int leavesReaderTermBytes(LeavesReader *pReader){
+  assert( !pReader->eof );
+  return leafReaderTermBytes(&pReader->leafReader);
+}
+static const char *leavesReaderTerm(LeavesReader *pReader){
+  assert( !pReader->eof );
+  return leafReaderTerm(&pReader->leafReader);
+}
+
+/* Access the doclist data for the current term. */
+static int leavesReaderDataBytes(LeavesReader *pReader){
+  assert( !pReader->eof );
+  return leafReaderDataBytes(&pReader->leafReader);
+}
+static const char *leavesReaderData(LeavesReader *pReader){
+  assert( !pReader->eof );
+  return leafReaderData(&pReader->leafReader);
+}
+
+static int leavesReaderAtEnd(LeavesReader *pReader){
+  return pReader->eof;
+}
+
+/* loadSegmentLeaves() may not read all the way to SQLITE_DONE, thus
+** leaving the statement handle open, which locks the table.
+*/
+/* TODO(shess) This "solution" is not satisfactory.  Really, there
+** should be check-in function for all statement handles which
+** arranges to call sqlite3_reset().  This most likely will require
+** modification to control flow all over the place, though, so for now
+** just punt.
+**
+** Note the the current system assumes that segment merges will run to
+** completion, which is why this particular probably hasn't arisen in
+** this case.  Probably a brittle assumption.
+*/
+static int leavesReaderReset(LeavesReader *pReader){
+  return sqlite3_reset(pReader->pStmt);
+}
+
+static void leavesReaderDestroy(LeavesReader *pReader){
+  /* If idx is -1, that means we're using a non-cached statement
+  ** handle in the optimize() case, so we need to release it.
+  */
+  if( pReader->pStmt!=NULL && pReader->idx==-1 ){
+    sqlite3_finalize(pReader->pStmt);
+  }
+  leafReaderDestroy(&pReader->leafReader);
+  dataBufferDestroy(&pReader->rootData);
+  SCRAMBLE(pReader);
+}
+
+/* Initialize pReader with the given root data (if iStartBlockid==0
+** the leaf data was entirely contained in the root), or from the
+** stream of blocks between iStartBlockid and iEndBlockid, inclusive.
+*/
+static int leavesReaderInit(fulltext_vtab *v,
+			    int idx,
+			    sqlite_int64 iStartBlockid,
+			    sqlite_int64 iEndBlockid,
+			    const char *pRootData, int nRootData,
+			    LeavesReader *pReader){
+  CLEAR(pReader);
+  pReader->idx = idx;
+
+  dataBufferInit(&pReader->rootData, 0);
+  if( iStartBlockid==0 ){
+    /* Entire leaf level fit in root data. */
+    dataBufferReplace(&pReader->rootData, pRootData, nRootData);
+    leafReaderInit(pReader->rootData.pData, pReader->rootData.nData,
+		   &pReader->leafReader);
+  }else{
+    sqlite3_stmt *s;
+    int rc = sql_get_leaf_statement(v, idx, &s);
+    if( rc!=SQLITE_OK ) return rc;
+
+    rc = sqlite3_bind_int64(s, 1, iStartBlockid);
+    if( rc!=SQLITE_OK ) return rc;
+
+    rc = sqlite3_bind_int64(s, 2, iEndBlockid);
+    if( rc!=SQLITE_OK ) return rc;
+
+    rc = sqlite3_step(s);
+    if( rc==SQLITE_DONE ){
+      pReader->eof = 1;
+      return SQLITE_OK;
+    }
+    if( rc!=SQLITE_ROW ) return rc;
+
+    pReader->pStmt = s;
+    leafReaderInit(sqlite3_column_blob(pReader->pStmt, 0),
+		   sqlite3_column_bytes(pReader->pStmt, 0),
+		   &pReader->leafReader);
+  }
+  return SQLITE_OK;
+}
+
+/* Step the current leaf forward to the next term.  If we reach the
+** end of the current leaf, step forward to the next leaf block.
+*/
+static int leavesReaderStep(fulltext_vtab *v, LeavesReader *pReader){
+  assert( !leavesReaderAtEnd(pReader) );
+  leafReaderStep(&pReader->leafReader);
+
+  if( leafReaderAtEnd(&pReader->leafReader) ){
+    int rc;
+    if( pReader->rootData.pData ){
+      pReader->eof = 1;
+      return SQLITE_OK;
+    }
+    rc = sqlite3_step(pReader->pStmt);
+    if( rc!=SQLITE_ROW ){
+      pReader->eof = 1;
+      return rc==SQLITE_DONE ? SQLITE_OK : rc;
+    }
+    leafReaderDestroy(&pReader->leafReader);
+    leafReaderInit(sqlite3_column_blob(pReader->pStmt, 0),
+		   sqlite3_column_bytes(pReader->pStmt, 0),
+		   &pReader->leafReader);
+  }
+  return SQLITE_OK;
+}
+
+/* Order LeavesReaders by their term, ignoring idx.  Readers at eof
+** always sort to the end.
+*/
+static int leavesReaderTermCmp(LeavesReader *lr1, LeavesReader *lr2){
+  if( leavesReaderAtEnd(lr1) ){
+    if( leavesReaderAtEnd(lr2) ) return 0;
+    return 1;
+  }
+  if( leavesReaderAtEnd(lr2) ) return -1;
+
+  return leafReaderTermCmp(&lr1->leafReader,
+			   leavesReaderTerm(lr2), leavesReaderTermBytes(lr2),
+			   0);
+}
+
+/* Similar to leavesReaderTermCmp(), with additional ordering by idx
+** so that older segments sort before newer segments.
+*/
+static int leavesReaderCmp(LeavesReader *lr1, LeavesReader *lr2){
+  int c = leavesReaderTermCmp(lr1, lr2);
+  if( c!=0 ) return c;
+  return lr1->idx-lr2->idx;
+}
+
+/* Assume that pLr[1]..pLr[nLr] are sorted.  Bubble pLr[0] into its
+** sorted position.
+*/
+static void leavesReaderReorder(LeavesReader *pLr, int nLr){
+  while( nLr>1 && leavesReaderCmp(pLr, pLr+1)>0 ){
+    LeavesReader tmp = pLr[0];
+    pLr[0] = pLr[1];
+    pLr[1] = tmp;
+    nLr--;
+    pLr++;
+  }
+}
+
+/* Initializes pReaders with the segments from level iLevel, returning
+** the number of segments in *piReaders.  Leaves pReaders in sorted
+** order.
+*/
+static int leavesReadersInit(fulltext_vtab *v, int iLevel,
+			     LeavesReader *pReaders, int *piReaders){
+  sqlite3_stmt *s;
+  int i, rc = sql_get_statement(v, SEGDIR_SELECT_LEVEL_STMT, &s);
+  if( rc!=SQLITE_OK ) return rc;
+
+  rc = sqlite3_bind_int(s, 1, iLevel);
+  if( rc!=SQLITE_OK ) return rc;
+
+  i = 0;
+  while( (rc = sqlite3_step(s))==SQLITE_ROW ){
+    sqlite_int64 iStart = sqlite3_column_int64(s, 0);
+    sqlite_int64 iEnd = sqlite3_column_int64(s, 1);
+    const char *pRootData = sqlite3_column_blob(s, 2);
+    int nRootData = sqlite3_column_bytes(s, 2);
+
+    assert( i<MERGE_COUNT );
+    rc = leavesReaderInit(v, i, iStart, iEnd, pRootData, nRootData,
+			  &pReaders[i]);
+    if( rc!=SQLITE_OK ) break;
+
+    i++;
+  }
+  if( rc!=SQLITE_DONE ){
+    while( i-->0 ){
+      leavesReaderDestroy(&pReaders[i]);
+    }
+    return rc;
+  }
+
+  *piReaders = i;
+
+  /* Leave our results sorted by term, then age. */
+  while( i-- ){
+    leavesReaderReorder(pReaders+i, *piReaders-i);
+  }
+  return SQLITE_OK;
+}
+
+/* Merge doclists from pReaders[nReaders] into a single doclist, which
+** is written to pWriter.  Assumes pReaders is ordered oldest to
+** newest.
+*/
+/* TODO(shess) Consider putting this inline in segmentMerge(). */
+static int leavesReadersMerge(fulltext_vtab *v,
+			      LeavesReader *pReaders, int nReaders,
+			      LeafWriter *pWriter){
+  DLReader dlReaders[MERGE_COUNT];
+  const char *pTerm = leavesReaderTerm(pReaders);
+  int i, nTerm = leavesReaderTermBytes(pReaders);
+
+  assert( nReaders<=MERGE_COUNT );
+
+  for(i=0; i<nReaders; i++){
+    dlrInit(&dlReaders[i], DL_DEFAULT,
+	    leavesReaderData(pReaders+i),
+	    leavesReaderDataBytes(pReaders+i));
+  }
+
+  return leafWriterStepMerge(v, pWriter, pTerm, nTerm, dlReaders, nReaders);
+}
+
+/* Forward ref due to mutual recursion with segdirNextIndex(). */
+static int segmentMerge(fulltext_vtab *v, int iLevel);
+
+/* Put the next available index at iLevel into *pidx.  If iLevel
+** already has MERGE_COUNT segments, they are merged to a higher
+** level to make room.
+*/
+static int segdirNextIndex(fulltext_vtab *v, int iLevel, int *pidx){
+  int rc = segdir_max_index(v, iLevel, pidx);
+  if( rc==SQLITE_DONE ){	      /* No segments at iLevel. */
+    *pidx = 0;
+  }else if( rc==SQLITE_ROW ){
+    if( *pidx==(MERGE_COUNT-1) ){
+      rc = segmentMerge(v, iLevel);
+      if( rc!=SQLITE_OK ) return rc;
+      *pidx = 0;
+    }else{
+      (*pidx)++;
+    }
+  }else{
+    return rc;
+  }
+  return SQLITE_OK;
+}
+
+/* Merge MERGE_COUNT segments at iLevel into a new segment at
+** iLevel+1.  If iLevel+1 is already full of segments, those will be
+** merged to make room.
+*/
+static int segmentMerge(fulltext_vtab *v, int iLevel){
+  LeafWriter writer;
+  LeavesReader lrs[MERGE_COUNT];
+  int i, rc, idx = 0;
+
+  /* Determine the next available segment index at the next level,
+  ** merging as necessary.
+  */
+  rc = segdirNextIndex(v, iLevel+1, &idx);
+  if( rc!=SQLITE_OK ) return rc;
+
+  /* TODO(shess) This assumes that we'll always see exactly
+  ** MERGE_COUNT segments to merge at a given level.  That will be
+  ** broken if we allow the developer to request preemptive or
+  ** deferred merging.
+  */
+  memset(&lrs, '\0', sizeof(lrs));
+  rc = leavesReadersInit(v, iLevel, lrs, &i);
+  if( rc!=SQLITE_OK ) return rc;
+  assert( i==MERGE_COUNT );
+
+  leafWriterInit(iLevel+1, idx, &writer);
+
+  /* Since leavesReaderReorder() pushes readers at eof to the end,
+  ** when the first reader is empty, all will be empty.
+  */
+  while( !leavesReaderAtEnd(lrs) ){
+    /* Figure out how many readers share their next term. */
+    for(i=1; i<MERGE_COUNT && !leavesReaderAtEnd(lrs+i); i++){
+      if( 0!=leavesReaderTermCmp(lrs, lrs+i) ) break;
+    }
+
+    rc = leavesReadersMerge(v, lrs, i, &writer);
+    if( rc!=SQLITE_OK ) goto err;
+
+    /* Step forward those that were merged. */
+    while( i-->0 ){
+      rc = leavesReaderStep(v, lrs+i);
+      if( rc!=SQLITE_OK ) goto err;
+
+      /* Reorder by term, then by age. */
+      leavesReaderReorder(lrs+i, MERGE_COUNT-i);
+    }
+  }
+
+  for(i=0; i<MERGE_COUNT; i++){
+    leavesReaderDestroy(&lrs[i]);
+  }
+
+  rc = leafWriterFinalize(v, &writer);
+  leafWriterDestroy(&writer);
+  if( rc!=SQLITE_OK ) return rc;
+
+  /* Delete the merged segment data. */
+  return segdir_delete(v, iLevel);
+
+ err:
+  for(i=0; i<MERGE_COUNT; i++){
+    leavesReaderDestroy(&lrs[i]);
+  }
+  leafWriterDestroy(&writer);
+  return rc;
+}
+
+/* Accumulate the union of *acc and *pData into *acc. */
+static void docListAccumulateUnion(DataBuffer *acc,
+				   const char *pData, int nData) {
+  DataBuffer tmp = *acc;
+  dataBufferInit(acc, tmp.nData+nData);
+  docListUnion(tmp.pData, tmp.nData, pData, nData, acc);
+  dataBufferDestroy(&tmp);
+}
+
+/* TODO(shess) It might be interesting to explore different merge
+** strategies, here.  For instance, since this is a sorted merge, we
+** could easily merge many doclists in parallel.  With some
+** comprehension of the storage format, we could merge all of the
+** doclists within a leaf node directly from the leaf node's storage.
+** It may be worthwhile to merge smaller doclists before larger
+** doclists, since they can be traversed more quickly - but the
+** results may have less overlap, making them more expensive in a
+** different way.
+*/
+
+/* Scan pReader for pTerm/nTerm, and merge the term's doclist over
+** *out (any doclists with duplicate docids overwrite those in *out).
+** Internal function for loadSegmentLeaf().
+*/
+static int loadSegmentLeavesInt(fulltext_vtab *v, LeavesReader *pReader,
+				const char *pTerm, int nTerm, int isPrefix,
+				DataBuffer *out){
+  /* doclist data is accumulated into pBuffers similar to how one does
+  ** increment in binary arithmetic.  If index 0 is empty, the data is
+  ** stored there.  If there is data there, it is merged and the
+  ** results carried into position 1, with further merge-and-carry
+  ** until an empty position is found.
+  */
+  DataBuffer *pBuffers = NULL;
+  int nBuffers = 0, nMaxBuffers = 0, rc;
+
+  assert( nTerm>0 );
+
+  for(rc=SQLITE_OK; rc==SQLITE_OK && !leavesReaderAtEnd(pReader);
+      rc=leavesReaderStep(v, pReader)){
+    /* TODO(shess) Really want leavesReaderTermCmp(), but that name is
+    ** already taken to compare the terms of two LeavesReaders.  Think
+    ** on a better name.  [Meanwhile, break encapsulation rather than
+    ** use a confusing name.]
+    */
+    int c = leafReaderTermCmp(&pReader->leafReader, pTerm, nTerm, isPrefix);
+    if( c>0 ) break;	  /* Past any possible matches. */
+    if( c==0 ){
+      const char *pData = leavesReaderData(pReader);
+      int iBuffer, nData = leavesReaderDataBytes(pReader);
+
+      /* Find the first empty buffer. */
+      for(iBuffer=0; iBuffer<nBuffers; ++iBuffer){
+	if( 0==pBuffers[iBuffer].nData ) break;
+      }
+
+      /* Out of buffers, add an empty one. */
+      if( iBuffer==nBuffers ){
+	if( nBuffers==nMaxBuffers ){
+	  DataBuffer *p;
+	  nMaxBuffers += 20;
+
+	  /* Manual realloc so we can handle NULL appropriately. */
+	  p = sqlite3_malloc(nMaxBuffers*sizeof(*pBuffers));
+	  if( p==NULL ){
+	    rc = SQLITE_NOMEM;
+	    break;
+	  }
+
+	  if( nBuffers>0 ){
+	    assert(pBuffers!=NULL);
+	    memcpy(p, pBuffers, nBuffers*sizeof(*pBuffers));
+	    sqlite3_free(pBuffers);
+	  }
+	  pBuffers = p;
+	}
+	dataBufferInit(&(pBuffers[nBuffers]), 0);
+	nBuffers++;
+      }
+
+      /* At this point, must have an empty at iBuffer. */
+      assert(iBuffer<nBuffers && pBuffers[iBuffer].nData==0);
+
+      /* If empty was first buffer, no need for merge logic. */
+      if( iBuffer==0 ){
+	dataBufferReplace(&(pBuffers[0]), pData, nData);
+      }else{
+	/* pAcc is the empty buffer the merged data will end up in. */
+	DataBuffer *pAcc = &(pBuffers[iBuffer]);
+	DataBuffer *p = &(pBuffers[0]);
+
+	/* Handle position 0 specially to avoid need to prime pAcc
+	** with pData/nData.
+	*/
+	dataBufferSwap(p, pAcc);
+	docListAccumulateUnion(pAcc, pData, nData);
+
+	/* Accumulate remaining doclists into pAcc. */
+	for(++p; p<pAcc; ++p){
+	  docListAccumulateUnion(pAcc, p->pData, p->nData);
+
+	  /* dataBufferReset() could allow a large doclist to blow up
+	  ** our memory requirements.
+	  */
+	  if( p->nCapacity<1024 ){
+	    dataBufferReset(p);
+	  }else{
+	    dataBufferDestroy(p);
+	    dataBufferInit(p, 0);
+	  }
+	}
+      }
+    }
+  }
+
+  /* Union all the doclists together into *out. */
+  /* TODO(shess) What if *out is big?  Sigh. */
+  if( rc==SQLITE_OK && nBuffers>0 ){
+    int iBuffer;
+    for(iBuffer=0; iBuffer<nBuffers; ++iBuffer){
+      if( pBuffers[iBuffer].nData>0 ){
+	if( out->nData==0 ){
+	  dataBufferSwap(out, &(pBuffers[iBuffer]));
+	}else{
+	  docListAccumulateUnion(out, pBuffers[iBuffer].pData,
+				 pBuffers[iBuffer].nData);
+	}
+      }
+    }
+  }
+
+  while( nBuffers-- ){
+    dataBufferDestroy(&(pBuffers[nBuffers]));
+  }
+  if( pBuffers!=NULL ) sqlite3_free(pBuffers);
+
+  return rc;
+}
+
+/* Call loadSegmentLeavesInt() with pData/nData as input. */
+static int loadSegmentLeaf(fulltext_vtab *v, const char *pData, int nData,
+			   const char *pTerm, int nTerm, int isPrefix,
+			   DataBuffer *out){
+  LeavesReader reader;
+  int rc;
+
+  assert( nData>1 );
+  assert( *pData=='\0' );
+  rc = leavesReaderInit(v, 0, 0, 0, pData, nData, &reader);
+  if( rc!=SQLITE_OK ) return rc;
+
+  rc = loadSegmentLeavesInt(v, &reader, pTerm, nTerm, isPrefix, out);
+  leavesReaderReset(&reader);
+  leavesReaderDestroy(&reader);
+  return rc;
+}
+
+/* Call loadSegmentLeavesInt() with the leaf nodes from iStartLeaf to
+** iEndLeaf (inclusive) as input, and merge the resulting doclist into
+** out.
+*/
+static int loadSegmentLeaves(fulltext_vtab *v,
+			     sqlite_int64 iStartLeaf, sqlite_int64 iEndLeaf,
+			     const char *pTerm, int nTerm, int isPrefix,
+			     DataBuffer *out){
+  int rc;
+  LeavesReader reader;
+
+  assert( iStartLeaf<=iEndLeaf );
+  rc = leavesReaderInit(v, 0, iStartLeaf, iEndLeaf, NULL, 0, &reader);
+  if( rc!=SQLITE_OK ) return rc;
+
+  rc = loadSegmentLeavesInt(v, &reader, pTerm, nTerm, isPrefix, out);
+  leavesReaderReset(&reader);
+  leavesReaderDestroy(&reader);
+  return rc;
+}
+
+/* Taking pData/nData as an interior node, find the sequence of child
+** nodes which could include pTerm/nTerm/isPrefix.  Note that the
+** interior node terms logically come between the blocks, so there is
+** one more blockid than there are terms (that block contains terms >=
+** the last interior-node term).
+*/
+/* TODO(shess) The calling code may already know that the end child is
+** not worth calculating, because the end may be in a later sibling
+** node.  Consider whether breaking symmetry is worthwhile.  I suspect
+** it is not worthwhile.
+*/
+static void getChildrenContaining(const char *pData, int nData,
+				  const char *pTerm, int nTerm, int isPrefix,
+				  sqlite_int64 *piStartChild,
+				  sqlite_int64 *piEndChild){
+  InteriorReader reader;
+
+  assert( nData>1 );
+  assert( *pData!='\0' );
+  interiorReaderInit(pData, nData, &reader);
+
+  /* Scan for the first child which could contain pTerm/nTerm. */
+  while( !interiorReaderAtEnd(&reader) ){
+    if( interiorReaderTermCmp(&reader, pTerm, nTerm, 0)>0 ) break;
+    interiorReaderStep(&reader);
+  }
+  *piStartChild = interiorReaderCurrentBlockid(&reader);
+
+  /* Keep scanning to find a term greater than our term, using prefix
+  ** comparison if indicated.  If isPrefix is false, this will be the
+  ** same blockid as the starting block.
+  */
+  while( !interiorReaderAtEnd(&reader) ){
+    if( interiorReaderTermCmp(&reader, pTerm, nTerm, isPrefix)>0 ) break;
+    interiorReaderStep(&reader);
+  }
+  *piEndChild = interiorReaderCurrentBlockid(&reader);
+
+  interiorReaderDestroy(&reader);
+
+  /* Children must ascend, and if !prefix, both must be the same. */
+  assert( *piEndChild>=*piStartChild );
+  assert( isPrefix || *piStartChild==*piEndChild );
+}
+
+/* Read block at iBlockid and pass it with other params to
+** getChildrenContaining().
+*/
+static int loadAndGetChildrenContaining(
+  fulltext_vtab *v,
+  sqlite_int64 iBlockid,
+  const char *pTerm, int nTerm, int isPrefix,
+  sqlite_int64 *piStartChild, sqlite_int64 *piEndChild
+){
+  sqlite3_stmt *s = NULL;
+  int rc;
+
+  assert( iBlockid!=0 );
+  assert( pTerm!=NULL );
+  assert( nTerm!=0 );	     /* TODO(shess) Why not allow this? */
+  assert( piStartChild!=NULL );
+  assert( piEndChild!=NULL );
+
+  rc = sql_get_statement(v, BLOCK_SELECT_STMT, &s);
+  if( rc!=SQLITE_OK ) return rc;
+
+  rc = sqlite3_bind_int64(s, 1, iBlockid);
+  if( rc!=SQLITE_OK ) return rc;
+
+  rc = sqlite3_step(s);
+  if( rc==SQLITE_DONE ) return SQLITE_ERROR;
+  if( rc!=SQLITE_ROW ) return rc;
+
+  getChildrenContaining(sqlite3_column_blob(s, 0), sqlite3_column_bytes(s, 0),
+			pTerm, nTerm, isPrefix, piStartChild, piEndChild);
+
+  /* We expect only one row.  We must execute another sqlite3_step()
+   * to complete the iteration; otherwise the table will remain
+   * locked. */
+  rc = sqlite3_step(s);
+  if( rc==SQLITE_ROW ) return SQLITE_ERROR;
+  if( rc!=SQLITE_DONE ) return rc;
+
+  return SQLITE_OK;
+}
+
+/* Traverse the tree represented by pData[nData] looking for
+** pTerm[nTerm], placing its doclist into *out.  This is internal to
+** loadSegment() to make error-handling cleaner.
+*/
+static int loadSegmentInt(fulltext_vtab *v, const char *pData, int nData,
+			  sqlite_int64 iLeavesEnd,
+			  const char *pTerm, int nTerm, int isPrefix,
+			  DataBuffer *out){
+  /* Special case where root is a leaf. */
+  if( *pData=='\0' ){
+    return loadSegmentLeaf(v, pData, nData, pTerm, nTerm, isPrefix, out);
+  }else{
+    int rc;
+    sqlite_int64 iStartChild, iEndChild;
+
+    /* Process pData as an interior node, then loop down the tree
+    ** until we find the set of leaf nodes to scan for the term.
+    */
+    getChildrenContaining(pData, nData, pTerm, nTerm, isPrefix,
+			  &iStartChild, &iEndChild);
+    while( iStartChild>iLeavesEnd ){
+      sqlite_int64 iNextStart, iNextEnd;
+      rc = loadAndGetChildrenContaining(v, iStartChild, pTerm, nTerm, isPrefix,
+					&iNextStart, &iNextEnd);
+      if( rc!=SQLITE_OK ) return rc;
+
+      /* If we've branched, follow the end branch, too. */
+      if( iStartChild!=iEndChild ){
+	sqlite_int64 iDummy;
+	rc = loadAndGetChildrenContaining(v, iEndChild, pTerm, nTerm, isPrefix,
+					  &iDummy, &iNextEnd);
+	if( rc!=SQLITE_OK ) return rc;
+      }
+
+      assert( iNextStart<=iNextEnd );
+      iStartChild = iNextStart;
+      iEndChild = iNextEnd;
+    }
+    assert( iStartChild<=iLeavesEnd );
+    assert( iEndChild<=iLeavesEnd );
+
+    /* Scan through the leaf segments for doclists. */
+    return loadSegmentLeaves(v, iStartChild, iEndChild,
+			     pTerm, nTerm, isPrefix, out);
+  }
+}
+
+/* Call loadSegmentInt() to collect the doclist for pTerm/nTerm, then
+** merge its doclist over *out (any duplicate doclists read from the
+** segment rooted at pData will overwrite those in *out).
+*/
+/* TODO(shess) Consider changing this to determine the depth of the
+** leaves using either the first characters of interior nodes (when
+** ==1, we're one level above the leaves), or the first character of
+** the root (which will describe the height of the tree directly).
+** Either feels somewhat tricky to me.
+*/
+/* TODO(shess) The current merge is likely to be slow for large
+** doclists (though it should process from newest/smallest to
+** oldest/largest, so it may not be that bad).	It might be useful to
+** modify things to allow for N-way merging.  This could either be
+** within a segment, with pairwise merges across segments, or across
+** all segments at once.
+*/
+static int loadSegment(fulltext_vtab *v, const char *pData, int nData,
+		       sqlite_int64 iLeavesEnd,
+		       const char *pTerm, int nTerm, int isPrefix,
+		       DataBuffer *out){
+  DataBuffer result;
+  int rc;
+
+  assert( nData>1 );
+
+  /* This code should never be called with buffered updates. */
+  assert( v->nPendingData<0 );
+
+  dataBufferInit(&result, 0);
+  rc = loadSegmentInt(v, pData, nData, iLeavesEnd,
+		      pTerm, nTerm, isPrefix, &result);
+  if( rc==SQLITE_OK && result.nData>0 ){
+    if( out->nData==0 ){
+      DataBuffer tmp = *out;
+      *out = result;
+      result = tmp;
+    }else{
+      DataBuffer merged;
+      DLReader readers[2];
+
+      dlrInit(&readers[0], DL_DEFAULT, out->pData, out->nData);
+      dlrInit(&readers[1], DL_DEFAULT, result.pData, result.nData);
+      dataBufferInit(&merged, out->nData+result.nData);
+      docListMerge(&merged, readers, 2);
+      dataBufferDestroy(out);
+      *out = merged;
+      dlrDestroy(&readers[0]);
+      dlrDestroy(&readers[1]);
+    }
+  }
+  dataBufferDestroy(&result);
+  return rc;
+}
+
+/* Scan the database and merge together the posting lists for the term
+** into *out.
+*/
+static int termSelect(fulltext_vtab *v, int iColumn,
+		      const char *pTerm, int nTerm, int isPrefix,
+		      DocListType iType, DataBuffer *out){
+  DataBuffer doclist;
+  sqlite3_stmt *s;
+  int rc = sql_get_statement(v, SEGDIR_SELECT_ALL_STMT, &s);
+  if( rc!=SQLITE_OK ) return rc;
+
+  /* This code should never be called with buffered updates. */
+  assert( v->nPendingData<0 );
+
+  dataBufferInit(&doclist, 0);
+
+  /* Traverse the segments from oldest to newest so that newer doclist
+  ** elements for given docids overwrite older elements.
+  */
+  while( (rc = sqlite3_step(s))==SQLITE_ROW ){
+    const char *pData = sqlite3_column_blob(s, 2);
+    const int nData = sqlite3_column_bytes(s, 2);
+    const sqlite_int64 iLeavesEnd = sqlite3_column_int64(s, 1);
+    rc = loadSegment(v, pData, nData, iLeavesEnd, pTerm, nTerm, isPrefix,
+		     &doclist);
+    if( rc!=SQLITE_OK ) goto err;
+  }
+  if( rc==SQLITE_DONE ){
+    if( doclist.nData!=0 ){
+      /* TODO(shess) The old term_select_all() code applied the column
+      ** restrict as we merged segments, leading to smaller buffers.
+      ** This is probably worthwhile to bring back, once the new storage
+      ** system is checked in.
+      */
+      if( iColumn==v->nColumn) iColumn = -1;
+      docListTrim(DL_DEFAULT, doclist.pData, doclist.nData,
+		  iColumn, iType, out);
+    }
+    rc = SQLITE_OK;
+  }
+
+ err:
+  dataBufferDestroy(&doclist);
+  return rc;
+}
+
+/****************************************************************/
+/* Used to hold hashtable data for sorting. */
+typedef struct TermData {
+  const char *pTerm;
+  int nTerm;
+  DLCollector *pCollector;
+} TermData;
+
+/* Orders TermData elements in strcmp fashion ( <0 for less-than, 0
+** for equal, >0 for greater-than).
+*/
+static int termDataCmp(const void *av, const void *bv){
+  const TermData *a = (const TermData *)av;
+  const TermData *b = (const TermData *)bv;
+  int n = a->nTerm<b->nTerm ? a->nTerm : b->nTerm;
+  int c = memcmp(a->pTerm, b->pTerm, n);
+  if( c!=0 ) return c;
+  return a->nTerm-b->nTerm;
+}
+
+/* Order pTerms data by term, then write a new level 0 segment using
+** LeafWriter.
+*/
+static int writeZeroSegment(fulltext_vtab *v, fts3Hash *pTerms){
+  fts3HashElem *e;
+  int idx, rc, i, n;
+  TermData *pData;
+  LeafWriter writer;
+  DataBuffer dl;
+
+  /* Determine the next index at level 0, merging as necessary. */
+  rc = segdirNextIndex(v, 0, &idx);
+  if( rc!=SQLITE_OK ) return rc;
+
+  n = fts3HashCount(pTerms);
+  pData = sqlite3_malloc(n*sizeof(TermData));
+
+  for(i = 0, e = fts3HashFirst(pTerms); e; i++, e = fts3HashNext(e)){
+    assert( i<n );
+    pData[i].pTerm = fts3HashKey(e);
+    pData[i].nTerm = fts3HashKeysize(e);
+    pData[i].pCollector = fts3HashData(e);
+  }
+  assert( i==n );
+
+  /* TODO(shess) Should we allow user-defined collation sequences,
+  ** here?  I think we only need that once we support prefix searches.
+  */
+  if( n>1 ) qsort(pData, n, sizeof(*pData), termDataCmp);
+
+  /* TODO(shess) Refactor so that we can write directly to the segment
+  ** DataBuffer, as happens for segment merges.
+  */
+  leafWriterInit(0, idx, &writer);
+  dataBufferInit(&dl, 0);
+  for(i=0; i<n; i++){
+    dataBufferReset(&dl);
+    dlcAddDoclist(pData[i].pCollector, &dl);
+    rc = leafWriterStep(v, &writer,
+			pData[i].pTerm, pData[i].nTerm, dl.pData, dl.nData);
+    if( rc!=SQLITE_OK ) goto err;
+  }
+  rc = leafWriterFinalize(v, &writer);
+
+ err:
+  dataBufferDestroy(&dl);
+  sqlite3_free(pData);
+  leafWriterDestroy(&writer);
+  return rc;
+}
+
+/* If pendingTerms has data, free it. */
+static int clearPendingTerms(fulltext_vtab *v){
+  if( v->nPendingData>=0 ){
+    fts3HashElem *e;
+    for(e=fts3HashFirst(&v->pendingTerms); e; e=fts3HashNext(e)){
+      dlcDelete(fts3HashData(e));
+    }
+    fts3HashClear(&v->pendingTerms);
+    v->nPendingData = -1;
+  }
+  return SQLITE_OK;
+}
+
+/* If pendingTerms has data, flush it to a level-zero segment, and
+** free it.
+*/
+static int flushPendingTerms(fulltext_vtab *v){
+  if( v->nPendingData>=0 ){
+    int rc = writeZeroSegment(v, &v->pendingTerms);
+    if( rc==SQLITE_OK ) clearPendingTerms(v);
+    return rc;
+  }
+  return SQLITE_OK;
+}
+
+/* If pendingTerms is "too big", or docid is out of order, flush it.
+** Regardless, be certain that pendingTerms is initialized for use.
+*/
+static int initPendingTerms(fulltext_vtab *v, sqlite_int64 iDocid){
+  /* TODO(shess) Explore whether partially flushing the buffer on
+  ** forced-flush would provide better performance.  I suspect that if
+  ** we ordered the doclists by size and flushed the largest until the
+  ** buffer was half empty, that would let the less frequent terms
+  ** generate longer doclists.
+  */
+  if( iDocid<=v->iPrevDocid || v->nPendingData>kPendingThreshold ){
+    int rc = flushPendingTerms(v);
+    if( rc!=SQLITE_OK ) return rc;
+  }
+  if( v->nPendingData<0 ){
+    fts3HashInit(&v->pendingTerms, FTS3_HASH_STRING, 1);
+    v->nPendingData = 0;
+  }
+  v->iPrevDocid = iDocid;
+  return SQLITE_OK;
+}
+
+/* This function implements the xUpdate callback; it is the top-level entry
+ * point for inserting, deleting or updating a row in a full-text table. */
+static int fulltextUpdate(sqlite3_vtab *pVtab, int nArg, sqlite3_value **ppArg,
+			  sqlite_int64 *pRowid){
+  fulltext_vtab *v = (fulltext_vtab *) pVtab;
+  int rc;
+
+  FTSTRACE(("FTS3 Update %p\n", pVtab));
+
+  if( nArg<2 ){
+    rc = index_delete(v, sqlite3_value_int64(ppArg[0]));
+    if( rc==SQLITE_OK ){
+      /* If we just deleted the last row in the table, clear out the
+      ** index data.
+      */
+      rc = content_exists(v);
+      if( rc==SQLITE_ROW ){
+	rc = SQLITE_OK;
+      }else if( rc==SQLITE_DONE ){
+	/* Clear the pending terms so we don't flush a useless level-0
+	** segment when the transaction closes.
+	*/
+	rc = clearPendingTerms(v);
+	if( rc==SQLITE_OK ){
+	  rc = segdir_delete_all(v);
+	}
+      }
+    }
+  } else if( sqlite3_value_type(ppArg[0]) != SQLITE_NULL ){
+    /* An update:
+     * ppArg[0] = old rowid
+     * ppArg[1] = new rowid
+     * ppArg[2..2+v->nColumn-1] = values
+     * ppArg[2+v->nColumn] = value for magic column (we ignore this)
+     * ppArg[2+v->nColumn+1] = value for docid
+     */
+    sqlite_int64 rowid = sqlite3_value_int64(ppArg[0]);
+    if( sqlite3_value_type(ppArg[1]) != SQLITE_INTEGER ||
+	sqlite3_value_int64(ppArg[1]) != rowid ){
+      rc = SQLITE_ERROR;  /* we don't allow changing the rowid */
+    }else if( sqlite3_value_type(ppArg[2+v->nColumn+1]) != SQLITE_INTEGER ||
+	      sqlite3_value_int64(ppArg[2+v->nColumn+1]) != rowid ){
+      rc = SQLITE_ERROR;  /* we don't allow changing the docid */
+    }else{
+      assert( nArg==2+v->nColumn+2);
+      rc = index_update(v, rowid, &ppArg[2]);
+    }
+  } else {
+    /* An insert:
+     * ppArg[1] = requested rowid
+     * ppArg[2..2+v->nColumn-1] = values
+     * ppArg[2+v->nColumn] = value for magic column (we ignore this)
+     * ppArg[2+v->nColumn+1] = value for docid
+     */
+    sqlite3_value *pRequestDocid = ppArg[2+v->nColumn+1];
+    assert( nArg==2+v->nColumn+2);
+    if( SQLITE_NULL != sqlite3_value_type(pRequestDocid) &&
+	SQLITE_NULL != sqlite3_value_type(ppArg[1]) ){
+      /* TODO(shess) Consider allowing this to work if the values are
+      ** identical.  I'm inclined to discourage that usage, though,
+      ** given that both rowid and docid are special columns.  Better
+      ** would be to define one or the other as the default winner,
+      ** but should it be fts3-centric (docid) or SQLite-centric
+      ** (rowid)?
+      */
+      rc = SQLITE_ERROR;
+    }else{
+      if( SQLITE_NULL == sqlite3_value_type(pRequestDocid) ){
+	pRequestDocid = ppArg[1];
+      }
+      rc = index_insert(v, pRequestDocid, &ppArg[2], pRowid);
+    }
+  }
+
+  return rc;
+}
+
+static int fulltextSync(sqlite3_vtab *pVtab){
+  FTSTRACE(("FTS3 xSync()\n"));
+  return flushPendingTerms((fulltext_vtab *)pVtab);
+}
+
+static int fulltextBegin(sqlite3_vtab *pVtab){
+  fulltext_vtab *v = (fulltext_vtab *) pVtab;
+  FTSTRACE(("FTS3 xBegin()\n"));
+
+  /* Any buffered updates should have been cleared by the previous
+  ** transaction.
+  */
+  assert( v->nPendingData<0 );
+  return clearPendingTerms(v);
+}
+
+static int fulltextCommit(sqlite3_vtab *pVtab){
+  fulltext_vtab *v = (fulltext_vtab *) pVtab;
+  FTSTRACE(("FTS3 xCommit()\n"));
+
+  /* Buffered updates should have been cleared by fulltextSync(). */
+  assert( v->nPendingData<0 );
+  return clearPendingTerms(v);
+}
+
+static int fulltextRollback(sqlite3_vtab *pVtab){
+  FTSTRACE(("FTS3 xRollback()\n"));
+  return clearPendingTerms((fulltext_vtab *)pVtab);
+}
+
+/*
+** Implementation of the snippet() function for FTS3
+*/
+static void snippetFunc(
+  sqlite3_context *pContext,
+  int argc,
+  sqlite3_value **argv
+){
+  fulltext_cursor *pCursor;
+  if( argc<1 ) return;
+  if( sqlite3_value_type(argv[0])!=SQLITE_BLOB ||
+      sqlite3_value_bytes(argv[0])!=sizeof(pCursor) ){
+    sqlite3_result_error(pContext, "illegal first argument to html_snippet",-1);
+  }else{
+    const char *zStart = "<b>";
+    const char *zEnd = "</b>";
+    const char *zEllipsis = "<b>...</b>";
+    memcpy(&pCursor, sqlite3_value_blob(argv[0]), sizeof(pCursor));
+    if( argc>=2 ){
+      zStart = (const char*)sqlite3_value_text(argv[1]);
+      if( argc>=3 ){
+	zEnd = (const char*)sqlite3_value_text(argv[2]);
+	if( argc>=4 ){
+	  zEllipsis = (const char*)sqlite3_value_text(argv[3]);
+	}
+      }
+    }
+    snippetAllOffsets(pCursor);
+    snippetText(pCursor, zStart, zEnd, zEllipsis);
+    sqlite3_result_text(pContext, pCursor->snippet.zSnippet,
+			pCursor->snippet.nSnippet, SQLITE_STATIC);
+  }
+}
+
+/*
+** Implementation of the offsets() function for FTS3
+*/
+static void snippetOffsetsFunc(
+  sqlite3_context *pContext,
+  int argc,
+  sqlite3_value **argv
+){
+  fulltext_cursor *pCursor;
+  if( argc<1 ) return;
+  if( sqlite3_value_type(argv[0])!=SQLITE_BLOB ||
+      sqlite3_value_bytes(argv[0])!=sizeof(pCursor) ){
+    sqlite3_result_error(pContext, "illegal first argument to offsets",-1);
+  }else{
+    memcpy(&pCursor, sqlite3_value_blob(argv[0]), sizeof(pCursor));
+    snippetAllOffsets(pCursor);
+    snippetOffsetText(&pCursor->snippet);
+    sqlite3_result_text(pContext,
+			pCursor->snippet.zOffset, pCursor->snippet.nOffset,
+			SQLITE_STATIC);
+  }
+}
+
+/* OptLeavesReader is nearly identical to LeavesReader, except that
+** where LeavesReader is geared towards the merging of complete
+** segment levels (with exactly MERGE_COUNT segments), OptLeavesReader
+** is geared towards implementation of the optimize() function, and
+** can merge all segments simultaneously.  This version may be
+** somewhat less efficient than LeavesReader because it merges into an
+** accumulator rather than doing an N-way merge, but since segment
+** size grows exponentially (so segment count logrithmically) this is
+** probably not an immediate problem.
+*/
+/* TODO(shess): Prove that assertion, or extend the merge code to
+** merge tree fashion (like the prefix-searching code does).
+*/
+/* TODO(shess): OptLeavesReader and LeavesReader could probably be
+** merged with little or no loss of performance for LeavesReader.  The
+** merged code would need to handle >MERGE_COUNT segments, and would
+** also need to be able to optionally optimize away deletes.
+*/
+typedef struct OptLeavesReader {
+  /* Segment number, to order readers by age. */
+  int segment;
+  LeavesReader reader;
+} OptLeavesReader;
+
+static int optLeavesReaderAtEnd(OptLeavesReader *pReader){
+  return leavesReaderAtEnd(&pReader->reader);
+}
+static int optLeavesReaderTermBytes(OptLeavesReader *pReader){
+  return leavesReaderTermBytes(&pReader->reader);
+}
+static const char *optLeavesReaderData(OptLeavesReader *pReader){
+  return leavesReaderData(&pReader->reader);
+}
+static int optLeavesReaderDataBytes(OptLeavesReader *pReader){
+  return leavesReaderDataBytes(&pReader->reader);
+}
+static const char *optLeavesReaderTerm(OptLeavesReader *pReader){
+  return leavesReaderTerm(&pReader->reader);
+}
+static int optLeavesReaderStep(fulltext_vtab *v, OptLeavesReader *pReader){
+  return leavesReaderStep(v, &pReader->reader);
+}
+static int optLeavesReaderTermCmp(OptLeavesReader *lr1, OptLeavesReader *lr2){
+  return leavesReaderTermCmp(&lr1->reader, &lr2->reader);
+}
+/* Order by term ascending, segment ascending (oldest to newest), with
+** exhausted readers to the end.
+*/
+static int optLeavesReaderCmp(OptLeavesReader *lr1, OptLeavesReader *lr2){
+  int c = optLeavesReaderTermCmp(lr1, lr2);
+  if( c!=0 ) return c;
+  return lr1->segment-lr2->segment;
+}
+/* Bubble pLr[0] to appropriate place in pLr[1..nLr-1].  Assumes that
+** pLr[1..nLr-1] is already sorted.
+*/
+static void optLeavesReaderReorder(OptLeavesReader *pLr, int nLr){
+  while( nLr>1 && optLeavesReaderCmp(pLr, pLr+1)>0 ){
+    OptLeavesReader tmp = pLr[0];
+    pLr[0] = pLr[1];
+    pLr[1] = tmp;
+    nLr--;
+    pLr++;
+  }
+}
+
+/* optimize() helper function.	Put the readers in order and iterate
+** through them, merging doclists for matching terms into pWriter.
+** Returns SQLITE_OK on success, or the SQLite error code which
+** prevented success.
+*/
+static int optimizeInternal(fulltext_vtab *v,
+			    OptLeavesReader *readers, int nReaders,
+			    LeafWriter *pWriter){
+  int i, rc = SQLITE_OK;
+  DataBuffer doclist, merged, tmp;
+
+  /* Order the readers. */
+  i = nReaders;
+  while( i-- > 0 ){
+    optLeavesReaderReorder(&readers[i], nReaders-i);
+  }
+
+  dataBufferInit(&doclist, LEAF_MAX);
+  dataBufferInit(&merged, LEAF_MAX);
+
+  /* Exhausted readers bubble to the end, so when the first reader is
+  ** at eof, all are at eof.
+  */
+  while( !optLeavesReaderAtEnd(&readers[0]) ){
+
+    /* Figure out how many readers share the next term. */
+    for(i=1; i<nReaders && !optLeavesReaderAtEnd(&readers[i]); i++){
+      if( 0!=optLeavesReaderTermCmp(&readers[0], &readers[i]) ) break;
+    }
+
+    /* Special-case for no merge. */
+    if( i==1 ){
+      /* Trim deletions from the doclist. */
+      dataBufferReset(&merged);
+      docListTrim(DL_DEFAULT,
+		  optLeavesReaderData(&readers[0]),
+		  optLeavesReaderDataBytes(&readers[0]),
+		  -1, DL_DEFAULT, &merged);
+    }else{
+      DLReader dlReaders[MERGE_COUNT];
+      int iReader, nReaders;
+
+      /* Prime the pipeline with the first reader's doclist.  After
+      ** one pass index 0 will reference the accumulated doclist.
+      */
+      dlrInit(&dlReaders[0], DL_DEFAULT,
+	      optLeavesReaderData(&readers[0]),
+	      optLeavesReaderDataBytes(&readers[0]));
+      iReader = 1;
+
+      assert( iReader<i );  /* Must execute the loop at least once. */
+      while( iReader<i ){
+	/* Merge 16 inputs per pass. */
+	for( nReaders=1; iReader<i && nReaders<MERGE_COUNT;
+	     iReader++, nReaders++ ){
+	  dlrInit(&dlReaders[nReaders], DL_DEFAULT,
+		  optLeavesReaderData(&readers[iReader]),
+		  optLeavesReaderDataBytes(&readers[iReader]));
+	}
+
+	/* Merge doclists and swap result into accumulator. */
+	dataBufferReset(&merged);
+	docListMerge(&merged, dlReaders, nReaders);
+	tmp = merged;
+	merged = doclist;
+	doclist = tmp;
+
+	while( nReaders-- > 0 ){
+	  dlrDestroy(&dlReaders[nReaders]);
+	}
+
+	/* Accumulated doclist to reader 0 for next pass. */
+	dlrInit(&dlReaders[0], DL_DEFAULT, doclist.pData, doclist.nData);
+      }
+
+      /* Destroy reader that was left in the pipeline. */
+      dlrDestroy(&dlReaders[0]);
+
+      /* Trim deletions from the doclist. */
+      dataBufferReset(&merged);
+      docListTrim(DL_DEFAULT, doclist.pData, doclist.nData,
+		  -1, DL_DEFAULT, &merged);
+    }
+
+    /* Only pass doclists with hits (skip if all hits deleted). */
+    if( merged.nData>0 ){
+      rc = leafWriterStep(v, pWriter,
+			  optLeavesReaderTerm(&readers[0]),
+			  optLeavesReaderTermBytes(&readers[0]),
+			  merged.pData, merged.nData);
+      if( rc!=SQLITE_OK ) goto err;
+    }
+
+    /* Step merged readers to next term and reorder. */
+    while( i-- > 0 ){
+      rc = optLeavesReaderStep(v, &readers[i]);
+      if( rc!=SQLITE_OK ) goto err;
+
+      optLeavesReaderReorder(&readers[i], nReaders-i);
+    }
+  }
+
+ err:
+  dataBufferDestroy(&doclist);
+  dataBufferDestroy(&merged);
+  return rc;
+}
+
+/* Implement optimize() function for FTS3.  optimize(t) merges all
+** segments in the fts index into a single segment.  't' is the magic
+** table-named column.
+*/
+static void optimizeFunc(sqlite3_context *pContext,
+			 int argc, sqlite3_value **argv){
+  fulltext_cursor *pCursor;
+  if( argc>1 ){
+    sqlite3_result_error(pContext, "excess arguments to optimize()",-1);
+  }else if( sqlite3_value_type(argv[0])!=SQLITE_BLOB ||
+	    sqlite3_value_bytes(argv[0])!=sizeof(pCursor) ){
+    sqlite3_result_error(pContext, "illegal first argument to optimize",-1);
+  }else{
+    fulltext_vtab *v;
+    int i, rc, iMaxLevel;
+    OptLeavesReader *readers;
+    int nReaders;
+    LeafWriter writer;
+    sqlite3_stmt *s;
+
+    memcpy(&pCursor, sqlite3_value_blob(argv[0]), sizeof(pCursor));
+    v = cursor_vtab(pCursor);
+
+    /* Flush any buffered updates before optimizing. */
+    rc = flushPendingTerms(v);
+    if( rc!=SQLITE_OK ) goto err;
+
+    rc = segdir_count(v, &nReaders, &iMaxLevel);
+    if( rc!=SQLITE_OK ) goto err;
+    if( nReaders==0 || nReaders==1 ){
+      sqlite3_result_text(pContext, "Index already optimal", -1,
+			  SQLITE_STATIC);
+      return;
+    }
+
+    rc = sql_get_statement(v, SEGDIR_SELECT_ALL_STMT, &s);
+    if( rc!=SQLITE_OK ) goto err;
+
+    readers = sqlite3_malloc(nReaders*sizeof(readers[0]));
+    if( readers==NULL ) goto err;
+
+    /* Note that there will already be a segment at this position
+    ** until we call segdir_delete() on iMaxLevel.
+    */
+    leafWriterInit(iMaxLevel, 0, &writer);
+
+    i = 0;
+    while( (rc = sqlite3_step(s))==SQLITE_ROW ){
+      sqlite_int64 iStart = sqlite3_column_int64(s, 0);
+      sqlite_int64 iEnd = sqlite3_column_int64(s, 1);
+      const char *pRootData = sqlite3_column_blob(s, 2);
+      int nRootData = sqlite3_column_bytes(s, 2);
+
+      assert( i<nReaders );
+      rc = leavesReaderInit(v, -1, iStart, iEnd, pRootData, nRootData,
+			    &readers[i].reader);
+      if( rc!=SQLITE_OK ) break;
+
+      readers[i].segment = i;
+      i++;
+    }
+
+    /* If we managed to succesfully read them all, optimize them. */
+    if( rc==SQLITE_DONE ){
+      assert( i==nReaders );
+      rc = optimizeInternal(v, readers, nReaders, &writer);
+    }
+
+    while( i-- > 0 ){
+      leavesReaderDestroy(&readers[i].reader);
+    }
+    sqlite3_free(readers);
+
+    /* If we've successfully gotten to here, delete the old segments
+    ** and flush the interior structure of the new segment.
+    */
+    if( rc==SQLITE_OK ){
+      for( i=0; i<=iMaxLevel; i++ ){
+	rc = segdir_delete(v, i);
+	if( rc!=SQLITE_OK ) break;
+      }
+
+      if( rc==SQLITE_OK ) rc = leafWriterFinalize(v, &writer);
+    }
+
+    leafWriterDestroy(&writer);
+
+    if( rc!=SQLITE_OK ) goto err;
+
+    sqlite3_result_text(pContext, "Index optimized", -1, SQLITE_STATIC);
+    return;
+
+    /* TODO(shess): Error-handling needs to be improved along the
+    ** lines of the dump_ functions.
+    */
+ err:
+    {
+      char buf[512];
+      sqlite3_snprintf(sizeof(buf), buf, "Error in optimize: %s",
+		       sqlite3_errmsg(sqlite3_context_db_handle(pContext)));
+      sqlite3_result_error(pContext, buf, -1);
+    }
+  }
+}
+
+#ifdef SQLITE_TEST
+/* Generate an error of the form "<prefix>: <msg>".  If msg is NULL,
+** pull the error from the context's db handle.
+*/
+static void generateError(sqlite3_context *pContext,
+			  const char *prefix, const char *msg){
+  char buf[512];
+  if( msg==NULL ) msg = sqlite3_errmsg(sqlite3_context_db_handle(pContext));
+  sqlite3_snprintf(sizeof(buf), buf, "%s: %s", prefix, msg);
+  sqlite3_result_error(pContext, buf, -1);
+}
+
+/* Helper function to collect the set of terms in the segment into
+** pTerms.  The segment is defined by the leaf nodes between
+** iStartBlockid and iEndBlockid, inclusive, or by the contents of
+** pRootData if iStartBlockid is 0 (in which case the entire segment
+** fit in a leaf).
+*/
+static int collectSegmentTerms(fulltext_vtab *v, sqlite3_stmt *s,
+			       fts3Hash *pTerms){
+  const sqlite_int64 iStartBlockid = sqlite3_column_int64(s, 0);
+  const sqlite_int64 iEndBlockid = sqlite3_column_int64(s, 1);
+  const char *pRootData = sqlite3_column_blob(s, 2);
+  const int nRootData = sqlite3_column_bytes(s, 2);
+  LeavesReader reader;
+  int rc = leavesReaderInit(v, 0, iStartBlockid, iEndBlockid,
+			    pRootData, nRootData, &reader);
+  if( rc!=SQLITE_OK ) return rc;
+
+  while( rc==SQLITE_OK && !leavesReaderAtEnd(&reader) ){
+    const char *pTerm = leavesReaderTerm(&reader);
+    const int nTerm = leavesReaderTermBytes(&reader);
+    void *oldValue = sqlite3Fts3HashFind(pTerms, pTerm, nTerm);
+    void *newValue = (void *)((char *)oldValue+1);
+
+    /* From the comment before sqlite3Fts3HashInsert in fts3_hash.c,
+    ** the data value passed is returned in case of malloc failure.
+    */
+    if( newValue==sqlite3Fts3HashInsert(pTerms, pTerm, nTerm, newValue) ){
+      rc = SQLITE_NOMEM;
+    }else{
+      rc = leavesReaderStep(v, &reader);
+    }
+  }
+
+  leavesReaderDestroy(&reader);
+  return rc;
+}
+
+/* Helper function to build the result string for dump_terms(). */
+static int generateTermsResult(sqlite3_context *pContext, fts3Hash *pTerms){
+  int iTerm, nTerms, nResultBytes, iByte;
+  char *result;
+  TermData *pData;
+  fts3HashElem *e;
+
+  /* Iterate pTerms to generate an array of terms in pData for
+  ** sorting.
+  */
+  nTerms = fts3HashCount(pTerms);
+  assert( nTerms>0 );
+  pData = sqlite3_malloc(nTerms*sizeof(TermData));
+  if( pData==NULL ) return SQLITE_NOMEM;
+
+  nResultBytes = 0;
+  for(iTerm = 0, e = fts3HashFirst(pTerms); e; iTerm++, e = fts3HashNext(e)){
+    nResultBytes += fts3HashKeysize(e)+1;   /* Term plus trailing space */
+    assert( iTerm<nTerms );
+    pData[iTerm].pTerm = fts3HashKey(e);
+    pData[iTerm].nTerm = fts3HashKeysize(e);
+    pData[iTerm].pCollector = fts3HashData(e);	/* unused */
+  }
+  assert( iTerm==nTerms );
+
+  assert( nResultBytes>0 );   /* nTerms>0, nResultsBytes must be, too. */
+  result = sqlite3_malloc(nResultBytes);
+  if( result==NULL ){
+    sqlite3_free(pData);
+    return SQLITE_NOMEM;
+  }
+
+  if( nTerms>1 ) qsort(pData, nTerms, sizeof(*pData), termDataCmp);
+
+  /* Read the terms in order to build the result. */
+  iByte = 0;
+  for(iTerm=0; iTerm<nTerms; ++iTerm){
+    memcpy(result+iByte, pData[iTerm].pTerm, pData[iTerm].nTerm);
+    iByte += pData[iTerm].nTerm;
+    result[iByte++] = ' ';
+  }
+  assert( iByte==nResultBytes );
+  assert( result[nResultBytes-1]==' ' );
+  result[nResultBytes-1] = '\0';
+
+  /* Passes away ownership of result. */
+  sqlite3_result_text(pContext, result, nResultBytes-1, sqlite3_free);
+  sqlite3_free(pData);
+  return SQLITE_OK;
+}
+
+/* Implements dump_terms() for use in inspecting the fts3 index from
+** tests.  TEXT result containing the ordered list of terms joined by
+** spaces.  dump_terms(t, level, idx) dumps the terms for the segment
+** specified by level, idx (in %_segdir), while dump_terms(t) dumps
+** all terms in the index.  In both cases t is the fts table's magic
+** table-named column.
+*/
+static void dumpTermsFunc(
+  sqlite3_context *pContext,
+  int argc, sqlite3_value **argv
+){
+  fulltext_cursor *pCursor;
+  if( argc!=3 && argc!=1 ){
+    generateError(pContext, "dump_terms", "incorrect arguments");
+  }else if( sqlite3_value_type(argv[0])!=SQLITE_BLOB ||
+	    sqlite3_value_bytes(argv[0])!=sizeof(pCursor) ){
+    generateError(pContext, "dump_terms", "illegal first argument");
+  }else{
+    fulltext_vtab *v;
+    fts3Hash terms;
+    sqlite3_stmt *s = NULL;
+    int rc;
+
+    memcpy(&pCursor, sqlite3_value_blob(argv[0]), sizeof(pCursor));
+    v = cursor_vtab(pCursor);
+
+    /* If passed only the cursor column, get all segments.  Otherwise
+    ** get the segment described by the following two arguments.
+    */
+    if( argc==1 ){
+      rc = sql_get_statement(v, SEGDIR_SELECT_ALL_STMT, &s);
+    }else{
+      rc = sql_get_statement(v, SEGDIR_SELECT_SEGMENT_STMT, &s);
+      if( rc==SQLITE_OK ){
+	rc = sqlite3_bind_int(s, 1, sqlite3_value_int(argv[1]));
+	if( rc==SQLITE_OK ){
+	  rc = sqlite3_bind_int(s, 2, sqlite3_value_int(argv[2]));
+	}
+      }
+    }
+
+    if( rc!=SQLITE_OK ){
+      generateError(pContext, "dump_terms", NULL);
+      return;
+    }
+
+    /* Collect the terms for each segment. */
+    sqlite3Fts3HashInit(&terms, FTS3_HASH_STRING, 1);
+    while( (rc = sqlite3_step(s))==SQLITE_ROW ){
+      rc = collectSegmentTerms(v, s, &terms);
+      if( rc!=SQLITE_OK ) break;
+    }
+
+    if( rc!=SQLITE_DONE ){
+      sqlite3_reset(s);
+      generateError(pContext, "dump_terms", NULL);
+    }else{
+      const int nTerms = fts3HashCount(&terms);
+      if( nTerms>0 ){
+	rc = generateTermsResult(pContext, &terms);
+	if( rc==SQLITE_NOMEM ){
+	  generateError(pContext, "dump_terms", "out of memory");
+	}else{
+	  assert( rc==SQLITE_OK );
+	}
+      }else if( argc==3 ){
+	/* The specific segment asked for could not be found. */
+	generateError(pContext, "dump_terms", "segment not found");
+      }else{
+	/* No segments found. */
+	/* TODO(shess): It should be impossible to reach this.	This
+	** case can only happen for an empty table, in which case
+	** SQLite has no rows to call this function on.
+	*/
+	sqlite3_result_null(pContext);
+      }
+    }
+    sqlite3Fts3HashClear(&terms);
+  }
+}
+
+/* Expand the DL_DEFAULT doclist in pData into a text result in
+** pContext.
+*/
+static void createDoclistResult(sqlite3_context *pContext,
+				const char *pData, int nData){
+  DataBuffer dump;
+  DLReader dlReader;
+
+  assert( pData!=NULL && nData>0 );
+
+  dataBufferInit(&dump, 0);
+  dlrInit(&dlReader, DL_DEFAULT, pData, nData);
+  for( ; !dlrAtEnd(&dlReader); dlrStep(&dlReader) ){
+    char buf[256];
+    PLReader plReader;
+
+    plrInit(&plReader, &dlReader);
+    if( DL_DEFAULT==DL_DOCIDS || plrAtEnd(&plReader) ){
+      sqlite3_snprintf(sizeof(buf), buf, "[%lld] ", dlrDocid(&dlReader));
+      dataBufferAppend(&dump, buf, strlen(buf));
+    }else{
+      int iColumn = plrColumn(&plReader);
+
+      sqlite3_snprintf(sizeof(buf), buf, "[%lld %d[",
+		       dlrDocid(&dlReader), iColumn);
+      dataBufferAppend(&dump, buf, strlen(buf));
+
+      for( ; !plrAtEnd(&plReader); plrStep(&plReader) ){
+	if( plrColumn(&plReader)!=iColumn ){
+	  iColumn = plrColumn(&plReader);
+	  sqlite3_snprintf(sizeof(buf), buf, "] %d[", iColumn);
+	  assert( dump.nData>0 );
+	  dump.nData--;			    /* Overwrite trailing space. */
+	  assert( dump.pData[dump.nData]==' ');
+	  dataBufferAppend(&dump, buf, strlen(buf));
+	}
+	if( DL_DEFAULT==DL_POSITIONS_OFFSETS ){
+	  sqlite3_snprintf(sizeof(buf), buf, "%d,%d,%d ",
+			   plrPosition(&plReader),
+			   plrStartOffset(&plReader), plrEndOffset(&plReader));
+	}else if( DL_DEFAULT==DL_POSITIONS ){
+	  sqlite3_snprintf(sizeof(buf), buf, "%d ", plrPosition(&plReader));
+	}else{
+	  assert( NULL=="Unhandled DL_DEFAULT value");
+	}
+	dataBufferAppend(&dump, buf, strlen(buf));
+      }
+      plrDestroy(&plReader);
+
+      assert( dump.nData>0 );
+      dump.nData--;			/* Overwrite trailing space. */
+      assert( dump.pData[dump.nData]==' ');
+      dataBufferAppend(&dump, "]] ", 3);
+    }
+  }
+  dlrDestroy(&dlReader);
+
+  assert( dump.nData>0 );
+  dump.nData--;			    /* Overwrite trailing space. */
+  assert( dump.pData[dump.nData]==' ');
+  dump.pData[dump.nData] = '\0';
+  assert( dump.nData>0 );
+
+  /* Passes ownership of dump's buffer to pContext. */
+  sqlite3_result_text(pContext, dump.pData, dump.nData, sqlite3_free);
+  dump.pData = NULL;
+  dump.nData = dump.nCapacity = 0;
+}
+
+/* Implements dump_doclist() for use in inspecting the fts3 index from
+** tests.  TEXT result containing a string representation of the
+** doclist for the indicated term.  dump_doclist(t, term, level, idx)
+** dumps the doclist for term from the segment specified by level, idx
+** (in %_segdir), while dump_doclist(t, term) dumps the logical
+** doclist for the term across all segments.  The per-segment doclist
+** can contain deletions, while the full-index doclist will not
+** (deletions are omitted).
+**
+** Result formats differ with the setting of DL_DEFAULTS.  Examples:
+**
+** DL_DOCIDS: [1] [3] [7]
+** DL_POSITIONS: [1 0[0 4] 1[17]] [3 1[5]]
+** DL_POSITIONS_OFFSETS: [1 0[0,0,3 4,23,26] 1[17,102,105]] [3 1[5,20,23]]
+**
+** In each case the number after the outer '[' is the docid.  In the
+** latter two cases, the number before the inner '[' is the column
+** associated with the values within.  For DL_POSITIONS the numbers
+** within are the positions, for DL_POSITIONS_OFFSETS they are the
+** position, the start offset, and the end offset.
+*/
+static void dumpDoclistFunc(
+  sqlite3_context *pContext,
+  int argc, sqlite3_value **argv
+){
+  fulltext_cursor *pCursor;
+  if( argc!=2 && argc!=4 ){
+    generateError(pContext, "dump_doclist", "incorrect arguments");
+  }else if( sqlite3_value_type(argv[0])!=SQLITE_BLOB ||
+	    sqlite3_value_bytes(argv[0])!=sizeof(pCursor) ){
+    generateError(pContext, "dump_doclist", "illegal first argument");
+  }else if( sqlite3_value_text(argv[1])==NULL ||
+	    sqlite3_value_text(argv[1])[0]=='\0' ){
+    generateError(pContext, "dump_doclist", "empty second argument");
+  }else{
+    const char *pTerm = (const char *)sqlite3_value_text(argv[1]);
+    const int nTerm = strlen(pTerm);
+    fulltext_vtab *v;
+    int rc;
+    DataBuffer doclist;
+
+    memcpy(&pCursor, sqlite3_value_blob(argv[0]), sizeof(pCursor));
+    v = cursor_vtab(pCursor);
+
+    dataBufferInit(&doclist, 0);
+
+    /* termSelect() yields the same logical doclist that queries are
+    ** run against.
+    */
+    if( argc==2 ){
+      rc = termSelect(v, v->nColumn, pTerm, nTerm, 0, DL_DEFAULT, &doclist);
+    }else{
+      sqlite3_stmt *s = NULL;
+
+      /* Get our specific segment's information. */
+      rc = sql_get_statement(v, SEGDIR_SELECT_SEGMENT_STMT, &s);
+      if( rc==SQLITE_OK ){
+	rc = sqlite3_bind_int(s, 1, sqlite3_value_int(argv[2]));
+	if( rc==SQLITE_OK ){
+	  rc = sqlite3_bind_int(s, 2, sqlite3_value_int(argv[3]));
+	}
+      }
+
+      if( rc==SQLITE_OK ){
+	rc = sqlite3_step(s);
+
+	if( rc==SQLITE_DONE ){
+	  dataBufferDestroy(&doclist);
+	  generateError(pContext, "dump_doclist", "segment not found");
+	  return;
+	}
+
+	/* Found a segment, load it into doclist. */
+	if( rc==SQLITE_ROW ){
+	  const sqlite_int64 iLeavesEnd = sqlite3_column_int64(s, 1);
+	  const char *pData = sqlite3_column_blob(s, 2);
+	  const int nData = sqlite3_column_bytes(s, 2);
+
+	  /* loadSegment() is used by termSelect() to load each
+	  ** segment's data.
+	  */
+	  rc = loadSegment(v, pData, nData, iLeavesEnd, pTerm, nTerm, 0,
+			   &doclist);
+	  if( rc==SQLITE_OK ){
+	    rc = sqlite3_step(s);
+
+	    /* Should not have more than one matching segment. */
+	    if( rc!=SQLITE_DONE ){
+	      sqlite3_reset(s);
+	      dataBufferDestroy(&doclist);
+	      generateError(pContext, "dump_doclist", "invalid segdir");
+	      return;
+	    }
+	    rc = SQLITE_OK;
+	  }
+	}
+      }
+
+      sqlite3_reset(s);
+    }
+
+    if( rc==SQLITE_OK ){
+      if( doclist.nData>0 ){
+	createDoclistResult(pContext, doclist.pData, doclist.nData);
+      }else{
+	/* TODO(shess): This can happen if the term is not present, or
+	** if all instances of the term have been deleted and this is
+	** an all-index dump.  It may be interesting to distinguish
+	** these cases.
+	*/
+	sqlite3_result_text(pContext, "", 0, SQLITE_STATIC);
+      }
+    }else if( rc==SQLITE_NOMEM ){
+      /* Handle out-of-memory cases specially because if they are
+      ** generated in fts3 code they may not be reflected in the db
+      ** handle.
+      */
+      /* TODO(shess): Handle this more comprehensively.
+      ** sqlite3ErrStr() has what I need, but is internal.
+      */
+      generateError(pContext, "dump_doclist", "out of memory");
+    }else{
+      generateError(pContext, "dump_doclist", NULL);
+    }
+
+    dataBufferDestroy(&doclist);
+  }
+}
+#endif
+
+/*
+** This routine implements the xFindFunction method for the FTS3
+** virtual table.
+*/
+static int fulltextFindFunction(
+  sqlite3_vtab *pVtab,
+  int nArg,
+  const char *zName,
+  void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
+  void **ppArg
+){
+  if( strcmp(zName,"snippet")==0 ){
+    *pxFunc = snippetFunc;
+    return 1;
+  }else if( strcmp(zName,"offsets")==0 ){
+    *pxFunc = snippetOffsetsFunc;
+    return 1;
+  }else if( strcmp(zName,"optimize")==0 ){
+    *pxFunc = optimizeFunc;
+    return 1;
+#ifdef SQLITE_TEST
+    /* NOTE(shess): These functions are present only for testing
+    ** purposes.  No particular effort is made to optimize their
+    ** execution or how they build their results.
+    */
+  }else if( strcmp(zName,"dump_terms")==0 ){
+    /* fprintf(stderr, "Found dump_terms\n"); */
+    *pxFunc = dumpTermsFunc;
+    return 1;
+  }else if( strcmp(zName,"dump_doclist")==0 ){
+    /* fprintf(stderr, "Found dump_doclist\n"); */
+    *pxFunc = dumpDoclistFunc;
+    return 1;
+#endif
+  }
+  return 0;
+}
+
+/*
+** Rename an fts3 table.
+*/
+static int fulltextRename(
+  sqlite3_vtab *pVtab,
+  const char *zName
+){
+  fulltext_vtab *p = (fulltext_vtab *)pVtab;
+  int rc = SQLITE_NOMEM;
+  char *zSql = sqlite3_mprintf(
+    "ALTER TABLE %Q.'%q_content'  RENAME TO '%q_content';"
+    "ALTER TABLE %Q.'%q_segments' RENAME TO '%q_segments';"
+    "ALTER TABLE %Q.'%q_segdir'   RENAME TO '%q_segdir';"
+    , p->zDb, p->zName, zName
+    , p->zDb, p->zName, zName
+    , p->zDb, p->zName, zName
+  );
+  if( zSql ){
+    rc = sqlite3_exec(p->db, zSql, 0, 0, 0);
+    sqlite3_free(zSql);
+  }
+  return rc;
+}
+
+static const sqlite3_module fts3Module = {
+  /* iVersion	   */ 0,
+  /* xCreate	   */ fulltextCreate,
+  /* xConnect	   */ fulltextConnect,
+  /* xBestIndex    */ fulltextBestIndex,
+  /* xDisconnect   */ fulltextDisconnect,
+  /* xDestroy	   */ fulltextDestroy,
+  /* xOpen	   */ fulltextOpen,
+  /* xClose	   */ fulltextClose,
+  /* xFilter	   */ fulltextFilter,
+  /* xNext	   */ fulltextNext,
+  /* xEof	   */ fulltextEof,
+  /* xColumn	   */ fulltextColumn,
+  /* xRowid	   */ fulltextRowid,
+  /* xUpdate	   */ fulltextUpdate,
+  /* xBegin	   */ fulltextBegin,
+  /* xSync	   */ fulltextSync,
+  /* xCommit	   */ fulltextCommit,
+  /* xRollback	   */ fulltextRollback,
+  /* xFindFunction */ fulltextFindFunction,
+  /* xRename */       fulltextRename,
+};
+
+static void hashDestroy(void *p){
+  fts3Hash *pHash = (fts3Hash *)p;
+  sqlite3Fts3HashClear(pHash);
+  sqlite3_free(pHash);
+}
+
+
+int sqlite3Fts3InitHashTable(sqlite3 *, fts3Hash *, const char *);
+
+/*
+** Initialise the fts3 extension. If this extension is built as part
+** of the sqlite library, then this function is called directly by
+** SQLite. If fts3 is built as a dynamically loadable extension, this
+** function is called by the sqlite3_extension_init() entry point.
+*/
+int sqlite3Fts3Init(sqlite3 *db){
+  int rc = SQLITE_OK;
+
+  /* Create the virtual table wrapper around the hash-table and overload
+  ** the two scalar functions. If this is successful, register the
+  ** module with sqlite.
+  */
+  if( SQLITE_OK==rc
+   && SQLITE_OK==(rc = sqlite3_overload_function(db, "snippet", -1))
+   && SQLITE_OK==(rc = sqlite3_overload_function(db, "offsets", -1))
+   && SQLITE_OK==(rc = sqlite3_overload_function(db, "optimize", -1))
+#ifdef SQLITE_TEST
+   && SQLITE_OK==(rc = sqlite3_overload_function(db, "dump_terms", -1))
+   && SQLITE_OK==(rc = sqlite3_overload_function(db, "dump_doclist", -1))
+#endif
+  ){
+    return sqlite3_create_module_v2(
+	db, "trackerfts", &fts3Module, 0, 0
+    );
+  }
+
+  /* An error has occured. Delete the hash table and return the error code. */
+  assert( rc!=SQLITE_OK );
+
+  return rc;
+}
+
+
+int sqlite3_extension_init(
+  sqlite3 *db,
+  char **pzErrMsg,
+  const sqlite3_api_routines *pApi
+){
+  SQLITE_EXTENSION_INIT2(pApi)
+  return sqlite3Fts3Init(db);
+}
+

Added: trunk/src/tracker-fts/tracker-fts.h
==============================================================================
--- (empty file)
+++ trunk/src/tracker-fts/tracker-fts.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,26 @@
+/*
+** 2006 Oct 10
+**
+** 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.
+**
+******************************************************************************
+**
+** This header file is used by programs that want to link against the
+** FTS3 library.  All it does is declare the sqlite3Fts3Init() interface.
+*/
+#include <sqlite3.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif	/* __cplusplus */
+
+int sqlite3Fts3Init(sqlite3 *db);
+
+#ifdef __cplusplus
+}  /* extern "C" */
+#endif	/* __cplusplus */

Added: trunk/src/tracker-indexer/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/tracker-indexer/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,89 @@
+include $(top_srcdir)/Makefile.decl
+
+SUBDIRS = modules
+
+INCLUDES =								\
+	-DSHAREDIR=\""$(datadir)"\"					\
+	-DLIBDIR=\""$(libdir)"\"					\
+	-DLOCALEDIR=\""$(localedir)"\" 					\
+	-DINDEXER_MODULES_DIR=\""$(libdir)/tracker/indexer-modules"\"	\
+	-DLIBEXEC_PATH=\""$(libexecdir)"\"				\
+	-DG_LOG_DOMAIN=\"Tracker\"					\
+	-I$(top_srcdir)/src						\
+	$(DBUS_CFLAGS)							\
+	$(PANGO_CFLAGS)							\
+	$(GMODULE_CFLAGS)						
+
+libexec_PROGRAMS = tracker-indexer
+
+tracker_indexer_SOURCES =						\
+	tracker-dbus.c							\
+	tracker-dbus.h							\
+	tracker-indexer.c						\
+	tracker-indexer.h						\
+	tracker-indexer-db.c						\
+	tracker-indexer-db.h						\
+	tracker-indexer-module.c					\
+	tracker-indexer-module.h					\
+	tracker-main.c							\
+	tracker-marshal-main.c						\
+	tracker-metadata.c						\
+	tracker-metadata.h						\
+	tracker-metadata-utils.c					\
+	tracker-metadata-utils.h					\
+	tracker-module.h
+
+tracker_indexer_LDADD =							\
+	$(top_builddir)/src/libtracker-db/libtracker-db.la 		\
+	$(top_builddir)/src/libtracker-common/libtracker-common.la 	\
+	$(top_builddir)/src/libstemmer/libstemmer.la	 		\
+	$(trackerd_win_libs)						\
+	$(DBUS_LIBS)							\
+	$(GMODULE_LIBS)							\
+	$(GTHREAD_LIBS)							\
+	$(PANGO_LIBS)							\
+	$(GIO_LIBS)							\
+	$(GLIB2_LIBS)							\
+	-lz								\
+	-lm
+
+#
+# Note: This rule is slightly different from the normal rule we would
+# use here because it copies the resulting file on success to the
+# tracker-indexer directory in the unit tests so that they can build.
+# We do this for the marshal generated source and for the dbus
+# generated glue files.
+#
+# Should we add the files for the unit tests to CLEANFILES?
+#
+
+tracker-marshal.h: tracker-marshal.list
+	$(GLIB_GENMARSHAL) $< --prefix=tracker_marshal --header > $@ && \
+	$(LN_S) -sf $(top_srcdir)/src/tracker-indexer/$@ $(top_srcdir)/tests/tracker-indexer/$@
+
+tracker-marshal.c: tracker-marshal.list
+	$(GLIB_GENMARSHAL) $< --prefix=tracker_marshal --body > $@ && 	\
+	$(LN_S) -sf $(top_srcdir)/src/tracker-indexer/$@ $(top_srcdir)/tests/tracker-indexer/$@
+
+tracker-marshal-main.c: tracker-marshal.c tracker-marshal.h
+
+marshal_sources =                                         		\
+        tracker-marshal.h                             			\
+        tracker-marshal.c
+
+dbus_sources = 								\
+	tracker-indexer-glue.h
+
+%-glue.h: $(top_srcdir)/data/dbus/%.xml
+	$(DBUSBINDINGTOOL) --mode=glib-server --output=$@ --prefix=$(subst -,_,$*) $^ && \
+	$(LN_S) -sf $(top_srcdir)/src/tracker-indexer/$@ $(top_srcdir)/tests/tracker-indexer/$@
+
+BUILT_SOURCES = 							\
+	$(dbus_sources)							\
+	$(marshal_sources)
+
+CLEANFILES = $(BUILT_SOURCES)
+
+EXTRA_DIST = 								\
+	$(BUILT_SOURCES)						\
+	tracker-marshal.list

Added: trunk/src/tracker-indexer/modules/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/tracker-indexer/modules/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,64 @@
+include $(top_srcdir)/Makefile.decl
+
+module_flags = -module -avoid-version -no-undefined
+indexer_modulesdir = $(libdir)/tracker/indexer-modules
+
+INCLUDES =								\
+	-DSHAREDIR=\""$(datadir)"\"					\
+	-DLIBDIR=\""$(libdir)"\"					\
+	-DLOCALEDIR=\""$(localedir)"\" 					\
+	-DINDEXER_MODULES_DIR=\"$(indexer_modulesdir)\"			\
+	-DG_LOG_DOMAIN=\"Tracker\"					\
+	-D_GNU_SOURCE							\
+	-I$(top_srcdir)/src						\
+	$(GMODULE_CFLAGS)						\
+	$(GIO_CFLAGS)							\
+	$(GLIB2_CFLAGS)							\
+	$(GCONF_CFLAGS)							\
+	$(GMIME_CFLAGS)
+
+indexer_modules_LTLIBRARIES = 						\
+	libtracker-indexer-applications.la				\
+	libtracker-indexer-files.la					\
+	libtracker-indexer-gaim-conversations.la
+
+# Applications module
+libtracker_indexer_applications_la_SOURCES = applications.c
+libtracker_indexer_applications_la_LDFLAGS = $(module_flags)
+libtracker_indexer_applications_la_LIBADD = 				\
+	$(GMODULE_LIBS)							\
+	$(GIO_LIBS)							\
+	$(GLIB2_LIBS)
+
+# Files module
+libtracker_indexer_files_la_SOURCES = files.c
+libtracker_indexer_files_la_LDFLAGS = $(module_flags)
+libtracker_indexer_files_la_LIBADD = 					\
+	$(top_builddir)/src/libtracker-db/libtracker-db.la		\
+	$(GMODULE_LIBS)							\
+	$(GIO_LIBS)							\
+	$(GLIB2_LIBS)
+
+# Instant messaging module
+libtracker_indexer_gaim_conversations_la_SOURCES = gaim-conversations.c
+libtracker_indexer_gaim_conversations_la_LDFLAGS = $(module_flags)
+libtracker_indexer_gaim_conversations_la_LIBADD = 			\
+	$(GMODULE_LIBS)							\
+	$(GIO_LIBS)							\
+	$(GLIB2_LIBS)
+
+if HAVE_GCONF
+
+indexer_modules_LTLIBRARIES += 						\
+	libtracker-indexer-evolution.la
+
+# Evolution
+libtracker_indexer_evolution_la_SOURCES = evolution.c
+libtracker_indexer_evolution_la_LDFLAGS = $(module_flags)
+libtracker_indexer_evolution_la_LIBADD = 				\
+	$(GMODULE_LIBS)							\
+	$(GLIB2_LIBS)							\
+	$(GCONF_LIBS)							\
+	$(GMIME_LIBS)
+
+endif

Added: trunk/src/tracker-indexer/modules/applications.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-indexer/modules/applications.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,155 @@
+/* Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <stdlib.h>
+#include <glib.h>
+#include <tracker-indexer/tracker-module.h>
+#include <tracker-indexer/tracker-metadata.h>
+
+#define GROUP_DESKTOP_ENTRY "Desktop Entry"
+#define KEY_TYPE	    "Type"
+#define KEY_HIDDEN	    "Hidden"
+#define KEY_NAME	    "Name"
+#define KEY_GENERIC_NAME    "GenericName"
+#define KEY_COMMENT	    "Comment"
+#define KEY_EXECUTABLE	    "Exec"
+#define KEY_ICON	    "Icon"
+#define KEY_MIMETYPE	    "MimeType"
+#define KEY_CATEGORIES	    "Categories"
+
+#define METADATA_FILE_NAME	  "File:Name"
+#define METADATA_APP_NAME	  "App:Name"
+#define METADATA_APP_DISPLAY_NAME "App:DisplayName"
+#define METADATA_APP_GENERIC_NAME "App:GenericName"
+#define METADATA_APP_COMMENT	  "App:Comment"
+#define METADATA_APP_EXECUTABLE   "App:Exec"
+#define METADATA_APP_ICON	  "App:Icon"
+#define METADATA_APP_MIMETYPE	  "App:MimeType"
+#define METADATA_APP_CATEGORIES   "App:Categories"
+
+G_CONST_RETURN gchar *
+tracker_module_get_name (void)
+{
+	/* Return module name here */
+	return "Applications";
+}
+
+static void
+insert_data_from_desktop_file (TrackerMetadata *metadata,
+			       const gchar     *metadata_key,
+			       GKeyFile        *desktop_file,
+			       const gchar     *key,
+			       gboolean		use_locale)
+{
+	gchar *str;
+
+	if (use_locale) {
+		str = g_key_file_get_locale_string (desktop_file, GROUP_DESKTOP_ENTRY, key, NULL, NULL);
+	} else {
+		str = g_key_file_get_string (desktop_file, GROUP_DESKTOP_ENTRY, key, NULL);
+	}
+
+	if (str) {
+		tracker_metadata_insert (metadata, metadata_key, str);
+	}
+}
+
+static void
+insert_list_from_desktop_file (TrackerMetadata *metadata,
+			       const gchar     *metadata_key,
+			       GKeyFile        *desktop_file,
+			       const gchar     *key,
+			       gboolean		use_locale)
+{
+	gchar **arr;
+
+	if (use_locale) {
+		arr = g_key_file_get_locale_string_list (desktop_file, GROUP_DESKTOP_ENTRY, key, NULL, NULL, NULL);
+	} else {
+		arr = g_key_file_get_string_list (desktop_file, GROUP_DESKTOP_ENTRY, key, NULL, NULL);
+	}
+
+	if (arr) {
+		GList *list = NULL;
+		gint i;
+
+		for (i = 0; arr[i]; i++) {
+			list = g_list_prepend (list, arr[i]);
+		}
+
+		list = g_list_reverse (list);
+		g_free (arr);
+
+		tracker_metadata_insert_multiple_values (metadata, metadata_key, list);
+	}
+}
+
+TrackerMetadata *
+tracker_module_file_get_metadata (TrackerFile *file)
+{
+	TrackerMetadata *metadata;
+	GKeyFile *key_file;
+	gchar *type, *filename;
+
+	/* Check we're dealing with a desktop file */
+	if (!g_str_has_suffix (file->path, ".desktop")) {
+		return NULL;
+	}
+
+	key_file = g_key_file_new ();
+
+	if (!g_key_file_load_from_file (key_file, file->path, G_KEY_FILE_NONE, NULL)) {
+		g_key_file_free (key_file);
+		return NULL;
+	}
+
+	if (g_key_file_get_boolean (key_file, GROUP_DESKTOP_ENTRY, KEY_HIDDEN, NULL)) {
+		g_key_file_free (key_file);
+		return NULL;
+	}
+
+	type = g_key_file_get_string (key_file, GROUP_DESKTOP_ENTRY, KEY_TYPE, NULL);
+
+	if (!type || g_ascii_strcasecmp (type, "Application") != 0) {
+		g_key_file_free (key_file);
+		g_free (type);
+		return NULL;
+	}
+
+	/* Begin collecting data */
+	metadata = tracker_metadata_new ();
+
+	insert_data_from_desktop_file (metadata, METADATA_APP_NAME, key_file, KEY_NAME, FALSE);
+	insert_data_from_desktop_file (metadata, METADATA_APP_DISPLAY_NAME, key_file, KEY_NAME, TRUE);
+	insert_data_from_desktop_file (metadata, METADATA_APP_GENERIC_NAME, key_file, KEY_GENERIC_NAME, TRUE);
+	insert_data_from_desktop_file (metadata, METADATA_APP_COMMENT, key_file, KEY_COMMENT, TRUE);
+	insert_data_from_desktop_file (metadata, METADATA_APP_EXECUTABLE, key_file, KEY_EXECUTABLE, FALSE);
+	insert_data_from_desktop_file (metadata, METADATA_APP_ICON, key_file, KEY_ICON, FALSE);
+
+	insert_list_from_desktop_file (metadata, METADATA_APP_MIMETYPE, key_file, KEY_MIMETYPE, FALSE);
+	insert_list_from_desktop_file (metadata, METADATA_APP_CATEGORIES, key_file, KEY_CATEGORIES, FALSE);
+
+	filename = g_filename_display_basename (file->path);
+	tracker_metadata_insert (metadata, METADATA_FILE_NAME, filename);
+
+	g_key_file_free (key_file);
+	g_free (type);
+
+	return metadata;
+}

Added: trunk/src/tracker-indexer/modules/dummy.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-indexer/modules/dummy.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,138 @@
+/* Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <glib.h>
+#include <tracker-indexer/tracker-metadata.h>
+
+void
+tracker_module_init (void)
+{
+	/* Implementing this function is optional.
+	 *
+	 * Allocate here all static resources for the module.
+	 */
+}
+
+void
+tracker_module_shutdown (void)
+{
+	/* Implementing this function is optional.
+	 *
+	 * Free here all resources allocated in tracker_module_init()
+	 */
+}
+
+G_CONST_RETURN gchar *
+tracker_module_get_name (void)
+{
+	/* Return module name here */
+	return "Dummy";
+}
+
+gpointer
+tracker_module_file_get_data (const gchar *path)
+{
+	/* Implementing this function is optional.
+	 *
+	 * Return here private, module specific data for path.
+	 * Given this data is attached to the file until it isn't
+	 * needed anymore. This is usually used for files that
+	 * contain sets of data that should be considered as separate
+	 * entities (for example, mboxes), so the module can internally
+	 * keep the state. Also see tracker_module_file_iter_contents().
+	 */
+	return NULL;
+}
+
+gchar *
+tracker_module_file_get_service_type (TrackerFile *file)
+{
+	/* Implementing this function is optional.
+	 *
+	 * Return the service type for the incoming path.
+	 *
+	 * If this function is not implemented, the indexer will use
+	 * the name of the module (tracker_module_get_name) as service.
+	 *
+	 */
+	return NULL;
+}
+
+void
+tracker_module_file_free_data (gpointer file_data)
+{
+	/* Implementing this function is optional
+	 *
+	 * Free the data created previously
+	 * through tracker_module_file_get_data()
+	 */
+}
+
+void
+tracker_module_file_get_uri (TrackerFile  *file,
+			     gchar	 **dirname,
+			     gchar	 **basename)
+{
+	/* Implementing this function is optional
+	 *
+	 * Return dirname/basename for the current item, with this
+	 * method modules can specify different URIs for different
+	 * elements contained in the file. Also see
+	 * tracker_module_file_iter_contents()
+	 */
+	*dirname = g_path_get_dirname (file->path);
+	*basename = g_path_get_basename (file->path);
+}
+
+TrackerMetadata *
+tracker_module_file_get_metadata (TrackerFile *file)
+{
+	/* Return a hashtable filled with metadata for file, given the
+	 * current state. Also see tracker_module_file_iter_contents()
+	 */
+	return NULL;
+}
+
+gchar *
+tracker_module_file_get_text (TrackerFile *file)
+{
+	/* Implementing this function is optional
+	 *
+	 * Return here full text for file, given the current state,
+	 * also see tracker_module_file_iter_contents()
+	 */
+	return NULL;
+}
+
+gboolean
+tracker_module_file_iter_contents (TrackerFile *file)
+{
+	/* Implementing this function is optional
+	 *
+	 * This function is meant to iterate the internal state,
+	 * so it points to the next entity inside the file.
+	 * In case there is such next entity, this function must
+	 * return TRUE, else, returning FALSE will make the indexer
+	 * think it is done with this file and move on to the next one.
+	 *
+	 * What an "entity" is considered is left to the module
+	 * implementation.
+	 */
+	return FALSE;
+}

Added: trunk/src/tracker-indexer/modules/evolution.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-indexer/modules/evolution.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,1626 @@
+/* Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <gmime/gmime.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <gconf/gconf-client.h>
+#include <tracker-indexer/tracker-module.h>
+#include <tracker-indexer/tracker-metadata.h>
+#include <tracker-indexer/tracker-metadata-utils.h>
+#include <libtracker-common/tracker-utils.h>
+#include <libtracker-common/tracker-file-utils.h>
+#include <libtracker-common/tracker-type-utils.h>
+
+#define METADATA_FILE_PATH	     "File:Path"
+#define METADATA_FILE_NAME	     "File:Name"
+#define METADATA_EMAIL_RECIPIENT     "Email:Recipient"
+#define METADATA_EMAIL_DATE	     "Email:Date"
+#define METADATA_EMAIL_SENDER	     "Email:Sender"
+#define METADATA_EMAIL_SUBJECT	     "Email:Subject"
+#define METADATA_EMAIL_SENT_TO	     "Email:SentTo"
+#define METADATA_EMAIL_CC	     "Email:CC"
+
+typedef union EvolutionFileData EvolutionFileData;
+typedef struct EvolutionLocalData EvolutionLocalData;
+typedef struct EvolutionImapData EvolutionImapData;
+typedef struct EvolutionAccountContext EvolutionAccountContext;
+typedef enum MailStorageType MailStorageType;
+
+enum MailStorageType {
+	MAIL_STORAGE_NONE,
+	MAIL_STORAGE_LOCAL,
+	MAIL_STORAGE_IMAP
+};
+
+struct EvolutionLocalData {
+	MailStorageType type;
+	GMimeStream *stream;
+	GMimeParser *parser;
+	GMimeMessage *message;
+
+	GList *mime_parts;
+	GList *current_mime_part;
+};
+
+struct EvolutionImapData {
+	MailStorageType type;
+	gint fd;
+	FILE *summary;
+	guint n_messages;
+	guint cur_message;
+	gchar *cur_message_uid;
+
+	GList *mime_parts;
+	GList *current_mime_part;
+};
+
+union EvolutionFileData {
+	MailStorageType type;
+	EvolutionLocalData mbox;
+	EvolutionImapData imap;
+};
+
+enum SummaryDataType {
+	SUMMARY_TYPE_INT32,
+	SUMMARY_TYPE_UINT32,
+	SUMMARY_TYPE_STRING,
+	SUMMARY_TYPE_TOKEN,
+	SUMMARY_TYPE_TIME_T
+};
+
+struct EvolutionAccountContext {
+	gchar *account;
+	gchar *uid;
+};
+
+enum EvolutionFlags {
+	EVOLUTION_MESSAGE_ANSWERED     = 1 << 0,
+	EVOLUTION_MESSAGE_DELETED      = 1 << 1,
+	EVOLUTION_MESSAGE_DRAFT        = 1 << 2,
+	EVOLUTION_MESSAGE_FLAGGED      = 1 << 3,
+	EVOLUTION_MESSAGE_SEEN	       = 1 << 4,
+	EVOLUTION_MESSAGE_ATTACHMENTS  = 1 << 5,
+	EVOLUTION_MESSAGE_ANSWERED_ALL = 1 << 6,
+	EVOLUTION_MESSAGE_JUNK	       = 1 << 7,
+	EVOLUTION_MESSAGE_SECURE       = 1 << 8
+};
+
+
+static gchar *local_dir = NULL;
+static gchar *imap_dir = NULL;
+static GHashTable *accounts = NULL;
+
+
+void   get_imap_accounts (void);
+
+
+static gboolean
+read_summary (FILE *summary,
+	      ...)
+{
+	va_list args;
+	gint value_type;
+
+	if (!summary) {
+		return FALSE;
+	}
+
+	va_start (args, summary);
+
+	while ((value_type = va_arg (args, gint)) != -1) {
+		switch (value_type) {
+		case SUMMARY_TYPE_TIME_T: {
+			time_t value, *dest;
+
+			if (fread (&value, sizeof (time_t), 1, summary) != 1) {
+				return FALSE;
+			}
+
+			dest = va_arg (args, time_t*);
+
+			if (dest) {
+				*dest = g_ntohl (value);
+			}
+			break;
+		}
+		case SUMMARY_TYPE_INT32: {
+			gint32 value, *dest;
+
+			if (fread (&value, sizeof (gint32), 1, summary) != 1) {
+				return FALSE;
+			}
+
+			dest = va_arg (args, gint32*);
+
+			if (dest) {
+				*dest = g_ntohl (value);
+			}
+			break;
+		}
+		case SUMMARY_TYPE_UINT32: {
+			guint32 *dest, value = 0;
+			gint c;
+
+			while (((c = fgetc (summary)) & 0x80) == 0 && c != EOF) {
+				value |= c;
+				value <<= 7;
+			}
+
+			if (c == EOF) {
+				return FALSE;
+			} else {
+				value |= (c & 0x7f);
+			}
+
+			dest = va_arg (args, guint32*);
+
+			if (dest) {
+				*dest = value;
+			}
+			break;
+		}
+		case SUMMARY_TYPE_STRING:
+		case SUMMARY_TYPE_TOKEN: {
+			guint32 len;
+			gchar *str, **dest;
+
+			/* read string length */
+			read_summary (summary, SUMMARY_TYPE_UINT32, &len, -1);
+			dest = va_arg (args, gchar **);
+
+			if (dest) {
+				*dest = NULL;
+			}
+
+			if (value_type == SUMMARY_TYPE_TOKEN) {
+				if (len < 32) {
+					continue;
+				} else {
+					len -= 31;
+				}
+			}
+
+			if (len <= 1) {
+				continue;
+			}
+
+			str = g_try_malloc0 (len);
+			if (!str) {
+				return FALSE;
+			}
+
+			if (fread (str, len - 1, 1, summary) != 1) {
+				g_free (str);
+				return FALSE;
+			}
+
+			if (dest) {
+				*dest = str;
+			} else {
+				g_free (str);
+			}
+
+			break;
+		}
+		default:
+			break;
+		}
+	}
+
+	va_end (args);
+
+	return TRUE;
+}
+
+void
+tracker_module_init (void)
+{
+	g_mime_init (0);
+	get_imap_accounts ();
+
+	local_dir = g_build_filename (g_get_home_dir (), ".evolution", "mail", "local", G_DIR_SEPARATOR_S, NULL);
+	imap_dir = g_build_filename (g_get_home_dir (), ".evolution", "mail", "imap", G_DIR_SEPARATOR_S, NULL);
+}
+
+void
+tracker_module_shutdown (void)
+{
+	g_mime_shutdown ();
+
+	g_hash_table_destroy (accounts);
+	g_free (local_dir);
+	g_free (imap_dir);
+}
+
+G_CONST_RETURN gchar *
+tracker_module_get_name (void)
+{
+	/* Return module name here */
+	return "EvolutionEmails";
+}
+
+static void
+account_start_element_handler (GMarkupParseContext *context,
+			       const gchar	   *element_name,
+			       const gchar	   **attr_names,
+			       const gchar	   **attr_values,
+			       gpointer		   user_data,
+			       GError		   **error)
+{
+	EvolutionAccountContext *account_context;
+	gint i = 0;
+
+	if (strcmp (element_name, "account") != 0) {
+		return;
+	}
+
+	account_context = (EvolutionAccountContext *) user_data;
+
+	while (attr_names[i]) {
+		if (strcmp (attr_names[i], "uid") == 0) {
+			account_context->uid = g_strdup (attr_values[i]);
+			return;
+		}
+
+		i++;
+	}
+}
+
+static gchar *
+get_account_name_from_imap_uri (const gchar *imap_uri)
+{
+	const gchar *start, *at, *semic;
+	gchar *user_name, *at_host_name, *account_name;
+
+	/* Assume url schema is:
+	 * imap://foo imap free fr/;etc
+	 * or
+	 * imap://foo;auth=DIGEST-MD5 imap bar com/;etc
+	 *
+	 * We try to get "foo imap free fr".
+	 */
+
+	if (!g_str_has_prefix (imap_uri, "imap://")) {
+		return NULL;
+	}
+
+	user_name = at_host_name = account_name = NULL;
+
+	/* check for embedded @ and then look for first colon after that */
+	start = imap_uri + 7;
+	at = strchr (start, '@');
+	semic = strchr (start, ';');
+
+	if ( strlen (imap_uri) < 7 || at == NULL ) {
+		return g_strdup ("Unknown");
+	}
+
+	if (semic < at) {
+		/* we have a ";auth=FOO host" schema
+		   Set semic to the next semicolon, which ends the hostname. */
+		user_name = g_strndup (start, semic - start);
+		/* look for ';' at the end of the domain name */
+		semic = strchr (at, ';');
+	} else {
+		user_name = g_strndup (start, at - start);
+	}
+
+	at_host_name = g_strndup (at, (semic - 1) - at);
+
+	account_name = g_strconcat (user_name, at_host_name, NULL);
+
+	g_free (user_name);
+	g_free (at_host_name);
+
+	return account_name;
+}
+
+static void
+account_text_handler (GMarkupParseContext  *context,
+		      const gchar	   *text,
+		      gsize		    text_len,
+		      gpointer		    user_data,
+		      GError		  **error)
+{
+	EvolutionAccountContext *account_context;
+	const GSList *uri_element, *source_element;
+	gchar *url;
+
+	uri_element = g_markup_parse_context_get_element_stack (context);
+	source_element = uri_element->next;
+
+	if (strcmp ((gchar *) uri_element->data, "url") != 0 ||
+	    !source_element ||
+	    strcmp ((gchar *) source_element->data, "source") != 0) {
+		return;
+	}
+
+	account_context = (EvolutionAccountContext *) user_data;
+
+	url = g_strndup (text, text_len);
+	account_context->account = get_account_name_from_imap_uri (url);
+	g_free (url);
+}
+
+void
+get_imap_accounts (void)
+{
+	GConfClient *client;
+	GMarkupParser parser = { 0 };
+	GMarkupParseContext *parse_context;
+	GSList *list, *l;
+	EvolutionAccountContext account_context = { 0 };
+
+	client = gconf_client_get_default ();
+
+	list = gconf_client_get_list (client,
+				      "/apps/evolution/mail/accounts",
+				      GCONF_VALUE_STRING,
+				      NULL);
+
+	parser.start_element = account_start_element_handler;
+	parser.text = account_text_handler;
+	parse_context = g_markup_parse_context_new (&parser, 0, &account_context, NULL);
+
+	accounts = g_hash_table_new_full (g_str_hash, g_str_equal,
+					  (GDestroyNotify) g_free,
+					  (GDestroyNotify) g_free);
+
+	for (l = list; l; l = l->next) {
+		g_markup_parse_context_parse (parse_context, (const gchar *) l->data, -1, NULL);
+
+		if (account_context.account &&
+		    account_context.uid) {
+			g_hash_table_insert (accounts,
+					     account_context.account,
+					     account_context.uid);
+		} else {
+			g_free (account_context.account);
+			g_free (account_context.uid);
+		}
+	}
+
+	g_markup_parse_context_free (parse_context);
+
+	g_slist_foreach (list, (GFunc) g_free, NULL);
+	g_slist_free (list);
+}
+
+static MailStorageType
+get_mail_storage_type_from_path (const gchar *path)
+{
+	MailStorageType type = MAIL_STORAGE_NONE;
+	gchar *basename;
+
+	basename = g_path_get_basename (path);
+
+	if (g_str_has_prefix (path, local_dir) &&
+	    strchr (basename, '.') == NULL) {
+		type = MAIL_STORAGE_LOCAL;
+	} else if (g_str_has_prefix (path, imap_dir) &&
+		   strcmp (basename, "summary") == 0) {
+		type = MAIL_STORAGE_IMAP;
+	}
+
+	/* Exclude non wanted folders */
+	if (strcasestr (path, "junk") ||
+	    strcasestr (path, "spam") ||
+	    strcasestr (path, "trash") ||
+	    strcasestr (path, "drafts") ||
+	    strcasestr (path, "sent") ||
+	    strcasestr (path, "outbox")) {
+		type = MAIL_STORAGE_NONE;
+	}
+
+	g_free (basename);
+
+	return type;
+}
+
+static GMimeStream *
+email_get_stream (const gchar *path,
+		  gint	       flags,
+		  off_t        start)
+{
+	GMimeStream *stream;
+	gint fd;
+
+	fd = g_open (path, flags, S_IRUSR | S_IWUSR);
+
+	if (fd == -1) {
+		return NULL;
+	}
+
+	stream = g_mime_stream_fs_new_with_bounds (fd, start, -1);
+
+	if (!stream) {
+		close (fd);
+	}
+
+	return stream;
+}
+
+static gint
+read_summary_header (FILE *summary)
+{
+	gint32 version, n_messages;
+
+	read_summary (summary,
+		      SUMMARY_TYPE_INT32, &version,
+		      SUMMARY_TYPE_INT32, NULL,		/* flags */
+		      SUMMARY_TYPE_INT32, NULL,		/* nextuid */
+		      SUMMARY_TYPE_TIME_T, NULL,	/* time */
+		      SUMMARY_TYPE_INT32, &n_messages,
+		      -1);
+
+	if ((version < 0x100 && version >= 13)) {
+		read_summary (summary,
+			      SUMMARY_TYPE_INT32, NULL, /* unread count*/
+			      SUMMARY_TYPE_INT32, NULL, /* deleted count*/
+			      SUMMARY_TYPE_INT32, NULL, /* junk count */
+			      -1);
+	}
+
+	if (version != 0x30c) {
+		read_summary (summary,
+			      SUMMARY_TYPE_INT32, NULL,
+			      SUMMARY_TYPE_INT32, NULL,
+			      -1);
+	}
+
+	return n_messages;
+}
+
+gpointer
+tracker_module_file_get_data (const gchar *path)
+{
+	EvolutionFileData *data = NULL;
+	MailStorageType type;
+
+	type = get_mail_storage_type_from_path (path);
+
+	if (type == MAIL_STORAGE_NONE) {
+		return NULL;
+	}
+
+	data = g_slice_new0 (EvolutionFileData);
+	data->type = type;
+
+	if (type == MAIL_STORAGE_IMAP) {
+		EvolutionImapData *imap_data;
+
+		imap_data = (EvolutionImapData *) data;
+
+		imap_data->fd = tracker_file_open (path, TRUE);
+
+		if (imap_data->fd == -1) {
+			return NULL;
+		}
+
+		imap_data->summary = fdopen (imap_data->fd, "r");
+		imap_data->n_messages = read_summary_header (imap_data->summary);
+		imap_data->cur_message = 1;
+
+                if (imap_data->n_messages > 0) {
+                        /* save current message uid */
+                        read_summary (imap_data->summary,
+                                      SUMMARY_TYPE_STRING, &imap_data->cur_message_uid,	/* message uid */
+                                      -1);
+                }
+        } else {
+		EvolutionLocalData *local_data;
+
+		local_data = (EvolutionLocalData *) data;
+
+#if defined(__linux__)
+		local_data->stream = email_get_stream (path, O_RDONLY | O_NOATIME, 0);
+#else
+		local_data->stream = email_get_stream (path, O_RDONLY, 0);
+#endif
+
+		if (local_data->stream) {
+			local_data->parser = g_mime_parser_new_with_stream (local_data->stream);
+			g_mime_parser_set_scan_from (local_data->parser, TRUE);
+
+			/* Initialize to the first message */
+			local_data->message = g_mime_parser_construct_message (local_data->parser);
+		}
+	}
+
+	return data;
+}
+
+static void
+free_imap_data (EvolutionImapData *data)
+{
+	fclose (data->summary);
+	close (data->fd);
+}
+
+static void
+free_local_data (EvolutionLocalData *data)
+{
+	if (data->mime_parts) {
+		g_list_foreach (data->mime_parts, (GFunc) g_object_unref, NULL);
+		g_list_free (data->mime_parts);
+	}
+
+	if (data->message) {
+		g_object_unref (data->message);
+	}
+
+	if (data->parser) {
+		g_object_unref (data->parser);
+	}
+
+	if (data->stream) {
+		g_mime_stream_close (data->stream);
+		g_object_unref (data->stream);
+	}
+}
+
+void
+tracker_module_file_free_data (gpointer file_data)
+{
+	EvolutionFileData *data;
+
+	data = (EvolutionFileData *) file_data;
+
+	if (data->type == MAIL_STORAGE_LOCAL) {
+		free_local_data ((EvolutionLocalData *) data);
+	} else if (data->type == MAIL_STORAGE_IMAP) {
+		free_imap_data ((EvolutionImapData *) data);
+	}
+
+	g_slice_free (EvolutionFileData, data);
+}
+
+static gint
+get_mbox_message_id (GMimeMessage *message)
+{
+	const gchar *header, *pos;
+	gchar *number;
+	gint id;
+
+	header = g_mime_message_get_header (message, "X-Evolution");
+	pos = strchr (header, '-');
+
+	number = g_strndup (header, pos - header);
+	id = strtoul (number, NULL, 16);
+
+	g_free (number);
+
+	return id;
+}
+
+static guint
+get_mbox_message_flags (GMimeMessage *message)
+{
+	const gchar *header, *pos;
+
+	header = g_mime_message_get_header (message, "X-Evolution");
+	pos = strchr (header, '-');
+
+	return (guint) strtoul (pos + 1, NULL, 16);
+}
+
+static void
+get_mbox_uri (TrackerFile   *file,
+	      GMimeMessage  *message,
+	      gchar	   **dirname,
+	      gchar	   **basename)
+{
+	gchar *dir, *name;
+
+	dir = tracker_string_replace (file->path, local_dir, NULL);
+	dir = tracker_string_remove (dir, ".sbd");
+
+	name = g_strdup_printf ("%s;uid=%d", dir, get_mbox_message_id (message));
+
+	*dirname = g_strdup ("email://local local");
+	*basename = name;
+
+	g_free (dir);
+}
+
+static void
+get_mbox_attachment_uri (TrackerFile   *file,
+			 GMimeMessage  *message,
+			 GMimePart     *part,
+			 gchar	      **dirname,
+			 gchar	      **basename)
+{
+	gchar *dir;
+
+	dir = tracker_string_replace (file->path, local_dir, NULL);
+	dir = tracker_string_remove (dir, ".sbd");
+
+	*dirname = g_strdup_printf ("email://local local/%s;uid=%d",
+				    dir, get_mbox_message_id (message));
+	*basename = g_strdup (g_mime_part_get_filename (part));
+
+	g_free (dir);
+}
+
+static GList *
+get_mbox_recipient_list (GMimeMessage *message,
+			 const gchar  *type)
+{
+	GList *list = NULL;
+	const InternetAddressList *addresses;
+
+	addresses = g_mime_message_get_recipients (message, type);
+
+	while (addresses) {
+		InternetAddress *address;
+		gchar *str;
+
+		address = addresses->address;
+
+		if (address->name && address->value.addr) {
+			str = g_strdup_printf ("%s %s", address->name, address->value.addr);
+		} else if (address->value.addr) {
+			str = g_strdup (address->value.addr);
+		} else if (address->name) {
+			str = g_strdup (address->name);
+		} else {
+			str = NULL;
+		}
+
+		if (str) {
+			list = g_list_prepend (list, str);
+		}
+
+		addresses = addresses->next;
+	}
+
+	return g_list_reverse (list);
+}
+
+static TrackerMetadata *
+get_metadata_for_data_wrapper (GMimeDataWrapper *wrapper)
+{
+	TrackerMetadata *metadata;
+	GMimeStream *stream;
+	gchar *path;
+	gint fd;
+
+	path = g_build_filename (g_get_tmp_dir (), "tracker-evolution-module-XXXXXX", NULL);
+	fd = g_mkstemp (path);
+	metadata = NULL;
+
+	stream = g_mime_stream_fs_new (fd);
+
+	if (g_mime_data_wrapper_write_to_stream (wrapper, stream) != -1) {
+		g_mime_stream_flush (stream);
+
+		metadata = tracker_metadata_utils_get_data (path);
+		g_unlink (path);
+	}
+
+	g_mime_stream_close (stream);
+	g_object_unref (stream);
+	g_free (path);
+
+	return metadata;
+}
+
+static TrackerMetadata *
+get_metadata_for_mbox_attachment (TrackerFile  *file,
+				  GMimeMessage *message,
+				  GMimePart    *part)
+{
+	TrackerMetadata *metadata;
+	GMimeDataWrapper *content;
+
+	content = g_mime_part_get_content_object (part);
+
+	if (!content) {
+		return NULL;
+	}
+
+	metadata = get_metadata_for_data_wrapper (content);
+
+	if (metadata) {
+		gchar *dirname, *basename;
+
+		get_mbox_attachment_uri (file, message, part,
+					 &dirname, &basename);
+
+		tracker_metadata_insert (metadata, METADATA_FILE_PATH, dirname);
+		tracker_metadata_insert (metadata, METADATA_FILE_NAME, basename);
+	}
+
+	g_object_unref (content);
+
+	return metadata;
+}
+
+static TrackerMetadata *
+get_metadata_for_mbox (TrackerFile *file)
+{
+	EvolutionLocalData *data;
+	GMimeMessage *message;
+	TrackerMetadata *metadata;
+	gchar *dirname, *basename;
+	time_t date;
+	GList *list;
+	guint flags;
+
+	data = file->data;
+	message = data->message;
+
+	if (!message) {
+		return NULL;
+	}
+
+	flags = get_mbox_message_flags (message);
+
+	if (flags & EVOLUTION_MESSAGE_JUNK ||
+	    flags & EVOLUTION_MESSAGE_DELETED) {
+		return NULL;
+	}
+
+	if (data->current_mime_part) {
+		/* We're processing an attachment */
+		return get_metadata_for_mbox_attachment (file, message, data->current_mime_part->data);
+	}
+
+	metadata = tracker_metadata_new ();
+
+	get_mbox_uri (file, message, &dirname, &basename);
+	tracker_metadata_insert (metadata, METADATA_FILE_PATH, dirname);
+	tracker_metadata_insert (metadata, METADATA_FILE_NAME, basename);
+
+	g_mime_message_get_date (message, &date, NULL);
+	tracker_metadata_insert (metadata, METADATA_EMAIL_DATE,
+				 tracker_guint_to_string (date));
+
+	tracker_metadata_insert (metadata, METADATA_EMAIL_SENDER,
+				 g_strdup (g_mime_message_get_sender (message)));
+	tracker_metadata_insert (metadata, METADATA_EMAIL_SUBJECT,
+				 g_strdup (g_mime_message_get_subject (message)));
+
+	list = get_mbox_recipient_list (message, GMIME_RECIPIENT_TYPE_TO);
+	tracker_metadata_insert_multiple_values (metadata, METADATA_EMAIL_SENT_TO, list);
+
+	list = get_mbox_recipient_list (message, GMIME_RECIPIENT_TYPE_CC);
+	tracker_metadata_insert_multiple_values (metadata, METADATA_EMAIL_CC, list);
+
+	return metadata;
+}
+
+static void
+skip_content_info (FILE *summary)
+{
+	guint32 count, i;
+
+	if (fgetc (summary)) {
+		read_summary (summary,
+			      SUMMARY_TYPE_TOKEN, NULL,
+			      SUMMARY_TYPE_TOKEN, NULL,
+			      SUMMARY_TYPE_UINT32, &count,
+			      -1);
+
+		if (count <= 500) {
+			for (i = 0; i < count; i++) {
+				read_summary (summary,
+					      SUMMARY_TYPE_TOKEN, NULL,
+					      SUMMARY_TYPE_TOKEN, NULL,
+					      -1);
+			}
+		}
+
+		read_summary (summary,
+			      SUMMARY_TYPE_TOKEN, NULL,
+			      SUMMARY_TYPE_TOKEN, NULL,
+			      SUMMARY_TYPE_TOKEN, NULL,
+			      SUMMARY_TYPE_UINT32, NULL,
+			      -1);
+	}
+
+	read_summary (summary,
+		      SUMMARY_TYPE_UINT32, &count,
+		      -1);
+
+	for (i = 0; i < count; i++) {
+		skip_content_info (summary);
+	}
+}
+
+static gboolean
+get_imap_attachment_info (const gchar		 *mime_file,
+			  gchar			**name,
+			  GMimePartEncodingType  *encoding)
+{
+	GMimeContentType *mime;
+	gchar *tmp, *mime_content;
+	const gchar *pos_content_type, *pos_encoding, *pos_end_encoding;
+
+	if (name) {
+		*name = NULL;
+	}
+
+	if (encoding) {
+		*encoding = GMIME_PART_ENCODING_DEFAULT;
+	}
+
+	if (!g_file_get_contents (mime_file, &tmp, NULL, NULL)) {
+		return FALSE;
+	}
+
+	/* all text content in lower case for comparisons */
+	mime_content = g_ascii_strdown (tmp, -1);
+	g_free (tmp);
+
+	pos_content_type = strstr (mime_content, "content-type:");
+
+	if (pos_content_type) {
+		pos_encoding = strstr (pos_content_type, "content-transfer-encoding:");
+	}
+
+	if (!pos_content_type || !pos_encoding) {
+		g_free (mime_content);
+		return FALSE;
+	}
+
+	pos_content_type += strlen ("content-type:");
+	pos_encoding += strlen ("content-transfer-encoding:");
+
+	/* ignore spaces, tab or line returns */
+	while (*pos_content_type != '\0' && (*pos_content_type == ' ' || *pos_content_type == '\t' || *pos_content_type == '\n')) {
+		pos_content_type++;
+	}
+
+	while (*pos_encoding != '\0' && *pos_encoding == ' ') {
+		pos_encoding++;
+	}
+
+	if (*pos_content_type == '\0' ||
+	    *pos_encoding == '\0') {
+		g_free (mime_content);
+		return FALSE;
+	}
+
+	mime = g_mime_content_type_new_from_string (pos_content_type);
+
+	if (mime) {
+		if (name) {
+			*name = g_strdup (g_mime_content_type_get_parameter (mime, "name"));
+		}
+
+		g_mime_content_type_destroy (mime);
+	}
+
+	if (name && !*name) {
+		g_free (mime_content);
+		return FALSE;
+	}
+
+	/* Find end of encoding */
+	pos_end_encoding = pos_encoding;
+
+	while (*pos_end_encoding != '\0' &&
+	       *pos_end_encoding != ' ' &&
+	       *pos_end_encoding != '\n' &&
+	       *pos_end_encoding != '\t') {
+		pos_end_encoding++;
+	}
+
+	if (encoding && pos_encoding != pos_end_encoding) {
+		gchar *encoding_str = g_strndup (pos_encoding, pos_end_encoding - pos_encoding);
+
+		if (strcmp (encoding_str, "7bit") == 0) {
+			*encoding = GMIME_PART_ENCODING_7BIT;
+		} else if (strcmp (encoding_str, "8bit") == 0) {
+			*encoding = GMIME_PART_ENCODING_7BIT;
+		} else if (strcmp (encoding_str, "binary") == 0) {
+			*encoding = GMIME_PART_ENCODING_BINARY;
+		} else if (strcmp (encoding_str, "base64") == 0) {
+			*encoding = GMIME_PART_ENCODING_BASE64;
+		} else if (strcmp (encoding_str, "quoted-printable") == 0) {
+			*encoding = GMIME_PART_ENCODING_QUOTEDPRINTABLE;
+		} else if (strcmp (encoding_str, "x-uuencode") == 0) {
+			*encoding = GMIME_PART_ENCODING_UUENCODE;
+		}
+
+		g_free (encoding_str);
+	}
+
+	g_free (mime_content);
+
+	return TRUE;
+}
+
+static void
+get_imap_uri (TrackerFile  *file,
+	      const gchar  *uid,
+	      gchar	  **uri_base,
+	      gchar	  **basename)
+{
+	GList *keys, *k;
+	gchar *path, *dir, *subdirs;
+
+	path = file->path;
+	keys = g_hash_table_get_keys (accounts);
+	*uri_base = *basename = NULL;
+
+	for (k = keys; k; k = k->next) {
+		if (strstr (path, k->data)) {
+			*uri_base = g_strdup_printf ("email://%s", (gchar *) g_hash_table_lookup (accounts, k->data));
+
+			dir = g_build_filename (imap_dir, k->data, NULL);
+
+			/* now remove all relevant info to create the email:// basename */
+			subdirs = g_strdup (path);
+			subdirs = tracker_string_remove (subdirs, dir);
+			subdirs = tracker_string_remove (subdirs, "/folders");
+			subdirs = tracker_string_remove (subdirs, "/subfolders");
+			subdirs = tracker_string_remove (subdirs, "/summary");
+
+			*basename = g_strdup_printf ("%s;uid=%s", subdirs, uid);
+
+			g_free (subdirs);
+			g_free (dir);
+
+			break;
+		}
+	}
+
+	g_list_free (keys);
+
+	return;
+}
+
+static void
+get_imap_attachment_uri (TrackerFile  *file,
+			 gchar	     **dirname,
+			 gchar	     **basename)
+{
+	EvolutionImapData *data;
+	gchar *message_dirname, *message_basename, *name;
+
+	data = file->data;
+
+	if (!get_imap_attachment_info (data->current_mime_part->data, &name, NULL)) {
+		return;
+	}
+
+	get_imap_uri (file, data->cur_message_uid, &message_dirname, &message_basename);
+	*dirname = g_strdup_printf ("%s/%s", message_dirname, message_basename);
+	*basename = name;
+
+	g_free (message_dirname);
+	g_free (message_basename);
+}
+
+static GList *
+get_imap_recipient_list (const gchar *str)
+{
+	GList *list = NULL;
+	gchar **arr;
+	gint i;
+
+	if (!str) {
+		return NULL;
+	}
+
+	arr = g_strsplit (str, ",", -1);
+
+	for (i = 0; arr[i]; i++) {
+		g_strstrip (arr[i]);
+		list = g_list_prepend (list, g_strdup (arr[i]));
+	}
+
+	g_strfreev (arr);
+
+	return g_list_reverse (list);
+}
+
+static gchar *
+get_imap_message_path (TrackerFile *file,
+		       const gchar *uid)
+{
+	gchar *prefix, *message_path;
+
+	prefix = g_strndup (file->path, strlen (file->path) - strlen ("summary"));
+	message_path = g_strconcat (prefix, uid, ".", NULL);
+	g_free (prefix);
+
+	return message_path;
+}
+
+static TrackerMetadata *
+get_metadata_for_imap_attachment (TrackerFile *file,
+				  const gchar *mime_file)
+{
+	TrackerMetadata *metadata;
+	GMimeStream *stream;
+	GMimeDataWrapper *wrapper;
+	GMimePartEncodingType encoding;
+	gchar *path, *name;
+
+	if (!get_imap_attachment_info (mime_file, &name, &encoding)) {
+		return NULL;
+	}
+
+	path = g_strdup (mime_file);
+	path = tracker_string_remove (path, ".MIME");
+
+#if defined(__linux__)
+	stream = email_get_stream (path, O_RDONLY | O_NOATIME, 0);
+#else
+	stream = email_get_stream (path, O_RDONLY, 0);
+#endif
+
+	if (!stream) {
+		g_free (name);
+		g_free (path);
+		return NULL;
+	}
+
+	wrapper = g_mime_data_wrapper_new_with_stream (stream, encoding);
+	metadata = get_metadata_for_data_wrapper (wrapper);
+
+	if (metadata) {
+		EvolutionImapData *data;
+		gchar *dirname, *basename;
+
+		data = file->data;
+
+		get_imap_uri (file,
+			      data->cur_message_uid,
+			      &dirname, &basename);
+
+		tracker_metadata_insert (metadata, METADATA_FILE_NAME, g_strdup (name));
+		tracker_metadata_insert (metadata, METADATA_FILE_PATH,
+					 g_strdup_printf ("%s/%s", dirname, basename));
+
+		g_free (dirname);
+		g_free (basename);
+	}
+
+	g_object_unref (wrapper);
+	g_object_unref (stream);
+	g_free (name);
+	g_free (path);
+
+	return metadata;
+}
+
+static TrackerMetadata *
+get_metadata_for_imap (TrackerFile *file)
+{
+	EvolutionImapData *data;
+	TrackerMetadata *metadata = NULL;
+	gchar *dirname, *basename;
+	gchar *subject, *from, *to, *cc;
+	gint32 i, count, flags;
+	time_t date;
+	GList *list;
+	gboolean deleted;
+
+	data = file->data;
+
+	if (data->cur_message > data->n_messages) {
+		return NULL;
+	}
+
+	if (data->current_mime_part) {
+		return get_metadata_for_imap_attachment (file, data->current_mime_part->data);
+	}
+
+	if (!read_summary (data->summary,
+			   SUMMARY_TYPE_UINT32, &flags, /* flags */
+			   -1)) {
+		return NULL;
+	}
+
+	deleted = ((flags & EVOLUTION_MESSAGE_JUNK) != 0 ||
+		   (flags & EVOLUTION_MESSAGE_DELETED) != 0);
+
+	subject = NULL;
+	from = NULL;
+	to = NULL;
+	cc = NULL;
+
+	if (!read_summary (data->summary,
+			   SUMMARY_TYPE_UINT32, NULL,	  /* size */
+			   SUMMARY_TYPE_TIME_T, NULL,	  /* date sent */
+			   SUMMARY_TYPE_TIME_T, &date,	  /* date received */
+			   SUMMARY_TYPE_STRING, &subject, /* subject */
+			   SUMMARY_TYPE_STRING, &from,	  /* from */
+			   SUMMARY_TYPE_STRING, &to,	  /* to */
+			   SUMMARY_TYPE_STRING, &cc,	  /* cc */
+			   SUMMARY_TYPE_STRING, NULL,	  /* mlist */
+			   -1)) {
+		g_free (subject);
+		g_free (from);
+		g_free (to);
+		g_free (cc);
+		return NULL;
+	}
+
+	if (!deleted) {
+		metadata = tracker_metadata_new ();
+		get_imap_uri (file, data->cur_message_uid, &dirname, &basename);
+
+		tracker_metadata_insert (metadata, METADATA_FILE_PATH, dirname);
+		tracker_metadata_insert (metadata, METADATA_FILE_NAME, basename);
+
+		tracker_metadata_insert (metadata, METADATA_EMAIL_DATE,
+					 tracker_guint_to_string (date));
+
+		tracker_metadata_insert (metadata, METADATA_EMAIL_SENDER, from);
+		tracker_metadata_insert (metadata, METADATA_EMAIL_SUBJECT, subject);
+
+		list = get_imap_recipient_list (to);
+		tracker_metadata_insert_multiple_values (metadata, METADATA_EMAIL_SENT_TO, list);
+
+		list = get_imap_recipient_list (cc);
+		tracker_metadata_insert_multiple_values (metadata, METADATA_EMAIL_CC, list);
+	}
+
+	g_free (to);
+	g_free (cc);
+
+	if (!read_summary (data->summary,
+			   SUMMARY_TYPE_INT32, NULL,
+			   SUMMARY_TYPE_INT32, NULL,
+			   SUMMARY_TYPE_UINT32, &count,
+			   -1)) {
+		goto corruption;
+	}
+
+	/* references */
+	for (i = 0; i < count; i++) {
+		if (read_summary (data->summary,
+				  SUMMARY_TYPE_INT32, NULL,
+				  SUMMARY_TYPE_INT32, NULL,
+				  -1)) {
+			continue;
+		}
+
+		goto corruption;
+	}
+
+	if (!read_summary (data->summary, SUMMARY_TYPE_UINT32, &count, -1)) {
+		goto corruption;
+	}
+
+	/* user flags */
+	for (i = 0; i < count; i++) {
+		if (read_summary (data->summary, SUMMARY_TYPE_STRING, NULL, -1)) {
+			continue;
+		}
+
+		goto corruption;
+	}
+
+	if (!read_summary (data->summary, SUMMARY_TYPE_UINT32, &count, -1)) {
+		goto corruption;
+	}
+
+	/* user tags */
+	for (i = 0; i < count; i++) {
+		if (read_summary (data->summary,
+				  SUMMARY_TYPE_STRING, NULL,
+				  SUMMARY_TYPE_STRING, NULL,
+				  -1)) {
+			continue;
+		}
+
+		goto corruption;
+	}
+
+	/* server flags */
+	if (!read_summary (data->summary,
+			   SUMMARY_TYPE_UINT32, NULL,
+			   -1)) {
+		goto corruption;
+	}
+
+	skip_content_info (data->summary);
+
+	return metadata;
+
+corruption:
+	/* assume corruption */
+	if (metadata) {
+		tracker_metadata_free (metadata);
+	}
+
+	return NULL;
+}
+
+void
+tracker_module_file_get_uri (TrackerFile  *file,
+			     gchar	 **dirname,
+			     gchar	 **basename)
+{
+	EvolutionFileData *file_data;
+
+	file_data = file->data;
+
+	if (!file_data) {
+		/* It isn't any of the files the
+		 * module is interested for */
+		return;
+	}
+
+	switch (file_data->type) {
+	case MAIL_STORAGE_LOCAL: {
+		EvolutionLocalData *data = file->data;
+
+		if (!data->message) {
+			return;
+		}
+
+		if (data->current_mime_part) {
+			/* We're currently on an attachment */
+			get_mbox_attachment_uri (file, data->message,
+						 data->current_mime_part->data,
+						 dirname, basename);
+		} else {
+			get_mbox_uri (file, data->message, dirname, basename);
+		}
+
+		break;
+	}
+	case MAIL_STORAGE_IMAP: {
+		EvolutionImapData *data = file->data;
+
+		if (data->current_mime_part) {
+			/* We're currently on an attachment */
+			get_imap_attachment_uri (file, dirname, basename);
+		} else {
+			get_imap_uri (file, data->cur_message_uid, dirname, basename);
+		}
+
+		break;
+	}
+	default:
+		break;
+	}
+}
+
+gchar *
+tracker_module_file_get_service_type (TrackerFile *file)
+{
+	EvolutionFileData *data;
+
+	data = file->data;
+
+	if (!data) {
+		/* It isn't any of the files the module handles */
+		return NULL;
+	}
+
+	if (data->type == MAIL_STORAGE_LOCAL) {
+		EvolutionLocalData *local_data = file->data;
+
+		if (local_data->current_mime_part) {
+			return g_strdup ("EvolutionAttachments");
+		}
+	} else if (data->type == MAIL_STORAGE_IMAP) {
+		EvolutionImapData *imap_data = file->data;
+
+		if (imap_data->current_mime_part) {
+			return g_strdup ("EvolutionAttachments");
+		}
+	}
+
+	return g_strdup ("EvolutionEmails");
+}
+
+TrackerMetadata *
+tracker_module_file_get_metadata (TrackerFile *file)
+{
+	EvolutionFileData *data;
+
+	data = file->data;
+
+	if (!data) {
+		/* It isn't any of the files the
+		 * module is interested for */
+		return NULL;
+	}
+
+	switch (data->type) {
+	case MAIL_STORAGE_LOCAL:
+		return get_metadata_for_mbox (file);
+	case MAIL_STORAGE_IMAP:
+		return get_metadata_for_imap (file);
+	default:
+		break;
+	}
+
+	return NULL;
+}
+
+static gchar *
+get_message_encoding (GMimeMessage *message)
+{
+        const gchar *content_type, *start_encoding, *end_encoding;
+
+        content_type = g_mime_message_get_header (message, "Content-Type");
+
+        if (!content_type) {
+                return NULL;
+        }
+
+        start_encoding = strstr (content_type, "charset=");
+
+        if (!start_encoding) {
+                return NULL;
+        }
+
+        start_encoding += strlen ("charset=");
+
+        if (start_encoding[0] == '"') {
+                /* encoding is quoted */
+                start_encoding++;
+                end_encoding = strstr (start_encoding, "\"");
+        } else {
+                end_encoding = strstr (start_encoding, ";");
+        }
+
+        if (end_encoding) {
+                return g_strndup (start_encoding, end_encoding - start_encoding);
+        } else {
+                return g_strdup (start_encoding);
+        }
+}
+
+static gchar *
+get_text_for_mbox (TrackerFile *file)
+{
+        EvolutionLocalData *data;
+        gboolean is_html;
+        gchar *text, *encoding, *utf8_text;
+
+	data = file->data;
+
+	if (data->current_mime_part) {
+		/* FIXME: Extract text from attachments */
+		return NULL;
+	}
+
+        text = g_mime_message_get_body (data->message, TRUE, &is_html);
+
+        if (!text) {
+                return NULL;
+        }
+
+        encoding = get_message_encoding (data->message);
+
+        if (!encoding) {
+                /* FIXME: could still puke on non-utf8
+                 * messages without proper content type
+                 */
+                return text;
+        }
+
+        utf8_text = g_convert (text, -1, "utf8", encoding, NULL, NULL, NULL);
+
+        g_free (encoding);
+        g_free (text);
+
+        return utf8_text;
+}
+
+static gchar *
+get_text_for_imap (TrackerFile *file)
+{
+	EvolutionImapData *data;
+	gchar *message_path;
+	gchar *body = NULL;
+
+	data = file->data;
+
+	if (data->current_mime_part) {
+		/* FIXME: Extract text from attachments */
+		return NULL;
+	}
+
+	message_path = get_imap_message_path (file, data->cur_message_uid);
+	g_file_get_contents (message_path, &body, NULL, NULL);
+	g_free (message_path);
+
+	return body;
+}
+
+gchar *
+tracker_module_file_get_text (TrackerFile *file)
+{
+	EvolutionFileData *data;
+	gchar *text = NULL;
+
+	data = file->data;
+
+	if (!data) {
+		/* It isn't any of the files the
+		 * module is interested in */
+		return NULL;
+	}
+
+	if (data->type == MAIL_STORAGE_LOCAL) {
+		text = get_text_for_mbox (file);
+	} else if (data->type == MAIL_STORAGE_IMAP) {
+		text = get_text_for_imap (file);
+	}
+
+	return text;
+}
+
+static GList *
+extract_imap_mime_parts (TrackerFile *file)
+{
+	EvolutionImapData *data;
+	gboolean has_attachment = TRUE;
+	gint n_attachment = 0;
+	gchar *message_path;
+	GList *mime_parts = NULL;
+
+	data = file->data;
+	message_path = get_imap_message_path (file, data->cur_message_uid);
+
+	while (has_attachment) {
+		gchar *mime_file;
+
+		n_attachment++;
+		mime_file = g_strdup_printf ("%s%d.MIME", message_path, n_attachment);
+
+		if (g_file_test (mime_file, G_FILE_TEST_EXISTS)) {
+			mime_parts = g_list_prepend (mime_parts, mime_file);
+		} else {
+			g_free (mime_file);
+			has_attachment = FALSE;
+		}
+	}
+
+	g_free (message_path);
+
+	return g_list_reverse (mime_parts);;
+}
+
+static void
+extract_mbox_mime_parts (GMimeObject *object,
+			 gpointer     user_data)
+{
+	GList **list = (GList **) user_data;
+	GMimePart *part;
+	const gchar *disposition, *filename;
+
+	if (GMIME_IS_MESSAGE_PART (object)) {
+		GMimeMessage *message;
+
+		message = g_mime_message_part_get_message (GMIME_MESSAGE_PART (object));
+
+		if (message) {
+			g_mime_message_foreach_part (message, extract_mbox_mime_parts, user_data);
+			g_object_unref (message);
+		}
+
+		return;
+	} else if (GMIME_IS_MULTIPART (object)) {
+		g_mime_multipart_foreach (GMIME_MULTIPART (object), extract_mbox_mime_parts, user_data);
+		return;
+	}
+
+	part = GMIME_PART (object);
+	disposition = g_mime_part_get_content_disposition (part);
+
+	if (!disposition ||
+	    (strcmp (disposition, GMIME_DISPOSITION_ATTACHMENT) != 0 &&
+	     strcmp (disposition, GMIME_DISPOSITION_INLINE) != 0)) {
+		return;
+	}
+
+	filename = g_mime_part_get_filename (GMIME_PART (object));
+
+	if (!filename ||
+	    strcmp (filename, "signature.asc") == 0 ||
+	    strcmp (filename, "signature.pgp") == 0) {
+		return;
+	}
+
+	*list = g_list_prepend (*list, g_object_ref (object));
+}
+
+gboolean
+tracker_module_file_iter_contents (TrackerFile *file)
+{
+	EvolutionFileData *data;
+
+	data = file->data;
+
+	if (data->type == MAIL_STORAGE_IMAP) {
+		EvolutionImapData *imap_data = file->data;
+
+		/* Iterate through mime parts, if any */
+		if (!imap_data->mime_parts) {
+			imap_data->mime_parts = extract_imap_mime_parts (file);
+			imap_data->current_mime_part = imap_data->mime_parts;
+		} else {
+			imap_data->current_mime_part = imap_data->current_mime_part->next;
+		}
+
+		if (imap_data->current_mime_part) {
+			return TRUE;
+		}
+
+		g_list_foreach (imap_data->mime_parts, (GFunc) g_free, NULL);
+		g_list_free (imap_data->mime_parts);
+		imap_data->mime_parts = NULL;
+
+		g_free (imap_data->cur_message_uid);
+		imap_data->cur_message_uid = NULL;
+
+                /* save current message uid */
+                read_summary (imap_data->summary,
+                              SUMMARY_TYPE_STRING, &imap_data->cur_message_uid,	/* message uid */
+                              -1);
+
+		imap_data->cur_message++;
+
+		return (imap_data->cur_message < imap_data->n_messages);
+	} else if (data->type == MAIL_STORAGE_LOCAL) {
+		EvolutionLocalData *local_data = file->data;
+
+		if (!local_data->parser) {
+			return FALSE;
+		}
+
+		if (local_data->message) {
+			/* Iterate through mime parts, if any */
+			if (!local_data->mime_parts) {
+				g_mime_message_foreach_part (local_data->message,
+							     extract_mbox_mime_parts,
+							     &local_data->mime_parts);
+				local_data->current_mime_part = local_data->mime_parts;
+			} else {
+				local_data->current_mime_part = local_data->current_mime_part->next;
+			}
+
+			if (local_data->current_mime_part) {
+				return TRUE;
+			}
+
+			/* all possible mime parts have been already iterated, move on */
+			g_object_unref (local_data->message);
+
+			g_list_foreach (local_data->mime_parts, (GFunc) g_object_unref, NULL);
+			g_list_free (local_data->mime_parts);
+			local_data->mime_parts = NULL;
+		}
+
+		local_data->message = g_mime_parser_construct_message (local_data->parser);
+
+		return (local_data->message != NULL);
+	}
+
+	return FALSE;
+}

Added: trunk/src/tracker-indexer/modules/files.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-indexer/modules/files.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,157 @@
+/* Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <gio/gio.h>
+
+#include <libtracker-common/tracker-config.h>
+#include <libtracker-common/tracker-file-utils.h>
+#include <libtracker-common/tracker-os-dependant.h>
+#include <libtracker-common/tracker-ontology.h>
+#include <tracker-indexer/tracker-metadata-utils.h>
+#include <tracker-indexer/tracker-module.h>
+
+#define METADATA_FILE_NAME_DELIMITED "File:NameDelimited"
+#define METADATA_FILE_EXT	     "File:Ext"
+#define METADATA_FILE_PATH	     "File:Path"
+#define METADATA_FILE_NAME	     "File:Name"
+#define METADATA_FILE_LINK	     "File:Link"
+#define METADATA_FILE_MIMETYPE	     "File:Mime"
+#define METADATA_FILE_SIZE	     "File:Size"
+#define METADATA_FILE_MODIFIED	     "File:Modified"
+#define METADATA_FILE_ACCESSED	     "File:Accessed"
+
+G_CONST_RETURN gchar *
+tracker_module_get_name (void)
+{
+	/* Return module name here */
+	return "Files";
+}
+
+gchar *
+tracker_module_file_get_service_type (TrackerFile *file)
+{
+	gchar *mime_type;
+	gchar *service_type;
+
+	mime_type = tracker_file_get_mime_type (file->path);
+	service_type = tracker_ontology_get_service_by_mime (mime_type);
+	g_free (mime_type);
+
+	return service_type;
+}
+
+static gboolean
+check_exclude_file (const gchar *path)
+{
+	gchar *name;
+	guint i;
+
+	const gchar const *ignore_suffix[] = {
+		"~", ".o", ".la", ".lo", ".loT", ".in",
+		".csproj", ".m4", ".rej", ".gmo", ".orig",
+		".pc", ".omf", ".aux", ".tmp", ".po",
+		".vmdk",".vmx",".vmxf",".vmsd",".nvram",
+		".part", ".bak"
+	};
+
+	const gchar const *ignore_prefix[] = {
+		"autom4te", "conftest.", "confstat",
+		"config."
+	};
+
+	const gchar const *ignore_name[] = {
+		"po", "CVS", "aclocal", "Makefile", "CVS",
+		"SCCS", "ltmain.sh","libtool", "config.status",
+		"conftest", "confdefs.h"
+	};
+
+	if (g_str_has_prefix (path, "/proc/") ||
+	    g_str_has_prefix (path, "/dev/") ||
+	    g_str_has_prefix (path, "/tmp/") ||
+	    g_str_has_prefix (path, g_get_tmp_dir ())) {
+		return TRUE;
+	}
+
+	name = g_path_get_basename (path);
+
+	if (name[0] == '.') {
+		g_free (name);
+		return TRUE;
+	}
+
+	for (i = 0; i < G_N_ELEMENTS (ignore_suffix); i++) {
+		if (g_str_has_suffix (name, ignore_suffix[i])) {
+			g_free (name);
+			return TRUE;
+		}
+	}
+
+	for (i = 0; i < G_N_ELEMENTS (ignore_prefix); i++) {
+		if (g_str_has_prefix (name, ignore_prefix[i])) {
+			g_free (name);
+			return TRUE;
+		}
+	}
+
+	for (i = 0; i < G_N_ELEMENTS (ignore_name); i++) {
+		if (strcmp (name, ignore_name[i]) == 0) {
+			g_free (name);
+			return TRUE;
+		}
+	}
+
+	/* FIXME: check NoIndexFileTypes in configuration */
+
+	g_free (name);
+	return FALSE;
+}
+
+TrackerMetadata *
+tracker_module_file_get_metadata (TrackerFile *file)
+{
+	const gchar *path;
+
+	path = file->path;
+
+	if (check_exclude_file (path)) {
+		return NULL;
+	}
+
+	return tracker_metadata_utils_get_data (path);
+}
+
+gchar *
+tracker_module_file_get_text (TrackerFile *file)
+{
+	const gchar *path;
+
+	path = file->path;
+
+	if (check_exclude_file (path)) {
+		return NULL;
+	}
+
+	return tracker_metadata_utils_get_text (path);
+}

Added: trunk/src/tracker-indexer/modules/gaim-conversations.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-indexer/modules/gaim-conversations.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,28 @@
+/* Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <glib.h>
+#include <tracker-indexer/tracker-module.h>
+
+G_CONST_RETURN gchar *
+tracker_module_get_name (void)
+{
+	/* Return module name here */
+	return "GaimConversations";
+}

Added: trunk/src/tracker-indexer/tracker-dbus.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-indexer/tracker-dbus.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,206 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <string.h>
+
+#include <libtracker-common/tracker-log.h>
+
+#include "tracker-dbus.h"
+#include "tracker-indexer.h"
+#include "tracker-indexer-glue.h"
+
+#define THUMBNAILER_SERVICE	 "org.freedesktop.thumbnailer"
+#define THUMBNAILER_PATH	 "/org/freedesktop/thumbnailer/Generic"
+#define THUMBNAILER_INTERFACE	 "org.freedesktop.thumbnailer.Generic"
+
+static DBusGConnection *connection;
+static DBusGProxy      *proxy;
+static DBusGProxy      *thumb_proxy;
+
+static gboolean
+dbus_register_service (DBusGProxy  *proxy,
+		       const gchar *name)
+{
+	GError *error = NULL;
+	guint	result;
+
+	g_message ("Registering DBus service...\n"
+		   "  Name:'%s'",
+		   name);
+
+	if (!org_freedesktop_DBus_request_name (proxy,
+						name,
+						DBUS_NAME_FLAG_DO_NOT_QUEUE,
+						&result, &error)) {
+		g_critical ("Could not aquire name:'%s', %s",
+			    name,
+			    error ? error->message : "no error given");
+		g_error_free (error);
+
+		return FALSE;
+	}
+
+	if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+		g_critical ("DBus service name:'%s' is already taken, "
+			    "perhaps the application is already running?",
+			    name);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+name_owner_changed_cb (DBusGProxy *proxy,
+		       gchar	  *name,
+		       gchar	  *old_owner,
+		       gchar	  *new_owner,
+		       gpointer    user_data)
+{
+	if (strcmp (name, TRACKER_DAEMON_SERVICE) == 0 && (!new_owner || !*new_owner)) {
+		/* Tracker daemon has dissapeared from
+		 * the bus, shutdown the indexer.
+		 */
+		tracker_indexer_stop (TRACKER_INDEXER (user_data));
+	}
+}
+
+static gboolean
+dbus_register_object (GObject		    *object,
+		      DBusGConnection	    *connection,
+		      DBusGProxy	    *proxy,
+		      const DBusGObjectInfo *info,
+		      const gchar	    *path)
+{
+	g_message ("Registering DBus object...");
+	g_message ("  Path:'%s'", path);
+	g_message ("  Object Type:'%s'", G_OBJECT_TYPE_NAME (object));
+
+	dbus_g_object_type_install_info (G_OBJECT_TYPE (object), info);
+	dbus_g_connection_register_g_object (connection, path, object);
+
+	dbus_g_proxy_add_signal (proxy, "NameOwnerChanged",
+				 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+				 G_TYPE_INVALID);
+
+	dbus_g_proxy_connect_signal (proxy, "NameOwnerChanged",
+				     G_CALLBACK (name_owner_changed_cb),
+				     object, NULL);
+	return TRUE;
+}
+
+DBusGProxy*
+tracker_dbus_get_thumbnailer (void)
+{
+	return thumb_proxy;
+}
+
+static gboolean
+dbus_register_names (void)
+{
+	GError *error = NULL;
+
+	if (connection) {
+		g_critical ("The DBusGConnection is already set, have we already initialized?");
+		return FALSE;
+	}
+
+	if (proxy) {
+		g_critical ("The DBusGProxy is already set, have we already initialized?");
+		return FALSE;
+	}
+
+	connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
+
+	if (!connection) {
+		g_critical ("Could not connect to the DBus session bus, %s",
+			    error ? error->message : "no error given.");
+		g_clear_error (&error);
+		return FALSE;
+	}
+
+	/* The definitions below (DBUS_SERVICE_DBUS, etc) are
+	 * predefined for us to just use (dbus_g_proxy_...)
+	 */
+	proxy = dbus_g_proxy_new_for_name (connection,
+					   DBUS_SERVICE_DBUS,
+					   DBUS_PATH_DBUS,
+					   DBUS_INTERFACE_DBUS);
+
+	/* Register the service name for org.freedesktop.Tracker */
+	if (!dbus_register_service (proxy, TRACKER_INDEXER_SERVICE)) {
+		return FALSE;
+	}
+
+	thumb_proxy = dbus_g_proxy_new_for_name (connection,
+						 THUMBNAILER_SERVICE,
+						 THUMBNAILER_PATH,
+						 THUMBNAILER_INTERFACE);
+	return TRUE;
+}
+
+gboolean
+tracker_dbus_init (void)
+{
+	/* Don't reinitialize */
+	if (connection && proxy) {
+		return TRUE;
+	}
+
+	/* Register names and get proxy/connection details */
+	if (!dbus_register_names ()) {
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+void
+tracker_dbus_shutdown (void)
+{
+	if (proxy) {
+		g_object_unref (proxy);
+		proxy = NULL;
+	}
+
+	connection = NULL;
+}
+
+gboolean
+tracker_dbus_register_object (GObject *object)
+{
+	if (!connection || !proxy) {
+		g_critical ("DBus support must be initialized before registering objects!");
+		return FALSE;
+	}
+
+	if (TRACKER_IS_INDEXER (object)) {
+		return dbus_register_object (object,
+					     connection,
+					     proxy,
+					     &dbus_glib_tracker_indexer_object_info,
+					     TRACKER_INDEXER_PATH);
+	} else {
+		g_warning ("Object not handled by DBus");
+	}
+
+	return FALSE;
+}

Added: trunk/src/tracker-indexer/tracker-dbus.h
==============================================================================
--- (empty file)
+++ trunk/src/tracker-indexer/tracker-dbus.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,38 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKER_DBUS_H__
+#define __TRACKER_DBUS_H__
+
+#include <glib.h>
+
+#include <dbus/dbus-glib-bindings.h>
+
+G_BEGIN_DECLS
+
+gboolean    tracker_dbus_init		  (void);
+void	    tracker_dbus_shutdown	  (void);
+gboolean    tracker_dbus_register_object  (GObject *object);
+DBusGProxy* tracker_dbus_get_thumbnailer  (void);
+
+G_END_DECLS
+
+#endif /* __TRACKER_DBUS_H__ */

Added: trunk/src/tracker-indexer/tracker-indexer-db.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-indexer/tracker-indexer-db.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,871 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include <libtracker-common/tracker-type-utils.h>
+#include <libtracker-common/tracker-file-utils.h>
+
+#include <libtracker-db/tracker-db-manager.h>
+#include <libtracker-db/tracker-db-dbus.h>
+
+#include "tracker-indexer-db.h"
+
+guint32
+tracker_db_get_new_service_id (TrackerDBInterface *iface)
+{
+	guint32		    files_max;
+	TrackerDBResultSet *result_set;
+	TrackerDBInterface *temp_iface;
+	static guint32	    max = 0;
+
+	if (G_LIKELY (max != 0)) {
+		return ++max;
+	}
+
+	temp_iface = tracker_db_manager_get_db_interface (TRACKER_DB_FILE_METADATA);
+
+	result_set = tracker_db_interface_execute_query (temp_iface, NULL,
+							 "SELECT MAX(ID) AS A FROM Services");
+
+	if (result_set) {
+		GValue val = {0, };
+		_tracker_db_result_set_get_value (result_set, 0, &val);
+		if (G_VALUE_TYPE (&val) == G_TYPE_INT) {
+			max = g_value_get_int (&val);
+		}
+		g_value_unset (&val);
+		g_object_unref (result_set);
+	}
+
+	temp_iface = tracker_db_manager_get_db_interface (TRACKER_DB_EMAIL_METADATA);
+
+	result_set = tracker_db_interface_execute_query (temp_iface, NULL,
+							 "SELECT MAX(ID) AS A FROM Services");
+
+	if (result_set) {
+		GValue val = {0, };
+		_tracker_db_result_set_get_value (result_set, 0, &val);
+		if (G_VALUE_TYPE (&val) == G_TYPE_INT) {
+			files_max = g_value_get_int (&val);
+			max = MAX (files_max, max);
+		}
+		g_value_unset (&val);
+		g_object_unref (result_set);
+	}
+
+	return ++max;
+}
+
+void
+tracker_db_increment_stats (TrackerDBInterface *iface,
+			    TrackerService     *service)
+{
+	const gchar *service_type, *parent;
+
+	service_type = tracker_service_get_name (service);
+	parent = tracker_service_get_parent (service);
+
+	tracker_db_interface_execute_procedure (iface,
+						NULL,
+						"IncStat",
+						service_type,
+						NULL);
+
+	if (parent) {
+		tracker_db_interface_execute_procedure (iface,
+							NULL,
+							"IncStat",
+							parent,
+							NULL);
+	}
+}
+
+void
+tracker_db_decrement_stats (TrackerDBInterface *iface,
+			    TrackerService     *service)
+{
+	const gchar *service_type, *parent;
+
+	service_type = tracker_service_get_name (service);
+	parent = tracker_service_get_parent (service);
+
+	tracker_db_interface_execute_procedure (iface,
+						NULL,
+						"DecStat",
+						service_type,
+						NULL);
+
+	if (parent) {
+		tracker_db_interface_execute_procedure (iface,
+							NULL,
+							"DecStat",
+							parent,
+							NULL);
+	}
+}
+
+void
+tracker_db_create_event (TrackerDBInterface *iface,
+			   guint32 service_id,
+			   const gchar *type)
+{
+	gchar *service_id_str;
+
+	service_id_str = tracker_guint32_to_string (service_id);
+
+	tracker_db_interface_execute_procedure (iface, NULL, "CreateEvent",
+						service_id_str,
+						type,
+						NULL);
+
+	g_free (service_id_str);
+}
+
+gboolean
+tracker_db_check_service (TrackerService *service,
+			  const gchar	 *dirname,
+			  const gchar	 *basename,
+			  guint32	 *id,
+			  time_t	 *mtime)
+{
+	TrackerDBInterface *iface;
+	TrackerDBResultSet *result_set;
+	gchar *db_mtime_str;
+	guint db_id;
+	guint db_mtime;
+	gboolean found = FALSE;
+
+	db_id = db_mtime = 0;
+
+	iface = tracker_db_manager_get_db_interface_by_type (tracker_service_get_name (service),
+							     TRACKER_DB_CONTENT_TYPE_METADATA);
+
+	result_set = tracker_db_interface_execute_procedure (iface, NULL,
+							     "GetServiceID",
+							     dirname,
+							     basename,
+							     NULL);
+	if (result_set) {
+		tracker_db_result_set_get (result_set,
+					   0, &db_id,
+					   1, &db_mtime_str,
+					   -1);
+		g_object_unref (result_set);
+		found = TRUE;
+
+		if (db_mtime_str) {
+			db_mtime = tracker_string_to_date (db_mtime_str);
+			g_free (db_mtime_str);
+		}
+	}
+
+	if (id) {
+		*id = (guint32) db_id;
+	}
+
+	if (mtime) {
+		*mtime = (time_t) db_mtime;
+	}
+
+	return found;
+}
+
+guint
+tracker_db_get_service_type (const gchar *dirname,
+			     const gchar *basename)
+{
+	TrackerDBInterface *iface;
+	TrackerDBResultSet *result_set;
+	guint service_type_id;
+
+	/* We are asking this because the module cannot assign service_type -> probably it is files */
+	iface = tracker_db_manager_get_db_interface_by_type ("Files",
+							     TRACKER_DB_CONTENT_TYPE_METADATA);
+
+	result_set = tracker_db_interface_execute_procedure (iface, NULL,
+							     "GetServiceID",
+							     dirname,
+							     basename,
+							     NULL);
+	if (!result_set) {
+		return 0;
+	}
+
+	tracker_db_result_set_get (result_set, 3, &service_type_id, -1);
+	g_object_unref (result_set);
+
+	return service_type_id;
+}
+
+gboolean
+tracker_db_create_service (TrackerService  *service,
+			   guint32	    id,
+			   const gchar	   *dirname,
+			   const gchar	   *basename,
+			   TrackerMetadata *metadata)
+{
+	TrackerDBInterface *iface;
+	gchar *id_str, *service_type_id_str, *path;
+	gboolean is_dir, is_symlink, enabled;
+
+	if (!service) {
+		return FALSE;
+	}
+
+	iface = tracker_db_manager_get_db_interface_by_type (tracker_service_get_name (service),
+							     TRACKER_DB_CONTENT_TYPE_METADATA);
+
+	id_str = tracker_guint32_to_string (id);
+	service_type_id_str = tracker_gint_to_string (tracker_service_get_id (service));
+
+	path = g_build_filename (dirname, basename, NULL);
+
+	is_dir = g_file_test (path, G_FILE_TEST_IS_DIR);
+	is_symlink = g_file_test (path, G_FILE_TEST_IS_SYMLINK);
+
+	/* FIXME: do not hardcode arguments */
+	tracker_db_interface_execute_procedure (iface, NULL, "CreateService",
+						id_str,
+						dirname,
+						basename,
+						service_type_id_str,
+						is_dir ? "Folder" : tracker_metadata_lookup (metadata, "File:Mime"),
+						tracker_metadata_lookup (metadata, "File:Size"),
+						is_dir ? "1" : "0",
+						is_symlink ? "1" : "0",
+						"0", /* Offset */
+						tracker_metadata_lookup (metadata, "File:Modified"),
+						"0", /* Aux ID */
+						NULL);
+
+	enabled = is_dir ?
+		tracker_service_get_show_service_directories (service) :
+		tracker_service_get_show_service_files (service);
+
+	if (!enabled) {
+		tracker_db_interface_execute_query (iface, NULL,
+						    "Update services set Enabled = 0 where ID = %d",
+						    id);
+	}
+
+	g_free (id_str);
+	g_free (service_type_id_str);
+	g_free (path);
+
+	return TRUE;
+}
+
+static gchar *
+db_get_metadata (TrackerService *service,
+		 guint		 service_id,
+		 gboolean	 keywords)
+{
+	TrackerDBInterface *iface;
+	TrackerDBResultSet *result_set;
+	gchar		   *query;
+	GString		   *result;
+	gchar		   *str = NULL;
+
+	iface = tracker_db_manager_get_db_interface_by_type (tracker_service_get_name (service),
+							     TRACKER_DB_CONTENT_TYPE_METADATA);
+
+	result = g_string_new ("");
+
+	if (service_id < 1) {
+		return g_string_free (result, FALSE);
+	}
+
+	if (keywords) {
+		query = g_strdup_printf ("Select MetadataValue From ServiceKeywordMetadata WHERE serviceID = %d",
+					 service_id);
+	} else {
+		query = g_strdup_printf ("Select MetadataValue From ServiceMetadata WHERE serviceID = %d",
+					 service_id);
+	}
+
+	result_set = tracker_db_interface_execute_query (iface, NULL, query);
+	g_free (query);
+
+	if (result_set) {
+		gboolean valid = TRUE;
+
+		while (valid) {
+			tracker_db_result_set_get (result_set, 0, &str, -1);
+			result = g_string_append (result, str);
+			result = g_string_append (result, " ");
+			valid = tracker_db_result_set_iter_next (result_set);
+			g_free (str);
+		}
+
+		g_object_unref (result_set);
+	}
+
+	return g_string_free (result, FALSE);
+}
+
+static void
+result_set_to_metadata (TrackerDBResultSet *result_set,
+			TrackerMetadata    *metadata,
+			gboolean	    numeric,
+			gboolean	    only_embedded)
+{
+	TrackerField *field;
+	gchar	     *value;
+	gint	      numeric_value;
+	gint	      metadata_id;
+	gboolean      valid = TRUE;
+
+	while (valid) {
+		if (numeric) {
+			tracker_db_result_set_get (result_set,
+						   0, &metadata_id,
+						   1, &numeric_value,
+						   -1);
+			value = g_strdup_printf ("%d", numeric_value);
+		} else {
+			tracker_db_result_set_get (result_set,
+						   0, &metadata_id,
+						   1, &value,
+						   -1);
+		}
+
+		field = tracker_ontology_get_field_by_id (metadata_id);
+		if (!field) {
+			g_critical ("Field id %d in database but not in tracker-ontology",
+				    metadata_id);
+			g_free (value);
+			return;
+		}
+
+		if (tracker_field_get_embedded (field) || !only_embedded) {
+			if (tracker_field_get_multiple_values (field)) {
+				GList *new_values;
+				const GList *old_values;
+
+				new_values = NULL;
+				old_values = tracker_metadata_lookup_multiple_values (metadata,
+										      tracker_field_get_name (field));
+				if (old_values) {
+					new_values = g_list_copy ((GList *) old_values);
+				}
+
+				new_values = g_list_prepend (new_values, value);
+				tracker_metadata_insert_multiple_values (metadata,
+									 tracker_field_get_name (field),
+									 new_values);
+			} else {
+				tracker_metadata_insert (metadata,
+							 tracker_field_get_name (field),
+							 value);
+			}
+		} else {
+			g_free (value);
+		}
+
+		valid = tracker_db_result_set_iter_next (result_set);
+	}
+}
+
+TrackerMetadata *
+tracker_db_get_all_metadata (TrackerService *service,
+			     guint32	     service_id,
+			     gboolean	     only_embedded)
+{
+	TrackerDBInterface *iface;
+	TrackerDBResultSet *result_set = NULL;
+	gchar		   *service_id_str;
+	TrackerMetadata    *metadata;
+
+	metadata = tracker_metadata_new ();
+
+	service_id_str = g_strdup_printf ("%d", service_id);
+	iface = tracker_db_manager_get_db_interface_by_type (tracker_service_get_name (service),
+							     TRACKER_DB_CONTENT_TYPE_METADATA);
+
+	result_set = tracker_db_interface_execute_procedure (iface, NULL, "GetMetadataIDValue", service_id_str, NULL);
+	if (result_set) {
+		result_set_to_metadata (result_set, metadata, FALSE, only_embedded);
+		g_object_unref (result_set);
+	}
+
+	result_set = tracker_db_interface_execute_procedure (iface, NULL, "GetMetadataIDValueKeyword", service_id_str, NULL);
+	if (result_set) {
+		result_set_to_metadata (result_set, metadata, FALSE, only_embedded);
+		g_object_unref (result_set);
+	}
+
+	result_set = tracker_db_interface_execute_procedure (iface, NULL, "GetMetadataIDValueNumeric", service_id_str, NULL);
+	if (result_set) {
+		result_set_to_metadata (result_set, metadata, TRUE, only_embedded);
+		g_object_unref (result_set);
+	}
+
+	g_free (service_id_str);
+
+	return metadata;
+}
+
+void
+tracker_db_delete_service (TrackerService *service,
+			   guint32	   service_id)
+{
+
+	TrackerDBInterface *iface;
+	gchar *service_id_str;
+
+	if (service_id < 1) {
+		return;
+	}
+
+	iface = tracker_db_manager_get_db_interface_by_type (tracker_service_get_name (service),
+							     TRACKER_DB_CONTENT_TYPE_METADATA);
+
+	service_id_str = tracker_guint32_to_string (service_id);
+
+	/* Delete from services table */
+	tracker_db_interface_execute_procedure (iface,
+						NULL,
+						"DeleteService1",
+						service_id_str,
+						NULL);
+
+	g_free (service_id_str);
+}
+
+void
+tracker_db_move_service (TrackerService *service,
+			 const gchar	*from,
+			 const gchar	*to)
+{
+	TrackerDBInterface *iface;
+	GError *error = NULL;
+	gchar *from_dirname;
+	gchar *from_basename;
+	gchar *to_dirname;
+	gchar *to_basename;
+
+	iface = tracker_db_manager_get_db_interface_by_type (tracker_service_get_name (service),
+							     TRACKER_DB_CONTENT_TYPE_METADATA);
+
+	tracker_file_get_path_and_name (from,
+					&from_dirname,
+					&from_basename);
+	tracker_file_get_path_and_name (to,
+					&to_dirname,
+					&to_basename);
+
+	tracker_db_interface_execute_procedure (iface,
+						NULL,
+						"MoveService",
+						to_dirname, to_basename,
+						from_dirname, from_basename,
+						NULL);
+
+	/* FIXME: This procedure should use LIKE statement */
+	tracker_db_interface_execute_procedure (iface,
+						&error,
+						"MoveServiceChildren",
+						from,
+						to,
+						from,
+						NULL);
+
+	g_free (to_dirname);
+	g_free (to_basename);
+	g_free (from_dirname);
+	g_free (from_basename);
+}
+
+void
+tracker_db_delete_all_metadata (TrackerService *service,
+				guint32		service_id)
+{
+	TrackerDBInterface *iface;
+	gchar *service_id_str;
+
+	iface = tracker_db_manager_get_db_interface_by_type (tracker_service_get_name (service),
+							     TRACKER_DB_CONTENT_TYPE_METADATA);
+
+	service_id_str = tracker_guint32_to_string (service_id);
+
+	/* Delete from ServiceMetadata, ServiceKeywordMetadata,
+	 * ServiceNumberMetadata.
+	 */
+	tracker_db_interface_execute_procedure (iface,
+						NULL,
+						"DeleteServiceMetadata",
+						service_id_str,
+						NULL);
+	tracker_db_interface_execute_procedure (iface,
+						NULL,
+						"DeleteServiceKeywordMetadata",
+						service_id_str,
+						NULL);
+	tracker_db_interface_execute_procedure (iface,
+						NULL,
+						"DeleteServiceNumericMetadata",
+						service_id_str,
+						NULL);
+}
+
+gchar *
+tracker_db_get_unparsed_metadata (TrackerService *service,
+				  guint		  service_id)
+{
+	return db_get_metadata (service, service_id, TRUE);
+}
+
+gchar *
+tracker_db_get_parsed_metadata (TrackerService *service,
+				guint		service_id)
+{
+	return db_get_metadata (service, service_id, FALSE);
+}
+
+gchar **
+tracker_db_get_property_values (TrackerService *service_def,
+				guint32		id,
+				TrackerField   *field)
+{
+	TrackerDBInterface *iface;
+	TrackerDBResultSet *result_set = NULL;
+	gint		    metadata_key;
+	gchar		  **final_result = NULL;
+	gboolean	    is_numeric = FALSE;
+
+	iface = tracker_db_manager_get_db_interface_by_type (tracker_service_get_name (service_def),
+							     TRACKER_DB_CONTENT_TYPE_METADATA);
+	metadata_key = tracker_ontology_service_get_key_metadata (tracker_service_get_name (service_def),
+								  tracker_field_get_name (field));
+
+	if (metadata_key > 0) {
+		gchar *query;
+
+		query = g_strdup_printf ("SELECT KeyMetadata%d FROM Services WHERE id = '%d'",
+					 metadata_key,
+					 id);
+		result_set = tracker_db_interface_execute_query (iface,
+								 NULL,
+								 query,
+								 NULL);
+		g_free (query);
+	} else {
+		gchar *id_str;
+
+		id_str = tracker_guint32_to_string (id);
+
+		switch (tracker_field_get_data_type (field)) {
+		case TRACKER_FIELD_TYPE_KEYWORD:
+			result_set = tracker_db_interface_execute_procedure (iface, NULL,
+									     "GetMetadataKeyword",
+									     id_str,
+									     tracker_field_get_id (field),
+									     NULL);
+			break;
+		case TRACKER_FIELD_TYPE_INDEX:
+		case TRACKER_FIELD_TYPE_STRING:
+		case TRACKER_FIELD_TYPE_DOUBLE:
+			result_set = tracker_db_interface_execute_procedure (iface, NULL,
+									     "GetMetadata",
+									     id_str,
+									     tracker_field_get_id (field),
+									     NULL);
+			break;
+		case TRACKER_FIELD_TYPE_INTEGER:
+		case TRACKER_FIELD_TYPE_DATE:
+			result_set = tracker_db_interface_execute_procedure (iface, NULL,
+									     "GetMetadataNumeric",
+									     id_str,
+									     tracker_field_get_id (field),
+									     NULL);
+			is_numeric = TRUE;
+			break;
+		case TRACKER_FIELD_TYPE_FULLTEXT:
+			tracker_db_get_text (service_def, id);
+			break;
+		case TRACKER_FIELD_TYPE_BLOB:
+		case TRACKER_FIELD_TYPE_STRUCT:
+		case TRACKER_FIELD_TYPE_LINK:
+			/* not handled */
+		default:
+			break;
+		}
+		g_free (id_str);
+	}
+
+	if (result_set) {
+		if (tracker_db_result_set_get_n_rows (result_set) > 1) {
+			g_warning ("More than one result in tracker_db_get_property_value");
+		}
+
+		if (!is_numeric) {
+			final_result = tracker_dbus_query_result_to_strv (result_set, 0, NULL);
+		} else {
+			final_result = tracker_dbus_query_result_numeric_to_strv (result_set, 0, NULL);
+		}
+
+		g_object_unref (result_set);
+	}
+
+	return final_result;
+}
+
+void
+tracker_db_set_metadata (TrackerService *service,
+			 guint32	 id,
+			 TrackerField	*field,
+			 const gchar	*value,
+			 const gchar	*parsed_value)
+{
+	TrackerDBInterface *iface;
+	gint metadata_key;
+	gchar *id_str;
+	gchar *time_string;
+
+	id_str = tracker_guint32_to_string (id);
+	iface = tracker_db_manager_get_db_interface_by_type (tracker_service_get_name (service),
+							     TRACKER_DB_CONTENT_TYPE_METADATA);
+
+	switch (tracker_field_get_data_type (field)) {
+	case TRACKER_FIELD_TYPE_KEYWORD:
+		tracker_db_interface_execute_procedure (iface, NULL,
+							"SetMetadataKeyword",
+							id_str,
+							tracker_field_get_id (field),
+							value,
+							NULL);
+		break;
+
+	case TRACKER_FIELD_TYPE_INDEX:
+	case TRACKER_FIELD_TYPE_STRING:
+	case TRACKER_FIELD_TYPE_DOUBLE:
+		tracker_db_interface_execute_procedure (iface, NULL,
+							"SetMetadata",
+							id_str,
+							tracker_field_get_id (field),
+							parsed_value,
+							value,
+							NULL);
+		break;
+
+	case TRACKER_FIELD_TYPE_INTEGER:
+		tracker_db_interface_execute_procedure (iface, NULL,
+							"SetMetadataNumeric",
+							id_str,
+							tracker_field_get_id (field),
+							value,
+							NULL);
+		break;
+
+	case TRACKER_FIELD_TYPE_DATE:
+
+		time_string = tracker_date_to_time_string (value);
+
+		if (time_string) {
+			tracker_db_interface_execute_procedure (iface, NULL,
+								"SetMetadataNumeric",
+								id_str,
+								tracker_field_get_id (field),
+								time_string,
+								NULL);
+			g_free (time_string);
+		}
+		break;
+
+	case TRACKER_FIELD_TYPE_FULLTEXT:
+		tracker_db_set_text (service, id, value);
+		break;
+
+	case TRACKER_FIELD_TYPE_BLOB:
+	case TRACKER_FIELD_TYPE_STRUCT:
+	case TRACKER_FIELD_TYPE_LINK:
+		/* not handled */
+	default:
+		break;
+	}
+
+	metadata_key = tracker_ontology_service_get_key_metadata (tracker_service_get_name (service),
+								  tracker_field_get_name (field));
+	if (metadata_key > 0) {
+		tracker_db_interface_execute_query (iface, NULL,
+						    "update Services set KeyMetadata%d = '%s' where id = %d",
+						    metadata_key,
+						    value,
+						    id);
+	}
+
+	g_free (id_str);
+}
+
+void
+tracker_db_delete_metadata (TrackerService *service,
+			    guint32	    id,
+			    TrackerField   *field,
+			    const gchar    *value)
+{
+	TrackerDBInterface *iface;
+	gint metadata_key;
+	gchar *id_str;
+
+	id_str = tracker_guint32_to_string (id);
+	iface = tracker_db_manager_get_db_interface_by_type (tracker_service_get_name (service),
+							     TRACKER_DB_CONTENT_TYPE_METADATA);
+
+	switch (tracker_field_get_data_type (field)) {
+	case TRACKER_FIELD_TYPE_KEYWORD:
+		if (!value) {
+			g_debug ("Trying to remove keyword field with no specific value");
+			tracker_db_interface_execute_procedure (iface, NULL,
+								"DeleteMetadataKeyword",
+								id_str,
+								tracker_field_get_id (field),
+								NULL);
+		} else {
+			tracker_db_interface_execute_procedure (iface, NULL,
+								"DeleteMetadataKeywordValue",
+								id_str,
+								tracker_field_get_id (field),
+								value,
+								NULL);
+		}
+		break;
+
+	case TRACKER_FIELD_TYPE_INDEX:
+	case TRACKER_FIELD_TYPE_STRING:
+	case TRACKER_FIELD_TYPE_DOUBLE:
+		tracker_db_interface_execute_procedure (iface, NULL,
+							"DeleteMetadata",
+							id_str,
+							tracker_field_get_id (field),
+							NULL);
+		break;
+
+	case TRACKER_FIELD_TYPE_INTEGER:
+	case TRACKER_FIELD_TYPE_DATE:
+		tracker_db_interface_execute_procedure (iface, NULL,
+							"DeleteMetadataNumeric",
+							id_str,
+							tracker_field_get_id (field),
+							NULL);
+		break;
+
+	case TRACKER_FIELD_TYPE_FULLTEXT:
+		tracker_db_delete_text (service, id);
+		break;
+
+	case TRACKER_FIELD_TYPE_BLOB:
+	case TRACKER_FIELD_TYPE_STRUCT:
+	case TRACKER_FIELD_TYPE_LINK:
+		/* not handled */
+	default:
+		break;
+	}
+
+	metadata_key = tracker_ontology_service_get_key_metadata (tracker_service_get_name (service),
+								  tracker_field_get_name (field));
+	if (metadata_key > 0) {
+		tracker_db_interface_execute_query (iface, NULL,
+						    "update Services set KeyMetadata%d = '%s' where id = %d",
+						    metadata_key, "", id);
+	}
+
+	g_free (id_str);
+}
+
+void
+tracker_db_set_text (TrackerService *service,
+		     guint32	     id,
+		     const gchar    *text)
+{
+	TrackerDBInterface *iface;
+	TrackerField *field;
+	gchar *id_str;
+
+	id_str = tracker_guint32_to_string (id);
+	field = tracker_ontology_get_field_by_name ("File:Contents");
+	iface = tracker_db_manager_get_db_interface_by_type (tracker_service_get_name (service),
+							     TRACKER_DB_CONTENT_TYPE_CONTENTS);
+
+	tracker_db_interface_execute_procedure (iface, NULL,
+						"SaveServiceContents",
+						id_str,
+						tracker_field_get_id (field),
+						text,
+						NULL);
+	g_free (id_str);
+}
+
+gchar *
+tracker_db_get_text (TrackerService *service,
+		     guint32	     id)
+{
+	TrackerDBInterface *iface;
+	TrackerField	   *field;
+	gchar		   *service_id_str, *contents = NULL;
+	TrackerDBResultSet *result_set;
+
+	service_id_str = tracker_guint32_to_string (id);
+	field = tracker_ontology_get_field_by_name ("File:Contents");
+	iface = tracker_db_manager_get_db_interface_by_type (tracker_service_get_name (service),
+							     TRACKER_DB_CONTENT_TYPE_CONTENTS);
+
+	/* Delete contents if it has! */
+	result_set = tracker_db_interface_execute_procedure (iface, NULL,
+							     "GetContents",
+							     service_id_str,
+							     tracker_field_get_id (field),
+							     NULL);
+
+	if (result_set) {
+		tracker_db_result_set_get (result_set, 0, &contents, -1);
+		g_object_unref (result_set);
+	}
+
+	g_free (service_id_str);
+
+	return contents;
+}
+
+void
+tracker_db_delete_text (TrackerService *service,
+			guint32		id)
+{
+	TrackerDBInterface *iface;
+	TrackerField *field;
+	gchar *service_id_str;
+
+	service_id_str = tracker_guint32_to_string (id);
+	field = tracker_ontology_get_field_by_name ("File:Contents");
+	iface = tracker_db_manager_get_db_interface_by_type (tracker_service_get_name (service),
+							     TRACKER_DB_CONTENT_TYPE_CONTENTS);
+
+	/* Delete contents if it has! */
+	tracker_db_interface_execute_procedure (iface, NULL,
+						"DeleteContent",
+						service_id_str,
+						tracker_field_get_id (field),
+						NULL);
+
+	g_free (service_id_str);
+}

Added: trunk/src/tracker-indexer/tracker-indexer-db.h
==============================================================================
--- (empty file)
+++ trunk/src/tracker-indexer/tracker-indexer-db.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,103 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKER_INDEXER_DB_H__
+#define __TRACKER_INDEXER_DB_H__
+
+#include <libtracker-common/tracker-ontology.h>
+#include <libtracker-db/tracker-db-interface.h>
+#include "tracker-metadata.h"
+
+G_BEGIN_DECLS
+guint32		 tracker_db_get_new_service_id	  (TrackerDBInterface *iface);
+void		 tracker_db_increment_stats	  (TrackerDBInterface *iface,
+						   TrackerService     *service);
+void		 tracker_db_decrement_stats	  (TrackerDBInterface *iface,
+						   TrackerService     *service);
+
+/* Using path */
+gboolean	 tracker_db_check_service	  (TrackerService     *service,
+						   const gchar	      *dirname,
+						   const gchar	      *basename,
+						   guint32	      *id,
+						   time_t	      *mtime);
+guint		 tracker_db_get_service_type	  (const gchar	      *dirname,
+						   const gchar	      *basename);
+
+
+/* Services  */
+gboolean	 tracker_db_create_service	  (TrackerService     *service,
+						   guint32	       id,
+						   const gchar	      *dirname,
+						   const gchar	      *basename,
+						   TrackerMetadata    *metadata);
+void		 tracker_db_delete_service	  (TrackerService     *service,
+						   guint32	       id);
+void		 tracker_db_move_service	  (TrackerService     *service,
+						   const gchar	      *from,
+						   const gchar	      *to);
+
+
+/* Metadata */
+void		 tracker_db_set_metadata	  (TrackerService     *service,
+						   guint32	       id,
+						   TrackerField       *field,
+						   const gchar	      *value,
+						   const gchar	      *parsed_value);
+TrackerMetadata *tracker_db_get_all_metadata	  (TrackerService     *service,
+						   guint32	       id,
+						   gboolean	       only_embedded);
+gchar	*	 tracker_db_get_parsed_metadata   (TrackerService     *service,
+						   guint32	       id);
+gchar	*	 tracker_db_get_unparsed_metadata (TrackerService     *service,
+						   guint32	       id);
+gchar  **	 tracker_db_get_property_values   (TrackerService     *service_def,
+						   guint32	       id,
+						   TrackerField       *field_def);
+void		 tracker_db_delete_all_metadata   (TrackerService     *service,
+						   guint32	       id);
+void		 tracker_db_delete_metadata	  (TrackerService     *service,
+						   guint32	       id,
+						   TrackerField       *field,
+						   const gchar	      *value);
+
+/* Contents */
+void		 tracker_db_set_text		  (TrackerService     *service,
+						   guint32	       id,
+						   const gchar	      *text);
+gchar	*	 tracker_db_get_text		  (TrackerService     *service,
+						   guint32	       id);
+void		 tracker_db_delete_text		  (TrackerService     *service,
+						   guint32	       id);
+
+
+
+/* Events */
+void		 tracker_db_create_event	  (TrackerDBInterface *iface,
+						   guint32	       service_id,
+						   const gchar	      *type);
+
+
+
+
+G_END_DECLS
+
+#endif /* __TRACKER_INDEXER_DB_H__ */

Added: trunk/src/tracker-indexer/tracker-indexer-module.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-indexer/tracker-indexer-module.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,216 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <gmodule.h>
+
+#include "tracker-indexer-module.h"
+
+GModule *
+tracker_indexer_module_load (const gchar *module_name)
+{
+	gchar *full_name, *path;
+	GModule *module;
+
+	g_return_val_if_fail (module_name != NULL, NULL);
+
+	full_name = g_strdup_printf ("libtracker-indexer-%s", module_name);
+	path = g_build_filename (INDEXER_MODULES_DIR, full_name, NULL);
+
+	module = g_module_open (path, G_MODULE_BIND_LOCAL);
+
+	if (!module) {
+		g_warning ("Could not load indexer module '%s', %s\n", module_name, g_module_error ());
+	} else {
+		g_module_make_resident (module);
+	}
+
+	g_free (full_name);
+	g_free (path);
+
+	return module;
+}
+
+void
+tracker_indexer_module_init (GModule *module)
+{
+	TrackerModuleInit func;
+
+	if (g_module_symbol (module, "tracker_module_init", (gpointer *) &func)) {
+		(func) ();
+	}
+}
+
+void
+tracker_indexer_module_shutdown (GModule *module)
+{
+	TrackerModuleShutdown func;
+
+	if (g_module_symbol (module, "tracker_module_shutdown", (gpointer *) &func)) {
+		(func) ();
+	}
+}
+
+G_CONST_RETURN gchar *
+tracker_indexer_module_get_name (GModule *module)
+{
+	TrackerModuleGetNameFunc func;
+
+	if (g_module_symbol (module, "tracker_module_get_name", (gpointer *) &func)) {
+		return (func) ();
+	}
+
+	return NULL;
+}
+
+TrackerFile *
+tracker_indexer_module_file_new (GModule     *module,
+				 const gchar *path)
+{
+	TrackerModuleFileGetDataFunc func;
+	TrackerFile *file = NULL;
+
+	file = g_slice_new0 (TrackerFile);
+	file->path = g_strdup (path);
+
+	if (g_module_symbol (module, "tracker_module_file_get_data", (gpointer *) &func)) {
+		file->data = (func) (path);
+	}
+
+	return file;
+}
+
+void
+tracker_indexer_module_file_free (GModule     *module,
+				  TrackerFile *file)
+{
+	TrackerModuleFileFreeDataFunc func;
+
+	if (file->data &&
+	    g_module_symbol (module, "tracker_module_file_free_data", (gpointer *) &func)) {
+		(func) (file->data);
+	}
+
+	g_free (file->path);
+	g_slice_free (TrackerFile, file);
+}
+
+gboolean
+tracker_indexer_module_file_get_uri (GModule	  *module,
+				     TrackerFile  *file,
+				     gchar	 **dirname,
+				     gchar	 **basename)
+{
+	TrackerModuleFileGetUriFunc func;
+	gchar *tmp_dirname;
+	gchar *tmp_basename;
+
+	tmp_dirname = tmp_basename = NULL;
+
+	if (g_module_symbol (module, "tracker_module_file_get_uri", (gpointer *) &func)) {
+		(func) (file, &tmp_dirname, &tmp_basename);
+	} else {
+		tmp_dirname = g_path_get_dirname (file->path);
+		tmp_basename = g_path_get_basename (file->path);
+	}
+
+	if (tmp_dirname && tmp_basename) {
+		if (dirname) {
+			*dirname = tmp_dirname;
+		} else {
+			g_free (tmp_dirname);
+		}
+
+		if (basename) {
+			*basename = tmp_basename;
+		} else {
+			g_free (tmp_basename);
+		}
+
+		return TRUE;
+	} else {
+		g_free (tmp_dirname);
+		g_free (tmp_basename);
+
+		if (dirname) {
+			*dirname = NULL;
+		}
+
+		if (basename) {
+			*basename = NULL;
+		}
+
+		return FALSE;
+	}
+}
+
+gchar *
+tracker_indexer_module_file_get_service_type (GModule	  *module,
+					      TrackerFile *file)
+{
+	TrackerModuleFileGetServiceTypeFunc func;
+
+	if (g_module_symbol (module, "tracker_module_file_get_service_type", (gpointer *) &func)) {
+		return (func) (file);
+	} else {
+		return g_strdup (tracker_indexer_module_get_name (module));
+	}
+
+}
+
+
+TrackerMetadata *
+tracker_indexer_module_file_get_metadata (GModule     *module,
+					  TrackerFile *file)
+{
+	TrackerModuleFileGetMetadataFunc func;
+
+	if (g_module_symbol (module, "tracker_module_file_get_metadata", (gpointer *) &func)) {
+		return (func) (file);
+	}
+
+	return NULL;
+}
+
+gchar *
+tracker_indexer_module_file_get_text (GModule	  *module,
+				      TrackerFile *file)
+{
+	TrackerModuleFileGetText func;
+
+	if (g_module_symbol (module, "tracker_module_file_get_text", (gpointer *) &func)) {
+		return (func) (file);
+	}
+
+	return NULL;
+}
+
+gboolean
+tracker_indexer_module_file_iter_contents (GModule     *module,
+					   TrackerFile *file)
+{
+	TrackerModuleFileIterContents func;
+
+	if (file->data && g_module_symbol (module, "tracker_module_file_iter_contents", (gpointer *) &func)) {
+		return (func) (file);
+	}
+
+	return FALSE;
+}

Added: trunk/src/tracker-indexer/tracker-indexer-module.h
==============================================================================
--- (empty file)
+++ trunk/src/tracker-indexer/tracker-indexer-module.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKER_INDEXER_MODULE_H__
+#define __TRACKER_INDEXER_MODULE_H__
+
+#include <glib.h>
+#include "tracker-module.h"
+#include "tracker-metadata.h"
+
+G_BEGIN_DECLS
+
+GModule *		tracker_indexer_module_load		      (const gchar  *module_name);
+
+void			tracker_indexer_module_init		      (GModule	    *module);
+void			tracker_indexer_module_shutdown		      (GModule	    *module);
+
+G_CONST_RETURN gchar *	tracker_indexer_module_get_name		      (GModule	    *module);
+
+TrackerFile *		tracker_indexer_module_file_new		      (GModule	    *module,
+								       const gchar  *path);
+void			tracker_indexer_module_file_free	      (GModule	    *module,
+								       TrackerFile  *file);
+
+gboolean		tracker_indexer_module_file_get_uri	      (GModule	    *module,
+								       TrackerFile  *file,
+								       gchar	   **dirname,
+								       gchar	   **basename);
+gchar *			tracker_indexer_module_file_get_service_type  (GModule	    *module,
+								       TrackerFile  *file);
+TrackerMetadata *	tracker_indexer_module_file_get_metadata      (GModule	    *module,
+								       TrackerFile  *file);
+gchar *			tracker_indexer_module_file_get_text	      (GModule	    *module,
+								       TrackerFile  *file);
+
+gboolean		tracker_indexer_module_file_iter_contents     (GModule	    *module,
+								       TrackerFile  *file);
+
+G_END_DECLS
+
+#endif /* __TRACKER_INDEXER_MODULE_H__ */

Added: trunk/src/tracker-indexer/tracker-indexer.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-indexer/tracker-indexer.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,2591 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+/* The indexer works as a state machine, there are 3 different queues:
+ *
+ * * The files queue: the highest priority one, individual files are
+ *   stored here, waiting for metadata extraction, etc... files are
+ *   taken one by one in order to be processed, when this queue is
+ *   empty, a single token from the next queue is processed.
+ *
+ * * The directories queue: directories are stored here, waiting for
+ *   being inspected. When a directory is inspected, contained files
+ *   and directories will be prepended in their respective queues.
+ *   When this queue is empty, a single token from the next queue
+ *   is processed.
+ *
+ * * The modules list: indexing modules are stored here, these modules
+ *   can either prepend the files or directories to be inspected in
+ *   their respective queues.
+ *
+ * Once all queues are empty, all elements have been inspected, and the
+ * indexer will emit the ::finished signal, this behavior can be observed
+ * in the process_func() function.
+ *
+ * NOTE: Normally all indexing petitions will be sent over DBus, being
+ *	 everything just pushed in the files queue.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/statvfs.h>
+
+#include <glib/gstdio.h>
+#include <gio/gio.h>
+#include <gmodule.h>
+
+#include <libtracker-common/tracker-config.h>
+#include <libtracker-common/tracker-dbus.h>
+#include <libtracker-common/tracker-file-utils.h>
+#include <libtracker-common/tracker-hal.h>
+#include <libtracker-common/tracker-language.h>
+#include <libtracker-common/tracker-parser.h>
+#include <libtracker-common/tracker-ontology.h>
+#include <libtracker-common/tracker-module-config.h>
+#include <libtracker-common/tracker-utils.h>
+
+#include <libtracker-db/tracker-db-manager.h>
+#include <libtracker-db/tracker-db-index-manager.h>
+#include <libtracker-db/tracker-db-interface-sqlite.h>
+
+#include "tracker-indexer.h"
+#include "tracker-indexer-module.h"
+#include "tracker-indexer-db.h"
+#include "tracker-marshal.h"
+#include "tracker-module.h"
+
+#define TRACKER_INDEXER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRACKER_TYPE_INDEXER, TrackerIndexerPrivate))
+
+/* Flush every 'x' seconds */
+#define FLUSH_FREQUENCY		    60
+
+#define LOW_DISK_CHECK_FREQUENCY    10
+#define SIGNAL_STATUS_FREQUENCY     10
+
+/* Transaction every 'x' items */
+#define TRANSACTION_MAX		    2000
+
+/* Throttle defaults */
+#define THROTTLE_DEFAULT	    0
+#define THROTTLE_DEFAULT_ON_BATTERY 5
+
+#define TRACKER_INDEXER_ERROR	   "tracker-indexer-error-domain"
+#define TRACKER_INDEXER_ERROR_CODE  0
+
+typedef struct PathInfo PathInfo;
+typedef struct MetadataForeachData MetadataForeachData;
+typedef struct MetadataRequest MetadataRequest;
+typedef struct UpdateWordsForeachData UpdateWordsForeachData;
+typedef enum TrackerIndexerState TrackerIndexerState;
+
+struct TrackerIndexerPrivate {
+	GQueue *dir_queue;
+	GQueue *file_queue;
+	GQueue *modules_queue;
+
+	GHashTable *mtime_cache;
+
+	GList *module_names;
+	GQuark current_module;
+	GHashTable *indexer_modules;
+
+	gchar *db_dir;
+
+	TrackerDBIndex *file_index;
+	TrackerDBIndex *email_index;
+
+	TrackerDBInterface *file_metadata;
+	TrackerDBInterface *file_contents;
+	TrackerDBInterface *email_metadata;
+	TrackerDBInterface *email_contents;
+	TrackerDBInterface *common;
+	TrackerDBInterface *cache;
+
+	TrackerConfig *config;
+	TrackerLanguage *language;
+
+	TrackerHal *hal;
+
+	GTimer *timer;
+
+	guint idle_id;
+	guint pause_for_duration_id;
+	guint disk_space_check_id;
+	guint signal_status_id;
+	guint flush_id;
+
+	guint files_processed;
+	guint files_indexed;
+	guint items_processed;
+
+	gboolean in_transaction;
+	gboolean in_process;
+
+	guint state;
+};
+
+struct PathInfo {
+	GModule *module;
+	TrackerFile *file;
+	TrackerFile *other_file;
+	gchar *module_name;
+};
+
+struct MetadataForeachData {
+	TrackerLanguage *language;
+	TrackerConfig *config;
+	TrackerService *service;
+	gboolean add;
+	guint32 id;
+};
+
+struct UpdateWordsForeachData {
+	guint32 service_id;
+	guint32 service_type_id;
+};
+
+enum TrackerIndexerState {
+	TRACKER_INDEXER_STATE_FLUSHING	= 1 << 0,
+	TRACKER_INDEXER_STATE_PAUSED	= 1 << 1,
+	TRACKER_INDEXER_STATE_DISK_FULL = 1 << 2,
+	TRACKER_INDEXER_STATE_STOPPED	= 1 << 3
+};
+
+enum {
+	PROP_0,
+	PROP_RUNNING,
+};
+
+enum {
+	STATUS,
+	STARTED,
+	FINISHED,
+	MODULE_STARTED,
+	MODULE_FINISHED,
+	PAUSED,
+	CONTINUED,
+	LAST_SIGNAL
+};
+
+static gboolean process_func	       (gpointer	     data);
+static void	check_disk_space_start (TrackerIndexer	    *indexer);
+static void	state_set_flags        (TrackerIndexer	    *indexer,
+					TrackerIndexerState  state);
+static void	state_unset_flags      (TrackerIndexer	    *indexer,
+					TrackerIndexerState  state);
+static void	state_check	       (TrackerIndexer	    *indexer);
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE (TrackerIndexer, tracker_indexer, G_TYPE_OBJECT)
+
+static PathInfo *
+path_info_new (GModule *module,
+	       const gchar *module_name,
+	       const gchar *path,
+	       const gchar *other_path)
+{
+	PathInfo *info;
+
+	info = g_slice_new (PathInfo);
+	info->module = module;
+	info->module_name = g_strdup (module_name);
+	info->file = tracker_indexer_module_file_new (module, path);
+
+	if (G_UNLIKELY (other_path)) {
+		info->other_file = tracker_indexer_module_file_new (module, other_path);
+	} else {
+		info->other_file = NULL;
+	}
+
+	return info;
+}
+
+static void
+path_info_free (PathInfo *info)
+{
+	if (G_UNLIKELY (info->other_file)) {
+		tracker_indexer_module_file_free (info->module, info->other_file);
+	}
+
+	tracker_indexer_module_file_free (info->module, info->file);
+	g_free (info->module_name);
+	g_slice_free (PathInfo, info);
+}
+
+static void
+start_transaction (TrackerIndexer *indexer)
+{
+	g_debug ("Transaction start");
+
+	indexer->private->in_transaction = TRUE;
+
+	tracker_db_interface_start_transaction (indexer->private->cache);
+	tracker_db_interface_start_transaction (indexer->private->file_contents);
+	tracker_db_interface_start_transaction (indexer->private->email_contents);
+	tracker_db_interface_start_transaction (indexer->private->file_metadata);
+	tracker_db_interface_start_transaction (indexer->private->email_metadata);
+	tracker_db_interface_start_transaction (indexer->private->common);
+}
+
+static void
+stop_transaction (TrackerIndexer *indexer)
+{
+	if (!indexer->private->in_transaction) {
+		return;
+	}
+
+	tracker_db_interface_end_transaction (indexer->private->common);
+	tracker_db_interface_end_transaction (indexer->private->email_metadata);
+	tracker_db_interface_end_transaction (indexer->private->file_metadata);
+	tracker_db_interface_end_transaction (indexer->private->email_contents);
+	tracker_db_interface_end_transaction (indexer->private->file_contents);
+	tracker_db_interface_end_transaction (indexer->private->cache);
+
+	indexer->private->files_indexed += indexer->private->files_processed;
+	indexer->private->files_processed = 0;
+	indexer->private->in_transaction = FALSE;
+
+	g_debug ("Transaction commit");
+}
+
+static void
+signal_status (TrackerIndexer *indexer,
+	       const gchar    *why)
+{
+	gdouble seconds_elapsed;
+	guint	files_remaining;
+
+	files_remaining = g_queue_get_length (indexer->private->file_queue);
+	seconds_elapsed = g_timer_elapsed (indexer->private->timer, NULL);
+
+	if (indexer->private->files_indexed > 0 &&
+	    files_remaining > 0) {
+		gchar *str1;
+		gchar *str2;
+
+		str1 = tracker_seconds_estimate_to_string (seconds_elapsed,
+							   TRUE,
+							   indexer->private->files_indexed,
+							   files_remaining);
+		str2 = tracker_seconds_to_string (seconds_elapsed, TRUE);
+
+		g_message ("Indexed %d/%d, module:'%s', %s left, %s elapsed (%s)",
+			   indexer->private->files_indexed,
+			   indexer->private->files_indexed + files_remaining,
+			   g_quark_to_string (indexer->private->current_module),
+			   str1,
+			   str2,
+			   why);
+
+		g_free (str2);
+		g_free (str1);
+	}
+
+	g_signal_emit (indexer, signals[STATUS], 0,
+		       seconds_elapsed,
+		       g_quark_to_string (indexer->private->current_module),
+		       indexer->private->files_indexed,
+		       files_remaining);
+}
+
+static gboolean
+flush_data (TrackerIndexer *indexer)
+{
+	indexer->private->flush_id = 0;
+
+	state_set_flags (indexer, TRACKER_INDEXER_STATE_FLUSHING);
+
+	if (indexer->private->in_transaction) {
+		stop_transaction (indexer);
+	}
+
+	tracker_db_index_flush (indexer->private->file_index);
+	tracker_db_index_flush (indexer->private->email_index);
+	signal_status (indexer, "flush");
+
+	indexer->private->items_processed = 0;
+
+	state_unset_flags (indexer, TRACKER_INDEXER_STATE_FLUSHING);
+
+	return FALSE;
+}
+
+static void
+stop_scheduled_flush (TrackerIndexer *indexer)
+{
+	if (indexer->private->flush_id) {
+		g_source_remove (indexer->private->flush_id);
+		indexer->private->flush_id = 0;
+	}
+}
+
+static void
+schedule_flush (TrackerIndexer *indexer,
+		gboolean	immediately)
+{
+	if (indexer->private->state != 0) {
+		return;
+	}
+
+	if (immediately) {
+		/* No need to wait for flush timeout */
+		stop_scheduled_flush (indexer);
+		flush_data (indexer);
+		return;
+	}
+
+	/* Don't schedule more than one at the same time */
+	if (indexer->private->flush_id != 0) {
+		return;
+	}
+
+	indexer->private->flush_id = g_timeout_add_seconds (FLUSH_FREQUENCY,
+							    (GSourceFunc) flush_data,
+							    indexer);
+}
+
+#ifdef HAVE_HAL
+
+static void
+set_up_throttle (TrackerIndexer *indexer)
+{
+	gint throttle;
+
+	/* If on a laptop battery and the throttling is default (i.e.
+	 * 0), then set the throttle to be higher so we don't kill
+	 * the laptop battery.
+	 */
+	throttle = tracker_config_get_throttle (indexer->private->config);
+
+	if (tracker_hal_get_battery_in_use (indexer->private->hal)) {
+		g_message ("We are running on battery");
+
+		if (throttle == THROTTLE_DEFAULT) {
+			tracker_config_set_throttle (indexer->private->config,
+						     THROTTLE_DEFAULT_ON_BATTERY);
+			g_message ("Setting throttle from %d to %d",
+				   throttle,
+				   THROTTLE_DEFAULT_ON_BATTERY);
+		} else {
+			g_message ("Not setting throttle, it is currently set to %d",
+				   throttle);
+		}
+	} else {
+		g_message ("We are not running on battery");
+
+		if (throttle == THROTTLE_DEFAULT_ON_BATTERY) {
+			tracker_config_set_throttle (indexer->private->config,
+						     THROTTLE_DEFAULT);
+			g_message ("Setting throttle from %d to %d",
+				   throttle,
+				   THROTTLE_DEFAULT);
+		} else {
+			g_message ("Not setting throttle, it is currently set to %d",
+				   throttle);
+		}
+	}
+}
+
+static void
+notify_battery_in_use_cb (GObject *gobject,
+			  GParamSpec *arg1,
+			  gpointer user_data)
+{
+	set_up_throttle (TRACKER_INDEXER (user_data));
+}
+
+#endif /* HAVE_HAL */
+
+static void
+tracker_indexer_finalize (GObject *object)
+{
+	TrackerIndexerPrivate *priv;
+
+	priv = TRACKER_INDEXER_GET_PRIVATE (object);
+
+	/* Important! Make sure we flush if we are scheduled to do so,
+	 * and do that first.
+	 */
+	stop_scheduled_flush (TRACKER_INDEXER (object));
+
+	if (priv->pause_for_duration_id) {
+		g_source_remove (priv->pause_for_duration_id);
+	}
+
+	if (priv->idle_id) {
+		g_source_remove (priv->idle_id);
+	}
+
+	if (priv->disk_space_check_id) {
+		g_source_remove (priv->disk_space_check_id);
+	}
+
+	if (priv->signal_status_id) {
+		g_source_remove (priv->signal_status_id);
+	}
+
+	if (priv->timer) {
+		g_timer_destroy (priv->timer);
+	}
+
+#ifdef HAVE_HAL
+	g_signal_handlers_disconnect_by_func (priv->hal,
+					      notify_battery_in_use_cb,
+					      TRACKER_INDEXER (object));
+
+	g_object_unref (priv->hal);
+#endif /* HAVE_HAL */
+
+	g_object_unref (priv->language);
+	g_object_unref (priv->config);
+
+	g_object_unref (priv->file_index);
+	g_object_unref (priv->email_index);
+
+	g_free (priv->db_dir);
+
+	g_hash_table_unref (priv->indexer_modules);
+	g_list_free (priv->module_names);
+
+	g_queue_foreach (priv->modules_queue, (GFunc) g_free, NULL);
+	g_queue_free (priv->modules_queue);
+
+	g_hash_table_unref (priv->mtime_cache);
+
+	g_queue_foreach (priv->dir_queue, (GFunc) path_info_free, NULL);
+	g_queue_free (priv->dir_queue);
+
+	g_queue_foreach (priv->file_queue, (GFunc) path_info_free, NULL);
+	g_queue_free (priv->file_queue);
+
+	G_OBJECT_CLASS (tracker_indexer_parent_class)->finalize (object);
+}
+
+static void
+tracker_indexer_get_property (GObject	 *object,
+			      guint	  prop_id,
+			      GValue	 *value,
+			      GParamSpec *pspec)
+{
+	TrackerIndexerPrivate *priv;
+
+	priv = TRACKER_INDEXER_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_RUNNING:
+		g_value_set_boolean (value,
+				     tracker_indexer_get_running (TRACKER_INDEXER (object)));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+	}
+}
+
+static void
+tracker_indexer_class_init (TrackerIndexerClass *class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+	object_class->finalize = tracker_indexer_finalize;
+	object_class->get_property = tracker_indexer_get_property;
+
+	signals[STATUS] =
+		g_signal_new ("status",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (TrackerIndexerClass, status),
+			      NULL, NULL,
+			      tracker_marshal_VOID__DOUBLE_STRING_UINT_UINT,
+			      G_TYPE_NONE,
+			      4,
+			      G_TYPE_DOUBLE,
+			      G_TYPE_STRING,
+			      G_TYPE_UINT,
+			      G_TYPE_UINT);
+	signals[STARTED] =
+		g_signal_new ("started",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (TrackerIndexerClass, started),
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__VOID,
+			      G_TYPE_NONE,
+			      0);
+	signals[PAUSED] =
+		g_signal_new ("paused",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (TrackerIndexerClass, paused),
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__VOID,
+			      G_TYPE_NONE,
+			      0);
+	signals[CONTINUED] =
+		g_signal_new ("continued",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (TrackerIndexerClass, continued),
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__VOID,
+			      G_TYPE_NONE,
+			      0);
+	signals[FINISHED] =
+		g_signal_new ("finished",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (TrackerIndexerClass, finished),
+			      NULL, NULL,
+			      tracker_marshal_VOID__DOUBLE_UINT_BOOL,
+			      G_TYPE_NONE,
+			      3,
+			      G_TYPE_DOUBLE,
+			      G_TYPE_UINT,
+			      G_TYPE_BOOLEAN);
+	signals[MODULE_STARTED] =
+		g_signal_new ("module-started",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (TrackerIndexerClass, module_started),
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__STRING,
+			      G_TYPE_NONE,
+			      1,
+			      G_TYPE_STRING);
+	signals[MODULE_FINISHED] =
+		g_signal_new ("module-finished",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (TrackerIndexerClass, module_finished),
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__STRING,
+			      G_TYPE_NONE,
+			      1,
+			      G_TYPE_STRING);
+
+	g_object_class_install_property (object_class,
+					 PROP_RUNNING,
+					 g_param_spec_boolean ("running",
+							       "Running",
+							       "Whether the indexer is running",
+							       TRUE,
+							       G_PARAM_READABLE));
+
+	g_type_class_add_private (object_class, sizeof (TrackerIndexerPrivate));
+}
+
+static void
+close_module (GModule *module)
+{
+	tracker_indexer_module_shutdown (module);
+	g_module_close (module);
+}
+
+static void
+check_started (TrackerIndexer *indexer)
+{
+	TrackerIndexerState state;
+
+	state = indexer->private->state;
+
+	if (!(state & TRACKER_INDEXER_STATE_STOPPED)) {
+		return;
+	}
+
+	state_unset_flags (indexer, TRACKER_INDEXER_STATE_STOPPED);
+
+	g_timer_destroy (indexer->private->timer);
+	indexer->private->timer = g_timer_new ();
+
+	/* Open indexes */
+	tracker_db_index_open (indexer->private->file_index);
+	tracker_db_index_open (indexer->private->email_index);
+
+	g_signal_emit (indexer, signals[STARTED], 0);
+}
+
+static void
+check_stopped (TrackerIndexer *indexer,
+	       gboolean        interrupted)
+{
+	gchar	*str;
+	gdouble  seconds_elapsed;
+
+	/* No more modules to query, we're done */
+	g_timer_stop (indexer->private->timer);
+	seconds_elapsed = g_timer_elapsed (indexer->private->timer, NULL);
+
+	/* Flush remaining items */
+	schedule_flush (indexer, TRUE);
+
+	/* Close indexes */
+	tracker_db_index_close (indexer->private->file_index);
+	tracker_db_index_close (indexer->private->email_index);
+
+	state_set_flags (indexer, TRACKER_INDEXER_STATE_STOPPED);
+
+	/* Clean up temporary data */
+	g_hash_table_remove_all (indexer->private->mtime_cache);
+
+	/* Print out how long it took us */
+	str = tracker_seconds_to_string (seconds_elapsed, FALSE);
+
+	g_message ("Indexer finished in %s, %d files indexed in total",
+		   str,
+		   indexer->private->files_indexed);
+	g_free (str);
+
+	/* Finally signal done */
+	g_signal_emit (indexer, signals[FINISHED], 0,
+		       seconds_elapsed,
+		       indexer->private->files_indexed,
+		       interrupted);
+}
+
+static gboolean
+check_is_disk_space_low (TrackerIndexer *indexer)
+{
+	const gchar *path;
+	struct statvfs st;
+	gint limit;
+
+	limit = tracker_config_get_low_disk_space_limit (indexer->private->config);
+	path = indexer->private->db_dir;
+
+	if (limit < 1) {
+		return FALSE;
+	}
+
+	if (statvfs (path, &st) == -1) {
+		g_warning ("Could not statvfs '%s'", path);
+		return FALSE;
+	}
+
+	if (((long long) st.f_bavail * 100 / st.f_blocks) <= limit) {
+		g_warning ("Disk space is low");
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+static gboolean
+check_disk_space_cb (TrackerIndexer *indexer)
+{
+	gboolean disk_space_low;
+
+	disk_space_low = check_is_disk_space_low (indexer);
+
+	if (disk_space_low) {
+		state_set_flags (indexer, TRACKER_INDEXER_STATE_DISK_FULL);
+
+		/* The function above stops the low disk check, restart it */
+		check_disk_space_start (indexer);
+	} else {
+		state_unset_flags (indexer, TRACKER_INDEXER_STATE_DISK_FULL);
+	}
+
+	return TRUE;
+}
+
+static void
+check_disk_space_start (TrackerIndexer *indexer)
+{
+	TrackerIndexerPrivate *priv;
+	gint low_disk_space_limit;
+
+	priv = indexer->private;
+
+	if (priv->disk_space_check_id != 0) {
+		return;
+	}
+
+	low_disk_space_limit = tracker_config_get_low_disk_space_limit (priv->config);
+
+	if (low_disk_space_limit != -1) {
+		priv->disk_space_check_id = g_timeout_add_seconds (LOW_DISK_CHECK_FREQUENCY,
+								   (GSourceFunc) check_disk_space_cb,
+								   indexer);
+	}
+}
+
+static void
+check_disk_space_stop (TrackerIndexer *indexer)
+{
+	TrackerIndexerPrivate *priv;
+
+	priv = indexer->private;
+
+	if (priv->disk_space_check_id != 0) {
+		g_source_remove (priv->disk_space_check_id);
+		priv->disk_space_check_id = 0;
+	}
+}
+
+static gboolean
+signal_status_cb (TrackerIndexer *indexer)
+{
+	signal_status (indexer, "status update");
+	return TRUE;
+}
+
+static void
+signal_status_timeout_start (TrackerIndexer *indexer)
+{
+	TrackerIndexerPrivate *priv;
+
+	priv = indexer->private;
+
+	if (priv->signal_status_id == 0) {
+		priv->signal_status_id = g_timeout_add_seconds (SIGNAL_STATUS_FREQUENCY,
+								(GSourceFunc) signal_status_cb,
+								indexer);
+	}
+}
+
+static void
+signal_status_timeout_stop (TrackerIndexer *indexer)
+{
+	TrackerIndexerPrivate *priv;
+
+	priv = indexer->private;
+
+	if (priv->signal_status_id != 0) {
+		g_source_remove (priv->signal_status_id);
+		priv->signal_status_id = 0;
+	}
+}
+
+static void
+tracker_indexer_init (TrackerIndexer *indexer)
+{
+	TrackerIndexerPrivate *priv;
+	TrackerDBIndex *index;
+	GList *l;
+
+	priv = indexer->private = TRACKER_INDEXER_GET_PRIVATE (indexer);
+
+	priv->items_processed = 0;
+	priv->in_transaction = FALSE;
+	priv->dir_queue = g_queue_new ();
+	priv->file_queue = g_queue_new ();
+	priv->mtime_cache = g_hash_table_new_full (g_str_hash,
+						   g_str_equal,
+						   g_free,
+						   NULL);
+	priv->modules_queue = g_queue_new ();
+	priv->config = tracker_config_new ();
+
+#ifdef HAVE_HAL
+	priv->hal = tracker_hal_new ();
+
+	g_signal_connect (priv->hal, "notify::battery-in-use",
+			  G_CALLBACK (notify_battery_in_use_cb),
+			  indexer);
+
+	set_up_throttle (indexer);
+#endif /* HAVE_HAL */
+
+	priv->language = tracker_language_new (priv->config);
+
+	priv->db_dir = g_build_filename (g_get_user_cache_dir (),
+					 "tracker",
+					 NULL);
+
+	priv->module_names = tracker_module_config_get_modules ();
+	for (l = priv->module_names; l; l = l->next) {
+		g_quark_from_string (l->data);
+	}
+
+	priv->indexer_modules = g_hash_table_new_full (g_str_hash,
+						       g_str_equal,
+						       NULL,
+						       (GDestroyNotify) close_module);
+
+	for (l = priv->module_names; l; l = l->next) {
+		GModule *module;
+
+		if (!tracker_module_config_get_enabled (l->data)) {
+			continue;
+		}
+
+		module = tracker_indexer_module_load (l->data);
+
+		if (module) {
+			tracker_indexer_module_init (module);
+
+			g_hash_table_insert (priv->indexer_modules,
+					     l->data, module);
+		}
+	}
+
+	/* Set up indexer */
+	index = tracker_db_index_manager_get_index (TRACKER_DB_INDEX_FILE);
+	priv->file_index = g_object_ref (index);
+
+	index = tracker_db_index_manager_get_index (TRACKER_DB_INDEX_EMAIL);
+	priv->email_index = g_object_ref (index);
+
+	/* Set up databases, these pointers are mostly used to
+	 * start/stop transactions, since TrackerDBManager treats
+	 * interfaces as singletons, it's safe to just ask it
+	 * again for an interface.
+	 */
+	priv->cache = tracker_db_manager_get_db_interface (TRACKER_DB_CACHE);
+	priv->common = tracker_db_manager_get_db_interface (TRACKER_DB_COMMON);
+	priv->file_metadata = tracker_db_manager_get_db_interface (TRACKER_DB_FILE_METADATA);
+	priv->file_contents = tracker_db_manager_get_db_interface (TRACKER_DB_FILE_CONTENTS);
+	priv->email_metadata = tracker_db_manager_get_db_interface (TRACKER_DB_EMAIL_METADATA);
+	priv->email_contents = tracker_db_manager_get_db_interface (TRACKER_DB_EMAIL_CONTENTS);
+
+	/* Set up timer to know how long the process will take and took */
+	priv->timer = g_timer_new ();
+
+	/* Set up idle handler to process files/directories */
+	state_check (indexer);
+}
+
+static void
+add_file (TrackerIndexer *indexer,
+	  PathInfo *info)
+{
+	g_queue_push_tail (indexer->private->file_queue, info);
+
+	/* Make sure we are still running */
+	check_started (indexer);
+}
+
+static void
+add_directory (TrackerIndexer *indexer,
+	       PathInfo *info)
+{
+	g_queue_push_tail (indexer->private->dir_queue, info);
+
+	/* Make sure we are still running */
+	check_started (indexer);
+}
+
+static void
+index_metadata_item (TrackerField	 *field,
+		     const gchar	 *value,
+		     MetadataForeachData *data)
+{
+	TrackerDBIndex *index;
+	gchar *parsed_value;
+	gchar **arr;
+	gint service_id;
+	gint i;
+	gint score;
+
+	parsed_value = tracker_parser_text_to_string (value,
+						      data->language,
+						      tracker_config_get_max_word_length (data->config),
+						      tracker_config_get_min_word_length (data->config),
+						      tracker_field_get_filtered (field),
+						      tracker_field_get_filtered (field),
+						      tracker_field_get_delimited (field));
+
+	if (!parsed_value) {
+		return;
+	}
+
+	if (data->add) {
+		score = tracker_field_get_weight (field);
+	} else {
+		score = -1 * tracker_field_get_weight (field);
+	}
+
+	arr = g_strsplit (parsed_value, " ", -1);
+	service_id = tracker_service_get_id (data->service);
+	index = tracker_db_index_manager_get_index_by_service_id (service_id);
+
+	for (i = 0; arr[i]; i++) {
+		tracker_db_index_add_word (index,
+					   arr[i],
+					   data->id,
+					   tracker_service_get_id (data->service),
+					   score);
+	}
+
+	if (data->add) {
+		tracker_db_set_metadata (data->service, data->id, field, (gchar *) value, parsed_value);
+	} else {
+		tracker_db_delete_metadata (data->service, data->id, field, (gchar *)value);
+	}
+
+	g_strfreev (arr);
+	g_free (parsed_value);
+}
+
+static void
+index_metadata_foreach (TrackerField *field,
+			gpointer      value,
+			gpointer      user_data)
+{
+	MetadataForeachData *data;
+	gint throttle;
+
+	if (!value) {
+		return;
+	}
+
+	data = (MetadataForeachData *) user_data;
+
+	/* Throttle indexer, value 9 is from older code, why 9? */
+	throttle = tracker_config_get_throttle (data->config);
+	if (throttle > 9) {
+		tracker_throttle (data->config, throttle * 100);
+	}
+
+	if (!tracker_field_get_multiple_values (field)) {
+		index_metadata_item (field, value, data);
+	} else {
+		GList *list;
+
+		list = value;
+
+		while (list) {
+			index_metadata_item (field, list->data, data);
+			list = list->next;
+		}
+	}
+}
+
+static void
+index_metadata (TrackerIndexer	*indexer,
+		guint32		 id,
+		TrackerService	*service,
+		TrackerMetadata *metadata)
+{
+	MetadataForeachData data;
+
+	data.language = indexer->private->language;
+	data.config = indexer->private->config;
+	data.service = service;
+	data.id = id;
+	data.add = TRUE;
+
+	tracker_metadata_foreach (metadata, index_metadata_foreach, &data);
+
+	schedule_flush (indexer, FALSE);
+}
+
+static void
+unindex_metadata (TrackerIndexer  *indexer,
+		  guint32	   id,
+		  TrackerService  *service,
+		  TrackerMetadata *metadata)
+{
+	MetadataForeachData data;
+
+	data.language = indexer->private->language;
+	data.config = indexer->private->config;
+	data.service = service;
+	data.id = id;
+	data.add = FALSE;
+
+	tracker_metadata_foreach (metadata, index_metadata_foreach, &data);
+
+	schedule_flush (indexer, FALSE);
+}
+
+
+static void
+send_text_to_index (TrackerIndexer *indexer,
+		    gint	    service_id,
+		    gint	    service_type,
+		    const gchar    *text,
+		    gboolean	    full_parsing,
+		    gint	    weight_factor)
+{
+	TrackerDBIndex *index;
+	GHashTable     *parsed;
+	GHashTableIter	iter;
+	gpointer	key, value;
+
+	if (!text) {
+		return;
+	}
+
+	if (full_parsing) {
+		parsed = tracker_parser_text (NULL,
+					      text,
+					      weight_factor,
+					      indexer->private->language,
+					      tracker_config_get_max_words_to_index (indexer->private->config),
+					      tracker_config_get_max_word_length (indexer->private->config),
+					      tracker_config_get_min_word_length (indexer->private->config),
+					      tracker_config_get_enable_stemmer (indexer->private->config),
+					      FALSE);
+	} else {
+		/* We dont know the exact property weight.
+		   Big value works.
+		 */
+		parsed = tracker_parser_text_fast (NULL,
+						   text,
+						   weight_factor);
+	}
+
+	g_hash_table_iter_init (&iter, parsed);
+
+	index = tracker_db_index_manager_get_index_by_service_id (service_type);
+
+	while (g_hash_table_iter_next (&iter, &key, &value)) {
+		tracker_db_index_add_word (index,
+					   key,
+					   service_id,
+					   service_type,
+					   GPOINTER_TO_INT (value));
+	}
+
+	g_hash_table_unref (parsed);
+}
+
+static void
+index_text_with_parsing (TrackerIndexer *indexer,
+			 gint		 service_id,
+			 gint		 service_type_id,
+			 const gchar	*content,
+			 gint		 weight_factor)
+{
+	send_text_to_index (indexer,
+			    service_id,
+			    service_type_id,
+			    content,
+			    TRUE,
+			    weight_factor);
+}
+
+static void
+unindex_text_with_parsing (TrackerIndexer *indexer,
+			   gint		   service_id,
+			   gint		   service_type_id,
+			   const gchar	  *content,
+			   gint		   weight_factor)
+{
+	send_text_to_index (indexer,
+			    service_id,
+			    service_type_id,
+			    content,
+			    TRUE,
+			    weight_factor * -1);
+}
+
+static void
+index_text_no_parsing (TrackerIndexer *indexer,
+		       gint	       service_id,
+		       gint	       service_type_id,
+		       const gchar    *content,
+		       gchar	       weight_factor)
+{
+	send_text_to_index (indexer,
+			    service_id,
+			    service_type_id,
+			    content,
+			    FALSE,
+			    weight_factor);
+}
+
+static void
+unindex_text_no_parsing (TrackerIndexer *indexer,
+			 gint		 service_id,
+			 gint		 service_type_id,
+			 const gchar	*content,
+			 gint		 weight_factor)
+{
+	send_text_to_index (indexer,
+			    service_id,
+			    service_type_id,
+			    content,
+			    FALSE,
+			    weight_factor * -1);
+}
+
+static void
+update_word_foreach (gpointer key,
+		     gpointer value,
+		     gpointer user_data)
+{
+	TrackerDBIndex	       *index;
+	UpdateWordsForeachData *data;
+	gchar		       *word;
+	gint			score;
+
+	word = key;
+	score = GPOINTER_TO_INT (value);
+
+	data = user_data;
+
+	index = tracker_db_index_manager_get_index_by_service_id (data->service_type_id);
+
+	tracker_db_index_add_word (index,
+				   word,
+				   data->service_id,
+				   data->service_type_id,
+				   score);
+}
+
+static void
+update_words_no_parsing (TrackerIndexer *indexer,
+			 gint		 service_id,
+			 gint		 service_type_id,
+			 GHashTable	*words)
+{
+	UpdateWordsForeachData user_data;
+
+	user_data.service_id = service_id;
+	user_data.service_type_id = service_type_id;
+
+	g_hash_table_foreach (words, update_word_foreach, &user_data);
+}
+
+static void
+merge_word_table (gpointer key,
+		  gpointer value,
+		  gpointer user_data)
+{
+	GHashTable *new_table;
+	gpointer    k;
+	gpointer    v;
+	gchar	   *word;
+	gint	    new_score;
+
+	word = key;
+	new_score = GPOINTER_TO_INT (value);
+	new_table = user_data;
+
+	if (g_hash_table_lookup_extended (new_table, word, &k, &v)) {
+		gint old_score;
+		gint calculated_score;
+
+		old_score = GPOINTER_TO_INT (v);
+		calculated_score = old_score - new_score;
+
+		if (calculated_score != 0) {
+			g_hash_table_insert (new_table,
+					     g_strdup (word),
+					     GINT_TO_POINTER (calculated_score));
+		} else {
+			/* The word is the same in old and new text */
+			g_hash_table_remove (new_table, word);
+		}
+	} else {
+		g_hash_table_insert (new_table,
+				     g_strdup (word),
+				     GINT_TO_POINTER (0 - new_score));
+	}
+}
+
+static void
+item_update_content (TrackerIndexer *indexer,
+		     TrackerService *service,
+		     guint32	     id,
+		     const gchar    *old_text,
+		     const gchar    *new_text)
+{
+	GHashTable *old_words;
+	GHashTable *new_words;
+
+	if (!old_text && !new_text) {
+		return;
+	}
+
+	/* Service has/had full text */
+	old_words = tracker_parser_text (NULL,
+					 old_text,
+					 1,
+					 indexer->private->language,
+					 tracker_config_get_max_words_to_index (indexer->private->config),
+					 tracker_config_get_max_word_length (indexer->private->config),
+					 tracker_config_get_min_word_length (indexer->private->config),
+					 tracker_config_get_enable_stemmer (indexer->private->config),
+					 FALSE);
+
+	new_words = tracker_parser_text (NULL,
+					 new_text,
+					 1,
+					 indexer->private->language,
+					 tracker_config_get_max_words_to_index (indexer->private->config),
+					 tracker_config_get_max_word_length (indexer->private->config),
+					 tracker_config_get_min_word_length (indexer->private->config),
+					 tracker_config_get_enable_stemmer (indexer->private->config),
+					 FALSE);
+
+	/* Merge the score of the words from one and
+	 * other file new_table contains the words
+	 * with the updated scores
+	 */
+	g_hash_table_foreach (old_words, merge_word_table, new_words);
+
+	update_words_no_parsing (indexer,
+				 id,
+				 tracker_service_get_id (service),
+				 new_words);
+
+	/* Remove old text and set new one in the db */
+	if (old_text) {
+		tracker_db_delete_text (service, id);
+	}
+
+	if (new_text) {
+		tracker_db_set_text (service, id, new_text);
+	}
+
+	g_hash_table_unref (old_words);
+	g_hash_table_unref (new_words);
+}
+
+static void
+item_create_or_update (TrackerIndexer  *indexer,
+		       PathInfo        *info,
+		       const gchar     *dirname,
+		       const gchar     *basename,
+		       TrackerMetadata *metadata)
+{
+	TrackerService *service;
+	gchar *service_type;
+	gchar *text;
+	guint32 id;
+
+	service_type = tracker_indexer_module_file_get_service_type (info->module,
+								     info->file);
+
+	if (!service_type) {
+		return;
+	}
+
+	service = tracker_ontology_get_service_by_name (service_type);
+	g_free (service_type);
+
+	if (!service) {
+		return;
+	}
+
+	if (tracker_db_check_service (service, dirname, basename, &id, NULL)) {
+		TrackerMetadata *old_metadata;
+		gchar *old_text;
+		gchar *new_text;
+
+		/* Update case */
+		g_debug ("Updating item '%s/%s'", dirname, basename);
+
+		/*
+		 * Using DB directly: get old (embedded) metadata,
+		 * unindex, index the new metadata
+		 */
+		old_metadata = tracker_db_get_all_metadata (service, id, TRUE);
+		unindex_metadata (indexer, id, service, old_metadata);
+		index_metadata (indexer, id, service, metadata);
+
+		/* Take the old text -> the new one, calculate
+		 * difference and add the words.
+		 */
+		old_text = tracker_db_get_text (service, id);
+		new_text = tracker_indexer_module_file_get_text (info->module, info->file);
+
+		item_update_content (indexer, service, id, old_text, new_text);
+		g_free (old_text);
+		g_free (new_text);
+		tracker_metadata_free (old_metadata);
+
+		return;
+	}
+
+	g_debug ("Creating item '%s/%s'", dirname, basename);
+
+	/* Service wasn't previously indexed */
+	id = tracker_db_get_new_service_id (indexer->private->common);
+
+	tracker_db_create_service (service,
+				   id,
+				   dirname,
+				   basename,
+				   metadata);
+
+	tracker_db_create_event (indexer->private->cache, id, "Create");
+	tracker_db_increment_stats (indexer->private->common, service);
+
+	index_metadata (indexer, id, service, metadata);
+
+	text = tracker_indexer_module_file_get_text (info->module, info->file);
+
+	if (text) {
+		/* Save in the index */
+		index_text_with_parsing (indexer,
+					 id,
+					 tracker_service_get_id (service),
+					 text,
+					 1);
+
+		/* Save in the DB */
+		tracker_db_set_text (service, id, text);
+		g_free (text);
+	}
+}
+
+static void
+item_move (TrackerIndexer  *indexer,
+	   PathInfo	   *info,
+	   const gchar	   *dirname,
+	   const gchar	   *basename)
+{
+	TrackerService *service;
+	TrackerMetadata *metadata;
+	gchar *service_type;
+	guint32 id;
+
+	service_type = tracker_indexer_module_file_get_service_type (info->module,
+								     info->other_file);
+
+	if (!service_type) {
+		return;
+	}
+
+	service = tracker_ontology_get_service_by_name (service_type);
+	g_free (service_type);
+
+	if (!service) {
+		return;
+	}
+
+	g_debug ("Moving file from '%s' to '%s'",
+		 info->file->path,
+		 info->other_file->path);
+
+	/* Get 'source' ID */
+	if (!tracker_db_check_service (service,
+				       dirname,
+				       basename,
+				       &id,
+				       NULL)) {
+		g_message ("Source file '%s' not found in database to move",
+			   info->file->path);
+		return;
+	}
+
+	tracker_db_move_service (service,
+				 info->file->path,
+				 info->other_file->path);
+
+	/*
+	 * Using DB directly: get old (embedded) metadata, unindex,
+	 * index the new metadata
+	 */
+	metadata = tracker_db_get_all_metadata (service, id, TRUE);
+	unindex_metadata (indexer, id, service, metadata);
+	index_metadata (indexer, id, service, metadata);
+}
+
+static void
+item_delete (TrackerIndexer *indexer,
+	     PathInfo	    *info,
+	     const gchar    *dirname,
+	     const gchar    *basename)
+{
+	TrackerService *service;
+	gchar *content, *metadata;
+	const gchar *service_type;
+	guint service_id, service_type_id;
+
+	service_type = tracker_module_config_get_index_service (info->module_name);
+
+	g_debug ("Deleting item: '%s/%s'", dirname, basename);
+
+	if (!service_type || !service_type[0]) {
+		gchar *name;
+
+		/* The file is not anymore in the filesystem. Obtain
+		 * the service type from the DB.
+		 */
+		service_type_id = tracker_db_get_service_type (dirname, basename);
+
+		if (service_type_id == 0) {
+			/* File didn't exist, nothing to delete */
+			return;
+		}
+
+		name = tracker_ontology_get_service_by_id (service_type_id);
+		service = tracker_ontology_get_service_by_name (name);
+		g_free (name);
+	} else {
+		service = tracker_ontology_get_service_by_name (service_type);
+		service_type_id = tracker_service_get_id (service);
+	}
+
+	tracker_db_check_service (service, dirname, basename, &service_id, NULL);
+
+	if (service_id < 1) {
+		g_debug ("  File does not exist anyway "
+			 "(dirname:'%s', basename:'%s')",
+			 dirname, basename);
+		return;
+	}
+
+	/* Get content, unindex the words and delete the contents */
+	content = tracker_db_get_text (service, service_id);
+	if (content) {
+		unindex_text_with_parsing (indexer,
+					   service_id,
+					   service_type_id,
+					   content,
+					   1);
+		g_free (content);
+		tracker_db_delete_text (service, service_id);
+	}
+
+	/* Get metadata from DB to remove it from the index */
+	metadata = tracker_db_get_parsed_metadata (service,
+						   service_id);
+	unindex_text_no_parsing (indexer,
+				 service_id,
+				 service_type_id,
+				 metadata,
+				 1000);
+	g_free (metadata);
+
+	/* The weight depends on metadata, but a number high enough
+	 * force deletion.
+	 */
+	metadata = tracker_db_get_unparsed_metadata (service,
+						     service_id);
+	unindex_text_with_parsing (indexer,
+				   service_id,
+				   service_type_id,
+				   metadata,
+				   1000);
+	g_free (metadata);
+
+	/* Delete service */
+	tracker_db_delete_service (service, service_id);
+	tracker_db_delete_all_metadata (service, service_id);
+
+	tracker_db_decrement_stats (indexer->private->common, service);
+}
+
+static gboolean
+handle_metadata_add (TrackerIndexer *indexer,
+		     const gchar    *service_type,
+		     const gchar    *uri,
+		     const gchar    *property,
+		     GStrv	     values,
+		     GError	   **error)
+{
+	TrackerService *service;
+	TrackerField *field;
+	guint service_id, i;
+	gchar *joined, *dirname = NULL, *basename = NULL;
+	gint len;
+
+	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+	check_started (indexer);
+
+	service = tracker_ontology_get_service_by_name (service_type);
+	if (!service) {
+		g_set_error (error,
+			     g_quark_from_string (TRACKER_INDEXER_ERROR),
+			     TRACKER_INDEXER_ERROR_CODE,
+			     "Unknown service type: '%s'",
+			     service_type);
+		return FALSE;
+	}
+
+	field = tracker_ontology_get_field_by_name (property);
+	if (!field) {
+		g_set_error (error,
+			     g_quark_from_string (TRACKER_INDEXER_ERROR),
+			     TRACKER_INDEXER_ERROR_CODE,
+			     "Unknown field type: '%s'",
+			     property);
+		return FALSE;
+	}
+
+	if (tracker_field_get_embedded (field)) {
+		g_set_error (error,
+			     g_quark_from_string (TRACKER_INDEXER_ERROR),
+			     TRACKER_INDEXER_ERROR_CODE,
+			     "Field type: '%s' is embedded and not writable",
+			     property);
+		return FALSE;
+	}
+
+	len = g_strv_length (values);
+
+	if (!tracker_field_get_multiple_values (field) && len > 1) {
+		g_set_error (error,
+			     g_quark_from_string (TRACKER_INDEXER_ERROR),
+			     TRACKER_INDEXER_ERROR_CODE,
+			     "Field type: '%s' doesnt support multiple values (trying to set %d)",
+			     property,
+			     len);
+		return FALSE;
+	}
+
+	tracker_file_get_path_and_name (uri, &dirname, &basename);
+
+	tracker_db_check_service (service,
+				  dirname,
+				  basename,
+				  &service_id,
+				  NULL);
+	g_free (dirname);
+	g_free (basename);
+
+	if (service_id < 1) {
+		g_set_error (error,
+			     g_quark_from_string (TRACKER_INDEXER_ERROR),
+			     TRACKER_INDEXER_ERROR_CODE,
+			     "File '%s' doesnt exist in the DB", uri);
+		return FALSE;
+	}
+
+	if (!tracker_field_get_multiple_values (field)) {
+		/* Remove old value from DB and index */
+		gchar **old_contents;
+
+		old_contents = tracker_db_get_property_values (service,
+							       service_id,
+							       field);
+		len = g_strv_length (old_contents);
+
+		if (old_contents && len > 1) {
+			g_critical ("Seems to be multiple values in field:'%s' that doesn allow that",
+				    tracker_field_get_name (field));
+		} else if (old_contents && len == 1) {
+			if (tracker_field_get_filtered (field)) {
+				unindex_text_with_parsing (indexer,
+							   service_id,
+							   tracker_service_get_id (service),
+							   old_contents[0],
+							   tracker_field_get_weight (field));
+			} else {
+				unindex_text_no_parsing (indexer,
+							 service_id,
+							 tracker_service_get_id (service),
+							 old_contents[0],
+							 tracker_field_get_weight (field));
+			}
+			tracker_db_delete_metadata (service, service_id, field, old_contents[0]);
+			g_strfreev (old_contents);
+		}
+	}
+
+	for (i = 0; values[i] != NULL; i++) {
+		g_debug ("Setting metadata: service_type '%s' id '%d' field '%s' value '%s'",
+			 tracker_service_get_name (service),
+			 service_id,
+			 tracker_field_get_name (field),
+			 values[i]);
+
+		tracker_db_set_metadata (service,
+					 service_id,
+					 field,
+					 values[i],
+					 NULL);
+	}
+
+	joined = g_strjoinv (" ", values);
+	if (tracker_field_get_filtered (field)) {
+		index_text_no_parsing (indexer,
+				       service_id,
+				       tracker_service_get_id (service),
+				       joined,
+				       tracker_field_get_weight (field));
+	} else {
+		index_text_with_parsing (indexer,
+					 service_id,
+					 tracker_service_get_id (service),
+					 joined,
+					 tracker_field_get_weight (field));
+	}
+	g_free (joined);
+
+	return TRUE;
+}
+
+static gboolean
+handle_metadata_remove (TrackerIndexer *indexer,
+			const gchar    *service_type,
+			const gchar    *uri,
+			const gchar    *property,
+			GStrv		values,
+			GError	      **error)
+{
+	TrackerService *service;
+	TrackerField *field;
+	guint service_id, i;
+	gchar *joined = NULL, *dirname = NULL, *basename = NULL;
+
+	check_started (indexer);
+
+	service = tracker_ontology_get_service_by_name (service_type);
+	if (!service) {
+		g_set_error (error,
+			     g_quark_from_string (TRACKER_INDEXER_ERROR),
+			     TRACKER_INDEXER_ERROR_CODE,
+			     "Unknown service type: '%s'",
+			     service_type);
+		return FALSE;
+	}
+
+	field = tracker_ontology_get_field_by_name (property);
+	if (!field) {
+		g_set_error (error,
+			     g_quark_from_string (TRACKER_INDEXER_ERROR),
+			     TRACKER_INDEXER_ERROR_CODE,
+			     "Unknown field type: '%s'",
+			     property);
+		return FALSE;
+	}
+
+	if (tracker_field_get_embedded (field)) {
+		g_set_error (error,
+			     g_quark_from_string (TRACKER_INDEXER_ERROR),
+			     TRACKER_INDEXER_ERROR_CODE,
+			     "Field type: '%s' is embedded and cannot be deleted",
+			     property);
+		return FALSE;
+	}
+
+	tracker_file_get_path_and_name (uri, &dirname, &basename);
+
+	tracker_db_check_service (service, dirname, basename, &service_id, NULL);
+
+	g_free (dirname);
+	g_free (basename);
+
+	if (service_id < 1) {
+		g_set_error (error,
+			     g_quark_from_string (TRACKER_INDEXER_ERROR),
+			     TRACKER_INDEXER_ERROR_CODE,
+			     "File '%s' doesnt exist in the DB", uri);
+		return FALSE;
+	}
+
+	/* If we receive concrete values, we delete those rows in the
+	 * db. Otherwise, retrieve the old values of the property and
+	 * remove all their instances for the file
+	 */
+	if (g_strv_length (values) > 0) {
+		for (i = 0; values[i] != NULL; i++) {
+			tracker_db_delete_metadata (service,
+						    service_id,
+						    field,
+						    values[i]);
+		}
+		joined = g_strjoinv (" ", values);
+	} else {
+		gchar **old_contents;
+
+		old_contents = tracker_db_get_property_values (service,
+							       service_id,
+							       field);
+		if (old_contents) {
+			tracker_db_delete_metadata (service,
+						    service_id,
+						    field,
+						    NULL);
+
+			joined = g_strjoinv (" ", old_contents);
+			g_strfreev (old_contents);
+		}
+	}
+
+	/* Now joined contains the words to unindex */
+	if (tracker_field_get_filtered (field)) {
+		unindex_text_with_parsing (indexer,
+					   service_id,
+					   tracker_service_get_id (service),
+					   joined,
+					   tracker_field_get_weight (field));
+	} else {
+		unindex_text_no_parsing (indexer,
+					 service_id,
+					 tracker_service_get_id (service),
+					 joined,
+					 tracker_field_get_weight (field));
+	}
+
+	g_free (joined);
+
+	return TRUE;
+}
+
+static gboolean
+should_index_file (TrackerIndexer *indexer,
+		   PathInfo	  *info,
+		   const gchar	  *dirname,
+		   const gchar	  *basename)
+{
+	TrackerService *service;
+	gchar *service_type;
+	const gchar *str;
+	gboolean is_dir;
+	gboolean should_be_cached;
+	struct stat st;
+	time_t mtime;
+
+	service_type = tracker_indexer_module_file_get_service_type (info->module, info->file);
+
+	if (!service_type) {
+		return FALSE;
+	}
+
+	service = tracker_ontology_get_service_by_name (service_type);
+	g_free (service_type);
+
+	if (!service) {
+		return TRUE;
+	}
+
+	/* Check the file/directory exists. If it doesn't we
+	 * definitely want to index it.
+	 */
+	if (!tracker_db_check_service (service,
+				       dirname,
+				       basename,
+				       NULL,
+				       &mtime)) {
+		return TRUE;
+	}
+
+	/* So, if we are here, then the file or directory DID exist
+	 * in the database already. Now we need to check if the
+	 * parent directory mtime matches the mtime we have for it in
+	 * the database. If it does, then we can ignore any files
+	 * immediately in this parent directory.
+	 */
+	if (g_lstat (info->file->path, &st) == -1) {
+		return TRUE;
+	}
+
+	/* It is most efficient to keep a hash table of mtime values
+	 * which are out of sync with the database. Since there are
+	 * more likely to be less of those than actual directories.
+	 * So we keep a list of them and if the dirname is in the
+	 * hash table we then say it needs reindexing. If not, we
+	 * assume that it must be up to date.
+	 *
+	 * This entry in the hash table is removed after each index is
+	 * complete.
+	 *
+	 * Note: info->file->path = '/tmp/foo/bar'
+	 *	 dirname	  = '/tmp/foo'
+	 *	 basename	  = 'bar'
+	 *
+	 * Example A. PathInfo is file.
+	 *   1) Lookup 'dirname', if exists then
+	 *     --> return TRUE
+	 *   2) Check 'dirname' mtime is newer, if not then
+	 *     --> return FALSE
+	 *   3) Add to hash table
+	 *     --> return TRUE
+	 *
+	 * Example B. PathInfo is directory.
+	 *   1) Lookup 'info->file->path', if exists then
+	 *     --> return TRUE
+	 *   2) Check 'info->file->path' mtime is newer, if not then
+	 *     --> return FALSE
+	 *   3) Add to hash table
+	 *     --> return TRUE
+	 */
+	is_dir = S_ISDIR (st.st_mode);
+	should_be_cached = TRUE;
+
+	/* Choose the path we evaluate based on if we have a directory
+	 * or not. All operations are done using the same string.
+	 */
+	if (is_dir) {
+		str = info->file->path;
+	} else {
+		str = dirname;
+	}
+
+	/* Step 1. */
+	if (g_hash_table_lookup (indexer->private->mtime_cache, str)) {
+		gboolean should_index;
+
+		if (!is_dir) {
+			/* Only index files in this directory which
+			 * have an old mtime.
+			 */
+			should_index = st.st_mtime > mtime;
+		} else {
+			/* We always index directories */
+			should_index = TRUE;
+		}
+
+		g_debug ("%s:'%s' exists in cache, %s",
+			 is_dir ? "Path" : "Parent path",
+			 str,
+			 should_index ? "should index" : "should not index");
+
+		return should_index;
+	}
+
+	/* Step 2. */
+	if (!is_dir) {
+		gchar *parent_dirname;
+		gchar *parent_basename;
+		gboolean exists;
+
+		/* FIXME: What if there is no parent? */
+		parent_dirname = g_path_get_dirname (dirname);
+		parent_basename = g_path_get_basename (dirname);
+
+		/* We don't have the mtime for the dirname yet, we do
+		 * if this is a info->file->path of course.
+		 */
+		exists = tracker_db_check_service (service,
+						   parent_dirname,
+						   parent_basename,
+						   NULL,
+						   &mtime);
+		if (!exists) {
+			g_message ("Expected path '%s/%s' to exist, not in database?",
+				   parent_dirname,
+				   parent_basename);
+
+			g_free (parent_basename);
+			g_free (parent_dirname);
+
+			return TRUE;
+		}
+
+		if (g_lstat (dirname, &st) == -1) {
+			g_message ("Expected path '%s' to exist, could not stat()",
+				   parent_dirname);
+
+			g_free (parent_basename);
+			g_free (parent_dirname);
+
+			return TRUE;
+		}
+
+		g_free (parent_basename);
+		g_free (parent_dirname);
+	}
+
+	if (st.st_mtime <= mtime) {
+		g_debug ("%s:'%s' has indifferent mtime and should not be indexed",
+			 is_dir ? "Path" : "Parent path",
+			 str);
+
+		return FALSE;
+	}
+
+	/* Step 3. */
+	g_debug ("%s:'%s' being added to cache and should be indexed",
+		 is_dir ? "Path" : "Parent path",
+		 str);
+
+	g_hash_table_replace (indexer->private->mtime_cache,
+			      g_strdup (str),
+			      GINT_TO_POINTER (1));
+
+	return TRUE;
+}
+static gboolean
+process_file (TrackerIndexer *indexer,
+	      PathInfo	     *info)
+{
+	TrackerMetadata *metadata;
+	gchar *dirname;
+	gchar *basename;
+
+	/* Note: If info->other_file is set, the PathInfo is for a
+	 * MOVE event not for normal file event.
+	 */
+
+	/* Set the current module */
+	indexer->private->current_module = g_quark_from_string (info->module_name);
+
+	if (!tracker_indexer_module_file_get_uri (info->module,
+						  info->file,
+						  &dirname,
+						  &basename)) {
+		return !tracker_indexer_module_file_iter_contents (info->module, info->file);
+	}
+
+	/*
+	 * FIRST:
+	 * ======
+	 * We don't check if we should index files for MOVE events.
+	 *
+	 * SECOND:
+	 * =======
+	 * Here we do a quick check to see if this is an email URI or
+	 * not. For emails we don't check if we should index them, we
+	 * always do since it is ONLY summary files we index and we
+	 * need to look at them to know if anything changed anyway.
+	 * The only improvement we could have here is an mtime check.
+	 * This is yet to do.
+	 *
+	 * The info->file->path is the REAL location. The dirname and
+	 * basename which are returned by the module are combined to
+	 * look like:
+	 *
+	 *   email://1192717939 16218 20 petunia//INBOX;uid=1
+	 *
+	 * We simply check the dirname[0] to make sure it isn't an
+	 * email based dirname.
+	 */
+	if (G_LIKELY (!info->other_file) && dirname[0] == G_DIR_SEPARATOR) {
+		if (!should_index_file (indexer, info, dirname, basename)) {
+			g_debug ("File is already up to date: '%s'", info->file->path);
+
+			g_free (dirname);
+			g_free (basename);
+
+			return TRUE;
+		}
+	}
+
+	/* Sleep to throttle back indexing */
+	tracker_throttle (indexer->private->config, 100);
+
+	/* For normal files create or delete the item with the
+	 * metadata. For move PathInfo we use the db function to move
+	 * a service and set the metadata.
+	 */
+	if (G_UNLIKELY (info->other_file)) {
+		item_move (indexer, info, dirname, basename);
+	} else {
+		metadata = tracker_indexer_module_file_get_metadata (info->module, info->file);
+
+		if (metadata) {
+			item_create_or_update (indexer, info, dirname, basename, metadata);
+			tracker_metadata_free (metadata);
+		} else {
+			item_delete (indexer, info, dirname, basename);
+		}
+	}
+
+	indexer->private->items_processed++;
+
+	g_free (dirname);
+	g_free (basename);
+
+	return !tracker_indexer_module_file_iter_contents (info->module, info->file);
+}
+
+static void
+process_directory (TrackerIndexer *indexer,
+		   PathInfo *info,
+		   gboolean recurse)
+{
+	const gchar *name;
+	GDir *dir;
+
+	g_debug ("Processing directory:'%s'", info->file->path);
+
+	dir = g_dir_open (info->file->path, 0, NULL);
+
+	if (!dir) {
+		return;
+	}
+
+	while ((name = g_dir_read_name (dir)) != NULL) {
+		PathInfo *new_info;
+		gchar *path;
+
+		if (name[0] == '.') {
+			continue;
+		}
+
+		path = g_build_filename (info->file->path, name, NULL);
+
+		new_info = path_info_new (info->module, info->module_name, path, NULL);
+		add_file (indexer, new_info);
+
+		if (recurse && g_file_test (path, G_FILE_TEST_IS_DIR)) {
+			new_info = path_info_new (info->module, info->module_name, path, NULL);
+			add_directory (indexer, new_info);
+		}
+
+		g_free (path);
+	}
+
+	g_dir_close (dir);
+}
+
+static void
+process_module_emit_signals (TrackerIndexer *indexer,
+			     const gchar *next_module_name)
+{
+	/* Signal the last module as finished */
+	g_signal_emit (indexer, signals[MODULE_FINISHED], 0,
+		       g_quark_to_string (indexer->private->current_module));
+
+	/* Set current module */
+	indexer->private->current_module = g_quark_from_string (next_module_name);
+
+	/* Signal the next module as started */
+	if (next_module_name) {
+		g_signal_emit (indexer, signals[MODULE_STARTED], 0,
+			       next_module_name);
+	}
+}
+
+static void
+process_module (TrackerIndexer *indexer,
+		const gchar *module_name)
+{
+	GModule *module;
+	GList *dirs, *d;
+
+	module = g_hash_table_lookup (indexer->private->indexer_modules, module_name);
+
+	/* Signal module start/stop */
+	process_module_emit_signals (indexer, module_name);
+
+	if (!module) {
+		/* No need to signal stopped here, we will get that
+		 * signal the next time this function is called.
+		 */
+		g_message ("No module for:'%s'", module_name);
+		return;
+	}
+
+	g_message ("Starting module:'%s'", module_name);
+
+	dirs = tracker_module_config_get_monitor_recurse_directories (module_name);
+	g_return_if_fail (dirs != NULL);
+
+	for (d = dirs; d; d = d->next) {
+		PathInfo *info;
+
+		info = path_info_new (module, module_name, d->data, NULL);
+		add_directory (indexer, info);
+	}
+
+	g_list_free (dirs);
+}
+
+static gboolean
+process_func (gpointer data)
+{
+	TrackerIndexer *indexer;
+	PathInfo *path;
+
+	indexer = TRACKER_INDEXER (data);
+
+	indexer->private->in_process = TRUE;
+
+	if (G_UNLIKELY (!indexer->private->in_transaction)) {
+		start_transaction (indexer);
+	}
+
+	if ((path = g_queue_peek_head (indexer->private->file_queue)) != NULL) {
+		/* Process file */
+		if (process_file (indexer, path)) {
+			indexer->private->files_processed++;
+			path = g_queue_pop_head (indexer->private->file_queue);
+			path_info_free (path);
+		}
+	} else if ((path = g_queue_pop_head (indexer->private->dir_queue)) != NULL) {
+		/* Process directory contents */
+		process_directory (indexer, path, TRUE);
+		path_info_free (path);
+	} else {
+		gchar *module_name;
+
+		/* Dirs/files queues are empty, process the next module */
+		module_name = g_queue_pop_head (indexer->private->modules_queue);
+
+		if (!module_name) {
+			/* Signal the last module as finished */
+			process_module_emit_signals (indexer, NULL);
+
+			/* We are no longer processing, this is
+			 * needed so that when we call
+			 * check_stopped() it cleans up the idle
+			 * handler and id.
+			 */
+			indexer->private->in_process = FALSE;
+
+			/* Signal stopped and clean up */
+			check_stopped (indexer, FALSE);
+			check_disk_space_stop (indexer);
+
+			return FALSE;
+		}
+
+		process_module (indexer, module_name);
+		g_free (module_name);
+	}
+
+	if (indexer->private->items_processed > TRANSACTION_MAX) {
+		schedule_flush (indexer, TRUE);
+	}
+
+	indexer->private->in_process = FALSE;
+
+	if (indexer->private->state != 0) {
+		/* Some flag has been set, meaning the idle function should stop */
+		indexer->private->idle_id = 0;
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+TrackerIndexer *
+tracker_indexer_new (void)
+{
+	return g_object_new (TRACKER_TYPE_INDEXER, NULL);
+}
+
+gboolean
+tracker_indexer_get_running (TrackerIndexer *indexer)
+{
+	TrackerIndexerState state;
+
+	g_return_val_if_fail (TRACKER_IS_INDEXER (indexer), FALSE);
+
+	state = indexer->private->state;
+
+	return ((state & TRACKER_INDEXER_STATE_PAUSED) == 0 &&
+		(state & TRACKER_INDEXER_STATE_STOPPED) == 0);
+}
+
+static void
+state_check (TrackerIndexer *indexer)
+{
+	TrackerIndexerState state;
+
+	state = indexer->private->state;
+
+	if ((state & TRACKER_INDEXER_STATE_FLUSHING) ||
+	    (state & TRACKER_INDEXER_STATE_DISK_FULL) ||
+	    (state & TRACKER_INDEXER_STATE_STOPPED) ||
+	    (state & TRACKER_INDEXER_STATE_PAUSED)) {
+		check_disk_space_stop (indexer);
+		signal_status_timeout_stop (indexer);
+		stop_scheduled_flush (indexer);
+		stop_transaction (indexer);
+
+		/* Actually, we don't want to remove/add back the idle
+		 * function if we're in the middle of processing one item,
+		 * as we could end up with a detached idle function running
+		 * plus a newly created one, instead we don't remove the source
+		 * here and make the idle function return FALSE if any flag is set.
+		 */
+		if (indexer->private->idle_id &&
+		    !indexer->private->in_process) {
+			g_source_remove (indexer->private->idle_id);
+			indexer->private->idle_id = 0;
+		}
+	} else {
+		check_disk_space_start (indexer);
+		signal_status_timeout_start (indexer);
+
+		if (indexer->private->idle_id == 0) {
+			indexer->private->idle_id = g_idle_add (process_func, indexer);
+		}
+	}
+}
+
+static void
+state_set_flags (TrackerIndexer      *indexer,
+		 TrackerIndexerState  state)
+{
+	indexer->private->state |= state;
+	state_check (indexer);
+}
+
+static void
+state_unset_flags (TrackerIndexer      *indexer,
+		   TrackerIndexerState	state)
+{
+	indexer->private->state &= ~(state);
+	state_check (indexer);
+}
+
+void
+tracker_indexer_set_running (TrackerIndexer *indexer,
+			     gboolean	     running)
+{
+	TrackerIndexerState state;
+
+	state = indexer->private->state;
+
+	if (running && (state & TRACKER_INDEXER_STATE_PAUSED)) {
+		state_unset_flags (indexer, TRACKER_INDEXER_STATE_PAUSED);
+
+		tracker_db_index_set_paused (indexer->private->file_index, FALSE);
+		tracker_db_index_set_paused (indexer->private->email_index, FALSE);
+
+		g_signal_emit (indexer, signals[CONTINUED], 0);
+	} else if (!running && !(state & TRACKER_INDEXER_STATE_PAUSED)) {
+		state_set_flags (indexer, TRACKER_INDEXER_STATE_PAUSED);
+
+		tracker_db_index_set_paused (indexer->private->file_index, TRUE);
+		tracker_db_index_set_paused (indexer->private->email_index, TRUE);
+
+		g_signal_emit (indexer, signals[PAUSED], 0);
+	}
+}
+
+void
+tracker_indexer_stop (TrackerIndexer *indexer)
+{
+	g_return_if_fail (TRACKER_IS_INDEXER (indexer));
+
+	check_stopped (indexer, TRUE);
+}
+
+void
+tracker_indexer_pause (TrackerIndexer	      *indexer,
+		       DBusGMethodInvocation  *context,
+		       GError		     **error)
+{
+	guint request_id;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (TRACKER_IS_INDEXER (indexer), context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to pause the indexer");
+
+	if (tracker_indexer_get_running (indexer)) {
+		tracker_dbus_request_comment (request_id,
+					      "Pausing indexing");
+
+		tracker_indexer_set_running (indexer, FALSE);
+	}
+
+	dbus_g_method_return (context);
+
+	tracker_dbus_request_success (request_id);
+}
+
+static gboolean
+pause_for_duration_cb (gpointer user_data)
+{
+	TrackerIndexer *indexer;
+
+	indexer = TRACKER_INDEXER (user_data);
+
+	tracker_indexer_set_running (indexer, TRUE);
+	indexer->private->pause_for_duration_id = 0;
+
+	return FALSE;
+}
+
+void
+tracker_indexer_pause_for_duration (TrackerIndexer	   *indexer,
+				    guint		    seconds,
+				    DBusGMethodInvocation  *context,
+				    GError		  **error)
+{
+	guint request_id;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (TRACKER_IS_INDEXER (indexer), context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to pause the indexer for %d seconds",
+				  seconds);
+
+	if (tracker_indexer_get_running (indexer)) {
+		if (indexer->private->in_transaction) {
+			tracker_dbus_request_comment (request_id,
+						      "Committing transactions");
+		}
+
+		tracker_dbus_request_comment (request_id,
+					      "Pausing indexing");
+
+		tracker_indexer_set_running (indexer, FALSE);
+
+		indexer->private->pause_for_duration_id =
+			g_timeout_add_seconds (seconds,
+					       pause_for_duration_cb,
+					       indexer);
+	}
+
+	dbus_g_method_return (context);
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_indexer_continue (TrackerIndexer	 *indexer,
+			  DBusGMethodInvocation  *context,
+			  GError		**error)
+{
+	guint request_id;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (TRACKER_IS_INDEXER (indexer), context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to continue the indexer");
+
+	if (tracker_indexer_get_running (indexer) == FALSE) {
+		tracker_dbus_request_comment (request_id,
+					      "Continuing indexing");
+
+		tracker_indexer_set_running (indexer, TRUE);
+	}
+
+	dbus_g_method_return (context);
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_indexer_process_all (TrackerIndexer *indexer)
+{
+	GList *l;
+
+	for (l = indexer->private->module_names; l; l = l->next) {
+		g_queue_push_tail (indexer->private->modules_queue, g_strdup (l->data));
+	}
+}
+
+void
+tracker_indexer_files_check (TrackerIndexer *indexer,
+			     const gchar *module_name,
+			     GStrv files,
+			     DBusGMethodInvocation *context,
+			     GError **error)
+{
+	GModule *module;
+	guint request_id;
+	gint i;
+	GError *actual_error = NULL;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (TRACKER_IS_INDEXER (indexer), context);
+	tracker_dbus_async_return_if_fail (files != NULL, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to check %d files",
+				  g_strv_length (files));
+
+	module = g_hash_table_lookup (indexer->private->indexer_modules, module_name);
+
+	if (!module) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "The module '%s' is not loaded",
+					     module_name);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	/* Add files to the queue */
+	for (i = 0; files[i]; i++) {
+		PathInfo *info;
+
+		info = path_info_new (module, module_name, files[i], NULL);
+		add_file (indexer, info);
+	}
+
+	dbus_g_method_return (context);
+	tracker_dbus_request_success (request_id);
+}
+
+/* FIXME: Should get rid of this DBus method */
+void
+tracker_indexer_files_update (TrackerIndexer *indexer,
+			      const gchar *module_name,
+			      GStrv files,
+			      DBusGMethodInvocation *context,
+			      GError **error)
+{
+	tracker_indexer_files_check (indexer, module_name,
+				     files, context, error);
+}
+
+/* FIXME: Should get rid of this DBus method */
+void
+tracker_indexer_files_delete (TrackerIndexer *indexer,
+			      const gchar *module_name,
+			      GStrv files,
+			      DBusGMethodInvocation *context,
+			      GError **error)
+{
+	tracker_indexer_files_check (indexer, module_name,
+				     files, context, error);
+}
+
+void
+tracker_indexer_file_move (TrackerIndexer	  *indexer,
+			   const gchar		  *module_name,
+			   gchar		  *from,
+			   gchar		  *to,
+			   DBusGMethodInvocation  *context,
+			   GError		 **error)
+{
+	GModule *module;
+	guint request_id;
+	GError *actual_error;
+	PathInfo *info;
+
+	tracker_dbus_async_return_if_fail (TRACKER_IS_INDEXER (indexer), context);
+	tracker_dbus_async_return_if_fail (from != NULL, context);
+	tracker_dbus_async_return_if_fail (to != NULL, context);
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to move '%s' to '%s'",
+				  from, to);
+
+	module = g_hash_table_lookup (indexer->private->indexer_modules, module_name);
+
+	if (!module) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "The module '%s' is not loaded",
+					     module_name);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	/* Add files to the queue */
+	info = path_info_new (module, module_name, from, to);
+	add_file (indexer, info);
+
+	dbus_g_method_return (context);
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_indexer_property_set (TrackerIndexer	     *indexer,
+			      const gchar	     *service_type,
+			      const gchar	     *uri,
+			      const gchar	     *property,
+			      GStrv		      values,
+			      DBusGMethodInvocation  *context,
+			      GError		    **error)
+{
+	guint	request_id;
+	GError *actual_error = NULL;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (TRACKER_IS_INDEXER (indexer), context);
+	tracker_dbus_async_return_if_fail (service_type != NULL, context);
+	tracker_dbus_async_return_if_fail (uri != NULL, context);
+	tracker_dbus_async_return_if_fail (property != NULL, context);
+	tracker_dbus_async_return_if_fail (values != NULL, context);
+	tracker_dbus_async_return_if_fail (g_strv_length (values) > 0, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to set %d values in property '%s' for file '%s' ",
+				  g_strv_length (values),
+				  property,
+				  uri);
+
+	if (!handle_metadata_add (indexer,
+				  service_type,
+				  uri,
+				  property,
+				  values,
+				  &actual_error)) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     NULL);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	schedule_flush (indexer, TRUE);
+
+	dbus_g_method_return (context);
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_indexer_property_remove (TrackerIndexer		*indexer,
+				 const gchar		*service_type,
+				 const gchar		*uri,
+				 const gchar		*property,
+				 GStrv			 values,
+				 DBusGMethodInvocation	*context,
+				 GError		       **error)
+{
+	guint	request_id;
+	GError *actual_error = NULL;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (TRACKER_IS_INDEXER (indexer), context);
+	tracker_dbus_async_return_if_fail (service_type != NULL, context);
+	tracker_dbus_async_return_if_fail (uri != NULL, context);
+	tracker_dbus_async_return_if_fail (property != NULL, context);
+	tracker_dbus_async_return_if_fail (values != NULL, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to remove %d values in property '%s' for file '%s' ",
+				  g_strv_length (values),
+				  property,
+				  uri);
+
+	if (!handle_metadata_remove (indexer,
+				     service_type,
+				     uri,
+				     property,
+				     values,
+				     &actual_error)) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     NULL);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	dbus_g_method_return (context);
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_indexer_shutdown (TrackerIndexer	 *indexer,
+			  DBusGMethodInvocation  *context,
+			  GError		**error)
+{
+	guint request_id;
+
+	tracker_dbus_async_return_if_fail (TRACKER_IS_INDEXER (indexer), context);
+
+	request_id = tracker_dbus_get_next_request_id ();
+	tracker_dbus_request_new (request_id,
+				  "DBus request to shutdown the indexer");
+
+	tracker_indexer_stop (indexer);
+
+	dbus_g_method_return (context);
+	tracker_dbus_request_success (request_id);
+}

Added: trunk/src/tracker-indexer/tracker-indexer.h
==============================================================================
--- (empty file)
+++ trunk/src/tracker-indexer/tracker-indexer.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,134 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKER_INDEXER_H__
+#define __TRACKER_INDEXER_H__
+
+#include <glib-object.h>
+#include <dbus/dbus-glib.h>
+
+#define TRACKER_DAEMON_SERVICE	     "org.freedesktop.Tracker"
+#define TRACKER_INDEXER_SERVICE      "org.freedesktop.Tracker.Indexer"
+#define TRACKER_INDEXER_PATH	     "/org/freedesktop/Tracker/Indexer"
+#define TRACKER_INDEXER_INTERFACE    "org.freedesktop.Tracker.Indexer"
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_INDEXER	     (tracker_indexer_get_type())
+#define TRACKER_INDEXER(o)	     (G_TYPE_CHECK_INSTANCE_CAST ((o), TRACKER_TYPE_INDEXER, TrackerIndexer))
+#define TRACKER_INDEXER_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c),    TRACKER_TYPE_INDEXER, TrackerIndexerClass))
+#define TRACKER_IS_INDEXER(o)	     (G_TYPE_CHECK_INSTANCE_TYPE ((o), TRACKER_TYPE_INDEXER))
+#define TRACKER_IS_INDEXER_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c),    TRACKER_TYPE_INDEXER))
+#define TRACKER_INDEXER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o),  TRACKER_TYPE_INDEXER, TrackerIndexerClass))
+
+typedef struct TrackerIndexer	     TrackerIndexer;
+typedef struct TrackerIndexerClass   TrackerIndexerClass;
+typedef struct TrackerIndexerPrivate TrackerIndexerPrivate;
+
+struct TrackerIndexer {
+	GObject parent_instance;
+
+	/* Private struct */
+	TrackerIndexerPrivate *private;
+};
+
+struct TrackerIndexerClass {
+	GObjectClass parent_class;
+
+	void (*status)		(TrackerIndexer *indexer,
+				 gdouble	 seconds_elapsed,
+				 const gchar	*current_module_name,
+				 guint		 items_indexed,
+				 guint		 items_remaining);
+	void (*started)		(TrackerIndexer *indexer);
+	void (*paused)		(TrackerIndexer *indexer);
+	void (*continued)	(TrackerIndexer *indexer);
+	void (*finished)	(TrackerIndexer *indexer,
+				 gdouble	 seconds_elapsed,
+				 guint		 items_indexed);
+	void (*module_started)	(TrackerIndexer *indexer,
+				 const gchar	*module_name);
+	void (*module_finished) (TrackerIndexer *indexer,
+				 const gchar	*module_name);
+};
+
+GType		tracker_indexer_get_type	   (void) G_GNUC_CONST;
+
+TrackerIndexer *tracker_indexer_new		   (void);
+gboolean	tracker_indexer_get_running	   (TrackerIndexer	   *indexer);
+void		tracker_indexer_set_running	   (TrackerIndexer	   *indexer,
+						    gboolean		    running);
+void		tracker_indexer_stop		   (TrackerIndexer	   *indexer);
+void		tracker_indexer_process_all	   (TrackerIndexer	   *indexer);
+
+/* DBus methods */
+void		tracker_indexer_pause		   (TrackerIndexer	   *indexer,
+						    DBusGMethodInvocation  *context,
+						    GError		  **error);
+void		tracker_indexer_pause_for_duration (TrackerIndexer	   *indexer,
+						    guint		    seconds,
+						    DBusGMethodInvocation  *context,
+						    GError		  **error);
+void		tracker_indexer_continue	   (TrackerIndexer	   *indexer,
+						    DBusGMethodInvocation  *context,
+						    GError		  **error);
+void		tracker_indexer_files_check	   (TrackerIndexer	   *indexer,
+						    const gchar		   *module,
+						    GStrv		    files,
+						    DBusGMethodInvocation  *context,
+						    GError		  **error);
+void		tracker_indexer_files_update	   (TrackerIndexer	   *indexer,
+						    const gchar		   *module,
+						    GStrv		    files,
+						    DBusGMethodInvocation  *context,
+						    GError		  **error);
+void		tracker_indexer_files_delete	   (TrackerIndexer	   *indexer,
+						    const gchar		   *module,
+						    GStrv		    files,
+						    DBusGMethodInvocation  *context,
+						    GError		  **error);
+void		tracker_indexer_file_move	   (TrackerIndexer	   *indexer,
+						    const gchar		   *module_name,
+						    gchar		   *from,
+						    gchar		   *to,
+						    DBusGMethodInvocation  *context,
+						    GError		  **error);
+void		tracker_indexer_property_set	   (TrackerIndexer	   *indexer,
+						    const gchar		   *service_type,
+						    const gchar		   *uri,
+						    const gchar		   *property,
+						    GStrv		    values,
+						    DBusGMethodInvocation  *context,
+						    GError		  **error);
+void		tracker_indexer_property_remove    (TrackerIndexer	   *indexer,
+						    const gchar		   *service_type,
+						    const gchar		   *uri,
+						    const gchar		   *property,
+						    GStrv		    values,
+						    DBusGMethodInvocation  *context,
+						    GError		  **error);
+void		tracker_indexer_shutdown	   (TrackerIndexer	   *indexer,
+						    DBusGMethodInvocation  *context,
+						    GError		  **error);
+
+G_END_DECLS
+
+#endif /* __TRACKER_INDEXER_H__ */

Added: trunk/src/tracker-indexer/tracker-main.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-indexer/tracker-main.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,383 @@
+/* Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib-object.h>
+#include <glib/gi18n.h>
+
+#include <libtracker-common/tracker-config.h>
+#include <libtracker-common/tracker-ioprio.h>
+#include <libtracker-common/tracker-log.h>
+#include <libtracker-common/tracker-ontology.h>
+#include <libtracker-common/tracker-module-config.h>
+#include <libtracker-common/tracker-file-utils.h>
+
+#include <libtracker-db/tracker-db-manager.h>
+#include <libtracker-db/tracker-db-index-manager.h>
+
+#include "tracker-dbus.h"
+#include "tracker-indexer.h"
+#include "tracker-indexer-db.h"
+
+#define ABOUT								  \
+	"Tracker " VERSION "\n"						  \
+	"Copyright (c) 2005-2008 Jamie McCracken (jamiemcc gnome org)\n"
+
+#define LICENSE								  \
+	"This program is free software and comes without any warranty.\n" \
+	"It is licensed under version 2 or later of the General Public "  \
+	"License which can be viewed at:\n"				  \
+	"\n"								  \
+	"  http://www.gnu.org/licenses/gpl.txt\n";
+
+#define QUIT_TIMEOUT 300 /* 5 minutes worth of seconds */
+
+static GMainLoop    *main_loop;
+static guint	     quit_timeout_id;
+
+static gint	     verbosity = -1;
+static gboolean      process_all = FALSE;
+static gboolean      run_forever = FALSE;
+
+static GOptionEntry  entries[] = {
+	{ "verbosity", 'v', 0,
+	  G_OPTION_ARG_INT, &verbosity,
+	  N_("Logging, 0 = errors only, "
+	     "1 = minimal, 2 = detailed and 3 = debug (default = 0)"),
+	  NULL },
+	{ "process-all", 'p', 0,
+	  G_OPTION_ARG_NONE, &process_all,
+	  N_("Whether to process data from all configured modules to be indexed"),
+	  NULL },
+	{ "run-forever", 'f', 0,
+	  G_OPTION_ARG_NONE, &run_forever,
+	  N_("Run forever, only interesting for debugging purposes"),
+	  NULL },
+
+	{ NULL }
+};
+
+static void
+sanity_check_option_values (TrackerConfig *config)
+{
+	g_message ("General options:");
+	g_message ("  Verbosity  ............................  %d",
+		   tracker_config_get_verbosity (config));
+	g_message ("  Low memory mode  ......................  %s",
+		   tracker_config_get_low_memory_mode (config) ? "yes" : "no");
+
+	g_message ("Indexer options:");
+	g_message ("  Throttle level  .......................  %d",
+		   tracker_config_get_throttle (config));
+	g_message ("  File content indexing enabled  ........  %s",
+		   tracker_config_get_enable_content_indexing (config) ? "yes" : "no");
+	g_message ("  Thumbnail indexing enabled  ...........  %s",
+		   tracker_config_get_enable_thumbnails (config) ? "yes" : "no");
+	g_message ("  Indexer language code  ................  %s",
+		   tracker_config_get_language (config));
+	g_message ("  Stemmer enabled  ......................  %s",
+		   tracker_config_get_enable_stemmer (config) ? "yes" : "no");
+	g_message ("  Fast merges enabled  ..................  %s",
+		   tracker_config_get_fast_merges (config) ? "yes" : "no");
+	g_message ("  Disable indexing on battery  ..........  %s (initially = %s)",
+		   tracker_config_get_disable_indexing_on_battery (config) ? "yes" : "no",
+		   tracker_config_get_disable_indexing_on_battery_init (config) ? "yes" : "no");
+
+	if (tracker_config_get_low_disk_space_limit (config) == -1) {
+		g_message ("  Low disk space limit  .................  Disabled");
+	} else {
+		g_message ("  Low disk space limit  .................  %d%%",
+			   tracker_config_get_low_disk_space_limit (config));
+	}
+
+	g_message ("  Minimum index word length  ............  %d",
+		   tracker_config_get_min_word_length (config));
+	g_message ("  Maximum index word length  ............  %d",
+		   tracker_config_get_max_word_length (config));
+	g_message ("  Maximum text to index  ................  %d",
+		   tracker_config_get_max_text_to_index (config));
+	g_message ("  Maximum words to index  ...............  %d",
+		   tracker_config_get_max_words_to_index (config));
+	g_message ("  Maximum bucket count  .................  %d",
+		   tracker_config_get_max_bucket_count (config));
+	g_message ("  Minimum bucket count  .................  %d",
+		   tracker_config_get_min_bucket_count (config));
+}
+
+static void
+signal_handler (gint signo)
+{
+	static gboolean in_loop = FALSE;
+
+	/* die if we get re-entrant signals handler calls */
+	if (in_loop) {
+		exit (EXIT_FAILURE);
+	}
+
+	switch (signo) {
+	case SIGSEGV:
+		/* we are screwed if we get this so exit immediately! */
+		exit (EXIT_FAILURE);
+
+	case SIGBUS:
+	case SIGILL:
+	case SIGFPE:
+	case SIGABRT:
+	case SIGTERM:
+	case SIGINT:
+		in_loop = TRUE;
+		g_main_loop_quit (main_loop);
+
+	default:
+		if (g_strsignal (signo)) {
+			g_warning ("Received signal: %s", g_strsignal (signo));
+		}
+		break;
+	}
+}
+
+static void
+initialize_signal_handler (void)
+{
+#ifndef G_OS_WIN32
+	struct sigaction act, ign_act;
+	sigset_t	 empty_mask;
+
+	sigemptyset (&empty_mask);
+	act.sa_handler = signal_handler;
+	act.sa_mask    = empty_mask;
+	act.sa_flags   = 0;
+
+	ign_act.sa_handler = SIG_IGN;
+	ign_act.sa_mask = empty_mask;
+	ign_act.sa_flags = 0;
+
+	sigaction (SIGTERM, &act, NULL);
+	sigaction (SIGILL,  &act, NULL);
+	sigaction (SIGBUS,  &act, NULL);
+	sigaction (SIGFPE,  &act, NULL);
+	sigaction (SIGHUP,  &act, NULL);
+	sigaction (SIGSEGV, &act, NULL);
+	sigaction (SIGABRT, &act, NULL);
+	sigaction (SIGUSR1, &act, NULL);
+	sigaction (SIGINT,  &act, NULL);
+	/* sigaction (SIGPIPE, &ign_act, NULL); */
+#endif
+}
+
+static gboolean
+quit_timeout_cb (gpointer user_data)
+{
+	TrackerIndexer *indexer;
+
+	indexer = TRACKER_INDEXER (user_data);
+
+	if (!tracker_indexer_get_running (indexer)) {
+		g_message ("Indexer is still not running after %d seconds, quitting...",
+			   QUIT_TIMEOUT);
+		g_main_loop_quit (main_loop);
+		quit_timeout_id = 0;
+	} else {
+		g_message ("Indexer is now running, staying alive until finished...");
+	}
+
+	return FALSE;
+}
+
+static void
+indexer_finished_cb (TrackerIndexer *indexer,
+		     gdouble	     seconds_elapsed,
+		     guint	     items_indexed,
+		     gboolean	     interrupted,
+		     gpointer	     user_data)
+{
+	g_message ("Finished indexing sent items");
+
+	if (interrupted) {
+		g_message ("Indexer was told to shutdown");
+		g_main_loop_quit (main_loop);
+		return;
+	}
+
+	if (quit_timeout_id) {
+		g_message ("Cancelling previous quit timeout");
+		g_source_remove (quit_timeout_id);
+	}
+
+	if (!run_forever) {
+		g_message ("Waiting another %d seconds for more items before quitting...",
+			   QUIT_TIMEOUT);
+
+		quit_timeout_id = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT,
+							      QUIT_TIMEOUT,
+							      quit_timeout_cb,
+							      g_object_ref (indexer),
+							      (GDestroyNotify) g_object_unref);
+	}
+}
+
+gint
+main (gint argc, gchar *argv[])
+{
+	TrackerConfig *config;
+	TrackerIndexer *indexer;
+	TrackerDBManagerFlags flags = 0;
+	GOptionContext *context;
+	GError *error = NULL;
+	gchar *filename;
+
+	g_type_init ();
+
+	if (!g_thread_supported ()) {
+		g_thread_init (NULL);
+	}
+
+	setlocale (LC_ALL, "");
+
+	bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+	textdomain (GETTEXT_PACKAGE);
+
+	/* Set timezone info */
+	tzset ();
+
+	/* Translators: this messagge will apper immediately after the
+	 * usage string - Usage: COMMAND <THIS_MESSAGE>
+	 */
+	context = g_option_context_new (_("- start the tracker indexer"));
+
+	g_option_context_add_main_entries (context, entries, NULL);
+	g_option_context_parse (context, &argc, &argv, &error);
+	g_option_context_free (context);
+
+	g_print ("\n" ABOUT "\n" LICENSE "\n");
+	g_print ("Initializing tracker-indexer...\n");
+
+	initialize_signal_handler ();
+
+	/* Check XDG spec locations XDG_DATA_HOME _MUST_ be writable. */
+	if (!tracker_env_check_xdg_dirs ()) {
+		return EXIT_FAILURE;
+	}
+
+	/* Initialize logging */
+	config = tracker_config_new ();
+
+	if (verbosity > -1) {
+		tracker_config_set_verbosity (config, verbosity);
+	}
+
+	filename = g_build_filename (g_get_user_data_dir (),
+				     "tracker",
+				     "tracker-indexer.log",
+				     NULL);
+
+	tracker_log_init (filename, tracker_config_get_verbosity (config));
+	g_print ("Starting log:\n  File:'%s'\n", filename);
+	g_free (filename);
+
+	/* Make sure we initialize DBus, this shows we are started
+	 * successfully when called upon from the daemon.
+	 */
+	if (!tracker_dbus_init ()) {
+		return EXIT_FAILURE;
+	}
+
+	sanity_check_option_values (config);
+
+	/* Initialize database manager */
+	if (tracker_config_get_low_memory_mode (config)) {
+		flags |= TRACKER_DB_MANAGER_LOW_MEMORY_MODE;
+	}
+
+	tracker_db_manager_init (flags, NULL);
+	if (!tracker_db_index_manager_init (0,
+					    tracker_config_get_min_bucket_count (config),
+					    tracker_config_get_max_bucket_count (config))) {
+		return EXIT_FAILURE;
+	}
+
+	tracker_module_config_init ();
+
+	/* Set IO priority */
+	tracker_ioprio_init ();
+
+	/* nice() uses attribute "warn_unused_result" and so complains
+	 * if we do not check its returned value. But it seems that
+	 * since glibc 2.2.4, nice() can return -1 on a successful
+	 * call so we have to check value of errno too. Stupid...
+	 */
+	if (nice (19) == -1 && errno) {
+		const gchar *str;
+
+		str = g_strerror (errno);
+		g_message ("Couldn't set nice value to 19, %s",
+			   str ? str : "no error given");
+	}
+
+	indexer = tracker_indexer_new ();
+
+	/* Make Tracker available for introspection */
+	if (!tracker_dbus_register_object (G_OBJECT (indexer))) {
+		return EXIT_FAILURE;
+	}
+
+	/* Create the indexer and run the main loop */
+	g_signal_connect (indexer, "finished",
+			  G_CALLBACK (indexer_finished_cb),
+			  NULL);
+
+	if (process_all) {
+		/* Tell the indexer to process all configured modules */
+		tracker_indexer_process_all (indexer);
+	}
+
+	g_message ("Starting...");
+
+	main_loop = g_main_loop_new (NULL, FALSE);
+	g_main_loop_run (main_loop);
+
+	g_message ("Shutdown started");
+
+	if (quit_timeout_id) {
+		g_source_remove (quit_timeout_id);
+	}
+
+	g_main_loop_unref (main_loop);
+	g_object_unref (indexer);
+	g_object_unref (config);
+
+	tracker_dbus_shutdown ();
+	tracker_db_index_manager_shutdown ();
+	tracker_db_manager_shutdown ();
+	tracker_module_config_shutdown ();
+	tracker_log_shutdown ();
+
+	return EXIT_SUCCESS;
+}

Added: trunk/src/tracker-indexer/tracker-marshal-main.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-indexer/tracker-marshal-main.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,2 @@
+#include "tracker-marshal.h"
+#include "tracker-marshal.c"

Added: trunk/src/tracker-indexer/tracker-marshal.list
==============================================================================
--- (empty file)
+++ trunk/src/tracker-indexer/tracker-marshal.list	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,2 @@
+VOID:DOUBLE,UINT,BOOL
+VOID:DOUBLE,STRING,UINT,UINT

Added: trunk/src/tracker-indexer/tracker-metadata-utils.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-indexer/tracker-metadata-utils.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,858 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gio/gio.h>
+
+#include <libtracker-common/tracker-file-utils.h>
+#include <libtracker-common/tracker-type-utils.h>
+#include <libtracker-common/tracker-os-dependant.h>
+#include <libtracker-common/tracker-ontology.h>
+
+#include "tracker-metadata-utils.h"
+#include "tracker-dbus.h"
+
+#define THUMBNAIL_RETRIEVAL_ENABLED
+#define HAVE_HILDON_THUMBNAIL
+
+#define METADATA_FILE_NAME_DELIMITED "File:NameDelimited"
+#define METADATA_FILE_EXT	     "File:Ext"
+#define METADATA_FILE_PATH	     "File:Path"
+#define METADATA_FILE_NAME	     "File:Name"
+#define METADATA_FILE_LINK	     "File:Link"
+#define METADATA_FILE_MIMETYPE	     "File:Mime"
+#define METADATA_FILE_SIZE	     "File:Size"
+#define METADATA_FILE_MODIFIED	     "File:Modified"
+#define METADATA_FILE_ACCESSED	     "File:Accessed"
+
+#undef	TRY_LOCALE_TO_UTF8_CONVERSION
+#define TEXT_MAX_SIZE		     1048576  /* bytes */
+#define TEXT_CHECK_SIZE		     65535    /* bytes */
+
+typedef struct {
+	GPid pid;
+	guint stdout_watch_id;
+	GIOChannel *stdin_channel;
+	GIOChannel *stdout_channel;
+	GMainLoop  *data_incoming_loop;
+	gpointer data;
+} ProcessContext;
+
+static void get_file_thumbnail (const gchar *path,
+				const gchar *mime);
+
+static ProcessContext *metadata_context = NULL;
+
+static void
+process_context_destroy (ProcessContext *context)
+{
+	g_io_channel_shutdown (context->stdin_channel, FALSE, NULL);
+	g_io_channel_unref (context->stdin_channel);
+
+	g_source_remove (context->stdout_watch_id);
+	g_io_channel_shutdown (context->stdout_channel, FALSE, NULL);
+	g_io_channel_unref (context->stdout_channel);
+
+	if (g_main_loop_is_running (context->data_incoming_loop)) {
+		g_main_loop_quit (context->data_incoming_loop);
+	}
+
+	g_main_loop_unref (context->data_incoming_loop);
+
+	g_spawn_close_pid (context->pid);
+
+	g_free (context);
+}
+
+static void
+process_context_child_watch_cb (GPid	 pid,
+				gint	 status,
+				gpointer user_data)
+{
+	g_debug ("Process '%d' exited with code: %d->'%s'",
+		 pid,
+		 status,
+		 g_strerror (status));
+
+	if (user_data == metadata_context) {
+		process_context_destroy (metadata_context);
+		metadata_context = NULL;
+	}
+}
+
+static ProcessContext *
+process_context_create (const gchar **argv,
+			GIOFunc       stdout_watch_func)
+{
+	ProcessContext *context;
+	GIOChannel *stdin_channel, *stdout_channel;
+	GIOFlags flags;
+	GPid pid;
+
+	if (!tracker_spawn_async_with_channels (argv,
+						10,
+						&pid,
+						&stdin_channel,
+						&stdout_channel,
+						NULL)) {
+		return NULL;
+	}
+
+	g_debug ("Process '%d' spawned for command:'%s'",
+		 pid,
+		 argv[0]);
+
+	context = g_new0 (ProcessContext, 1);
+	context->pid = pid;
+	context->stdin_channel = stdin_channel;
+	context->stdout_channel = stdout_channel;
+	context->data_incoming_loop = g_main_loop_new (NULL, FALSE);
+	context->stdout_watch_id = g_io_add_watch (stdout_channel,
+						   G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP,
+						   stdout_watch_func,
+						   context);
+
+	flags = g_io_channel_get_flags (context->stdout_channel);
+	flags |= G_IO_FLAG_NONBLOCK;
+
+	g_io_channel_set_flags (context->stdout_channel, flags, NULL);
+
+	g_child_watch_add (context->pid, process_context_child_watch_cb, context);
+
+	return context;
+}
+
+static gboolean
+metadata_read_cb (GIOChannel   *channel,
+		  GIOCondition	condition,
+		  gpointer	user_data)
+{
+	GPtrArray *array;
+	GIOStatus status;
+	gchar *line;
+
+	if (!metadata_context) {
+		return FALSE;
+	}
+
+	if ((condition & G_IO_HUP) || (condition & G_IO_ERR)) {
+		return FALSE;
+	}
+
+	array = metadata_context->data;
+	status = G_IO_STATUS_NORMAL;
+	line = NULL;
+
+	if ((condition & G_IO_IN) || (condition & G_IO_PRI)) {
+		do {
+			status = g_io_channel_read_line (metadata_context->stdout_channel,
+							 &line,
+							 NULL,
+							 NULL,
+							 NULL);
+
+			if (status == G_IO_STATUS_NORMAL && line && *line) {
+				g_strstrip (line);
+				g_strdelimit (line, ";", '\0');
+				g_ptr_array_add (array, line);
+			}
+		} while (status == G_IO_STATUS_NORMAL && line && *line);
+
+		if (status == G_IO_STATUS_EOF ||
+		    status == G_IO_STATUS_ERROR ||
+		    (status == G_IO_STATUS_NORMAL && !*line)) {
+			/* All extractor output has been processed */
+			g_main_loop_quit (metadata_context->data_incoming_loop);
+		}
+	}
+
+	return TRUE;
+}
+
+static gboolean
+metadata_setup (void)
+{
+	const gchar *argv[2] = {
+		LIBEXEC_PATH G_DIR_SEPARATOR_S "tracker-extract",
+		NULL
+	};
+
+	if (metadata_context) {
+		process_context_destroy (metadata_context);
+		metadata_context = NULL;
+	}
+
+	metadata_context = process_context_create (argv,
+						   metadata_read_cb);
+
+	if (!metadata_context) {
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gchar **
+metadata_query_file (const gchar *path,
+		     const gchar *mimetype)
+{
+	gchar *utf_path, *str;
+	GPtrArray *array;
+	GIOStatus status;
+	GError *error = NULL;
+
+	if (!path || !mimetype) {
+		return NULL;
+	}
+
+	if (!metadata_context && !metadata_setup ()) {
+		return NULL;
+	}
+
+	utf_path = g_filename_from_utf8 (path, -1, NULL, NULL, NULL);
+
+	if (!utf_path) {
+		g_free (utf_path);
+		return NULL;
+	}
+
+	array = g_ptr_array_sized_new (10);
+	metadata_context->data = array;
+
+	str = g_strdup_printf ("%s\n%s\n", utf_path, mimetype);
+	g_free (utf_path);
+
+	/* Write path and mimetype */
+	g_io_channel_write_chars (metadata_context->stdin_channel, str, -1, NULL, NULL);
+	status = g_io_channel_flush (metadata_context->stdin_channel, &error);
+
+	if (status == G_IO_STATUS_ERROR &&
+	    error &&
+	    g_error_matches (error, G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_PIPE)) {
+		/* Looks like the external extractor
+		 * process has died before the child watch
+		 * could handle it, try respawning.
+		 */
+		g_error_free (error);
+
+		metadata_setup ();
+		metadata_context->data = array;
+
+		g_io_channel_write_chars (metadata_context->stdin_channel, str, -1, NULL, NULL);
+		status = g_io_channel_flush (metadata_context->stdin_channel, NULL);
+
+		if (status == G_IO_STATUS_ERROR) {
+			/* No point in trying again */
+			g_free (str);
+			return NULL;
+		}
+	}
+
+	/* It will block here until all incoming
+	 * metadata has been processed
+	 */
+	g_main_loop_run (metadata_context->data_incoming_loop);
+
+	g_ptr_array_add (array, NULL);
+
+	if (metadata_context) {
+		metadata_context->data = NULL;
+	}
+
+	g_free (str);
+
+	return (gchar **) g_ptr_array_free (array, FALSE);
+}
+
+static void
+metadata_utils_get_embedded (const char      *path,
+			     const char      *mime_type,
+			     TrackerMetadata *metadata)
+{
+	gchar **values;
+	gchar  *service_type;
+	gint	i;
+
+	service_type = tracker_ontology_get_service_by_mime (mime_type);
+	if (!service_type) {
+		return;
+	}
+
+	if (!tracker_ontology_service_has_metadata (service_type)) {
+		g_free (service_type);
+		return;
+	}
+
+	g_free (service_type);
+
+	values = metadata_query_file (path, mime_type);
+
+	if (!values) {
+		return;
+	}
+
+	/* parse returned values and extract keys and associated metadata */
+	for (i = 0; values[i]; i++) {
+		gchar *meta_data, *sep;
+		const gchar *name, *value;
+		gchar *utf_value;
+
+		meta_data = values[i];
+		sep = strchr (meta_data, '=');
+
+		if (!sep)
+			continue;
+
+		/* zero out the separator, so we get
+		 * NULL-terminated name and value
+		 */
+		sep[0] = '\0';
+		name = meta_data;
+		value = sep + 1;
+
+		if (!name || !value)
+			continue;
+
+		if (!g_utf8_validate (value, -1, NULL)) {
+			utf_value = g_locale_to_utf8 (value, -1, NULL, NULL, NULL);
+		} else {
+			utf_value = g_strdup (value);
+		}
+
+		if (!utf_value)
+			continue;
+
+		tracker_metadata_insert (metadata, name, utf_value);
+	}
+
+	g_strfreev (values);
+}
+
+static gboolean
+get_file_content_read_cb (GIOChannel   *channel,
+			  GIOCondition	condition,
+			  gpointer	user_data)
+{
+	ProcessContext *context;
+	GString *text;
+	GIOStatus status;
+	gchar *line;
+
+	context = user_data;
+	text = context->data;;
+	status = G_IO_STATUS_NORMAL;
+
+	if (condition & G_IO_IN || condition & G_IO_PRI) {
+		do {
+			status = g_io_channel_read_line (channel, &line, NULL, NULL, NULL);
+
+			if (status == G_IO_STATUS_NORMAL) {
+				g_string_append (text, line);
+				g_free (line);
+			}
+		} while (status == G_IO_STATUS_NORMAL);
+
+		if (status == G_IO_STATUS_EOF ||
+		    status == G_IO_STATUS_ERROR) {
+			g_main_loop_quit (context->data_incoming_loop);
+			return TRUE;
+		}
+	}
+
+	if (condition & G_IO_ERR || condition & G_IO_HUP) {
+		g_main_loop_quit (context->data_incoming_loop);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+get_file_is_utf8 (GString *s,
+		  gssize  *bytes_valid)
+{
+	const gchar *end;
+
+	/* Check for UTF-8 validity, since we may
+	 * have cut off the end.
+	 */
+	if (g_utf8_validate (s->str, s->len, &end)) {
+		*bytes_valid = (gssize) s->len;
+		return TRUE;
+	}
+
+	*bytes_valid = end - s->str;
+
+	/* 4 is the maximum bytes for a UTF-8 character. */
+	if (*bytes_valid > 4) {
+		return FALSE;
+	}
+
+	if (g_utf8_get_char_validated (end, *bytes_valid) == (gunichar) -1) {
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+#ifdef TRY_LOCALE_TO_UTF8_CONVERSION
+
+static GString *
+get_file_in_locale (GString *s)
+{
+	GError *error = NULL;
+	gchar  *str;
+	gsize	bytes_read;
+	gsize	bytes_written;
+
+	str = g_locale_to_utf8 (s->str,
+				s->len,
+				&bytes_read,
+				&bytes_written,
+				&error);
+	if (error) {
+		g_debug ("  Conversion to UTF-8 read %d bytes, wrote %d bytes",
+			 bytes_read,
+			 bytes_written);
+		g_message ("Could not convert file from locale to UTF-8, %s",
+			   error->message);
+		g_error_free (error);
+		g_free (str);
+	} else {
+		g_string_assign (s, str);
+		g_free (str);
+	}
+
+	return s;
+}
+
+#endif /* TRY_LOCALE_TO_UTF8_CONVERSION */
+
+static gchar *
+get_file_content (const gchar *path)
+{
+	GFile		 *file;
+	GFileInputStream *stream;
+	GError		 *error = NULL;
+	GString		 *s;
+	gssize		  bytes;
+	gssize		  bytes_valid;
+	gssize		  bytes_read_total;
+	gssize		  buf_size;
+	gchar		  buf[TEXT_CHECK_SIZE];
+	gboolean	  has_more_data;
+	gboolean	  has_reached_max;
+	gboolean	  is_utf8;
+
+	file = g_file_new_for_path (path);
+	stream = g_file_read (file, NULL, &error);
+
+	if (error) {
+		g_message ("Could not get read file:'%s', %s",
+			   path,
+			   error->message);
+		g_error_free (error);
+		g_object_unref (file);
+
+		return NULL;
+	}
+
+	s = g_string_new ("");
+	has_reached_max = FALSE;
+	has_more_data = TRUE;
+	bytes_read_total = 0;
+	buf_size = TEXT_CHECK_SIZE - 1;
+
+	g_debug ("  Starting read...");
+
+	while (has_more_data && !has_reached_max && !error) {
+		gssize bytes_read;
+		gssize bytes_remaining;
+
+		/* Leave space for NULL termination and make sure we
+		 * add it at the end now.
+		 */
+		bytes_remaining = buf_size;
+		bytes_read = 0;
+
+		/* Loop until we hit the maximum */
+		for (bytes = -1; bytes != 0 && !error; ) {
+			bytes = g_input_stream_read (G_INPUT_STREAM (stream),
+						     buf,
+						     bytes_remaining,
+						     NULL,
+						     &error);
+
+			bytes_read += bytes;
+			bytes_remaining -= bytes;
+
+			g_debug ("  Read %d bytes",
+				 bytes);
+		}
+
+		/* Set the NULL termination after the last byte read */
+		buf[TEXT_CHECK_SIZE - bytes_remaining] = '\0';
+
+		/* First of all, check if this is the first time we
+		 * have tried to read the file up to the TEXT_CHECK_SIZE
+		 * limit. Then make sure that we read the maximum size
+		 * of the buffer. If we don't do this, there is the
+		 * case where we read 10 bytes in and it is just one
+		 * line with no '\n'. Once we have confirmed this we
+		 * check that the buffer has a '\n' to make sure the
+		 * file is worth indexing. Similarly if the file has
+		 * <= 3 bytes then we drop it.
+		 */
+		if (bytes_read_total == 0) {
+			if (bytes_read == buf_size &&
+			    strchr (buf, '\n') == NULL) {
+				g_debug ("  No '\\n' in the first %d bytes, not indexing file",
+					 buf_size);
+				break;
+			} else if (bytes_read <= 2) {
+				g_debug ("  File has less than 3 characters in it, not indexing file");
+				break;
+			}
+		}
+
+		/* Here we increment the bytes read total to evaluate
+		 * the next states. We don't do this before the
+		 * previous condition so we can know when we have
+		 * iterated > 1.
+		 */
+		bytes_read_total += bytes_read;
+
+		if (bytes_read != buf_size || bytes_read == 0) {
+			has_more_data = FALSE;
+		}
+
+		if (bytes_read_total >= TEXT_MAX_SIZE) {
+			has_reached_max = TRUE;
+		}
+
+		g_debug ("  Read %d bytes total, %d bytes this time, more data:%s, reached max:%s",
+			 bytes_read_total,
+			 bytes_read,
+			 has_more_data ? "yes" : "no",
+			 has_reached_max ? "yes" : "no");
+
+		/* The + 1 is for the NULL terminating byte */
+		s = g_string_append_len (s, buf, bytes_read + 1);
+	}
+
+	if (has_reached_max) {
+		g_debug ("  Maximum indexable limit reached");
+	}
+
+	if (error) {
+		g_message ("Could not read input stream for:'%s', %s",
+			   path,
+			   error->message);
+		g_error_free (error);
+		g_string_free (s, TRUE);
+		g_object_unref (stream);
+		g_object_unref (file);
+
+		return NULL;
+	}
+
+	/* Check for UTF-8 Validity, if not try to convert it to the
+	 * locale we are in.
+	 */
+	is_utf8 = get_file_is_utf8 (s, &bytes_valid);
+
+	/* Make sure the string is NULL terminated and in the case
+	 * where the string is valid UTF-8 up to the last character
+	 * which was cut off, NULL terminate to the last most valid
+	 * character.
+	 */
+#ifdef TRY_LOCALE_TO_UTF8_CONVERSION
+	if (!is_utf8) {
+		s = get_file_in_locale (s);
+	} else {
+		g_debug ("  Truncating to last valid UTF-8 character (%d/%d bytes)",
+			 bytes_valid,
+			 s->len);
+		s = g_string_truncate (s, bytes_valid);
+	}
+#else	/* TRY_LOCALE_TO_UTF8_CONVERSION */
+	g_debug ("  Truncating to last valid UTF-8 character (%d/%d bytes)",
+		 bytes_valid,
+		 s->len);
+	s = g_string_truncate (s, bytes_valid);
+#endif	/* TRY_LOCALE_TO_UTF8_CONVERSION */
+
+	g_object_unref (stream);
+	g_object_unref (file);
+
+	if (s->len < 1) {
+		g_string_free (s, TRUE);
+		s = NULL;
+	}
+
+	return s ? g_string_free (s, FALSE) : NULL;
+}
+
+#ifdef THUMBNAIL_RETRIEVAL_ENABLED
+#ifdef HAVE_HILDON_THUMBNAIL
+
+static void
+get_file_thumbnail_queue_cb (DBusGProxy     *proxy,
+			     DBusGProxyCall *call,
+			     gpointer	     user_data)
+{
+	GError *error = NULL;
+	guint	handle;
+
+	/* FIXME: What is the point of this? */
+	dbus_g_proxy_end_call (proxy, call, &error,
+			       G_TYPE_UINT, &handle,
+			       G_TYPE_INVALID);
+}
+
+#endif /* HAVE_HILDON_THUMBNAIL */
+
+static void
+get_file_thumbnail (const gchar *path,
+		    const gchar *mime)
+{
+#ifdef HAVE_HILDON_THUMBNAIL
+	static gchar   *batch[51];
+	static guint	count = 0;
+	static gboolean not_available = FALSE;
+
+	if (not_available) {
+		return;
+	}
+
+	if (count < 51) {
+		batch[count++] = g_strdup (path);
+	}
+
+	if (count == 51) {
+		guint i;
+
+		batch[51] = NULL;
+
+		g_debug ("Requesting thumbnails");
+
+		dbus_g_proxy_begin_call (tracker_dbus_get_thumbnailer (),
+					 "Queue",
+					 get_file_thumbnail_queue_cb,
+					 NULL, NULL,
+					 G_TYPE_STRV, batch,
+					 G_TYPE_UINT, 0,
+					 G_TYPE_INVALID);
+
+		for (i = 0; i <= count; i++) {
+			g_free (batch[i]);
+		}
+
+		count = 0;
+	}
+#else /* HAVE_HILDON_THUMBNAIL */
+	ProcessContext *context;
+
+	GString *thumbnail;
+	gchar *argv[5];
+
+	argv[0] = g_strdup (LIBEXEC_PATH G_DIR_SEPARATOR_S "tracker-thumbnailer");
+	argv[1] = g_filename_from_utf8 (path, -1, NULL, NULL, NULL);
+	argv[2] = g_strdup (mime);
+	argv[3] = g_strdup ("normal");
+	argv[4] = NULL;
+
+	context = process_context_create ((const gchar **) argv,
+					  get_file_content_read_cb)
+
+	if (!context) {
+		return;
+	}
+
+	thumbnail = g_string_new (NULL);
+	context->data = thumbnail;
+
+	g_main_loop_run (context->data_incoming_loop);
+
+	g_free (argv[0]);
+	g_free (argv[1]);
+	g_free (argv[2]);
+	g_free (argv[3]);
+
+	process_context_destroy (context);
+
+	if (!thumbnail->str || !*thumbnail->str) {
+		g_string_free (thumbnail, TRUE);
+		return;
+	}
+
+	g_debug ("Got thumbnail '%s' for '%s'", thumbnail->str, path);
+
+	g_string_free (thumbnail, TRUE);
+#endif /* HAVE_HILDON_THUMBNAIL */
+}
+
+#endif /* THUMBNIAL_RETRIEVAL_ENABLED */
+
+static gchar *
+get_file_content_by_filter (const gchar *path,
+			    const gchar *mime)
+{
+	ProcessContext *context;
+	gchar *str, *text_filter_file;
+	gchar **argv;
+	GString *text;
+
+#ifdef G_OS_WIN32
+	str = g_strconcat (mime, "_filter.bat", NULL);
+#else
+	str = g_strconcat (mime, "_filter", NULL);
+#endif
+
+	text_filter_file = g_build_filename (LIBDIR,
+					     "tracker",
+					     "filters",
+					     str,
+					     NULL);
+
+	g_free (str);
+
+	if (!g_file_test (text_filter_file, G_FILE_TEST_EXISTS)) {
+		g_free (text_filter_file);
+		return NULL;
+	}
+
+	argv = g_new0 (gchar *, 3);
+	argv[0] = text_filter_file;
+	argv[1] = (gchar *) path;
+
+	g_message ("Extracting text for:'%s' using filter:'%s'", argv[1], argv[0]);
+
+	context = process_context_create ((const gchar **) argv,
+					  get_file_content_read_cb);
+
+	g_free (text_filter_file);
+	g_free (argv);
+
+	if (!context) {
+		return NULL;
+	}
+
+	text = g_string_new (NULL);
+	context->data = text;
+
+	/* It will block here until all incoming
+	 * text has been processed
+	 */
+	g_main_loop_run (context->data_incoming_loop);
+
+	process_context_destroy (context);
+
+	return g_string_free (text, FALSE);
+}
+
+gchar *
+tracker_metadata_utils_get_text (const gchar *path)
+{
+	gchar *mime_type;
+	gchar *service_type;
+	gchar *text;
+
+	mime_type = tracker_file_get_mime_type (path);
+	service_type = tracker_ontology_get_service_by_mime (mime_type);
+
+	/* No need to filter text based files - index them directly */
+	if (service_type &&
+	    (strcmp (service_type, "Text") == 0 ||
+	     strcmp (service_type, "Development") == 0)) {
+		text = get_file_content (path);
+	} else {
+		text = get_file_content_by_filter (path, mime_type);
+	}
+
+	g_free (service_type);
+	g_free (mime_type);
+
+	return text;
+}
+
+TrackerMetadata *
+tracker_metadata_utils_get_data (const gchar *path)
+{
+	TrackerMetadata *metadata;
+	struct stat st;
+	const gchar *ext;
+	gchar *mime_type;
+
+	if (g_lstat (path, &st) < 0) {
+		return NULL;
+	}
+
+	metadata = tracker_metadata_new ();
+	ext = strrchr (path, '.');
+
+	if (ext) {
+		tracker_metadata_insert (metadata, METADATA_FILE_EXT, g_strdup (ext + 1));
+	}
+
+	mime_type = tracker_file_get_mime_type (path);
+
+	tracker_metadata_insert (metadata, METADATA_FILE_NAME,
+				 g_filename_display_basename (path));
+	tracker_metadata_insert (metadata, METADATA_FILE_PATH,
+				 g_path_get_dirname (path));
+	tracker_metadata_insert (metadata, METADATA_FILE_NAME_DELIMITED,
+				 g_filename_to_utf8 (path, -1, NULL, NULL, NULL));
+	tracker_metadata_insert (metadata, METADATA_FILE_MIMETYPE,
+				 mime_type);
+
+	if (mime_type) {
+		/* FIXME:
+		 * We should determine here for which items we do and for which
+		 * items we don't want to pre-create the thumbnail. */
+
+		get_file_thumbnail (path, mime_type);
+	}
+
+	if (S_ISLNK (st.st_mode)) {
+		gchar *link_path;
+
+		link_path = g_file_read_link (path, NULL);
+		tracker_metadata_insert (metadata, METADATA_FILE_LINK,
+					 g_filename_to_utf8 (link_path, -1, NULL, NULL, NULL));
+		g_free (link_path);
+	}
+
+	/* FIXME: These should be dealt directly as integer/times/whatever, not strings */
+	tracker_metadata_insert (metadata, METADATA_FILE_SIZE,
+				 tracker_guint_to_string (st.st_size));
+	tracker_metadata_insert (metadata, METADATA_FILE_MODIFIED,
+				 tracker_date_to_string (st.st_mtime));
+	tracker_metadata_insert (metadata, METADATA_FILE_ACCESSED,
+				 tracker_date_to_string (st.st_atime));
+
+	metadata_utils_get_embedded (path, mime_type, metadata);
+
+	return metadata;
+}

Added: trunk/src/tracker-indexer/tracker-metadata-utils.h
==============================================================================
--- (empty file)
+++ trunk/src/tracker-indexer/tracker-metadata-utils.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,34 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKER_METADATA_UTILS_H__
+#define __TRACKER_METADATA_UTILS_H__
+
+#include "tracker-metadata.h"
+
+G_BEGIN_DECLS
+
+TrackerMetadata *tracker_metadata_utils_get_data (const gchar *path);
+gchar *		 tracker_metadata_utils_get_text (const gchar *path);
+
+G_END_DECLS
+
+#endif /* __TRACKER_METADATA_UTILS_H__ */

Added: trunk/src/tracker-indexer/tracker-metadata.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-indexer/tracker-metadata.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,151 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <glib.h>
+#include <libtracker-common/tracker-ontology.h>
+#include "tracker-metadata.h"
+
+struct TrackerMetadata {
+	GHashTable *table;
+};
+
+TrackerMetadata *
+tracker_metadata_new (void)
+{
+	TrackerMetadata *metadata;
+
+	metadata = g_slice_new (TrackerMetadata);
+	metadata->table = g_hash_table_new_full (g_direct_hash,
+						 g_direct_equal,
+						 (GDestroyNotify) g_object_unref,
+						 NULL);
+	return metadata;
+}
+
+static gboolean
+remove_metadata_foreach (gpointer key,
+			 gpointer value,
+			 gpointer user_data)
+{
+	TrackerField *field;
+
+	field = (TrackerField *) key;
+
+	if (tracker_field_get_multiple_values (field)) {
+		GList *list;
+
+		list = (GList *) value;
+		g_list_foreach (list, (GFunc) g_free, NULL);
+		g_list_free (list);
+	} else {
+		g_free (value);
+	}
+
+	return TRUE;
+}
+
+void
+tracker_metadata_free (TrackerMetadata *metadata)
+{
+	g_hash_table_foreach_remove (metadata->table,
+				     remove_metadata_foreach,
+				     NULL);
+
+	g_hash_table_destroy (metadata->table);
+	g_slice_free (TrackerMetadata, metadata);
+}
+
+void
+tracker_metadata_insert (TrackerMetadata *metadata,
+			 const gchar	 *field_name,
+			 gchar		 *value)
+{
+	TrackerField *field;
+
+	field = tracker_ontology_get_field_by_name (field_name);
+
+	if (!field) {
+		g_warning ("Field name '%s' has isn't described in the ontology", field_name);
+		g_free (value);
+		return;
+	}
+
+        g_return_if_fail (tracker_field_get_multiple_values (field) == FALSE);
+
+	g_hash_table_insert (metadata->table,
+			     g_object_ref (field),
+			     value);
+}
+
+void
+tracker_metadata_insert_multiple_values (TrackerMetadata *metadata,
+					 const gchar	 *field_name,
+					 GList		 *list)
+{
+	TrackerField *field;
+
+	field = tracker_ontology_get_field_by_name (field_name);
+
+	g_return_if_fail (TRACKER_IS_FIELD (field));
+	g_return_if_fail (tracker_field_get_multiple_values (field) == TRUE);
+
+	g_hash_table_insert (metadata->table,
+			     g_object_ref (field),
+			     list);
+}
+
+G_CONST_RETURN gchar *
+tracker_metadata_lookup (TrackerMetadata *metadata,
+			 const gchar	 *field_name)
+{
+	TrackerField *field;
+
+	field = tracker_ontology_get_field_by_name (field_name);
+
+	g_return_val_if_fail (TRACKER_IS_FIELD (field), NULL);
+	g_return_val_if_fail (tracker_field_get_multiple_values (field) == FALSE, NULL);
+
+	return g_hash_table_lookup (metadata->table, field);
+}
+
+G_CONST_RETURN GList *
+tracker_metadata_lookup_multiple_values (TrackerMetadata *metadata,
+					 const gchar	 *field_name)
+{
+	TrackerField *field;
+
+	field = tracker_ontology_get_field_by_name (field_name);
+
+	g_return_val_if_fail (TRACKER_IS_FIELD (field), NULL);
+	g_return_val_if_fail (tracker_field_get_multiple_values (field) == TRUE, NULL);
+
+	return g_hash_table_lookup (metadata->table, field);
+}
+
+void
+tracker_metadata_foreach (TrackerMetadata	 *metadata,
+			  TrackerMetadataForeach  func,
+			  gpointer		  user_data)
+{
+	g_hash_table_foreach (metadata->table,
+			      (GHFunc) func,
+			      user_data);
+}

Added: trunk/src/tracker-indexer/tracker-metadata.h
==============================================================================
--- (empty file)
+++ trunk/src/tracker-indexer/tracker-metadata.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKER_METADATA_H__
+#define __TRACKER_METADATA_H__
+
+#include <glib.h>
+#include <libtracker-common/tracker-field.h>
+
+typedef struct TrackerMetadata TrackerMetadata;
+
+typedef void (* TrackerMetadataForeach) (TrackerField *field,
+					 gpointer      value,
+					 gpointer      user_data);
+
+TrackerMetadata *      tracker_metadata_new    (void);
+void		       tracker_metadata_free   (TrackerMetadata *metadata);
+
+void		       tracker_metadata_insert		       (TrackerMetadata *metadata,
+								const gchar	*field_name,
+								gchar		*value);
+void		       tracker_metadata_insert_multiple_values (TrackerMetadata *metadata,
+								const gchar	*field_name,
+								GList		*list);
+
+G_CONST_RETURN gchar * tracker_metadata_lookup		       (TrackerMetadata *metadata,
+								const gchar	*field_name);
+G_CONST_RETURN GList * tracker_metadata_lookup_multiple_values (TrackerMetadata *metadata,
+								const gchar	*field_name);
+
+void		       tracker_metadata_foreach (TrackerMetadata	*metadata,
+						 TrackerMetadataForeach  func,
+						 gpointer		 user_data);
+
+G_END_DECLS
+
+#endif /* __TRACKER_METADATA_H__*/

Added: trunk/src/tracker-indexer/tracker-module.h
==============================================================================
--- (empty file)
+++ trunk/src/tracker-indexer/tracker-module.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,71 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKER_MODULE_H__
+#define __TRACKER_MODULE_H__
+
+G_BEGIN_DECLS
+
+#include <glib.h>
+#include <tracker-indexer/tracker-metadata.h>
+
+typedef struct TrackerFile TrackerFile;
+
+struct TrackerFile {
+	gchar	 *path;
+	gpointer  data;
+};
+
+typedef void		  (* TrackerModuleInit)			  (void);
+typedef void		  (* TrackerModuleShutdown)		  (void);
+
+typedef const gchar *	  (* TrackerModuleGetNameFunc)		  (void);
+typedef gchar **	  (* TrackerModuleGetDirectoriesFunc)	  (void);
+
+typedef gpointer	  (* TrackerModuleFileGetDataFunc)	  (const gchar	*path);
+typedef void		  (* TrackerModuleFileFreeDataFunc)	  (gpointer	 data);
+
+typedef gchar *		  (* TrackerModuleFileGetServiceTypeFunc) (TrackerFile	*file);
+typedef void		  (* TrackerModuleFileGetUriFunc)	  (TrackerFile	*file,
+								   gchar       **dirname,
+								   gchar       **basename);
+
+typedef TrackerMetadata * (* TrackerModuleFileGetMetadataFunc)	  (TrackerFile	*file);
+typedef gchar *		  (* TrackerModuleFileGetText)		  (TrackerFile	*path);
+typedef gboolean	  (* TrackerModuleFileIterContents)	  (TrackerFile	*path);
+
+
+void		      tracker_module_init		   (void);
+void		      tracker_module_shutdown		   (void);
+G_CONST_RETURN gchar *tracker_module_get_name		   (void);
+gpointer	      tracker_module_file_get_data	   (const gchar  *path);
+void		      tracker_module_file_free_data	   (gpointer	  file_data);
+gchar *		      tracker_module_file_get_service_type (TrackerFile  *file);
+void		      tracker_module_file_get_uri	   (TrackerFile  *file,
+							    gchar	**dirname,
+							    gchar	**basename);
+TrackerMetadata *     tracker_module_file_get_metadata	   (TrackerFile  *file);
+gchar *		      tracker_module_file_get_text	   (TrackerFile  *file);
+gboolean	      tracker_module_file_iter_contents    (TrackerFile  *file);
+
+G_END_DECLS
+
+#endif /* __TRACKER_MODULE_H__ */

Added: trunk/src/tracker-preferences/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/tracker-preferences/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,43 @@
+include $(top_srcdir)/Makefile.decl
+
+ INTLTOOL_DESKTOP_RULE@
+
+bin_PROGRAMS = tracker-preferences
+
+desktopdir = $(datadir)/applications
+desktop_in_files = tracker-preferences.desktop.in
+desktop_DATA = $(desktop_in_files:.desktop.in=.desktop)
+
+
+pkgdata_DATA = tracker-preferences.glade
+
+INCLUDES = \
+	-DTRACKER_DATADIR=\""$(datadir)/tracker"\" \
+	-DTRACKER_LOCALEDIR=\""$(localedir)"\" \
+	-DTRACKER_BINDIR=\""$(bindir)"\" \
+	$(GLIB_CFLAGS) \
+	$(GTK2_CFLAGS) \
+	$(LIBGLADE_CFLAGS) \
+	$(DBUS_CFLAGS) \
+	-I$(top_srcdir)/src/libtracker
+
+tracker_preferences_SOURCES = \
+	tracker-preferences.c \
+	tracker-preferences.h \
+	tracker-preferences-dialogs.c \
+	tracker-preferences-dialogs.h \
+	tracker-preferences-main.c \
+	tracker-preferences-utils.c \
+	tracker-preferences-utils.h
+
+tracker_preferences_LDADD = \
+	$(GLIB_LIBS) \
+	$(GTK2_LIBS) \
+	$(LIBGLADE_LIBS) \
+	$(DBUS_LIBS) \
+	$(top_builddir)/src/libtracker-common/libtracker-common.la \
+	$(top_builddir)/src/libtracker/libtrackerclient.la
+
+EXTRA_DIST = $(pkgdata_DATA)
+
+CLEANFILES = $(desktop_DATA)

Added: trunk/src/tracker-preferences/tracker-preferences-dialogs.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-preferences/tracker-preferences-dialogs.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,85 @@
+/* Tracker - indexer and metadata database engine
+ * Copyright (C) 2007, Saleem Abdulrasool (compnerd gentoo org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <gtk/gtk.h>
+#include "tracker-preferences-dialogs.h"
+
+static void
+activate_dialog_entry (GtkEntry * entry, gpointer user_data)
+{
+	gtk_dialog_response (GTK_DIALOG (user_data), GTK_RESPONSE_ACCEPT);
+}
+
+gchar *
+tracker_preferences_select_folder (void)
+{
+	gchar *folder = NULL;
+	GtkWidget *dialog =
+		gtk_file_chooser_dialog_new
+		("Tracker Preferences - Choose a folder",
+		 NULL,
+		 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
+		 GTK_STOCK_CANCEL,
+		 GTK_RESPONSE_CANCEL,
+		 GTK_STOCK_OPEN,
+		 GTK_RESPONSE_ACCEPT,
+		 NULL);
+
+	if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
+		folder = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER
+							(dialog));
+
+	gtk_widget_destroy (dialog);
+	return folder;
+}
+
+gchar *
+tracker_preferences_select_pattern (void)
+{
+	gchar *pattern = NULL;
+	GtkWidget *dialog =
+		gtk_dialog_new_with_buttons
+		("Tracker Preferences - Enter a file glob",
+		 NULL,
+		 GTK_DIALOG_MODAL,
+		 GTK_STOCK_OK,
+		 GTK_RESPONSE_ACCEPT,
+		 GTK_STOCK_CANCEL,
+		 GTK_RESPONSE_CANCEL,
+		 NULL);
+
+	GtkWidget *entry = gtk_entry_new ();
+	GtkWidget *label =
+		gtk_label_new ("Enter a file mask that you wish to ignore:");
+
+	g_signal_connect (G_OBJECT (entry), "activate",
+			  G_CALLBACK (activate_dialog_entry), dialog);
+
+	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), label, TRUE,
+			    TRUE, 0);
+	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), entry, TRUE,
+			    TRUE, 0);
+	gtk_widget_show_all (dialog);
+
+	if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
+		pattern = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
+
+	gtk_widget_destroy (dialog);
+	return pattern;
+}

Added: trunk/src/tracker-preferences/tracker-preferences-dialogs.h
==============================================================================
--- (empty file)
+++ trunk/src/tracker-preferences/tracker-preferences-dialogs.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,31 @@
+/* Tracker - indexer and metadata database engine
+ * Copyright (C) 2007, Saleem Abdulrasool (compnerd gentoo org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKER_PREFERENCES_DIALOGS_H__
+#define __TRACKER_PREFERENCES_DIALOGS_H__
+
+#include <glib.h>
+
+gchar *
+tracker_preferences_select_folder (void);
+
+gchar *
+tracker_preferences_select_pattern (void);
+
+#endif

Added: trunk/src/tracker-preferences/tracker-preferences-main.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-preferences/tracker-preferences-main.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,45 @@
+/* Tracker - indexer and metadata database engine
+ * Copyright (C) 2007, Saleem Abdulrasool (compnerd gentoo org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <stdlib.h>
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "tracker-preferences.h"
+#include "config.h"
+
+
+gint
+main (gint argc, gchar * argv[])
+{
+	TrackerPreferences *preferences = NULL;
+
+	bindtextdomain (GETTEXT_PACKAGE, TRACKER_LOCALEDIR);
+	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+	textdomain (GETTEXT_PACKAGE);
+
+	gtk_init (&argc, &argv);
+	preferences = tracker_preferences_new ();
+	gtk_main ();
+
+	g_object_unref (preferences);
+
+	return EXIT_SUCCESS;
+}

Added: trunk/src/tracker-preferences/tracker-preferences-utils.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-preferences/tracker-preferences-utils.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,137 @@
+/* Tracker - indexer and metadata database engine
+ * Copyright (C) 2007, Saleem Abdulrasool (compnerd gentoo org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "tracker-preferences-utils.h"
+
+static const gchar *claws_binaries[] = {
+	"claws-mail",
+	"sylpheed-claws-gtk2",
+	"sylpheed-claws",
+	"sylpheed"
+};
+
+static const gchar *thunderbird_binaries[] = {
+	"thunderbird",
+	"mozilla-thunderbird"
+};
+
+static const gchar *evolution_binaries[] = {
+	"evolution"
+};
+
+static const gchar *kmail_binaries[] = {
+	"kmail"
+};
+
+gchar *
+get_claws_command (void)
+{
+	guint i;
+	gchar *cmd = NULL;
+
+	for (i = 0; cmd == NULL && i < G_N_ELEMENTS (claws_binaries); i++)
+		cmd = g_find_program_in_path (claws_binaries[i]);
+
+	return cmd;
+}
+
+gchar *
+get_thunderbird_command (void)
+{
+	guint i;
+	gchar *cmd = NULL;
+
+	for (i = 0; cmd == NULL && i < G_N_ELEMENTS (thunderbird_binaries);
+	     i++)
+		cmd = g_find_program_in_path (thunderbird_binaries[i]);
+
+	return cmd;
+}
+
+gchar *
+get_evolution_command (void)
+{
+	guint i;
+	gchar *cmd = NULL;
+
+	for (i = 0; cmd == NULL && i < G_N_ELEMENTS (evolution_binaries); i++)
+		cmd = g_find_program_in_path (evolution_binaries[i]);
+
+	return cmd;
+}
+
+gchar *
+get_kmail_command (void)
+{
+	guint i;
+	gchar *cmd = NULL;
+
+	for (i = 0; cmd == NULL && i < G_N_ELEMENTS (kmail_binaries); i++)
+		cmd = g_find_program_in_path (kmail_binaries[i]);
+
+	return cmd;
+}
+
+gboolean
+evolution_available (void)
+{
+	gchar *command = get_evolution_command ();
+
+	if (!command)
+		return FALSE;
+
+	g_free (command);
+	return TRUE;
+}
+
+gboolean
+thunderbird_available (void)
+{
+	gchar *command = get_thunderbird_command ();
+
+	if (!command)
+		return FALSE;
+
+	g_free (command);
+	return TRUE;
+}
+
+gboolean
+kmail_available (void)
+{
+	gchar *command = get_kmail_command ();
+
+	if (!command)
+		return FALSE;
+
+	g_free (command);
+	return TRUE;
+}
+
+gboolean
+convert_available (void)
+{
+	gchar *command = g_find_program_in_path ("convert");
+
+	if (!command)
+		return FALSE;
+
+	g_free (command);
+	return TRUE;
+}

Added: trunk/src/tracker-preferences/tracker-preferences-utils.h
==============================================================================
--- (empty file)
+++ trunk/src/tracker-preferences/tracker-preferences-utils.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,48 @@
+/* Tracker - indexer and metadata database engine
+ * Copyright (C) 2007, Saleem Abdulrasool (compnerd gentoo org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+#ifndef __TRACKER_PREFERENCES_UTILS_H__
+#define __TRACKER_PREFERENCES_UTILS_H__
+
+#include <glib.h>
+
+gchar *
+get_claws_command (void);
+
+gchar *
+get_thunderbird_command (void);
+
+gchar *
+get_evolution_command (void);
+
+gchar *
+get_kmail_command (void);
+
+gboolean
+evolution_available (void);
+
+gboolean
+thunderbird_available (void);
+
+gboolean
+kmail_available (void);
+
+gboolean
+convert_available (void);
+
+#endif

Added: trunk/src/tracker-preferences/tracker-preferences.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-preferences/tracker-preferences.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,1181 @@
+/* Tracker - indexer and metadata database engine
+ * Copyright (C) 2007, Saleem Abdulrasool (compnerd gentoo org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+#include "config.h"
+
+#include <string.h>
+#include <glib/gi18n.h>
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <glade/glade.h>
+#include <stdlib.h>
+
+#include "../libtracker/tracker.h"
+/* #include "../trackerd/tracker-dbus.h" */
+#include "../libtracker-common/tracker-configuration.h"
+
+#include "tracker-preferences.h"
+#include "tracker-preferences-dialogs.h"
+
+#define TRACKER_DBUS_SERVICE   "org.freedesktop.Tracker"
+#define TRACKER_DBUS_PATH      "/org/freedesktop/Tracker"
+#define TRACKER_DBUS_INTERFACE "org.freedesktop.Tracker"
+
+#define TRACKER_PREFERENCES_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), TRACKER_TYPE_PREFERENCES, TrackerPreferencesPrivate))
+
+typedef struct _TrackerPreferencesPrivate {
+	GladeXML *gxml;
+	DBusGConnection *connection;
+	DBusGProxy *dbus_proxy;
+	DBusGProxy *tracker_proxy;
+} TrackerPreferencesPrivate;
+
+static void tracker_preferences_class_init (TrackerPreferencesClass *klass);
+static void tracker_preferences_init (GTypeInstance *instance, gpointer g_class);
+static void tracker_preferences_finalize (GObject *object);
+static void setup_page_general (TrackerPreferences *preferences);
+static void setup_page_files (TrackerPreferences *preferences);
+static void setup_page_emails (TrackerPreferences *preferences);
+static void setup_page_ignored_files (TrackerPreferences *preferences);
+static void setup_page_performance (TrackerPreferences *preferences);
+static void tracker_preferences_cmd_quit (GtkWidget *widget, GdkEvent *event, gpointer data);
+static void tracker_preferences_cmd_help (GtkWidget *widget, gpointer data);
+static void tracker_preferences_cmd_apply (GtkWidget *widget, gpointer data);
+static void tracker_preferences_cmd_cancel (GtkWidget *widget, gpointer data);
+static void tracker_preferences_cmd_ok (GtkWidget *widget, gpointer data);
+static void tracker_preferences_cmd_add_index_path (GtkWidget *widget, gpointer data);
+static void tracker_preferences_cmd_remove_index_path (GtkWidget *widget, gpointer data);
+static void tracker_preferences_cmd_add_crawled_path (GtkWidget *widget, gpointer data);
+static void tracker_preferences_cmd_remove_crawled_path (GtkWidget *widget, gpointer data);
+static void tracker_preferences_cmd_add_index_mailbox (GtkWidget *widget, gpointer data);
+static void tracker_preferences_cmd_remove_index_mailbox (GtkWidget *widget, gpointer data);
+static void tracker_preferences_cmd_add_ignore_path (GtkWidget *widget, gpointer data);
+static void tracker_preferences_cmd_remove_ignore_path (GtkWidget *widget, gpointer data);
+static void tracker_preferences_cmd_add_ignore_pattern (GtkWidget *widget, gpointer data);
+static void tracker_preferences_cmd_remove_ignore_pattern (GtkWidget *widget, gpointer data);
+static void append_item_to_list (TrackerPreferences *dialog, const gchar* const item,
+				 const gchar* const widget);
+static void remove_selection_from_list (TrackerPreferences *dialog,
+					const gchar* const widget);
+static GSList *treeview_get_values (GtkTreeView *treeview);
+static gint _strcmp (gconstpointer a, gconstpointer b);
+static void initialize_listview (GtkWidget *treeview);
+static void populate_list (GtkWidget *treeview, GSList *list);
+static gboolean str_slist_equal (GSList *a, GSList *b);
+
+static GObjectClass *parent_class = NULL;
+static gboolean flag_restart = FALSE;
+static gboolean flag_reindex = FALSE;
+static gboolean first_time = TRUE;
+static gboolean flag_quit = FALSE;
+static GtkWidget *main_window = NULL;
+
+
+static void
+tracker_preferences_class_init (TrackerPreferencesClass *klass)
+{
+	GObjectClass *g_class = G_OBJECT_CLASS (klass);
+	parent_class = g_type_class_peek_parent (klass);
+
+	g_type_class_add_private (klass, sizeof (TrackerPreferencesPrivate));
+
+	g_class->finalize = tracker_preferences_finalize;
+}
+
+static void
+tracker_preferences_init (GTypeInstance *instance, gpointer g_class)
+{
+	TrackerPreferences *self = TRACKER_PREFERENCES (instance);
+	TrackerPreferencesPrivate *priv =
+		TRACKER_PREFERENCES_GET_PRIVATE (self);
+
+	tracker_configuration_load ();
+
+	GtkWidget *widget = NULL;
+
+	priv->gxml =
+		glade_xml_new (TRACKER_DATADIR "/tracker-preferences.glade",
+			       NULL, NULL);
+
+	if (priv->gxml == NULL)
+		g_error ("Unable to find locate tracker-preferences.glade");
+
+	main_window = glade_xml_get_widget (priv->gxml, "dlgPreferences");
+
+	/* Hide window first to allow the dialog to reize itself without redrawing */
+	gtk_widget_hide (main_window);
+
+	gtk_window_set_icon_name (GTK_WINDOW (main_window), "tracker");
+	g_signal_connect (main_window, "delete-event",
+			  G_CALLBACK (tracker_preferences_cmd_quit), self);
+
+	/* Setup signals */
+	widget = glade_xml_get_widget (priv->gxml, "cmdHelp");
+	g_signal_connect (widget, "clicked", G_CALLBACK (tracker_preferences_cmd_help),
+			  self);
+	gtk_widget_hide (widget);
+
+	widget = glade_xml_get_widget (priv->gxml, "dialog-action_area1");
+	gtk_button_box_set_layout (GTK_BUTTON_BOX (widget),
+				   GTK_BUTTONBOX_END);
+
+	widget = glade_xml_get_widget (priv->gxml, "cmdApply");
+	g_signal_connect (widget, "clicked", G_CALLBACK (tracker_preferences_cmd_apply),
+			  self);
+
+	widget = glade_xml_get_widget (priv->gxml, "cmdCancel");
+	g_signal_connect (widget, "clicked", G_CALLBACK (tracker_preferences_cmd_cancel),
+			  self);
+
+	widget = glade_xml_get_widget (priv->gxml, "cmdOK");
+	g_signal_connect (widget, "clicked", G_CALLBACK (tracker_preferences_cmd_ok),
+			  self);
+
+	widget = glade_xml_get_widget (priv->gxml, "cmdAddIndexPath");
+	g_signal_connect (widget, "clicked",
+			  G_CALLBACK (tracker_preferences_cmd_add_index_path), self);
+
+	widget = glade_xml_get_widget (priv->gxml, "cmdRemoveIndexPath");
+	g_signal_connect (widget, "clicked",
+			  G_CALLBACK (tracker_preferences_cmd_remove_index_path), self);
+
+	widget = glade_xml_get_widget (priv->gxml, "cmdAddCrawledPath");
+	g_signal_connect (widget, "clicked",
+			  G_CALLBACK (tracker_preferences_cmd_add_crawled_path), self);
+
+	widget = glade_xml_get_widget (priv->gxml, "cmdRemoveCrawledPath");
+	g_signal_connect (widget, "clicked",
+			  G_CALLBACK (tracker_preferences_cmd_remove_crawled_path), self);
+
+	widget = glade_xml_get_widget (priv->gxml, "cmdAddIndexMailbox");
+	g_signal_connect (widget, "clicked",
+			  G_CALLBACK (tracker_preferences_cmd_add_index_mailbox), self);
+
+	widget = glade_xml_get_widget (priv->gxml, "cmdRemoveIndexMailbox");
+	g_signal_connect (widget, "clicked",
+			  G_CALLBACK (tracker_preferences_cmd_remove_index_mailbox), self);
+
+	widget = glade_xml_get_widget (priv->gxml, "cmdAddIgnorePath");
+	g_signal_connect (widget, "clicked",
+			  G_CALLBACK (tracker_preferences_cmd_add_ignore_path), self);
+
+	widget = glade_xml_get_widget (priv->gxml, "cmdRemoveIgnorePath");
+	g_signal_connect (widget, "clicked",
+			  G_CALLBACK (tracker_preferences_cmd_remove_ignore_path), self);
+
+	widget = glade_xml_get_widget (priv->gxml, "cmdAddIgnorePattern");
+	g_signal_connect (widget, "clicked",
+			  G_CALLBACK (tracker_preferences_cmd_add_ignore_pattern), self);
+
+	widget = glade_xml_get_widget (priv->gxml, "cmdRemoveIgnorePattern");
+	g_signal_connect (widget, "clicked",
+			  G_CALLBACK (tracker_preferences_cmd_remove_ignore_pattern), self);
+
+	/* Init dbus */
+	GError *error = NULL;
+	g_type_init ();
+
+	priv->connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
+
+	if (priv->connection == NULL) {
+		g_warning ("Unable to connect to dbus: %s\n", error->message);
+		g_error_free (error);
+		return;
+	}
+
+	priv->dbus_proxy = dbus_g_proxy_new_for_name (priv->connection,
+						      DBUS_SERVICE_DBUS,
+						      DBUS_PATH_DBUS,
+						      DBUS_INTERFACE_DBUS);
+
+	if (!priv->dbus_proxy) {
+		g_warning ("could not create proxy");
+		return;
+	}
+
+	priv->tracker_proxy = dbus_g_proxy_new_for_name (priv->connection,
+							 TRACKER_DBUS_SERVICE,
+							 TRACKER_DBUS_PATH,
+							 TRACKER_DBUS_INTERFACE);
+
+	if (!priv->tracker_proxy) {
+		g_warning ("could not create proxy");
+		return;
+	}
+
+	/* setup pages */
+	setup_page_general (self);
+	setup_page_files (self);
+	setup_page_emails (self);
+	setup_page_ignored_files (self);
+	setup_page_performance (self);
+
+	gtk_widget_show (main_window);
+}
+
+static void
+tracker_preferences_finalize (GObject *object)
+{
+	TrackerPreferences *self = TRACKER_PREFERENCES (object);
+	TrackerPreferencesPrivate *priv =
+		TRACKER_PREFERENCES_GET_PRIVATE (self);
+
+	g_object_unref (priv->gxml);
+
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+TrackerPreferences *
+tracker_preferences_new (void)
+{
+	TrackerPreferences *prefs;
+	prefs = g_object_new (TRACKER_TYPE_PREFERENCES, NULL);
+	return TRACKER_PREFERENCES (prefs);
+}
+
+
+static void
+set_bool_option (TrackerPreferencesPrivate *priv, const gchar *name, gboolean value)
+{
+	dbus_g_proxy_begin_call (priv->tracker_proxy,
+				 "SetBoolOption",
+				 NULL,
+				 NULL,
+				 NULL,
+				 G_TYPE_STRING, name,
+				 G_TYPE_BOOLEAN, value,
+				 G_TYPE_INVALID);
+}
+
+
+static void
+set_int_option (TrackerPreferencesPrivate *priv, const gchar *name, int value)
+{
+	dbus_g_proxy_begin_call (priv->tracker_proxy,
+				 "SetIntOption",
+				 NULL,
+				 NULL,
+				 NULL,
+				 G_TYPE_STRING, name,
+				 G_TYPE_INT, value,
+				 G_TYPE_INVALID);
+}
+
+static void
+setup_page_general (TrackerPreferences *preferences)
+{
+	TrackerPreferences *self = TRACKER_PREFERENCES (preferences);
+	TrackerPreferencesPrivate *priv =
+		TRACKER_PREFERENCES_GET_PRIVATE (self);
+
+	gint sleep = 45;
+	gchar *str_value = NULL;
+	gboolean value = FALSE;
+	GtkWidget *widget = NULL;
+
+	widget = glade_xml_get_widget (priv->gxml, "spnInitialSleep");
+	sleep = tracker_configuration_get_integer ("/General/InitialSleep", NULL);
+	gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), sleep);
+
+	widget = glade_xml_get_widget (priv->gxml, "chkEnableIndexing");
+	value = tracker_configuration_get_boolean ("/Indexing/EnableIndexing", NULL);
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), value);
+
+	widget = glade_xml_get_widget (priv->gxml, "chkEnableWatching");
+	value = tracker_configuration_get_boolean ("/Watches/EnableWatching", NULL);
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), value);
+
+	widget = glade_xml_get_widget (priv->gxml, "comLanguage");
+	str_value = tracker_configuration_get_string ("/Indexing/Language", NULL);
+	if (str_value == NULL) {
+		/* no value for language? Default to "en" */
+		str_value = g_strdup( "en" ) ;
+	}
+
+	gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 2);
+
+	gint i;
+	for (i = 0; i < 12; i++) {
+		if (strcasecmp (LanguageMap[i].language, str_value) == 0) {
+			gtk_combo_box_set_active (GTK_COMBO_BOX (widget), i);
+			break;
+		}
+	}
+
+	widget = glade_xml_get_widget (priv->gxml, "chkDisableBatteryIndex");
+	value = tracker_configuration_get_boolean ("/Indexing/BatteryIndex", NULL);
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), !value);
+
+	widget = glade_xml_get_widget (priv->gxml, "chkDisableBatteryInitialIndex");
+	value = tracker_configuration_get_boolean ("/Indexing/BatteryIndexInitial", NULL);
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), !value);
+}
+
+static void
+setup_page_performance (TrackerPreferences *preferences)
+{
+	TrackerPreferences *self = TRACKER_PREFERENCES (preferences);
+	TrackerPreferencesPrivate *priv =
+		TRACKER_PREFERENCES_GET_PRIVATE (self);
+
+	GtkWidget *widget = NULL;
+	gint value = 0;
+	gboolean bvalue = FALSE;
+
+	widget = glade_xml_get_widget (priv->gxml, "scaThrottle");
+	value = tracker_configuration_get_integer("/Indexing/Throttle", NULL);
+	gtk_range_set_value (GTK_RANGE (widget), value);
+
+	widget = glade_xml_get_widget (priv->gxml, "optReducedMemory");
+	bvalue = tracker_configuration_get_boolean ("/General/LowMemoryMode", NULL);
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), bvalue);
+
+	widget = glade_xml_get_widget (priv->gxml, "optNormal");
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), !bvalue);
+
+	widget = glade_xml_get_widget (priv->gxml, "chkFastMerges");
+	bvalue = tracker_configuration_get_boolean ("/Indexing/FastMerges", NULL);
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), bvalue);
+
+	widget = glade_xml_get_widget (priv->gxml, "spnMaxText");
+	value = tracker_configuration_get_integer ("/Performance/MaxTextToIndex", NULL);
+
+	value = value / 1024;
+	gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), value);
+
+	widget = glade_xml_get_widget (priv->gxml, "spnMaxWords");
+	value = tracker_configuration_get_integer ("/Performance/MaxWordsToIndex", NULL);
+
+	gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), value);
+
+}
+
+static void
+setup_page_files (TrackerPreferences *preferences)
+{
+	TrackerPreferences *self = TRACKER_PREFERENCES (preferences);
+	TrackerPreferencesPrivate *priv =
+		TRACKER_PREFERENCES_GET_PRIVATE (self);
+
+	GSList *list = NULL, *entry = NULL;
+	gboolean value = FALSE;
+	GtkWidget *widget = NULL;
+
+	widget = glade_xml_get_widget (priv->gxml, "chkIndexContents");
+	value = tracker_configuration_get_boolean ("/Indexing/EnableFileContentIndexing",
+						   NULL);
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), value);
+
+	widget = glade_xml_get_widget (priv->gxml, "chkGenerateThumbs");
+	value = tracker_configuration_get_boolean ("/Indexing/EnableThumbnails", NULL);
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), value);
+
+	widget = glade_xml_get_widget (priv->gxml, "chkSkipMountPoints");
+	value = tracker_configuration_get_boolean ("/Indexing/SkipMountPoints", NULL);
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), !value);
+
+	widget = glade_xml_get_widget (priv->gxml,
+				       "lstAdditionalPathIndexes");
+	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (widget), FALSE);
+	list = tracker_configuration_get_list ("/Watches/WatchDirectoryRoots",
+					       G_TYPE_STRING, NULL);
+
+	widget = glade_xml_get_widget (priv->gxml, "chkIndexHomeDirectory");
+	entry = g_slist_find_custom (list, g_get_home_dir (), _strcmp);
+
+	if (entry) {
+		list = g_slist_delete_link (list, entry);
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
+	} else {
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), FALSE);
+	}
+
+	widget = glade_xml_get_widget (priv->gxml, "lstAdditionalPathIndexes");
+
+	initialize_listview (widget);
+	populate_list (widget, list);
+	g_slist_free (list);
+
+	widget = glade_xml_get_widget (priv->gxml, "lstCrawledPaths");
+	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (widget), FALSE);
+	list = tracker_configuration_get_list ("/Watches/CrawlDirectory",
+					       G_TYPE_STRING, NULL);
+
+	initialize_listview (widget);
+	populate_list (widget, list);
+	g_slist_free (list);
+}
+
+static void
+setup_page_ignored_files (TrackerPreferences *preferences)
+{
+	TrackerPreferences *self = TRACKER_PREFERENCES (preferences);
+	TrackerPreferencesPrivate *priv =
+		TRACKER_PREFERENCES_GET_PRIVATE (self);
+
+	GSList *list = NULL;
+	GtkWidget *widget = NULL;
+
+	/* Ignore Paths */
+	widget = glade_xml_get_widget (priv->gxml, "lstIgnorePaths");
+	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (widget), FALSE);
+	list = tracker_configuration_get_list ("/Watches/NoWatchDirectory",
+					       G_TYPE_STRING, NULL);
+
+	initialize_listview (widget);
+	populate_list (widget, list);
+
+	g_slist_free (list);
+
+	/* Ignore File Patterns */
+	widget = glade_xml_get_widget (priv->gxml, "lstIgnoreFilePatterns");
+	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (widget), FALSE);
+	list = tracker_configuration_get_list ("/Indexing/NoIndexFileTypes",
+					       G_TYPE_STRING, NULL);
+
+	initialize_listview (widget);
+	populate_list (widget, list);
+
+	g_slist_free (list);
+}
+
+static void
+setup_page_emails (TrackerPreferences *preferences)
+{
+	TrackerPreferences *self = TRACKER_PREFERENCES (preferences);
+	TrackerPreferencesPrivate *priv =
+		TRACKER_PREFERENCES_GET_PRIVATE (self);
+
+	GtkWidget *widget = NULL;
+	gboolean value = FALSE;
+
+	widget = glade_xml_get_widget (priv->gxml,
+				       "chkEnableEvolutionIndexing");
+	value = tracker_configuration_get_boolean ("/Emails/IndexEvolutionEmails",
+						   NULL);
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), value);
+
+	widget = glade_xml_get_widget (priv->gxml,
+				       "chkEnableModestIndexing");
+	value = tracker_configuration_get_boolean ("/Emails/IndexModestEmails",
+						   NULL);
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), value);
+
+
+	widget = glade_xml_get_widget (priv->gxml,
+				       "chkEnableThunderbirdIndexing");
+	value = tracker_configuration_get_boolean ("/Emails/IndexThunderbirdEmails",
+						   NULL);
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), value);
+}
+
+static void
+tracker_preferences_cmd_quit (GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+	tracker_preferences_cmd_ok (NULL, data);
+}
+
+static void
+tracker_preferences_cmd_help (GtkWidget *widget, gpointer data)
+{
+}
+
+static void
+name_owner_changed (DBusGProxy *proxy, const gchar *name,
+		    const gchar *prev_owner, const gchar *new_owner,
+		    gpointer data)
+{
+	if (!g_str_equal (name, TRACKER_DBUS_SERVICE))
+		return;
+
+	if (!first_time)
+		return;
+
+	if (g_str_equal (new_owner, "")) {
+		/* tracker has exited */
+		const gchar *command = TRACKER_BINDIR "/trackerd";
+
+		if (!g_spawn_command_line_async (command, NULL))
+			g_warning ("Unable to execute command: %s", command);
+
+		first_time = FALSE;
+
+		if (flag_quit)
+			gtk_main_quit ();
+	}
+}
+
+static gboolean
+if_trackerd_start (TrackerPreferencesPrivate *priv)
+{
+	gchar *status = NULL;
+	TrackerClient *client = NULL;
+
+	client = tracker_connect (FALSE);
+
+	if (!client)
+		return FALSE;
+
+	status = tracker_get_status (client, NULL);
+	tracker_disconnect (client);
+
+	if (strcmp (status, "Shutdown") == 0)
+		return FALSE;
+	else
+		return TRUE;
+}
+
+
+static void
+restart_tracker (GtkDialog *dialog, gint response, gpointer data)
+{
+
+	gtk_widget_destroy (GTK_WIDGET (dialog));
+
+	if (response == GTK_RESPONSE_YES) {
+
+		TrackerPreferences *self = TRACKER_PREFERENCES (data);
+		TrackerPreferencesPrivate *priv = TRACKER_PREFERENCES_GET_PRIVATE (self);
+
+		first_time = TRUE;
+		dbus_g_proxy_add_signal (priv->dbus_proxy,
+					 "NameOwnerChanged",
+					 G_TYPE_STRING,
+					 G_TYPE_STRING,
+					 G_TYPE_STRING, G_TYPE_INVALID);
+		dbus_g_proxy_connect_signal (priv->dbus_proxy,
+					     "NameOwnerChanged",
+					     G_CALLBACK (name_owner_changed),
+					     self, NULL);
+
+		dbus_g_proxy_begin_call (priv->tracker_proxy,
+					 "Shutdown",
+					 NULL,
+					 NULL,
+					 NULL,
+					 G_TYPE_BOOLEAN,
+					 flag_reindex, G_TYPE_INVALID);
+		flag_restart = FALSE;
+		flag_reindex = FALSE;
+
+	} else if (flag_quit) {
+		gtk_main_quit ();
+	}
+}
+
+static void
+tracker_preferences_cmd_apply (GtkWidget *widget, gpointer data)
+{
+	TrackerPreferences *self = TRACKER_PREFERENCES (data);
+	TrackerPreferencesPrivate *priv =
+		TRACKER_PREFERENCES_GET_PRIVATE (self);
+
+	GSList *list = NULL;
+	GSList *list_old = NULL;
+	int ivalue, ivalue_old;
+	gboolean bvalue, bvalue_old;
+	gchar *str_value;
+
+	/* save general settings */
+	widget = glade_xml_get_widget (priv->gxml, "spnInitialSleep");
+	ivalue = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (widget));
+	tracker_configuration_set_integer ("/General/InitialSleep", ivalue);
+
+	widget = glade_xml_get_widget (priv->gxml, "chkEnableIndexing");
+	bvalue = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+	bvalue_old = tracker_configuration_get_boolean ("/Indexing/EnableIndexing",
+							NULL);
+	if (bvalue != bvalue_old) {
+		flag_restart = TRUE;
+		set_bool_option (priv, "EnableIndexing", bvalue);
+		tracker_configuration_set_boolean ("/Indexing/EnableIndexing", bvalue);
+	}
+
+	widget = glade_xml_get_widget (priv->gxml, "chkEnableWatching");
+	bvalue = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+	bvalue_old = tracker_configuration_get_boolean ("/Watches/EnableWatching",
+							NULL);
+	if (bvalue != bvalue_old) {
+		flag_restart = TRUE;
+		set_bool_option (priv, "EnableIndexing", bvalue);
+		tracker_configuration_set_boolean ("/Watches/EnableWatching", bvalue);
+	}
+
+	widget = glade_xml_get_widget (priv->gxml, "comLanguage");
+	gint i = gtk_combo_box_get_active (GTK_COMBO_BOX (widget));
+	str_value = tracker_configuration_get_string ("/Indexing/Language", NULL);
+	if (str_value == NULL) {
+		/* no value for language? Default to "en" */
+		str_value = g_strdup( "en" ) ;
+	}
+	if (i != -1) {
+		if (strcmp (str_value, LanguageMap[i].language) != 0) {
+			flag_restart = TRUE;
+			flag_reindex = TRUE;
+		}
+		tracker_configuration_set_string ("/Indexing/Language",
+						  LanguageMap[i].language);
+	}
+
+	widget = glade_xml_get_widget (priv->gxml, "chkDisableBatteryIndex");
+	bvalue = !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+	bvalue_old = tracker_configuration_get_boolean ("/Indexing/BatteryIndex",
+							NULL);
+	if (bvalue != bvalue_old) {
+		set_bool_option (priv, "BatteryIndex", bvalue);
+		tracker_configuration_set_boolean ("/Indexing/BatteryIndex", bvalue);
+	}
+
+	widget = glade_xml_get_widget (priv->gxml, "chkDisableBatteryInitialIndex");
+	bvalue = !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+	bvalue_old = tracker_configuration_get_boolean ("/Indexing/BatteryIndexInitial",
+							NULL);
+	if (bvalue != bvalue_old) {
+		set_bool_option (priv, "BatteryIndexInitial", bvalue);
+		tracker_configuration_set_boolean ("/Indexing/BatteryIndexInitial", bvalue);
+	}
+
+	/* files settings */
+	widget = glade_xml_get_widget (priv->gxml, "chkIndexContents");
+	bvalue = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+	bvalue_old = tracker_configuration_get_boolean ("/Indexing/EnableFileContentIndexing",
+							NULL);
+	if (bvalue != bvalue_old) {
+		flag_restart = TRUE;
+		flag_reindex = TRUE;
+		set_bool_option (priv, "IndexFileContents", bvalue);
+		tracker_configuration_set_boolean ("/Indexing/EnableFileContentIndexing",
+						   bvalue);
+	}
+
+	widget = glade_xml_get_widget (priv->gxml, "chkGenerateThumbs");
+	bvalue = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+	bvalue_old = tracker_configuration_get_boolean ("/Indexing/EnableThumbnails",
+							NULL);
+	if (bvalue != bvalue_old) {
+		flag_restart = TRUE;
+		flag_reindex = TRUE;
+		set_bool_option (priv, "GenerateThumbs", bvalue);
+		tracker_configuration_set_boolean ("/Indexing/EnableThumbnails", bvalue);
+	}
+
+	widget = glade_xml_get_widget (priv->gxml, "chkSkipMountPoints");
+	bvalue = !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+	bvalue_old = tracker_configuration_get_boolean ("/Indexing/SkipMountPoints",
+							NULL);
+	if (bvalue != bvalue_old) {
+		flag_restart = TRUE;
+		set_bool_option (priv, "SkipMountPoints", bvalue);
+		tracker_configuration_set_boolean ("/Indexing/SkipMountPoints", bvalue);
+	}
+
+	widget = glade_xml_get_widget (priv->gxml, "lstAdditionalPathIndexes");
+	list = treeview_get_values (GTK_TREE_VIEW (widget));
+
+	widget = glade_xml_get_widget (priv->gxml, "chkIndexHomeDirectory");
+	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) {
+		list = g_slist_prepend (list, g_strdup (g_get_home_dir ()));
+	}
+	list_old = tracker_configuration_get_list ("/Watches/WatchDirectoryRoots",
+						   G_TYPE_STRING, NULL);
+	if (!str_slist_equal (list, list_old)) {
+		flag_restart = TRUE;
+		tracker_configuration_set_list ("/Watches/WatchDirectoryRoots", G_TYPE_STRING,
+						list);
+	}
+
+	g_slist_free (list);
+	list = NULL;
+	g_slist_free (list_old);
+	list_old = NULL;
+
+	widget = glade_xml_get_widget (priv->gxml, "lstCrawledPaths");
+	list = treeview_get_values (GTK_TREE_VIEW (widget));
+	list_old = tracker_configuration_get_list ("/Watches/CrawlDirectory",
+						   G_TYPE_STRING, NULL);
+	if (!str_slist_equal (list, list_old)) {
+		flag_restart = TRUE;
+		tracker_configuration_set_list ("/Watches/CrawlDirectory", G_TYPE_STRING,
+						list);
+	}
+
+	g_slist_free (list);
+	list = NULL;
+	g_slist_free (list_old);
+	list_old = NULL;
+
+	/* ignored files settings */
+	widget = glade_xml_get_widget (priv->gxml, "lstIgnorePaths");
+	list = treeview_get_values (GTK_TREE_VIEW (widget));
+	list_old = tracker_configuration_get_list ("/Watches/NoWatchDirectory",
+						   G_TYPE_STRING, NULL);
+	if (!str_slist_equal (list, list_old)) {
+		flag_restart = TRUE;
+		tracker_configuration_set_list ("/Watches/NoWatchDirectory", G_TYPE_STRING,
+						list);
+	}
+
+	g_slist_free (list);
+	list = NULL;
+	g_slist_free (list_old);
+	list_old = NULL;
+
+	widget = glade_xml_get_widget (priv->gxml, "lstIgnoreFilePatterns");
+	list = treeview_get_values (GTK_TREE_VIEW (widget));
+	list_old = tracker_configuration_get_list ("/Indexing/NoIndexFileTypes",
+						   G_TYPE_STRING, NULL);
+	if (!str_slist_equal (list, list_old)) {
+		flag_restart = TRUE;
+		tracker_configuration_set_list ("/Indexing/NoIndexFileTypes", G_TYPE_STRING,
+						list);
+	}
+
+	g_slist_free (list);
+	list = NULL;
+	g_slist_free (list_old);
+	list_old = NULL;
+
+	/* Email settings */
+	widget = glade_xml_get_widget (priv->gxml, "chkEnableEvolutionIndexing");
+	bvalue = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+	bvalue_old = tracker_configuration_get_boolean ("/Emails/IndexEvolutionEmails",
+							NULL);
+	if (bvalue != bvalue_old) {
+		set_bool_option (priv, "EnableEvolution", bvalue);
+		tracker_configuration_set_boolean ("/Emails/IndexEvolutionEmails", bvalue);
+	}
+
+
+	widget = glade_xml_get_widget (priv->gxml, "chkEnableModestIndexing");
+	bvalue = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+	bvalue_old = tracker_configuration_get_boolean ("/Emails/IndexModestEmails",
+							NULL);
+	if (bvalue != bvalue_old) {
+		set_bool_option (priv, "EnableModest", bvalue);
+		tracker_configuration_set_boolean ("/Emails/IndexModestEmails", bvalue);
+	}
+
+	widget = glade_xml_get_widget (priv->gxml, "chkEnableThunderbirdIndexing");
+	bvalue = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+	bvalue_old = tracker_configuration_get_boolean ("/Emails/IndexThunderbirdEmails",
+							NULL);
+	if (bvalue != bvalue_old) {
+		set_bool_option (priv, "EnableThunderbird", bvalue);
+		tracker_configuration_set_boolean ("/Emails/IndexThunderbirdEmails", bvalue);
+	}
+
+	/* Performance settings */
+	widget = glade_xml_get_widget (priv->gxml, "scaThrottle");
+	ivalue = gtk_range_get_value (GTK_RANGE (widget));
+	ivalue_old = tracker_configuration_get_integer ("/Indexing/Throttle", NULL);
+	if (ivalue != ivalue_old) {
+		set_int_option (priv, "Throttle", ivalue);
+		tracker_configuration_set_integer ("/Indexing/Throttle", ivalue);
+	}
+
+	widget = glade_xml_get_widget (priv->gxml, "optReducedMemory");
+	bvalue = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+	bvalue_old = tracker_configuration_get_boolean ("/General/LowMemoryMode",
+							NULL);
+	if (bvalue != bvalue_old) {
+		set_bool_option (priv, "LowMemoryMode", bvalue);
+		tracker_configuration_set_boolean ("/General/LowMemoryMode", bvalue);
+	}
+
+	widget = glade_xml_get_widget (priv->gxml, "chkFastMerges");
+	bvalue = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+	bvalue_old = tracker_configuration_get_boolean ("/Indexing/FastMerges", NULL);
+	if (bvalue != bvalue_old) {
+		set_bool_option (priv, "FastMerges", bvalue);
+		tracker_configuration_set_boolean ("/Indexing/FastMerges", bvalue);
+	}
+
+	widget = glade_xml_get_widget (priv->gxml, "spnMaxText");
+	ivalue = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (widget))*1024;
+	ivalue_old = tracker_configuration_get_integer ("/Performance/MaxTextToIndex",
+							NULL);
+	if (ivalue != ivalue_old) {
+		set_int_option (priv, "MaxText", ivalue);
+		tracker_configuration_set_integer ("/Performance/MaxTextToIndex", ivalue);
+	}
+
+	widget = glade_xml_get_widget (priv->gxml, "spnMaxWords");
+	ivalue = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (widget));
+	ivalue_old = tracker_configuration_get_integer ("/Performance/MaxWordsToIndex",
+							NULL);
+	if (ivalue != ivalue_old) {
+		set_int_option (priv, "MaxWords", ivalue);
+		tracker_configuration_set_integer ("/Performance/MaxWordsToIndex", ivalue);
+	}
+
+	/* save config to distk */
+	tracker_configuration_save ();
+
+	if (flag_restart && if_trackerd_start (priv)) {
+		GtkWidget *dialog;
+		gchar *primary;
+		gchar *secondary;
+		gchar *button;
+
+		if (flag_reindex) {
+			primary = g_strdup (_("Data must be reindexed"));
+			secondary = g_strdup (_("In order for your changes to "
+						"take effect, Tracker must reindex your "
+						"files. Click the Reindex button to "
+						"start reindexing now, otherwise this "
+						"action will be performed the "
+						"next time the Tracker daemon "
+						"is restarted."));
+			button = g_strdup (_("_Reindex"));
+
+		} else {
+			primary = g_strdup (_("Tracker daemon must be "
+					      "restarted"));
+			secondary = g_strdup (_("In order for your changes to "
+						"take effect, the Tracker daemon "
+						"has to be restarted. Click the "
+						"Restart button to restart the "
+						"daemon now."));
+			button = g_strdup (_("_Restart"));
+		}
+
+		dialog = gtk_message_dialog_new (GTK_WINDOW (main_window),
+						 GTK_DIALOG_MODAL,
+						 GTK_MESSAGE_WARNING,
+						 GTK_BUTTONS_NONE,
+						 primary);
+
+		gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+							  secondary);
+
+		gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+					GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
+					button, GTK_RESPONSE_YES, NULL);
+
+		g_free (primary);
+		g_free (secondary);
+		g_free (button);
+
+		gtk_window_set_title (GTK_WINDOW (dialog), "");
+		gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+		gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 14);
+
+		g_signal_connect (G_OBJECT (dialog), "response", G_CALLBACK (restart_tracker), self);
+
+		gtk_widget_show (dialog);
+	} else if (flag_quit) {
+		tracker_configuration_free ();
+		gtk_main_quit ();
+	}
+}
+
+static void
+tracker_preferences_cmd_cancel (GtkWidget *widget, gpointer data)
+{
+	tracker_configuration_free ();
+	gtk_main_quit ();
+}
+
+static void
+tracker_preferences_cmd_ok (GtkWidget *widget, gpointer data)
+{
+	flag_quit = TRUE;
+	tracker_preferences_cmd_apply (widget, data);
+}
+
+static void
+tracker_preferences_cmd_add_crawled_path (GtkWidget *widget, gpointer data)
+{
+	TrackerPreferences *self = TRACKER_PREFERENCES (data);
+	gchar *path = tracker_preferences_select_folder ();
+
+	if (!path)
+		return;
+
+	append_item_to_list (self, path, "lstCrawledPaths");
+
+	g_free (path);
+}
+
+static void
+tracker_preferences_cmd_remove_crawled_path (GtkWidget *widget, gpointer data)
+{
+	TrackerPreferences *self = TRACKER_PREFERENCES (data);
+	remove_selection_from_list (self, "lstCrawledPaths");
+}
+
+static void
+tracker_preferences_cmd_add_index_path (GtkWidget *widget, gpointer data)
+{
+	TrackerPreferences *self = TRACKER_PREFERENCES (data);
+	TrackerPreferencesPrivate *priv =
+		TRACKER_PREFERENCES_GET_PRIVATE (self);
+
+	GtkWidget *item = NULL;
+	gchar *path = tracker_preferences_select_folder ();
+
+	if (!path)
+		return;
+
+	if (!strcasecmp (path, g_get_home_dir ())) {
+		item = glade_xml_get_widget (priv->gxml,
+					     "chkIndexHomeDirectory");
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (item), TRUE);
+	} else
+		append_item_to_list (self, path, "lstAdditionalPathIndexes");
+
+	g_free (path);
+}
+
+static void
+tracker_preferences_cmd_remove_index_path (GtkWidget *widget, gpointer data)
+{
+	TrackerPreferences *self = TRACKER_PREFERENCES (data);
+	remove_selection_from_list (self, "lstAdditionalPathIndexes");
+}
+
+static void
+tracker_preferences_cmd_add_index_mailbox (GtkWidget *widget, gpointer data)
+{
+	TrackerPreferences *self = TRACKER_PREFERENCES (data);
+
+	gchar *path = tracker_preferences_select_folder ();
+
+	if (!path)
+		return;
+
+	append_item_to_list (self, path, "lstAdditionalMBoxIndexes");
+	g_free (path);
+}
+
+static void
+tracker_preferences_cmd_remove_index_mailbox (GtkWidget *widget, gpointer data)
+{
+	TrackerPreferences *self = TRACKER_PREFERENCES (data);
+	remove_selection_from_list (self, "lstAdditionalMBoxIndexes");
+}
+
+static void
+tracker_preferences_cmd_add_ignore_path (GtkWidget *widget, gpointer data)
+{
+	TrackerPreferences *self = TRACKER_PREFERENCES (data);
+
+	gchar *path = tracker_preferences_select_folder ();
+
+	if (!path)
+		return;
+
+	append_item_to_list (self, path, "lstIgnorePaths");
+	g_free (path);
+}
+
+static void
+tracker_preferences_cmd_remove_ignore_path (GtkWidget *widget, gpointer data)
+{
+	TrackerPreferences *self = TRACKER_PREFERENCES (data);
+	remove_selection_from_list (self, "lstIgnorePaths");
+}
+
+static void
+tracker_preferences_cmd_add_ignore_pattern (GtkWidget *widget, gpointer data)
+{
+	TrackerPreferences *self = TRACKER_PREFERENCES (data);
+
+	gchar *pattern = tracker_preferences_select_pattern ();
+
+	if (!pattern)
+		return;
+
+	append_item_to_list (self, pattern, "lstIgnoreFilePatterns");
+}
+
+static void
+tracker_preferences_cmd_remove_ignore_pattern (GtkWidget *widget, gpointer data)
+{
+	TrackerPreferences *self = TRACKER_PREFERENCES (data);
+	remove_selection_from_list (self, "lstIgnoreFilePatterns");
+}
+
+static void
+append_item_to_list (TrackerPreferences *dialog, const gchar *const item,
+		     const gchar* const widget)
+{
+	TrackerPreferencesPrivate *priv =
+		TRACKER_PREFERENCES_GET_PRIVATE (dialog);
+
+	GtkTreeIter iter;
+	GtkWidget *view = glade_xml_get_widget (priv->gxml, widget);
+	GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
+
+	if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter))
+		do {
+			gchar *value = NULL;
+			gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, 0,
+					    &value, -1);
+
+			if (!strcasecmp (item, value))
+				return;
+		} while (gtk_tree_model_iter_next
+			 (GTK_TREE_MODEL (model), &iter));
+
+	gtk_list_store_append (GTK_LIST_STORE (model), &iter);
+	gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, item, -1);
+}
+
+static void
+remove_selection_from_list (TrackerPreferences *dialog,
+			    const gchar* const widget)
+{
+	TrackerPreferencesPrivate *priv =
+		TRACKER_PREFERENCES_GET_PRIVATE (dialog);
+
+	GtkTreeIter iter;
+	GtkWidget *view = glade_xml_get_widget (priv->gxml, widget);
+	GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
+	GtkTreeSelection *selection =
+		gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+
+	if (!gtk_tree_selection_get_selected (selection, &model, &iter))
+		return;
+
+	gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
+}
+
+static GSList *
+treeview_get_values (GtkTreeView *treeview)
+{
+	GtkTreeIter iter;
+	GSList *list = NULL;
+	GtkTreeModel *model =
+		gtk_tree_view_get_model (GTK_TREE_VIEW (treeview));
+
+	if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter))
+		do {
+			gchar *value = NULL;
+			gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, 0,
+					    &value, -1);
+
+			if (value) {
+				list = g_slist_prepend (list,
+							g_strdup (value));
+			}
+		} while (gtk_tree_model_iter_next
+			 (GTK_TREE_MODEL (model), &iter));
+
+	return list;
+}
+
+static gint
+_strcmp (gconstpointer a, gconstpointer b)
+{
+	if (a == NULL && b != NULL)
+		return -1;
+
+	if (a == NULL && b == NULL)
+		return 0;
+
+	if (a != NULL && b == NULL)
+		return 1;
+
+	return strcmp (a, b);
+}
+
+static void
+initialize_listview (GtkWidget *treeview)
+{
+	GtkListStore *store = NULL;
+	GtkCellRenderer *renderer = NULL;
+	GtkTreeViewColumn *column = NULL;
+
+	store = gtk_list_store_new (1, G_TYPE_STRING);
+	gtk_tree_view_set_model (GTK_TREE_VIEW (treeview),
+				 GTK_TREE_MODEL (store));
+	g_object_unref (store);	/* this will delete the store when the view is destroyed */
+
+	renderer = gtk_cell_renderer_text_new ();
+	column = gtk_tree_view_column_new_with_attributes ("Column 0",
+							   renderer, "text",
+							   0, NULL);
+	gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
+}
+
+static void
+populate_list (GtkWidget *treeview, GSList *list)
+{
+	GtkTreeModel *store;
+	GSList *tmp;
+
+	store = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview));
+
+	for (tmp = list; tmp; tmp = tmp->next) {
+		if (tmp->data) {
+			GtkTreeIter iter;
+			gchar *data = tmp->data;
+
+			gtk_list_store_append (GTK_LIST_STORE (store), &iter);
+			gtk_list_store_set (GTK_LIST_STORE (store), &iter, 0,
+					    data, -1);
+		}
+	}
+}
+
+static gboolean
+str_slist_equal (GSList *a, GSList *b)
+{
+	guint len_a = g_slist_length (a);
+	guint len_b = g_slist_length (b);
+
+	if (len_a != len_b)
+		return FALSE;
+
+	GSList *lst;
+	for (lst = a; lst; lst = lst->next) {
+		GSList *find = g_slist_find_custom (b, (const gchar *)(lst->data), _strcmp);
+		if (!find)
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+GType
+tracker_preferences_get_type (void)
+{
+	static GType type = 0;
+
+	if (type == 0) {
+		static const GTypeInfo info = {
+			sizeof (TrackerPreferencesClass),
+			NULL,	/* base_init */
+			NULL,	/* base_finalize */
+			(GClassInitFunc) tracker_preferences_class_init,	/* class_init */
+			NULL,	/* class_finalize */
+			NULL,	/* class_data */
+			sizeof (TrackerPreferences),
+			0,	/* n_preallocs */
+			tracker_preferences_init	/* instance_init */
+		};
+
+		type = g_type_register_static (G_TYPE_OBJECT,
+					       "TrackerPreferencesType",
+					       &info, 0);
+	}
+
+	return type;
+}

Added: trunk/src/tracker-preferences/tracker-preferences.desktop.in.in
==============================================================================
--- (empty file)
+++ trunk/src/tracker-preferences/tracker-preferences.desktop.in.in	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,14 @@
+[Desktop Entry]
+Encoding=UTF-8
+_Name=Search and Indexing
+_Comment=Configure file indexing with Tracker
+Icon=tracker
+Exec=tracker-preferences
+Terminal=false
+Type=Application
+Categories=Settings;
+StartupNotify=true
+X-GNOME-Bugzilla-Bugzilla=GNOME
+X-GNOME-Bugzilla-Product=tracker
+X-GNOME-Bugzilla-Component=Tracker Preferences
+X-GNOME-Bugzilla-Version= VERSION@

Added: trunk/src/tracker-preferences/tracker-preferences.glade
==============================================================================
--- (empty file)
+++ trunk/src/tracker-preferences/tracker-preferences.glade	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,1430 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--*- mode: xml -*-->
+<glade-interface>
+  <widget class="GtkDialog" id="dlgPreferences">
+    <property name="border_width">5</property>
+    <property name="title" translatable="yes">Tracker Preferences</property>
+    <property name="default_width">431</property>
+    <property name="default_height">462</property>
+    <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+    <property name="has_separator">False</property>
+    <child internal-child="vbox">
+      <widget class="GtkVBox" id="dialog-vbox1">
+        <property name="visible">True</property>
+        <property name="spacing">2</property>
+        <child>
+          <widget class="GtkNotebook" id="nbPreferences">
+            <property name="visible">True</property>
+            <property name="border_width">5</property>
+            <child>
+              <widget class="GtkVBox" id="vbox1">
+                <property name="visible">True</property>
+                <property name="border_width">12</property>
+                <property name="spacing">18</property>
+                <child>
+                  <widget class="GtkFrame" id="fraStartup">
+                    <property name="visible">True</property>
+                    <property name="label_xalign">0</property>
+                    <property name="shadow_type">GTK_SHADOW_NONE</property>
+                    <child>
+                      <widget class="GtkAlignment" id="alignment2">
+                        <property name="visible">True</property>
+                        <property name="top_padding">6</property>
+                        <property name="left_padding">12</property>
+                        <child>
+                          <widget class="GtkVBox" id="vbox20">
+                            <property name="visible">True</property>
+                            <property name="spacing">6</property>
+                            <child>
+                              <widget class="GtkHBox" id="hbox8">
+                                <property name="visible">True</property>
+                                <property name="spacing">12</property>
+                                <child>
+                                  <widget class="GtkLabel" id="lblInitialSleep">
+                                    <property name="visible">True</property>
+                                    <property name="label" translatable="yes">Index _delay: </property>
+                                    <property name="use_underline">True</property>
+                                    <property name="mnemonic_widget">spnInitialSleep</property>
+                                    <accessibility>
+                                      <atkrelation target="spnInitialSleep" type="label-for"/>
+                                    </accessibility>
+                                  </widget>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <widget class="GtkHBox" id="hbox16">
+                                    <property name="visible">True</property>
+                                    <property name="spacing">6</property>
+                                    <child>
+                                      <widget class="GtkSpinButton" id="spnInitialSleep">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">True</property>
+                                        <property name="adjustment">0 0 1000 1 300 300</property>
+                                        <property name="climb_rate">1</property>
+                                        <accessibility>
+                                          <atkrelation target="lblInitialSleep" type="labelled-by"/>
+                                          <atkrelation target="lblInitialSleepSeconds" type="labelled-by"/>
+                                        </accessibility>
+                                      </widget>
+                                    </child>
+                                    <child>
+                                      <widget class="GtkLabel" id="lblInitialSleepSeconds">
+                                        <property name="visible">True</property>
+                                        <property name="label" translatable="yes">seconds</property>
+                                        <accessibility>
+                                          <atkrelation target="spnInitialSleep" type="label-for"/>
+                                        </accessibility>
+                                      </widget>
+                                      <packing>
+                                        <property name="expand">False</property>
+                                        <property name="fill">False</property>
+                                        <property name="position">1</property>
+                                      </packing>
+                                    </child>
+                                  </widget>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="position">1</property>
+                                  </packing>
+                                </child>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="label5">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">&lt;b&gt;Startup&lt;/b&gt;</property>
+                        <property name="use_markup">True</property>
+                      </widget>
+                      <packing>
+                        <property name="type">label_item</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkFrame" id="frame5">
+                    <property name="visible">True</property>
+                    <property name="label_xalign">0</property>
+                    <property name="shadow_type">GTK_SHADOW_NONE</property>
+                    <child>
+                      <widget class="GtkAlignment" id="alignment9">
+                        <property name="visible">True</property>
+                        <property name="top_padding">6</property>
+                        <property name="left_padding">12</property>
+                        <child>
+                          <widget class="GtkVBox" id="vbox11">
+                            <property name="visible">True</property>
+                            <property name="spacing">6</property>
+                            <child>
+                              <widget class="GtkCheckButton" id="chkEnableIndexing">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">Enable i_ndexing</property>
+                                <property name="use_underline">True</property>
+                                <property name="response_id">0</property>
+                                <property name="active">True</property>
+                                <property name="draw_indicator">True</property>
+                              </widget>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkCheckButton" id="chkEnableWatching">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">Enable _watching</property>
+                                <property name="use_underline">True</property>
+                                <property name="response_id">0</property>
+                                <property name="active">True</property>
+                                <property name="draw_indicator">True</property>
+                              </widget>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="label12">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">&lt;b&gt;Indexing Options&lt;/b&gt;</property>
+                        <property name="use_markup">True</property>
+                      </widget>
+                      <packing>
+                        <property name="type">label_item</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkFrame" id="frame11">
+                    <property name="visible">True</property>
+                    <property name="label_xalign">0</property>
+                    <property name="shadow_type">GTK_SHADOW_NONE</property>
+                    <child>
+                      <widget class="GtkAlignment" id="alignment17">
+                        <property name="visible">True</property>
+                        <property name="top_padding">6</property>
+                        <property name="left_padding">12</property>
+                        <child>
+                          <widget class="GtkVBox" id="vbox17">
+                            <property name="visible">True</property>
+                            <property name="spacing">6</property>
+                            <child>
+                              <widget class="GtkHBox" id="hbox11">
+                                <property name="visible">True</property>
+                                <property name="spacing">12</property>
+                                <child>
+                                  <widget class="GtkLabel" id="label27">
+                                    <property name="visible">True</property>
+                                    <property name="label" translatable="yes">_Language:</property>
+                                    <property name="use_underline">True</property>
+                                    <property name="mnemonic_widget">comLanguage</property>
+                                  </widget>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">False</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <widget class="GtkComboBox" id="comLanguage">
+                                    <property name="visible">True</property>
+                                    <property name="items" translatable="yes">Danish
+Dutch
+English
+Finnish
+French
+German
+Hungarian
+Italian
+Norwegian
+Portuguese
+Russian
+Spanish
+Swedish</property>
+                                  </widget>
+                                  <packing>
+                                    <property name="position">1</property>
+                                  </packing>
+                                </child>
+                              </widget>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                              </packing>
+                            </child>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="label26">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">&lt;b&gt;Stemming&lt;/b&gt;</property>
+                        <property name="use_markup">True</property>
+                      </widget>
+                      <packing>
+                        <property name="type">label_item</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkFrame" id="frame13">
+                    <property name="visible">True</property>
+                    <property name="label_xalign">0</property>
+                    <property name="shadow_type">GTK_SHADOW_NONE</property>
+                    <child>
+                      <widget class="GtkAlignment" id="alignment19">
+                        <property name="visible">True</property>
+                        <property name="top_padding">6</property>
+                        <property name="left_padding">12</property>
+                        <child>
+                          <widget class="GtkVBox" id="vbox21">
+                            <property name="visible">True</property>
+                            <property name="spacing">6</property>
+                            <child>
+                              <widget class="GtkCheckButton" id="chkDisableBatteryIndex">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="label" translatable="yes">Disable all Indexing when on battery</property>
+                                <property name="use_underline">True</property>
+                                <property name="response_id">0</property>
+                                <property name="draw_indicator">True</property>
+                              </widget>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkCheckButton" id="chkDisableBatteryInitialIndex">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="label" translatable="yes">Disable initial index sweep when on battery</property>
+                                <property name="use_underline">True</property>
+                                <property name="response_id">0</property>
+                                <property name="draw_indicator">True</property>
+                              </widget>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="label37">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">&lt;b&gt;Power management&lt;/b&gt;</property>
+                        <property name="use_markup">True</property>
+                      </widget>
+                      <packing>
+                        <property name="type">label_item</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="position">3</property>
+                  </packing>
+                </child>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="label1">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">General</property>
+              </widget>
+              <packing>
+                <property name="type">tab</property>
+                <property name="tab_fill">False</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkVBox" id="vbox14">
+                <property name="visible">True</property>
+                <property name="border_width">12</property>
+                <property name="spacing">18</property>
+                <child>
+                  <widget class="GtkFrame" id="frame9">
+                    <property name="visible">True</property>
+                    <property name="label_xalign">0</property>
+                    <property name="shadow_type">GTK_SHADOW_NONE</property>
+                    <child>
+                      <widget class="GtkAlignment" id="alignment15">
+                        <property name="visible">True</property>
+                        <property name="top_padding">6</property>
+                        <property name="left_padding">12</property>
+                        <child>
+                          <widget class="GtkVBox" id="vbox15">
+                            <property name="visible">True</property>
+                            <property name="spacing">6</property>
+                            <child>
+                              <widget class="GtkCheckButton" id="chkIndexContents">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">Index _file contents</property>
+                                <property name="use_underline">True</property>
+                                <property name="response_id">0</property>
+                                <property name="active">True</property>
+                                <property name="draw_indicator">True</property>
+                              </widget>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkCheckButton" id="chkGenerateThumbs">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">Generate thum_bnails</property>
+                                <property name="use_underline">True</property>
+                                <property name="response_id">0</property>
+                                <property name="draw_indicator">True</property>
+                              </widget>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkCheckButton" id="chkSkipMountPoints">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">Index _mounted directories</property>
+                                <property name="use_underline">True</property>
+                                <property name="response_id">0</property>
+                                <property name="draw_indicator">True</property>
+                              </widget>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                                <property name="position">2</property>
+                              </packing>
+                            </child>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="label20">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">&lt;b&gt;Indexing&lt;/b&gt;</property>
+                        <property name="use_markup">True</property>
+                      </widget>
+                      <packing>
+                        <property name="type">label_item</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkFrame" id="fraGeneralIndexing">
+                    <property name="visible">True</property>
+                    <property name="label_xalign">0</property>
+                    <property name="shadow_type">GTK_SHADOW_NONE</property>
+                    <child>
+                      <widget class="GtkAlignment" id="alignment7">
+                        <property name="visible">True</property>
+                        <property name="top_padding">6</property>
+                        <property name="left_padding">12</property>
+                        <child>
+                          <widget class="GtkVBox" id="vbox7">
+                            <property name="visible">True</property>
+                            <property name="spacing">6</property>
+                            <child>
+                              <widget class="GtkCheckButton" id="chkIndexHomeDirectory">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">Index and watch my home _directory</property>
+                                <property name="use_underline">True</property>
+                                <property name="response_id">0</property>
+                                <property name="active">True</property>
+                                <property name="draw_indicator">True</property>
+                              </widget>
+                              <packing>
+                                <property name="expand">False</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkLabel" id="label8">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="label" translatable="yes">Additional paths to index and watch:</property>
+                                <property name="mnemonic_widget">lstAdditionalPathIndexes</property>
+                                <accessibility>
+                                  <atkrelation target="lstAdditionalPathIndexes" type="label-for"/>
+                                </accessibility>
+                              </widget>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkHBox" id="hbox1">
+                                <property name="visible">True</property>
+                                <property name="spacing">6</property>
+                                <child>
+                                  <widget class="GtkScrolledWindow" id="scrolledwindow1">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">False</property>
+                                    <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                                    <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                                    <property name="shadow_type">GTK_SHADOW_IN</property>
+                                    <child>
+                                      <widget class="GtkTreeView" id="lstAdditionalPathIndexes">
+                                        <property name="visible">True</property>
+                                        <property name="headers_visible">False</property>
+                                        <accessibility>
+                                          <atkrelation target="label8" type="labelled-by"/>
+                                        </accessibility>
+                                      </widget>
+                                    </child>
+                                  </widget>
+                                </child>
+                                <child>
+                                  <widget class="GtkVButtonBox" id="vbuttonbox1">
+                                    <property name="visible">True</property>
+                                    <property name="spacing">6</property>
+                                    <property name="layout_style">GTK_BUTTONBOX_START</property>
+                                    <child>
+                                      <widget class="GtkButton" id="cmdAddIndexPath">
+                                        <property name="visible">True</property>
+                                        <property name="label">gtk-add</property>
+                                        <property name="use_stock">True</property>
+                                        <property name="response_id">0</property>
+                                      </widget>
+                                    </child>
+                                    <child>
+                                      <widget class="GtkButton" id="cmdRemoveIndexPath">
+                                        <property name="visible">True</property>
+                                        <property name="label">gtk-remove</property>
+                                        <property name="use_stock">True</property>
+                                        <property name="response_id">0</property>
+                                      </widget>
+                                      <packing>
+                                        <property name="position">1</property>
+                                      </packing>
+                                    </child>
+                                  </widget>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="position">1</property>
+                                  </packing>
+                                </child>
+                              </widget>
+                              <packing>
+                                <property name="position">2</property>
+                              </packing>
+                            </child>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="label7">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">&lt;b&gt;Watch Directories&lt;/b&gt;</property>
+                        <property name="use_markup">True</property>
+                      </widget>
+                      <packing>
+                        <property name="type">label_item</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkFrame" id="frame10">
+                    <property name="visible">True</property>
+                    <property name="label_xalign">0</property>
+                    <property name="shadow_type">GTK_SHADOW_NONE</property>
+                    <child>
+                      <widget class="GtkAlignment" id="alignment16">
+                        <property name="visible">True</property>
+                        <property name="top_padding">6</property>
+                        <property name="left_padding">12</property>
+                        <child>
+                          <widget class="GtkVBox" id="vbox16">
+                            <property name="visible">True</property>
+                            <property name="spacing">6</property>
+                            <child>
+                              <widget class="GtkLabel" id="label21">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="label" translatable="yes">Additional paths to index on startup (but not watch for updates):</property>
+                                <property name="mnemonic_widget">lstAdditionalPathIndexes</property>
+                                <accessibility>
+                                  <atkrelation target="lstCrawledPaths" type="label-for"/>
+                                </accessibility>
+                              </widget>
+                              <packing>
+                                <property name="expand">False</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkHBox" id="hbox9">
+                                <property name="visible">True</property>
+                                <property name="spacing">6</property>
+                                <child>
+                                  <widget class="GtkScrolledWindow" id="scrolledwindow5">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">False</property>
+                                    <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                                    <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                                    <property name="shadow_type">GTK_SHADOW_IN</property>
+                                    <child>
+                                      <widget class="GtkTreeView" id="lstCrawledPaths">
+                                        <property name="visible">True</property>
+                                        <property name="headers_visible">False</property>
+                                        <accessibility>
+                                          <atkrelation target="label21" type="labelled-by"/>
+                                        </accessibility>
+                                      </widget>
+                                    </child>
+                                  </widget>
+                                </child>
+                                <child>
+                                  <widget class="GtkVButtonBox" id="vbuttonbox5">
+                                    <property name="visible">True</property>
+                                    <property name="spacing">6</property>
+                                    <property name="layout_style">GTK_BUTTONBOX_START</property>
+                                    <child>
+                                      <widget class="GtkButton" id="cmdAddCrawledPath">
+                                        <property name="visible">True</property>
+                                        <property name="label">gtk-add</property>
+                                        <property name="use_stock">True</property>
+                                        <property name="response_id">0</property>
+                                      </widget>
+                                    </child>
+                                    <child>
+                                      <widget class="GtkButton" id="cmdRemoveCrawledPath">
+                                        <property name="visible">True</property>
+                                        <property name="label">gtk-remove</property>
+                                        <property name="use_stock">True</property>
+                                        <property name="response_id">0</property>
+                                      </widget>
+                                      <packing>
+                                        <property name="position">1</property>
+                                      </packing>
+                                    </child>
+                                  </widget>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="position">1</property>
+                                  </packing>
+                                </child>
+                              </widget>
+                              <packing>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="label22">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">&lt;b&gt;Crawled Directories&lt;/b&gt;</property>
+                        <property name="use_markup">True</property>
+                      </widget>
+                      <packing>
+                        <property name="type">label_item</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="label19">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Files</property>
+              </widget>
+              <packing>
+                <property name="type">tab</property>
+                <property name="position">1</property>
+                <property name="tab_fill">False</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkVBox" id="vbox4">
+                <property name="visible">True</property>
+                <property name="border_width">12</property>
+                <property name="spacing">18</property>
+                <child>
+                  <widget class="GtkFrame" id="fraIgnoredPaths">
+                    <property name="visible">True</property>
+                    <property name="label_xalign">0</property>
+                    <property name="shadow_type">GTK_SHADOW_NONE</property>
+                    <child>
+                      <widget class="GtkAlignment" id="alignment13">
+                        <property name="visible">True</property>
+                        <property name="top_padding">6</property>
+                        <property name="left_padding">12</property>
+                        <child>
+                          <widget class="GtkVBox" id="vbIgnorePaths">
+                            <property name="visible">True</property>
+                            <property name="spacing">6</property>
+                            <child>
+                              <widget class="GtkLabel" id="label10">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="label" translatable="yes">Path roots to be ignored during indexing:</property>
+                                <property name="mnemonic_widget">lstIgnorePaths</property>
+                              </widget>
+                              <packing>
+                                <property name="expand">False</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkHBox" id="hbox2">
+                                <property name="visible">True</property>
+                                <property name="spacing">6</property>
+                                <child>
+                                  <widget class="GtkScrolledWindow" id="scrolledwindow2">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">False</property>
+                                    <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                                    <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                                    <property name="shadow_type">GTK_SHADOW_IN</property>
+                                    <child>
+                                      <widget class="GtkTreeView" id="lstIgnorePaths">
+                                        <property name="visible">True</property>
+                                      </widget>
+                                    </child>
+                                  </widget>
+                                </child>
+                                <child>
+                                  <widget class="GtkVButtonBox" id="vbuttonbox2">
+                                    <property name="visible">True</property>
+                                    <property name="spacing">6</property>
+                                    <property name="layout_style">GTK_BUTTONBOX_START</property>
+                                    <child>
+                                      <widget class="GtkButton" id="cmdAddIgnorePath">
+                                        <property name="visible">True</property>
+                                        <property name="label">gtk-add</property>
+                                        <property name="use_stock">True</property>
+                                        <property name="response_id">0</property>
+                                      </widget>
+                                    </child>
+                                    <child>
+                                      <widget class="GtkButton" id="cmdRemoveIgnorePath">
+                                        <property name="visible">True</property>
+                                        <property name="label">gtk-remove</property>
+                                        <property name="use_stock">True</property>
+                                        <property name="response_id">0</property>
+                                      </widget>
+                                      <packing>
+                                        <property name="position">1</property>
+                                      </packing>
+                                    </child>
+                                  </widget>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="position">1</property>
+                                  </packing>
+                                </child>
+                              </widget>
+                              <packing>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="label6">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">&lt;b&gt;Ignored Paths&lt;/b&gt;</property>
+                        <property name="use_markup">True</property>
+                      </widget>
+                      <packing>
+                        <property name="type">label_item</property>
+                      </packing>
+                    </child>
+                  </widget>
+                </child>
+                <child>
+                  <widget class="GtkFrame" id="fraIgnoredPatterns">
+                    <property name="visible">True</property>
+                    <property name="label_xalign">0</property>
+                    <property name="shadow_type">GTK_SHADOW_NONE</property>
+                    <child>
+                      <widget class="GtkAlignment" id="alignment14">
+                        <property name="visible">True</property>
+                        <property name="top_padding">6</property>
+                        <property name="left_padding">12</property>
+                        <child>
+                          <widget class="GtkVBox" id="vbIgnorePatterns">
+                            <property name="visible">True</property>
+                            <property name="spacing">6</property>
+                            <child>
+                              <widget class="GtkLabel" id="label11">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="label" translatable="yes">File patterns to ignore during indexing:</property>
+                                <property name="mnemonic_widget">lstIgnoreFilePatterns</property>
+                              </widget>
+                              <packing>
+                                <property name="expand">False</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkHBox" id="hbox3">
+                                <property name="visible">True</property>
+                                <property name="spacing">6</property>
+                                <child>
+                                  <widget class="GtkScrolledWindow" id="scrolledwindow3">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">False</property>
+                                    <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                                    <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                                    <property name="shadow_type">GTK_SHADOW_IN</property>
+                                    <child>
+                                      <widget class="GtkTreeView" id="lstIgnoreFilePatterns">
+                                        <property name="visible">True</property>
+                                      </widget>
+                                    </child>
+                                  </widget>
+                                </child>
+                                <child>
+                                  <widget class="GtkVButtonBox" id="vbuttonbox3">
+                                    <property name="visible">True</property>
+                                    <property name="spacing">6</property>
+                                    <property name="layout_style">GTK_BUTTONBOX_START</property>
+                                    <child>
+                                      <widget class="GtkButton" id="cmdAddIgnorePattern">
+                                        <property name="visible">True</property>
+                                        <property name="label">gtk-add</property>
+                                        <property name="use_stock">True</property>
+                                        <property name="response_id">0</property>
+                                      </widget>
+                                    </child>
+                                    <child>
+                                      <widget class="GtkButton" id="cmdRemoveIgnorePattern">
+                                        <property name="visible">True</property>
+                                        <property name="label">gtk-remove</property>
+                                        <property name="use_stock">True</property>
+                                        <property name="response_id">0</property>
+                                      </widget>
+                                      <packing>
+                                        <property name="position">1</property>
+                                      </packing>
+                                    </child>
+                                  </widget>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="position">1</property>
+                                  </packing>
+                                </child>
+                              </widget>
+                              <packing>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="label9">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">&lt;b&gt;Ignored File Patterns&lt;/b&gt;</property>
+                        <property name="use_markup">True</property>
+                      </widget>
+                      <packing>
+                        <property name="type">label_item</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="position">2</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="label3">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Ignored Files</property>
+              </widget>
+              <packing>
+                <property name="type">tab</property>
+                <property name="position">2</property>
+                <property name="tab_fill">False</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkVBox" id="vbox3">
+                <property name="visible">True</property>
+                <property name="border_width">12</property>
+                <property name="spacing">18</property>
+                <child>
+                  <widget class="GtkVBox" id="vbox8">
+                    <property name="visible">True</property>
+                    <property name="spacing">6</property>
+                    <child>
+                      <widget class="GtkCheckButton" id="chkEnableEvolutionIndexing">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Enable _Evolution email indexing</property>
+                        <property name="use_underline">True</property>
+                        <property name="response_id">0</property>
+                        <property name="active">True</property>
+                        <property name="draw_indicator">True</property>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkCheckButton" id="chkEnableModestIndexing">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Enable _Modest email indexing</property>
+                        <property name="use_underline">True</property>
+                        <property name="response_id">0</property>
+                        <property name="active">True</property>
+                        <property name="draw_indicator">True</property>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkCheckButton" id="chkEnableThunderbirdIndexing">
+                        <property name="visible">True</property>
+                        <property name="sensitive">False</property>
+                        <property name="label" translatable="yes">Enable _Thunderbird email indexing</property>
+                        <property name="use_underline">True</property>
+                        <property name="response_id">0</property>
+                        <property name="draw_indicator">True</property>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkCheckButton" id="chkEnableKMailIndexing">
+                        <property name="visible">True</property>
+                        <property name="sensitive">False</property>
+                        <property name="label" translatable="yes">Enable _KMail email indexing</property>
+                        <property name="use_underline">True</property>
+                        <property name="response_id">0</property>
+                        <property name="draw_indicator">True</property>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="position">2</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkVBox" id="vbox12">
+                    <property name="visible">True</property>
+                    <property name="sensitive">False</property>
+                    <property name="spacing">6</property>
+                    <child>
+                      <widget class="GtkLabel" id="label14">
+                        <property name="visible">True</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">Additional mbox style mailboxes to index:</property>
+                        <property name="mnemonic_widget">lstAdditionalMBoxIndexes</property>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkHBox" id="hbox5">
+                        <property name="visible">True</property>
+                        <property name="spacing">6</property>
+                        <child>
+                          <widget class="GtkScrolledWindow" id="scrolledwindow4">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                            <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                            <property name="shadow_type">GTK_SHADOW_IN</property>
+                            <child>
+                              <widget class="GtkTreeView" id="lstAdditionalMBoxIndexes">
+                                <property name="visible">True</property>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkVButtonBox" id="vbuttonbox4">
+                            <property name="visible">True</property>
+                            <property name="spacing">6</property>
+                            <property name="layout_style">GTK_BUTTONBOX_START</property>
+                            <child>
+                              <widget class="GtkButton" id="cmdAddIndexMailbox">
+                                <property name="visible">True</property>
+                                <property name="label">gtk-add</property>
+                                <property name="use_stock">True</property>
+                                <property name="response_id">0</property>
+                              </widget>
+                            </child>
+                            <child>
+                              <widget class="GtkButton" id="cmdRemoveIndexMailbox">
+                                <property name="visible">True</property>
+                                <property name="label">gtk-remove</property>
+                                <property name="use_stock">True</property>
+                                <property name="response_id">0</property>
+                              </widget>
+                              <packing>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </widget>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </widget>
+                      <packing>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="position">3</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="label2">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Email</property>
+              </widget>
+              <packing>
+                <property name="type">tab</property>
+                <property name="position">3</property>
+                <property name="tab_fill">False</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkVBox" id="vbox5">
+                <property name="visible">True</property>
+                <property name="border_width">12</property>
+                <property name="spacing">18</property>
+                <child>
+                  <widget class="GtkFrame" id="fraThrottling">
+                    <property name="visible">True</property>
+                    <property name="label_xalign">0</property>
+                    <property name="shadow_type">GTK_SHADOW_NONE</property>
+                    <child>
+                      <widget class="GtkAlignment" id="alignment6">
+                        <property name="visible">True</property>
+                        <property name="top_padding">6</property>
+                        <property name="left_padding">12</property>
+                        <child>
+                          <widget class="GtkVBox" id="vbox6">
+                            <property name="visible">True</property>
+                            <property name="spacing">6</property>
+                            <child>
+                              <widget class="GtkTable" id="table3">
+                                <property name="visible">True</property>
+                                <property name="n_rows">2</property>
+                                <property name="n_columns">3</property>
+                                <property name="column_spacing">12</property>
+                                <property name="row_spacing">6</property>
+                                <child>
+                                  <widget class="GtkLabel" id="lblThrottlingLevel">
+                                    <property name="visible">True</property>
+                                    <property name="xalign">0</property>
+                                    <property name="label" translatable="yes">Indexing speed:</property>
+                                    <accessibility>
+                                      <atkrelation target="scaThrottle" type="label-for"/>
+                                    </accessibility>
+                                  </widget>
+                                  <packing>
+                                    <property name="right_attach">3</property>
+                                    <property name="x_options">GTK_FILL</property>
+                                    <property name="y_options"></property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <widget class="GtkLabel" id="label24">
+                                    <property name="visible">True</property>
+                                    <property name="xalign">0</property>
+                                    <property name="yalign">0</property>
+                                    <property name="label" translatable="yes">Faster</property>
+                                  </widget>
+                                  <packing>
+                                    <property name="top_attach">1</property>
+                                    <property name="bottom_attach">2</property>
+                                    <property name="x_options">GTK_FILL</property>
+                                    <property name="y_options"></property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <widget class="GtkLabel" id="label23">
+                                    <property name="visible">True</property>
+                                    <property name="xalign">0</property>
+                                    <property name="yalign">0</property>
+                                    <property name="label" translatable="yes">Slower</property>
+                                  </widget>
+                                  <packing>
+                                    <property name="left_attach">2</property>
+                                    <property name="right_attach">3</property>
+                                    <property name="top_attach">1</property>
+                                    <property name="bottom_attach">2</property>
+                                    <property name="x_options">GTK_FILL</property>
+                                    <property name="y_options"></property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <widget class="GtkHScale" id="scaThrottle">
+                                    <property name="visible">True</property>
+                                    <property name="adjustment">0 0 99 1 1 0</property>
+                                    <property name="digits">0</property>
+                                    <accessibility>
+                                      <atkrelation target="lblThrottlingLevel" type="labelled-by"/>
+                                    </accessibility>
+                                  </widget>
+                                  <packing>
+                                    <property name="left_attach">1</property>
+                                    <property name="right_attach">2</property>
+                                    <property name="top_attach">1</property>
+                                    <property name="bottom_attach">2</property>
+                                    <property name="y_options">GTK_FILL</property>
+                                  </packing>
+                                </child>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="lblThrottling">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">&lt;b&gt;Throttling&lt;/b&gt;</property>
+                        <property name="use_markup">True</property>
+                      </widget>
+                      <packing>
+                        <property name="type">label_item</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkFrame" id="frame8">
+                    <property name="visible">True</property>
+                    <property name="label_xalign">0</property>
+                    <property name="shadow_type">GTK_SHADOW_NONE</property>
+                    <child>
+                      <widget class="GtkAlignment" id="alignment12">
+                        <property name="visible">True</property>
+                        <property name="top_padding">6</property>
+                        <property name="left_padding">12</property>
+                        <child>
+                          <widget class="GtkVBox" id="vbox13">
+                            <property name="visible">True</property>
+                            <property name="spacing">6</property>
+                            <child>
+                              <widget class="GtkRadioButton" id="optReducedMemory">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">_Minimize memory usage (slower indexing)</property>
+                                <property name="use_underline">True</property>
+                                <property name="response_id">0</property>
+                                <property name="draw_indicator">True</property>
+                              </widget>
+                              <packing>
+                                <property name="expand">False</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkRadioButton" id="optNormal">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">_Use additional memory for faster indexing</property>
+                                <property name="use_underline">True</property>
+                                <property name="response_id">0</property>
+                                <property name="active">True</property>
+                                <property name="draw_indicator">True</property>
+                                <property name="group">optReducedMemory</property>
+                              </widget>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="label18">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">&lt;b&gt;Resource Usage&lt;/b&gt;</property>
+                        <property name="use_markup">True</property>
+                      </widget>
+                      <packing>
+                        <property name="type">label_item</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkFrame" id="frame14">
+                    <property name="visible">True</property>
+                    <property name="label_xalign">0</property>
+                    <property name="shadow_type">GTK_SHADOW_NONE</property>
+                    <child>
+                      <widget class="GtkAlignment" id="alignment20">
+                        <property name="visible">True</property>
+                        <property name="top_padding">6</property>
+                        <property name="left_padding">12</property>
+                        <child>
+                          <widget class="GtkVBox" id="vbox22">
+                            <property name="visible">True</property>
+                            <property name="spacing">6</property>
+                            <child>
+                              <widget class="GtkCheckButton" id="chkFastMerges">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="label" translatable="yes">Perform fast index merges (may affect system performance)</property>
+                                <property name="use_underline">True</property>
+                                <property name="response_id">0</property>
+                                <property name="draw_indicator">True</property>
+                              </widget>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                              </packing>
+                            </child>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="label38">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">&lt;b&gt;Index Merging&lt;/b&gt;</property>
+                        <property name="use_markup">True</property>
+                      </widget>
+                      <packing>
+                        <property name="type">label_item</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkFrame" id="frame12">
+                    <property name="visible">True</property>
+                    <property name="label_xalign">0</property>
+                    <property name="shadow_type">GTK_SHADOW_NONE</property>
+                    <child>
+                      <widget class="GtkAlignment" id="alignment18">
+                        <property name="visible">True</property>
+                        <property name="top_padding">6</property>
+                        <property name="left_padding">12</property>
+                        <child>
+                          <widget class="GtkVBox" id="vbox18">
+                            <property name="visible">True</property>
+                            <child>
+                              <widget class="GtkTable" id="table2">
+                                <property name="visible">True</property>
+                                <property name="n_rows">2</property>
+                                <property name="n_columns">3</property>
+                                <property name="column_spacing">12</property>
+                                <property name="row_spacing">6</property>
+                                <child>
+                                  <placeholder/>
+                                </child>
+                                <child>
+                                  <widget class="GtkLabel" id="label30">
+                                    <property name="visible">True</property>
+                                    <property name="xalign">0</property>
+                                    <property name="label" translatable="yes">Maximum _amount of text to index:</property>
+                                    <property name="use_underline">True</property>
+                                    <property name="mnemonic_widget">spnMaxText</property>
+                                  </widget>
+                                  <packing>
+                                    <property name="x_options">GTK_FILL</property>
+                                    <property name="y_options"></property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <widget class="GtkLabel" id="label31">
+                                    <property name="visible">True</property>
+                                    <property name="xalign">0</property>
+                                    <property name="label" translatable="yes">Maximum number of unique _words to index:</property>
+                                    <property name="use_markup">True</property>
+                                    <property name="use_underline">True</property>
+                                    <property name="mnemonic_widget">spnMaxWords</property>
+                                  </widget>
+                                  <packing>
+                                    <property name="top_attach">1</property>
+                                    <property name="bottom_attach">2</property>
+                                    <property name="x_options">GTK_FILL</property>
+                                    <property name="y_options"></property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <widget class="GtkSpinButton" id="spnMaxText">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="adjustment">1024 1 1000000 1 300 300</property>
+                                    <property name="climb_rate">1</property>
+                                  </widget>
+                                  <packing>
+                                    <property name="left_attach">1</property>
+                                    <property name="right_attach">2</property>
+                                    <property name="x_options">GTK_FILL</property>
+                                    <property name="y_options"></property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <widget class="GtkSpinButton" id="spnMaxWords">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="adjustment">10000 1000 1000000 100 500 500</property>
+                                    <property name="climb_rate">1</property>
+                                  </widget>
+                                  <packing>
+                                    <property name="left_attach">1</property>
+                                    <property name="right_attach">2</property>
+                                    <property name="top_attach">1</property>
+                                    <property name="bottom_attach">2</property>
+                                    <property name="x_options">GTK_FILL</property>
+                                    <property name="y_options"></property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <widget class="GtkLabel" id="label33">
+                                    <property name="visible">True</property>
+                                    <property name="xalign">0</property>
+                                    <property name="label" translatable="yes">kB</property>
+                                  </widget>
+                                  <packing>
+                                    <property name="left_attach">2</property>
+                                    <property name="right_attach">3</property>
+                                    <property name="x_options">GTK_FILL</property>
+                                    <property name="y_options"></property>
+                                  </packing>
+                                </child>
+                              </widget>
+                              <packing>
+                                <property name="expand">False</property>
+                              </packing>
+                            </child>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="label32">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">&lt;b&gt;Indexing Limits (per file)&lt;/b&gt;</property>
+                        <property name="use_markup">True</property>
+                      </widget>
+                      <packing>
+                        <property name="type">label_item</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="position">3</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="position">4</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="label4">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Performance</property>
+              </widget>
+              <packing>
+                <property name="type">tab</property>
+                <property name="position">4</property>
+                <property name="tab_fill">False</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child internal-child="action_area">
+          <widget class="GtkHButtonBox" id="dialog-action_area1">
+            <property name="visible">True</property>
+            <property name="layout_style">GTK_BUTTONBOX_EDGE</property>
+            <child>
+              <widget class="GtkButton" id="cmdHelp">
+                <property name="visible">True</property>
+                <property name="label">gtk-help</property>
+                <property name="use_stock">True</property>
+                <property name="response_id">0</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkButton" id="cmdApply">
+                <property name="visible">True</property>
+                <property name="label">gtk-apply</property>
+                <property name="use_stock">True</property>
+                <property name="response_id">0</property>
+              </widget>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkButton" id="cmdCancel">
+                <property name="visible">True</property>
+                <property name="label">gtk-cancel</property>
+                <property name="use_stock">True</property>
+                <property name="response_id">0</property>
+              </widget>
+              <packing>
+                <property name="position">2</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkButton" id="cmdOK">
+                <property name="visible">True</property>
+                <property name="label">gtk-ok</property>
+                <property name="use_stock">True</property>
+                <property name="response_id">0</property>
+              </widget>
+              <packing>
+                <property name="position">3</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">GTK_PACK_END</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+</glade-interface>

Added: trunk/src/tracker-preferences/tracker-preferences.h
==============================================================================
--- (empty file)
+++ trunk/src/tracker-preferences/tracker-preferences.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,50 @@
+/* Tracker - indexer and metadata database engine
+ * Copyright (C) 2007, Saleem Abdulrasool (compnerd gentoo org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKER_PREFERENCES_H__
+#define __TRACKER_PREFERENCES_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_PREFERENCES	     (tracker_preferences_get_type())
+#define TRACKER_PREFERENCES(obj)	     (G_TYPE_CHECK_INSTANCE_CAST((obj), TRACKER_TYPE_PREFERENCES, TrackerPreferences))
+#define TRACKER_PREFERENCES_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), TRACKER_TYPE_PREFERENCES, TrackerPreferencesClass))
+#define TRACKER_IS_PREFERENCES(obj)	     (G_TYPE_CHECK_INSTANCE_TYPE((obj), TRACKER_TYPE_PREFERENCES))
+#define TRACKER_IS_PREFERENCES_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), TRACKER_TYPE_PREFERENCES))
+#define TRACKER_PREFERENCES_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), TRACKER_TYPE_PREFERENCES, TrackerPreferencesClass))
+
+typedef struct _TrackerPreferences {
+	GObject parent;
+} TrackerPreferences;
+
+typedef struct _TrackerPreferencesClass {
+	GObjectClass parent_class;
+} TrackerPreferencesClass;
+
+GType
+tracker_preferences_get_type (void);
+
+TrackerPreferences *
+tracker_preferences_new (void);
+
+G_END_DECLS
+
+#endif

Added: trunk/src/tracker-search-tool/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/tracker-search-tool/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,52 @@
+include $(top_srcdir)/Makefile.decl
+
+ INTLTOOL_DESKTOP_RULE@
+
+desktopdir = $(datadir)/applications
+icondir = $(datadir)/tracker/icons
+
+INCLUDES = 	\
+	-I$(top_srcdir)/src/libtracker				\
+	$(GLIB2_CFLAGS)						\
+	$(GNOME_UTILS_CFLAGS)					\
+	$(GNOMEVFS_CFLAGS)					\
+	$(GNOMEDESKTOP_CFLAGS)					\
+	$(DBUS_CFLAGS)						\
+	-DG_DISABLE_DEPRECATED					\
+	-DTRACKER_LOCALEDIR=\""$(localedir)"\" 			\
+	-DTRACKER_DATADIR=\""$(datadir)"\"			\
+	-DGREP_COMMAND=\""$(GREP_COMMAND)"\"			\
+	$(LIBTRACKERGTK_CFLAGS)					
+	
+bin_PROGRAMS = tracker-search-tool
+
+tracker_search_tool_SOURCES =     \
+	tracker-search-tool-support.c   \
+	tracker-search-tool-support.h   \
+	tracker-search-tool-callbacks.c \
+	tracker-search-tool-callbacks.h \
+	tracker-search-tool.c	        \
+	tracker-search-tool.h		\
+	sexy-icon-entry.c		\
+	sexy-icon-entry.h
+
+tracker_search_tool_LDADD =   \
+	$(GLIB2_LIBS) \
+	$(GNOME_UTILS_LIBS) \
+	$(GNOMEVFS_LIBS)    \
+	$(GNOMEDESKTOP_LIBS) \
+	$(DBUS_LIBS)	\
+        $(top_builddir)/src/libtracker-gtk/libtracker-gtk.la \
+	$(top_builddir)/src/libtracker/libtrackerclient.la 
+
+
+desktop_in_files = tracker-search-tool.desktop.in
+desktop_files = $(desktop_in_files:.desktop.in=.desktop)
+ 
+desktop_DATA = $(desktop_files)
+icon_DATA = thumbnail_frame.png
+
+EXTRA_DIST = $(icon_DATA)
+
+CLEANFILES = $(desktop_DATA)
+

Added: trunk/src/tracker-search-tool/sexy-icon-entry.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-search-tool/sexy-icon-entry.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,969 @@
+/*
+ * @file libsexy/sexy-icon-entry.c Entry widget
+ *
+ * @Copyright (C) 2004-2006 Christian Hammond.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA  02111-1307, USA.
+ */
+#include "sexy-icon-entry.h"
+#include <string.h>
+#include <gtk/gtk.h>
+
+#define ICON_MARGIN 2
+#define MAX_ICONS 2
+
+#define IS_VALID_ICON_ENTRY_POSITION(pos) \
+	((pos) == SEXY_ICON_ENTRY_PRIMARY || \
+	 (pos) == SEXY_ICON_ENTRY_SECONDARY)
+
+typedef struct
+{
+	GtkImage *icon;
+	gboolean highlight;
+	gboolean hovered;
+	GdkWindow *window;
+
+} SexyIconInfo;
+
+struct _SexyIconEntryPriv
+{
+	SexyIconInfo icons[MAX_ICONS];
+
+	gulong icon_released_id;
+};
+
+enum
+{
+	ICON_PRESSED,
+	ICON_RELEASED,
+	LAST_SIGNAL
+};
+
+static void sexy_icon_entry_class_init(SexyIconEntryClass *klass);
+static void sexy_icon_entry_editable_init(GtkEditableClass *iface);
+static void sexy_icon_entry_init(SexyIconEntry *entry);
+static void sexy_icon_entry_finalize(GObject *obj);
+static void sexy_icon_entry_destroy(GtkObject *obj);
+static void sexy_icon_entry_map(GtkWidget *widget);
+static void sexy_icon_entry_unmap(GtkWidget *widget);
+static void sexy_icon_entry_realize(GtkWidget *widget);
+static void sexy_icon_entry_unrealize(GtkWidget *widget);
+static void sexy_icon_entry_size_request(GtkWidget *widget,
+										  GtkRequisition *requisition);
+static void sexy_icon_entry_size_allocate(GtkWidget *widget,
+										   GtkAllocation *allocation);
+static gint sexy_icon_entry_expose(GtkWidget *widget, GdkEventExpose *event);
+static gint sexy_icon_entry_enter_notify(GtkWidget *widget,
+											   GdkEventCrossing *event);
+static gint sexy_icon_entry_leave_notify(GtkWidget *widget,
+											   GdkEventCrossing *event);
+static gint sexy_icon_entry_button_press(GtkWidget *widget,
+											   GdkEventButton *event);
+static gint sexy_icon_entry_button_release(GtkWidget *widget,
+												 GdkEventButton *event);
+
+static GtkEntryClass *parent_class = NULL;
+static guint signals[LAST_SIGNAL] = {0};
+
+G_DEFINE_TYPE_EXTENDED(SexyIconEntry, sexy_icon_entry, GTK_TYPE_ENTRY,
+					   0,
+					   G_IMPLEMENT_INTERFACE(GTK_TYPE_EDITABLE,
+											 sexy_icon_entry_editable_init));
+
+static void
+sexy_icon_entry_class_init(SexyIconEntryClass *klass)
+{
+	GObjectClass *gobject_class;
+	GtkObjectClass *object_class;
+	GtkWidgetClass *widget_class;
+	GtkEntryClass *entry_class;
+
+	parent_class = g_type_class_peek_parent(klass);
+
+	gobject_class = G_OBJECT_CLASS(klass);
+	object_class  = GTK_OBJECT_CLASS(klass);
+	widget_class  = GTK_WIDGET_CLASS(klass);
+	entry_class   = GTK_ENTRY_CLASS(klass);
+
+	gobject_class->finalize = sexy_icon_entry_finalize;
+
+	object_class->destroy = sexy_icon_entry_destroy;
+
+	widget_class->map = sexy_icon_entry_map;
+	widget_class->unmap = sexy_icon_entry_unmap;
+	widget_class->realize = sexy_icon_entry_realize;
+	widget_class->unrealize = sexy_icon_entry_unrealize;
+	widget_class->size_request = sexy_icon_entry_size_request;
+	widget_class->size_allocate = sexy_icon_entry_size_allocate;
+	widget_class->expose_event = sexy_icon_entry_expose;
+	widget_class->enter_notify_event = sexy_icon_entry_enter_notify;
+	widget_class->leave_notify_event = sexy_icon_entry_leave_notify;
+	widget_class->button_press_event = sexy_icon_entry_button_press;
+	widget_class->button_release_event = sexy_icon_entry_button_release;
+
+	/**
+	 * SexyIconEntry::icon-pressed:
+	 * @entry: The entry on which the signal is emitted.
+	 * @icon_pos: The position of the clicked icon.
+	 * @button: The mouse button clicked.
+	 *
+	 * The ::icon-pressed signal is emitted when an icon is clicked.
+	 */
+	signals[ICON_PRESSED] =
+		g_signal_new("icon_pressed",
+					 G_TYPE_FROM_CLASS(gobject_class),
+					 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+					 G_STRUCT_OFFSET(SexyIconEntryClass, icon_pressed),
+					 NULL, NULL,
+					 gtk_marshal_VOID__INT_INT,
+					 G_TYPE_NONE, 2,
+					 G_TYPE_INT,
+					 G_TYPE_INT);
+
+	/**
+	 * SexyIconEntry::icon-released:
+	 * @entry: The entry on which the signal is emitted.
+	 * @icon_pos: The position of the clicked icon.
+	 * @button: The mouse button clicked.
+	 *
+	 * The ::icon-released signal is emitted on the button release from a
+	 * mouse click.
+	 */
+	signals[ICON_RELEASED] =
+		g_signal_new("icon_released",
+					 G_TYPE_FROM_CLASS(gobject_class),
+					 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+					 G_STRUCT_OFFSET(SexyIconEntryClass, icon_released),
+					 NULL, NULL,
+					 gtk_marshal_VOID__INT_INT,
+					 G_TYPE_NONE, 2,
+					 G_TYPE_INT,
+					 G_TYPE_INT);
+}
+
+static void
+sexy_icon_entry_editable_init(GtkEditableClass *iface)
+{
+};
+
+static void
+sexy_icon_entry_init(SexyIconEntry *entry)
+{
+	entry->priv = g_new0(SexyIconEntryPriv, 1);
+}
+
+static void
+sexy_icon_entry_finalize(GObject *obj)
+{
+	SexyIconEntry *entry;
+
+	g_return_if_fail(obj != NULL);
+	g_return_if_fail(SEXY_IS_ICON_ENTRY(obj));
+
+	entry = SEXY_ICON_ENTRY(obj);
+
+	g_free(entry->priv);
+
+	if (G_OBJECT_CLASS(parent_class)->finalize)
+		G_OBJECT_CLASS(parent_class)->finalize(obj);
+}
+
+static void
+sexy_icon_entry_destroy(GtkObject *obj)
+{
+	SexyIconEntry *entry;
+
+	entry = SEXY_ICON_ENTRY(obj);
+
+	sexy_icon_entry_set_icon(entry, SEXY_ICON_ENTRY_PRIMARY, NULL);
+	sexy_icon_entry_set_icon(entry, SEXY_ICON_ENTRY_SECONDARY, NULL);
+
+	if (GTK_OBJECT_CLASS(parent_class)->destroy)
+		GTK_OBJECT_CLASS(parent_class)->destroy(obj);
+}
+
+static void
+sexy_icon_entry_map(GtkWidget *widget)
+{
+	if (GTK_WIDGET_REALIZED(widget) && !GTK_WIDGET_MAPPED(widget))
+	{
+		SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
+		int i;
+
+		GTK_WIDGET_CLASS(parent_class)->map(widget);
+
+		for (i = 0; i < MAX_ICONS; i++)
+		{
+			if (entry->priv->icons[i].icon != NULL)
+				gdk_window_show(entry->priv->icons[i].window);
+		}
+	}
+}
+
+static void
+sexy_icon_entry_unmap(GtkWidget *widget)
+{
+	if (GTK_WIDGET_MAPPED(widget))
+	{
+		SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
+		int i;
+
+		for (i = 0; i < MAX_ICONS; i++)
+		{
+			if (entry->priv->icons[i].icon != NULL)
+				gdk_window_hide(entry->priv->icons[i].window);
+		}
+
+		GTK_WIDGET_CLASS(parent_class)->unmap(widget);
+	}
+}
+
+static gint
+get_icon_width(SexyIconEntry *entry, SexyIconEntryPosition icon_pos)
+{
+	GtkRequisition requisition;
+	gint menu_icon_width;
+	gint width;
+	SexyIconInfo *icon_info = &entry->priv->icons[icon_pos];
+
+	if (icon_info->icon == NULL)
+		return 0;
+
+	gtk_widget_size_request(GTK_WIDGET(icon_info->icon), &requisition);
+	gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &menu_icon_width, NULL);
+
+	width = MAX(requisition.width, menu_icon_width);
+
+	return width;
+}
+
+static void
+get_borders(SexyIconEntry *entry, gint *xborder, gint *yborder)
+{
+	GtkWidget *widget = GTK_WIDGET(entry);
+	gint focus_width;
+	gboolean interior_focus;
+
+	gtk_widget_style_get(widget,
+						 "interior-focus", &interior_focus,
+						 "focus-line-width", &focus_width,
+						 NULL);
+
+	if (gtk_entry_get_has_frame(GTK_ENTRY(entry)))
+	{
+		*xborder = widget->style->xthickness;
+		*yborder = widget->style->ythickness;
+	}
+	else
+	{
+		*xborder = 0;
+		*yborder = 0;
+	}
+
+	if (!interior_focus)
+	{
+		*xborder += focus_width;
+		*yborder += focus_width;
+	}
+}
+
+static void
+get_text_area_size(SexyIconEntry *entry, GtkAllocation *alloc)
+{
+	GtkWidget *widget = GTK_WIDGET(entry);
+	GtkRequisition requisition;
+	gint xborder, yborder;
+
+	gtk_widget_get_child_requisition(widget, &requisition);
+	get_borders(entry, &xborder, &yborder);
+
+	alloc->x      = xborder;
+	alloc->y      = yborder;
+	alloc->width  = widget->allocation.width - xborder * 2;
+	alloc->height = requisition.height	 - yborder * 2;
+}
+
+static void
+get_icon_allocation(SexyIconEntry *icon_entry,
+					gboolean left,
+					GtkAllocation *widget_alloc,
+					GtkAllocation *text_area_alloc,
+					GtkAllocation *allocation,
+					SexyIconEntryPosition *icon_pos)
+{
+	gboolean rtl;
+
+	rtl = (gtk_widget_get_direction(GTK_WIDGET(icon_entry)) ==
+		   GTK_TEXT_DIR_RTL);
+
+	if (left)
+		*icon_pos = (rtl ? SEXY_ICON_ENTRY_SECONDARY : SEXY_ICON_ENTRY_PRIMARY);
+	else
+		*icon_pos = (rtl ? SEXY_ICON_ENTRY_PRIMARY : SEXY_ICON_ENTRY_SECONDARY);
+
+	allocation->y = text_area_alloc->y;
+	allocation->width = get_icon_width(icon_entry, *icon_pos);
+	allocation->height = text_area_alloc->height;
+
+	if (left)
+		allocation->x = text_area_alloc->x + ICON_MARGIN;
+	else
+	{
+		allocation->x = text_area_alloc->x + text_area_alloc->width -
+				allocation->width - ICON_MARGIN;
+	}
+}
+
+static void
+sexy_icon_entry_realize(GtkWidget *widget)
+{
+	SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
+	GdkWindowAttr attributes;
+	gint attributes_mask;
+	int i;
+
+	GTK_WIDGET_CLASS(parent_class)->realize(widget);
+
+	attributes.x = 0;
+	attributes.y = 0;
+	attributes.width = 1;
+	attributes.height = 1;
+	attributes.window_type = GDK_WINDOW_CHILD;
+	attributes.wclass = GDK_INPUT_OUTPUT;
+	attributes.visual = gtk_widget_get_visual(widget);
+	attributes.colormap = gtk_widget_get_colormap(widget);
+	attributes.event_mask = gtk_widget_get_events(widget);
+	attributes.event_mask |=
+		(GDK_EXPOSURE_MASK
+		 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+		 | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
+
+	attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+
+	for (i = 0; i < MAX_ICONS; i++)
+	{
+		SexyIconInfo *icon_info;
+
+		icon_info = &entry->priv->icons[i];
+		icon_info->window = gdk_window_new(widget->window, &attributes,
+										   attributes_mask);
+		gdk_window_set_user_data(icon_info->window, widget);
+
+		gdk_window_set_background(icon_info->window,
+			&widget->style->base[GTK_WIDGET_STATE(widget)]);
+	}
+
+	gtk_widget_queue_resize(widget);
+}
+
+static void
+sexy_icon_entry_unrealize(GtkWidget *widget)
+{
+	SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
+	int i;
+
+	GTK_WIDGET_CLASS(parent_class)->unrealize(widget);
+
+	for (i = 0; i < MAX_ICONS; i++)
+	{
+		SexyIconInfo *icon_info = &entry->priv->icons[i];
+
+		gdk_window_destroy(icon_info->window);
+		icon_info->window = NULL;
+	}
+}
+
+static void
+sexy_icon_entry_size_request(GtkWidget *widget, GtkRequisition *requisition)
+{
+	GtkEntry *gtkentry;
+	SexyIconEntry *entry;
+	gint icon_widths = 0;
+	int i;
+
+	gtkentry = GTK_ENTRY(widget);
+	entry	 = SEXY_ICON_ENTRY(widget);
+
+	for (i = 0; i < MAX_ICONS; i++)
+	{
+		int icon_width = get_icon_width(entry, i);
+
+		if (icon_width > 0)
+			icon_widths += icon_width + ICON_MARGIN;
+	}
+
+	GTK_WIDGET_CLASS(parent_class)->size_request(widget, requisition);
+
+	if (icon_widths > requisition->width)
+		requisition->width += icon_widths;
+}
+
+static void
+place_windows(SexyIconEntry *icon_entry, GtkAllocation *widget_alloc)
+{
+	SexyIconEntryPosition left_icon_pos;
+	SexyIconEntryPosition right_icon_pos;
+	GtkAllocation left_icon_alloc;
+	GtkAllocation right_icon_alloc;
+	GtkAllocation text_area_alloc;
+
+	get_text_area_size(icon_entry, &text_area_alloc);
+	get_icon_allocation(icon_entry, TRUE, widget_alloc, &text_area_alloc,
+						&left_icon_alloc, &left_icon_pos);
+	get_icon_allocation(icon_entry, FALSE, widget_alloc, &text_area_alloc,
+						&right_icon_alloc, &right_icon_pos);
+
+	if (left_icon_alloc.width > 0)
+	{
+		text_area_alloc.x = left_icon_alloc.x + left_icon_alloc.width +
+				    ICON_MARGIN;
+	}
+
+	if (right_icon_alloc.width > 0)
+		text_area_alloc.width -= right_icon_alloc.width + ICON_MARGIN;
+
+	text_area_alloc.width -= text_area_alloc.x;
+
+	gdk_window_move_resize(icon_entry->priv->icons[left_icon_pos].window,
+						   left_icon_alloc.x, left_icon_alloc.y,
+						   left_icon_alloc.width, left_icon_alloc.height);
+
+	gdk_window_move_resize(icon_entry->priv->icons[right_icon_pos].window,
+						   right_icon_alloc.x, right_icon_alloc.y,
+						   right_icon_alloc.width, right_icon_alloc.height);
+
+	gdk_window_move_resize(GTK_ENTRY(icon_entry)->text_area,
+						   text_area_alloc.x, text_area_alloc.y,
+						   text_area_alloc.width, text_area_alloc.height);
+}
+
+static void
+sexy_icon_entry_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
+{
+	g_return_if_fail(SEXY_IS_ICON_ENTRY(widget));
+	g_return_if_fail(allocation != NULL);
+
+	widget->allocation = *allocation;
+
+	GTK_WIDGET_CLASS(parent_class)->size_allocate(widget, allocation);
+
+	if (GTK_WIDGET_REALIZED(widget))
+		place_windows(SEXY_ICON_ENTRY(widget), allocation);
+}
+
+static GdkPixbuf *
+get_pixbuf_from_icon(SexyIconEntry *entry, SexyIconEntryPosition icon_pos)
+{
+	GdkPixbuf *pixbuf = NULL;
+	gchar *stock_id;
+	SexyIconInfo *icon_info = &entry->priv->icons[icon_pos];
+	GtkIconSize size;
+
+	switch (gtk_image_get_storage_type(GTK_IMAGE(icon_info->icon)))
+	{
+		case GTK_IMAGE_PIXBUF:
+			pixbuf = gtk_image_get_pixbuf(GTK_IMAGE(icon_info->icon));
+			g_object_ref(pixbuf);
+			break;
+
+		case GTK_IMAGE_STOCK:
+			gtk_image_get_stock(GTK_IMAGE(icon_info->icon), &stock_id, &size);
+			pixbuf = gtk_widget_render_icon(GTK_WIDGET(entry),
+											stock_id, size, NULL);
+			break;
+
+		default:
+			return NULL;
+	}
+
+	return pixbuf;
+}
+
+/* Kudos to the gnome-panel guys. */
+static void
+colorshift_pixbuf(GdkPixbuf *dest, GdkPixbuf *src, int shift)
+{
+	gint i, j;
+	gint width, height, has_alpha, src_rowstride, dest_rowstride;
+	guchar *target_pixels;
+	guchar *original_pixels;
+	guchar *pix_src;
+	guchar *pix_dest;
+	int val;
+	guchar r, g, b;
+
+	has_alpha	= gdk_pixbuf_get_has_alpha(src);
+	width		= gdk_pixbuf_get_width(src);
+	height		= gdk_pixbuf_get_height(src);
+	src_rowstride	= gdk_pixbuf_get_rowstride(src);
+	dest_rowstride	= gdk_pixbuf_get_rowstride(dest);
+	original_pixels = gdk_pixbuf_get_pixels(src);
+	target_pixels	= gdk_pixbuf_get_pixels(dest);
+
+	for (i = 0; i < height; i++)
+	{
+		pix_dest = target_pixels   + i * dest_rowstride;
+		pix_src  = original_pixels + i * src_rowstride;
+
+		for (j = 0; j < width; j++)
+		{
+			r = *(pix_src++);
+			g = *(pix_src++);
+			b = *(pix_src++);
+
+			val = r + shift;
+			*(pix_dest++) = CLAMP(val, 0, 255);
+
+			val = g + shift;
+			*(pix_dest++) = CLAMP(val, 0, 255);
+
+			val = b + shift;
+			*(pix_dest++) = CLAMP(val, 0, 255);
+
+			if (has_alpha)
+				*(pix_dest++) = *(pix_src++);
+		}
+	}
+}
+
+static void
+draw_icon(GtkWidget *widget, SexyIconEntryPosition icon_pos)
+{
+	SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
+	SexyIconInfo *icon_info = &entry->priv->icons[icon_pos];
+	GdkPixbuf *pixbuf;
+	gint x, y, width, height;
+
+	if (icon_info->icon == NULL || !GTK_WIDGET_REALIZED(widget))
+		return;
+
+	if ((pixbuf = get_pixbuf_from_icon(entry, icon_pos)) == NULL)
+		return;
+
+	gdk_drawable_get_size(icon_info->window, &width, &height);
+
+	if (width == 1 || height == 1)
+	{
+		/*
+		 * size_allocate hasn't been called yet. These are the default values.
+		 */
+		return;
+	}
+
+	if (gdk_pixbuf_get_height(pixbuf) > height)
+	{
+		GdkPixbuf *temp_pixbuf;
+		int scale;
+
+		scale = height - (2 * ICON_MARGIN);
+
+		printf("scale = %d (height = %d)\n", scale, height);
+		temp_pixbuf = gdk_pixbuf_scale_simple(pixbuf, scale, scale,
+											  GDK_INTERP_BILINEAR);
+
+		g_object_unref(pixbuf);
+
+		pixbuf = temp_pixbuf;
+	}
+
+	x = (width  - gdk_pixbuf_get_width(pixbuf)) / 2;
+	y = (height - gdk_pixbuf_get_height(pixbuf)) / 2;
+
+	if (icon_info->hovered)
+	{
+		GdkPixbuf *temp_pixbuf;
+
+		temp_pixbuf = gdk_pixbuf_copy(pixbuf);
+
+		colorshift_pixbuf(temp_pixbuf, pixbuf, 30);
+
+		g_object_unref(pixbuf);
+
+		pixbuf = temp_pixbuf;
+	}
+
+	gdk_draw_pixbuf(icon_info->window, widget->style->black_gc, pixbuf,
+					0, 0, x, y, -1, -1,
+					GDK_RGB_DITHER_NORMAL, 0, 0);
+
+	g_object_unref(pixbuf);
+}
+
+static gint
+sexy_icon_entry_expose(GtkWidget *widget, GdkEventExpose *event)
+{
+	SexyIconEntry *entry;
+
+	g_return_val_if_fail(SEXY_IS_ICON_ENTRY(widget), FALSE);
+	g_return_val_if_fail(event != NULL, FALSE);
+
+	entry = SEXY_ICON_ENTRY(widget);
+
+	if (GTK_WIDGET_DRAWABLE(widget))
+	{
+		gboolean found = FALSE;
+		int i;
+
+		for (i = 0; i < MAX_ICONS && !found; i++)
+		{
+			SexyIconInfo *icon_info = &entry->priv->icons[i];
+
+			if (event->window == icon_info->window)
+			{
+				gint width;
+				GtkAllocation text_area_alloc;
+
+				get_text_area_size(entry, &text_area_alloc);
+				gdk_drawable_get_size(icon_info->window, &width, NULL);
+
+				gtk_paint_flat_box(widget->style, icon_info->window,
+								   GTK_WIDGET_STATE(widget), GTK_SHADOW_NONE,
+								   NULL, widget, "entry_bg",
+								   0, 0, width, text_area_alloc.height);
+
+				draw_icon(widget, i);
+
+				found = TRUE;
+			}
+		}
+
+		if (!found)
+			GTK_WIDGET_CLASS(parent_class)->expose_event(widget, event);
+	}
+
+	return FALSE;
+}
+
+static void
+update_icon(GObject *obj, GParamSpec *param, SexyIconEntry *entry)
+{
+	if (param != NULL)
+	{
+		const char *name = g_param_spec_get_name(param);
+
+		if (strcmp(name, "pixbuf")   && strcmp(name, "stock")  &&
+			strcmp(name, "image")	 && strcmp(name, "pixmap") &&
+			strcmp(name, "icon_set") && strcmp(name, "pixbuf_animation"))
+		{
+			return;
+		}
+	}
+
+	gtk_widget_queue_resize(GTK_WIDGET(entry));
+}
+
+static gint
+sexy_icon_entry_enter_notify(GtkWidget *widget, GdkEventCrossing *event)
+{
+	SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
+	int i;
+
+	for (i = 0; i < MAX_ICONS; i++)
+	{
+		if (event->window == entry->priv->icons[i].window)
+		{
+			if (sexy_icon_entry_get_icon_highlight(entry, i))
+			{
+				entry->priv->icons[i].hovered = TRUE;
+
+				update_icon(NULL, NULL, entry);
+
+				break;
+			}
+		}
+	}
+
+	return FALSE;
+}
+
+static gint
+sexy_icon_entry_leave_notify(GtkWidget *widget, GdkEventCrossing *event)
+{
+	SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
+	int i;
+
+	for (i = 0; i < MAX_ICONS; i++)
+	{
+		if (event->window == entry->priv->icons[i].window)
+		{
+			if (sexy_icon_entry_get_icon_highlight(entry, i))
+			{
+				entry->priv->icons[i].hovered = FALSE;
+
+				update_icon(NULL, NULL, entry);
+
+				break;
+			}
+		}
+	}
+
+	return FALSE;
+}
+
+static gint
+sexy_icon_entry_button_press(GtkWidget *widget, GdkEventButton *event)
+{
+	SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
+	int i;
+
+	for (i = 0; i < MAX_ICONS; i++)
+	{
+		if (event->window == entry->priv->icons[i].window)
+		{
+			if (event->button == 1 &&
+				sexy_icon_entry_get_icon_highlight(entry, i))
+			{
+				entry->priv->icons[i].hovered = FALSE;
+
+				update_icon(NULL, NULL, entry);
+			}
+
+			g_signal_emit(entry, signals[ICON_PRESSED], 0, i, event->button);
+
+			return TRUE;
+		}
+	}
+
+	if (GTK_WIDGET_CLASS(parent_class)->button_press_event)
+		return GTK_WIDGET_CLASS(parent_class)->button_press_event(widget,
+																  event);
+
+	return FALSE;
+}
+
+static gint
+sexy_icon_entry_button_release(GtkWidget *widget, GdkEventButton *event)
+{
+	SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
+	int i;
+
+	for (i = 0; i < MAX_ICONS; i++)
+	{
+		GdkWindow *icon_window = entry->priv->icons[i].window;
+
+		if (event->window == icon_window)
+		{
+			int width, height;
+			gdk_drawable_get_size(icon_window, &width, &height);
+
+			if (event->button == 1 &&
+				sexy_icon_entry_get_icon_highlight(entry, i) &&
+				event->x >= 0	  && event->y >= 0 &&
+				event->x <= width && event->y <= height)
+			{
+				entry->priv->icons[i].hovered = TRUE;
+
+				update_icon(NULL, NULL, entry);
+			}
+
+			g_signal_emit(entry, signals[ICON_RELEASED], 0, i, event->button);
+
+			return TRUE;
+		}
+	}
+
+	if (GTK_WIDGET_CLASS(parent_class)->button_release_event)
+		return GTK_WIDGET_CLASS(parent_class)->button_release_event(widget,
+																	event);
+
+	return FALSE;
+}
+
+/**
+ * sexy_icon_entry_new
+ *
+ * Creates a new SexyIconEntry widget.
+ *
+ * Returns a new #SexyIconEntry.
+ */
+GtkWidget *
+sexy_icon_entry_new(void)
+{
+	return GTK_WIDGET(g_object_new(SEXY_TYPE_ICON_ENTRY, NULL));
+}
+
+/**
+ * sexy_icon_entry_set_icon
+ * @entry: A #SexyIconEntry.
+ * @position: Icon position.
+ * @icon: A #GtkImage to set as the icon.
+ *
+ * Sets the icon shown in the entry
+ */
+void
+sexy_icon_entry_set_icon(SexyIconEntry *entry, SexyIconEntryPosition icon_pos,
+						 GtkImage *icon)
+{
+	SexyIconInfo *icon_info;
+
+	g_return_if_fail(entry != NULL);
+	g_return_if_fail(SEXY_IS_ICON_ENTRY(entry));
+	g_return_if_fail(IS_VALID_ICON_ENTRY_POSITION(icon_pos));
+	g_return_if_fail(icon == NULL || GTK_IS_IMAGE(icon));
+
+	icon_info = &entry->priv->icons[icon_pos];
+
+	if (icon == icon_info->icon)
+		return;
+
+	if (icon_pos == SEXY_ICON_ENTRY_SECONDARY &&
+		entry->priv->icon_released_id != 0)
+	{
+		g_signal_handler_disconnect(entry, entry->priv->icon_released_id);
+		entry->priv->icon_released_id = 0;
+	}
+
+	if (icon == NULL)
+	{
+		if (icon_info->icon != NULL)
+		{
+			gtk_widget_destroy(GTK_WIDGET(icon_info->icon));
+			icon_info->icon = NULL;
+
+			/*
+			 * Explicitly check, as the pointer may become invalidated
+			 * during destruction.
+			 */
+			if (icon_info->window != NULL && GDK_IS_WINDOW(icon_info->window))
+				gdk_window_hide(icon_info->window);
+		}
+	}
+	else
+	{
+		if (icon_info->window != NULL && icon_info->icon == NULL)
+			gdk_window_show(icon_info->window);
+
+		g_signal_connect(G_OBJECT(icon), "notify",
+						 G_CALLBACK(update_icon), entry);
+
+		icon_info->icon = icon;
+		g_object_ref(icon);
+	}
+
+	update_icon(NULL, NULL, entry);
+}
+
+/**
+ * sexy_icon_entry_set_icon_highlight
+ * @entry: A #SexyIconEntry;
+ * @position: Icon position.
+ * @highlight: TRUE if the icon should highlight on mouse-over
+ *
+ * Determines whether the icon will highlight on mouse-over.
+ */
+void
+sexy_icon_entry_set_icon_highlight(SexyIconEntry *entry,
+								   SexyIconEntryPosition icon_pos,
+								   gboolean highlight)
+{
+	SexyIconInfo *icon_info;
+
+	g_return_if_fail(entry != NULL);
+	g_return_if_fail(SEXY_IS_ICON_ENTRY(entry));
+	g_return_if_fail(IS_VALID_ICON_ENTRY_POSITION(icon_pos));
+
+	icon_info = &entry->priv->icons[icon_pos];
+
+	if (icon_info->highlight == highlight)
+		return;
+
+	icon_info->highlight = highlight;
+}
+
+/**
+ * sexy_icon_entry_get_icon
+ * @entry: A #SexyIconEntry.
+ * @position: Icon position.
+ *
+ * Retrieves the image used for the icon
+ *
+ * Returns: A #GtkImage.
+ */
+GtkImage *
+sexy_icon_entry_get_icon(const SexyIconEntry *entry,
+						 SexyIconEntryPosition icon_pos)
+{
+	g_return_val_if_fail(entry != NULL, NULL);
+	g_return_val_if_fail(SEXY_IS_ICON_ENTRY(entry), NULL);
+	g_return_val_if_fail(IS_VALID_ICON_ENTRY_POSITION(icon_pos), NULL);
+
+	return entry->priv->icons[icon_pos].icon;
+}
+
+/**
+ * sexy_icon_entry_get_icon_highlight
+ * @entry: A #SexyIconEntry.
+ * @position: Icon position.
+ *
+ * Retrieves whether entry will highlight the icon on mouseover.
+ *
+ * Returns: TRUE if icon highlights.
+ */
+gboolean
+sexy_icon_entry_get_icon_highlight(const SexyIconEntry *entry,
+								   SexyIconEntryPosition icon_pos)
+{
+	g_return_val_if_fail(entry != NULL, FALSE);
+	g_return_val_if_fail(SEXY_IS_ICON_ENTRY(entry), FALSE);
+	g_return_val_if_fail(IS_VALID_ICON_ENTRY_POSITION(icon_pos), FALSE);
+
+	return entry->priv->icons[icon_pos].highlight;
+}
+
+static void
+clear_button_clicked_cb(SexyIconEntry *icon_entry,
+						SexyIconEntryPosition icon_pos,
+						int button)
+{
+	if (icon_pos != SEXY_ICON_ENTRY_SECONDARY || button != 1)
+		return;
+
+	gtk_entry_set_text(GTK_ENTRY(icon_entry), "");
+}
+
+/**
+ * sexy_icon_entry_add_clear_button
+ * @icon_entry: A #SexyIconEntry.
+ *
+ * A convenience function to add a clear button to the end of the entry.
+ * This is useful for search boxes.
+ */
+void
+sexy_icon_entry_add_clear_button(SexyIconEntry *icon_entry)
+{
+	GtkWidget *icon;
+
+	g_return_if_fail(icon_entry != NULL);
+	g_return_if_fail(SEXY_IS_ICON_ENTRY(icon_entry));
+
+	icon = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
+	gtk_widget_show(icon);
+	sexy_icon_entry_set_icon(SEXY_ICON_ENTRY(icon_entry),
+							 SEXY_ICON_ENTRY_SECONDARY,
+							 GTK_IMAGE(icon));
+	sexy_icon_entry_set_icon_highlight(SEXY_ICON_ENTRY(icon_entry),
+									   SEXY_ICON_ENTRY_SECONDARY, TRUE);
+
+	if (icon_entry->priv->icon_released_id != 0)
+	{
+		g_signal_handler_disconnect(icon_entry,
+									icon_entry->priv->icon_released_id);
+	}
+
+	icon_entry->priv->icon_released_id =
+		g_signal_connect(G_OBJECT(icon_entry), "icon_released",
+						 G_CALLBACK(clear_button_clicked_cb), NULL);
+}

Added: trunk/src/tracker-search-tool/sexy-icon-entry.h
==============================================================================
--- (empty file)
+++ trunk/src/tracker-search-tool/sexy-icon-entry.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,101 @@
+/*
+ * @file libsexy/sexy-icon-entry.h Entry widget
+ *
+ * @Copyright (C) 2004-2006 Christian Hammond.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA  02111-1307, USA.
+ */
+#ifndef _SEXY_ICON_ENTRY_H_
+#define _SEXY_ICON_ENTRY_H_
+
+typedef struct _SexyIconEntry	   SexyIconEntry;
+typedef struct _SexyIconEntryClass SexyIconEntryClass;
+typedef struct _SexyIconEntryPriv  SexyIconEntryPriv;
+
+#include <gtk/gtkentry.h>
+#include <gtk/gtkimage.h>
+
+#define SEXY_TYPE_ICON_ENTRY (sexy_icon_entry_get_type())
+#define SEXY_ICON_ENTRY(obj) \
+		(G_TYPE_CHECK_INSTANCE_CAST((obj), SEXY_TYPE_ICON_ENTRY, SexyIconEntry))
+#define SEXY_ICON_ENTRY_CLASS(klass) \
+		(G_TYPE_CHECK_CLASS_CAST((klass), SEXY_TYPE_ICON_ENTRY, SexyIconEntryClass))
+#define SEXY_IS_ICON_ENTRY(obj) \
+		(G_TYPE_CHECK_INSTANCE_TYPE((obj), SEXY_TYPE_ICON_ENTRY))
+#define SEXY_IS_ICON_ENTRY_CLASS(klass) \
+		(G_TYPE_CHECK_CLASS_TYPE((klass), SEXY_TYPE_ICON_ENTRY))
+#define SEXY_ICON_ENTRY_GET_CLASS(obj) \
+		(G_TYPE_INSTANCE_GET_CLASS ((obj), SEXY_TYPE_ICON_ENTRY, SexyIconEntryClass))
+
+typedef enum
+{
+	SEXY_ICON_ENTRY_PRIMARY,
+	SEXY_ICON_ENTRY_SECONDARY
+
+} SexyIconEntryPosition;
+
+struct _SexyIconEntry
+{
+	GtkEntry parent_object;
+
+	SexyIconEntryPriv *priv;
+
+	void (*gtk_reserved1)(void);
+	void (*gtk_reserved2)(void);
+	void (*gtk_reserved3)(void);
+	void (*gtk_reserved4)(void);
+};
+
+struct _SexyIconEntryClass
+{
+	GtkEntryClass parent_class;
+
+	/* Signals */
+	void (*icon_pressed)(SexyIconEntry *entry, SexyIconEntryPosition icon_pos,
+						 int button);
+	void (*icon_released)(SexyIconEntry *entry, SexyIconEntryPosition icon_pos,
+						  int button);
+
+	void (*gtk_reserved1)(void);
+	void (*gtk_reserved2)(void);
+	void (*gtk_reserved3)(void);
+	void (*gtk_reserved4)(void);
+};
+
+G_BEGIN_DECLS
+
+GType sexy_icon_entry_get_type(void);
+
+GtkWidget *sexy_icon_entry_new(void);
+
+void sexy_icon_entry_set_icon(SexyIconEntry *entry,
+							  SexyIconEntryPosition position,
+							  GtkImage *icon);
+
+void sexy_icon_entry_set_icon_highlight(SexyIconEntry *entry,
+										SexyIconEntryPosition position,
+										gboolean highlight);
+
+GtkImage *sexy_icon_entry_get_icon(const SexyIconEntry *entry,
+								   SexyIconEntryPosition position);
+
+gboolean sexy_icon_entry_get_icon_highlight(const SexyIconEntry *entry,
+											SexyIconEntryPosition position);
+void sexy_icon_entry_add_clear_button(SexyIconEntry *icon_entry);
+
+G_END_DECLS
+
+#endif /* _SEXY_ICON_ENTRY_H_ */

Added: trunk/src/tracker-search-tool/thumbnail_frame.png
==============================================================================
Binary file. No diff available.

Added: trunk/src/tracker-search-tool/tracker-search-tool-callbacks.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-search-tool/tracker-search-tool-callbacks.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,1844 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * GNOME Search Tool
+ *
+ *  File:  tracker_search-callbacks.c
+ *
+ *  (C) 2002 the Free Software Foundation
+ *
+ *  Authors:	Dennis Cranston  <dennis_cranston yahoo com>
+ *		George Lebl
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <string.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <libgnomevfs/gnome-vfs-ops.h>
+#include <libgnomevfs/gnome-vfs-utils.h>
+#include <libgnomevfs/gnome-vfs-find-directory.h>
+#include <libgnome/gnome-desktop-item.h>
+
+#include <gnome.h>
+
+#include "tracker-search-tool.h"
+#include "tracker-search-tool-callbacks.h"
+#include "tracker-search-tool-support.h"
+#include "../libtracker-gtk/tracker-metadata-tile.h"
+
+#define SILENT_WINDOW_OPEN_LIMIT 5
+#define METADATA_IMAGE_WIDTH	128
+#define METADATA_IMAGE_HEIGHT	128
+
+#ifdef HAVE_GETPGID
+extern pid_t getpgid (pid_t);
+#endif
+
+gboolean row_selected_by_button_press_event;
+
+static void
+store_window_state_and_geometry (GSearchWindow *gsearch)
+{
+	tracker_search_gconf_set_int ("/apps/tracker-search-tool/default_window_width",
+				      gsearch->window_width);
+	tracker_search_gconf_set_int ("/apps/tracker-search-tool/default_window_height",
+				      gsearch->window_height);
+	tracker_search_gconf_set_boolean ("/apps/tracker-search-tool/default_window_maximized",
+					  gsearch->is_window_maximized);
+	tracker_set_stored_separator_position (gtk_paned_get_position (GTK_PANED (gsearch->pane)));
+}
+
+static void
+quit_application (GSearchWindow * gsearch)
+{
+	//GSearchCommandDetails * command_details = gsearch->command_details;
+	store_window_state_and_geometry (gsearch);
+	gtk_main_quit ();
+}
+
+void
+die_cb (GnomeClient * client,
+	gpointer data)
+{
+	quit_application ((GSearchWindow *) data);
+}
+
+void
+quit_cb (GtkWidget * widget,
+	 GdkEvent * event,
+	 gpointer data)
+{
+	quit_application ((GSearchWindow *) data);
+}
+
+void
+click_close_cb (GtkWidget * widget,
+		gpointer data)
+{
+	quit_application ((GSearchWindow *) data);
+}
+
+void
+click_stop_cb (GtkWidget * widget,
+	       gpointer data)
+{
+	GSearchWindow * gsearch = data;
+
+	if (gsearch->command_details->command_status == RUNNING) {
+		gtk_widget_set_sensitive (gsearch->stop_button, FALSE);
+	}
+}
+
+void
+click_help_cb (GtkWidget * widget,
+	       gpointer data)
+{
+	GtkWidget * window = data;
+	GError * error = NULL;
+
+	gnome_help_display_desktop_on_screen (NULL, "tracker-search-tool", "tracker-search-tool",
+					      NULL, gtk_widget_get_screen (widget), &error);
+	if (error) {
+		GtkWidget * dialog;
+
+		dialog = gtk_message_dialog_new (GTK_WINDOW (window),
+						 GTK_DIALOG_DESTROY_WITH_PARENT,
+						 GTK_MESSAGE_ERROR,
+						 GTK_BUTTONS_OK,
+						 _("Could not open help document."));
+		gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+							  error->message);
+
+		gtk_window_set_title (GTK_WINDOW (dialog), "");
+		gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+		gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 14);
+
+		g_signal_connect (G_OBJECT (dialog),
+				  "response",
+				  G_CALLBACK (gtk_widget_destroy), NULL);
+
+		gtk_widget_show (dialog);
+		g_error_free (error);
+	}
+}
+
+void
+click_expander_cb (GObject * object,
+		   GParamSpec * param_spec,
+		   gpointer data)
+{
+	GSearchWindow * gsearch = data;
+
+	if (gtk_expander_get_expanded (GTK_EXPANDER (object)) == TRUE) {
+		gtk_widget_show (gsearch->available_options_vbox);
+		gtk_window_set_geometry_hints (GTK_WINDOW (gsearch->window),
+					       GTK_WIDGET (gsearch->window),
+					       &gsearch->window_geometry,
+					       GDK_HINT_MIN_SIZE);
+	}
+	else {
+		GdkGeometry default_geometry = {DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT};
+
+		gtk_widget_hide (gsearch->available_options_vbox);
+		gtk_window_set_geometry_hints (GTK_WINDOW (gsearch->window),
+					       GTK_WIDGET (gsearch->window),
+					       &default_geometry,
+					       GDK_HINT_MIN_SIZE);
+	}
+}
+
+void
+size_allocate_cb (GtkWidget * widget,
+		  GtkAllocation * allocation,
+		  gpointer data)
+{
+	GtkWidget * button = data;
+
+	gtk_widget_set_size_request (button, allocation->width, -1);
+}
+
+/*
+void
+add_constraint_cb (GtkWidget * widget,
+		   gpointer data)
+{
+	GSearchWindow * gsearch = data;
+	gint index;
+
+	index = gtk_combo_box_get_active (GTK_COMBO_BOX (gsearch->available_options_combo_box));
+	add_constraint (gsearch, index, NULL, FALSE);
+}
+*/
+
+void
+remove_constraint_cb (GtkWidget * widget,
+		      gpointer data)
+{
+	GList * list = data;
+
+	GSearchWindow * gsearch = g_list_first (list)->data;
+	GSearchConstraint * constraint = g_list_last (list)->data;
+
+	gsearch->window_geometry.min_height -= WINDOW_HEIGHT_STEP;
+
+	gtk_window_set_geometry_hints (GTK_WINDOW (gsearch->window),
+				       GTK_WIDGET (gsearch->window),
+				       &gsearch->window_geometry,
+				       GDK_HINT_MIN_SIZE);
+
+	gtk_container_remove (GTK_CONTAINER (gsearch->available_options_vbox), widget->parent);
+
+	gsearch->available_options_selected_list =
+	    g_list_remove (gsearch->available_options_selected_list, constraint);
+
+	//set_constraint_selected_state (gsearch, constraint->constraint_id, FALSE);
+	set_constraint_gconf_boolean (constraint->constraint_id, FALSE);
+	g_slice_free (GSearchConstraint, constraint);
+	g_list_free (list);
+}
+
+void
+constraint_activate_cb (GtkWidget * widget,
+			gpointer data)
+{
+	GSearchWindow * gsearch = data;
+
+	if ((GTK_WIDGET_VISIBLE (gsearch->find_button)) &&
+	    (GTK_WIDGET_SENSITIVE (gsearch->find_button))) {
+		click_find_cb (gsearch->find_button, data);
+	}
+}
+
+void
+constraint_update_info_cb (GtkWidget * widget,
+			   gpointer data)
+{
+	static gchar * string;
+	GSearchConstraint * opt = data;
+
+	string = (gchar *) gtk_entry_get_text (GTK_ENTRY (widget));
+	update_constraint_info (opt, string);
+}
+
+void
+name_contains_activate_cb (GtkWidget * widget,
+			   gpointer data)
+{
+	GSearchWindow * gsearch = data;
+
+	if ((GTK_WIDGET_VISIBLE (gsearch->find_button)) &&
+	    (GTK_WIDGET_SENSITIVE (gsearch->find_button))) {
+		click_find_cb (gsearch->find_button, data);
+	}
+}
+
+gboolean
+text_changed_cb (GtkWidget * widget,
+		 gpointer data)
+{
+	GSearchWindow * gsearch = data;
+	const gchar * s = gtk_entry_get_text (GTK_ENTRY (gsearch->search_entry));
+
+	if (!tracker_is_empty_string (s)) {
+		gtk_widget_set_sensitive (gsearch->find_button, TRUE);
+	} else {
+		gtk_widget_set_sensitive (gsearch->find_button, FALSE);
+	}
+
+	return FALSE;
+}
+
+void
+click_find_cb (GtkWidget * widget,
+	       gpointer data)
+{
+	GSearchWindow * gsearch = data;
+	gchar	      * command = build_search_command (gsearch, TRUE);
+
+	if (command) {
+		start_new_search (gsearch, command);
+	}
+
+	g_free (command);
+}
+
+void
+next_results_cb (GtkWidget * widget,
+		 gpointer data)
+{
+	GSearchWindow * gsearch = data;
+
+	do_search (gsearch, gsearch->search_term, FALSE,
+		   gsearch->current_service->offset + MAX_SEARCH_RESULTS);
+}
+
+
+void
+prev_results_cb (GtkWidget * widget,
+		 gpointer data)
+{
+	GSearchWindow * gsearch = data;
+
+	do_search (gsearch, gsearch->search_term, FALSE,
+		   gsearch->current_service->offset - MAX_SEARCH_RESULTS);
+}
+
+void
+category_changed_cb (GtkTreeSelection * treeselection,
+		     gpointer data)
+{
+	select_category (treeselection, data);
+}
+
+static gint
+display_dialog_file_open_limit (GtkWidget * window,
+				gint count)
+{
+	GtkWidget * dialog;
+	GtkWidget * button;
+	gchar * primary;
+	gchar * secondary;
+	gint response;
+
+	primary = g_strdup_printf (ngettext ("Are you sure you want to open %d document?",
+					     "Are you sure you want to open %d documents?",
+					     count),
+				   count);
+
+	secondary = g_strdup_printf (ngettext ("This will open %d separate window.",
+					       "This will open %d separate windows.",
+					       count),
+				     count);
+
+	dialog = gtk_message_dialog_new (GTK_WINDOW (window),
+					 GTK_DIALOG_DESTROY_WITH_PARENT,
+					 GTK_MESSAGE_QUESTION,
+					 GTK_BUTTONS_CANCEL,
+					 primary);
+	gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+						  secondary);
+
+	gtk_window_set_title (GTK_WINDOW (dialog), "");
+	gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+	gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 14);
+
+	button = gtk_button_new_from_stock ("gtk-open");
+	GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+	gtk_widget_show (button);
+
+	gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, GTK_RESPONSE_OK);
+	gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+	response = gtk_dialog_run (GTK_DIALOG (dialog));
+	gtk_widget_destroy (dialog);
+	g_free (primary);
+	g_free (secondary);
+
+	return response;
+}
+
+static void
+display_dialog_could_not_open_file (GtkWidget * window,
+				    const gchar * file,
+				    const gchar * message)
+{
+	GtkWidget * dialog;
+	gchar * primary;
+
+	primary = g_strdup_printf (_("Could not open document \"%s\"."), file);
+
+	dialog = gtk_message_dialog_new (GTK_WINDOW (window),
+					 GTK_DIALOG_DESTROY_WITH_PARENT,
+					 GTK_MESSAGE_ERROR,
+					 GTK_BUTTONS_OK,
+					 primary);
+	gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+						  message);
+
+	gtk_window_set_title (GTK_WINDOW (dialog), "");
+	gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+	gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 14);
+
+	g_signal_connect (G_OBJECT (dialog),
+			  "response",
+			  G_CALLBACK (gtk_widget_destroy), NULL);
+
+	gtk_widget_show (dialog);
+	g_free (primary);
+}
+
+static void
+display_dialog_could_not_open_folder (GtkWidget * window,
+				      const gchar * folder)
+{
+	GtkWidget * dialog;
+	gchar * primary;
+
+	primary = g_strdup_printf (_("Could not open folder \"%s\"."), folder);
+
+	dialog = gtk_message_dialog_new (GTK_WINDOW (window),
+					 GTK_DIALOG_DESTROY_WITH_PARENT,
+					 GTK_MESSAGE_ERROR,
+					 GTK_BUTTONS_OK,
+					 primary);
+	gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+						  _("The nautilus file manager is not running."));
+
+	gtk_window_set_title (GTK_WINDOW (dialog), "");
+	gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+	gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 14);
+
+	g_signal_connect (G_OBJECT (dialog),
+			  "response",
+			  G_CALLBACK (gtk_widget_destroy), NULL);
+
+	gtk_widget_show (dialog);
+	g_free (primary);
+}
+
+void
+select_changed_cb (GtkTreeSelection *treeselection,
+		   gpointer user_data)
+{
+
+	GSearchWindow *gsearch = user_data;
+	tracker_update_metadata_tile (gsearch);
+
+#if 0
+
+	GtkTreeModel * model;
+	GtkTreeIter iter;
+	char *name, *path;
+
+	if (gtk_tree_selection_count_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection)) == 0) {
+		return;
+	}
+
+	gtk_tree_selection_get_selected (GTK_TREE_SELECTION (gsearch->search_results_selection),
+					 &model,
+					 &iter);
+
+	gtk_tree_model_get (GTK_TREE_MODEL (gsearch->search_results_list_store), &iter,
+			    COLUMN_NAME, &name,
+			    COLUMN_PATH, &path,
+			    -1);
+
+	//g_print ("selected uri is %s/%s\n", path, name);
+
+	g_free (path);
+
+	g_free (name);
+
+#endif
+}
+
+static GdkPixbuf *
+get_large_icon (const gchar * local_uri,
+		gboolean is_local_file,
+		GSearchWindow * gsearch)
+{
+	gchar	  * thumb_name = NULL;
+	GdkPixbuf * temp = NULL;
+
+	if (is_local_file) {
+		gchar *uri = gnome_vfs_get_uri_from_local_path (local_uri);
+
+		thumb_name = gnome_thumbnail_path_for_uri (uri, GNOME_THUMBNAIL_SIZE_NORMAL);
+
+		g_free (uri);
+	}
+
+	if (!thumb_name) {
+
+		gchar * icon_name = gnome_icon_lookup_sync  (gtk_icon_theme_get_default (),
+							     gsearch->thumbnail_factory,
+							     local_uri,
+							     NULL,
+							     GNOME_ICON_LOOKUP_FLAGS_SHOW_SMALL_IMAGES_AS_THEMSELVES | GNOME_ICON_LOOKUP_FLAGS_ALLOW_SVG_AS_THEMSELVES,
+							     0);
+
+		temp = gtk_icon_theme_load_icon (gtk_icon_theme_get_default(),
+					 icon_name,
+					 METADATA_IMAGE_HEIGHT,
+					 GTK_ICON_LOOKUP_FORCE_SVG,
+					 NULL);
+
+		g_free (icon_name);
+
+		return temp;
+	}
+
+	temp = gdk_pixbuf_new_from_file_at_scale (thumb_name, METADATA_IMAGE_WIDTH, METADATA_IMAGE_HEIGHT, TRUE, NULL);
+
+	g_free (thumb_name);
+
+	return temp;
+}
+
+
+void
+tracker_update_metadata_tile (GSearchWindow *gsearch)
+{
+	GtkTreeModel * model;
+	GList * list, * tmp;
+	ServiceType type;
+
+	if (gtk_tree_selection_count_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection)) != 1) {
+		tracker_metadata_tile_set_uri (TRACKER_METADATA_TILE (gsearch->metatile), NULL, 0, NULL, NULL);
+		//gtk_widget_hide (gsearch->metatile);
+		return;
+	}
+
+	list = gtk_tree_selection_get_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection),
+						     &model);
+	for (tmp = list; tmp; tmp = tmp->next) {
+
+		gboolean no_files_found = FALSE;
+		gchar *uri = NULL;
+		gchar *mime = NULL;
+		GdkPixbuf *pixbuf = NULL;
+		GtkTreeIter iter;
+
+		gtk_tree_model_get_iter (GTK_TREE_MODEL (gsearch->search_results_list_store), &iter, tmp->data);
+
+		gtk_tree_model_get (GTK_TREE_MODEL (gsearch->search_results_list_store), &iter,
+				    COLUMN_ICON, &pixbuf,
+				    COLUMN_URI, &uri,
+				    COLUMN_MIME, &mime,
+				    COLUMN_TYPE, &type,
+				    COLUMN_NO_FILES_FOUND, &no_files_found,
+				    -1);
+
+		/* get large icon for documents, images and videos only */
+		GdkPixbuf *large_icon = NULL;
+		if (type == SERVICE_DOCUMENTS || type == SERVICE_IMAGES || type == SERVICE_VIDEOS) {
+			large_icon = get_large_icon (uri, (gsearch->type < 10), gsearch);
+		}
+
+
+		if (large_icon) {
+			tracker_metadata_tile_set_uri (TRACKER_METADATA_TILE (gsearch->metatile), uri, type, mime, large_icon);
+			g_object_unref (G_OBJECT (large_icon));
+		} else {
+			tracker_metadata_tile_set_uri (TRACKER_METADATA_TILE (gsearch->metatile), uri, type, mime, pixbuf);
+		}
+
+
+		g_free (uri);
+		g_free (mime);
+	}
+
+	g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
+	g_list_free (list);
+}
+
+void
+open_file_cb (GtkAction * action,
+	      gpointer data)
+{
+	GSearchWindow * gsearch = data;
+	GtkTreeModel * model;
+	GList * list, * tmp;
+	char *exec = NULL;
+
+	if (gtk_tree_selection_count_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection)) == 0) {
+		return;
+	}
+
+	list = gtk_tree_selection_get_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection),
+						     &model);
+
+	if (g_list_length (list) > SILENT_WINDOW_OPEN_LIMIT) {
+		gint response;
+
+		response = display_dialog_file_open_limit (gsearch->window, g_list_length (list));
+
+		if (response == GTK_RESPONSE_CANCEL) {
+			g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
+			g_list_free (list);
+			return;
+		}
+	}
+
+	for (tmp = list; tmp; tmp = tmp->next) {
+
+		gboolean no_files_found = FALSE;
+		gchar * uri;
+		gchar * mime;
+		GtkTreeIter iter;
+
+		gtk_tree_model_get_iter (GTK_TREE_MODEL (gsearch->search_results_list_store), &iter, tmp->data);
+
+		gtk_tree_model_get (GTK_TREE_MODEL (gsearch->search_results_list_store), &iter,
+				    COLUMN_URI, &uri,
+				    COLUMN_MIME, &mime,
+				    COLUMN_EXEC, &exec,
+				    COLUMN_NO_FILES_FOUND, &no_files_found,
+				    -1);
+
+
+		if (gsearch->type == SERVICE_EMAILS) {
+			if (strstr (mime, "Evolution")) {
+				exec = g_strdup_printf ("evolution \"%s\"", uri);
+			} else if (strstr (mime, "Modest")) {
+				exec = g_strdup_printf ("modest-open \"%s\"", uri);
+			} else if (strstr (mime, "KMail")) {
+				exec = g_strdup_printf ("kmail --view \"%s\"", uri);
+			} else if (strstr (mime, "Thunderbird")) {
+				exec = g_strdup_printf ("thunderbird -viewtracker \"%s\"", uri);
+			} else {
+				exec = NULL;
+			}
+			if (exec) {
+				g_spawn_command_line_async (exec, NULL);
+				g_free (exec);
+			}
+
+		} else if (gsearch->type == SERVICE_APPLICATIONS) {
+			if (exec) {
+				gchar *my_exec = tracker_string_replace (exec, "%U", NULL);
+				g_spawn_command_line_async (my_exec, NULL);
+				g_free (my_exec);
+				g_free (exec);
+
+			} else {
+				display_dialog_could_not_open_file (gsearch->window, uri, _("Application could not be opened"));
+			}
+
+		} else {
+
+			gchar * file;
+			gchar * locale_file;
+
+			file = uri;
+			locale_file = g_locale_from_utf8 (file, -1, NULL, NULL, NULL);
+
+			if (!g_file_test (locale_file, G_FILE_TEST_EXISTS)) {
+				gtk_tree_selection_unselect_iter (GTK_TREE_SELECTION (gsearch->search_results_selection),
+								  &iter);
+				display_dialog_could_not_open_file (gsearch->window, uri,
+								    _("The document does not exist."));
+
+			} else if (open_file_with_xdg_open (gsearch->window, locale_file) == FALSE) {
+
+				if (open_file_with_application (gsearch->window, locale_file) == FALSE) {
+
+					if (launch_file (locale_file) == FALSE) {
+
+						if (g_file_test (locale_file, G_FILE_TEST_IS_DIR)) {
+
+							if (open_file_with_nautilus (gsearch->window, locale_file) == FALSE) {
+								display_dialog_could_not_open_folder (gsearch->window, uri);
+							}
+
+						} else {
+							display_dialog_could_not_open_file (gsearch->window, uri,
+										    _("There is no installed viewer capable "
+										      "of displaying the document."));
+						}
+					}
+				}
+			}
+			g_free (locale_file);
+		}
+		g_free (uri);
+
+	}
+	g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
+	g_list_free (list);
+}
+
+static gint
+display_dialog_folder_open_limit (GtkWidget * window,
+				  gint count)
+{
+	GtkWidget * dialog;
+	GtkWidget * button;
+	gchar * primary;
+	gchar * secondary;
+	gint response;
+
+	primary = g_strdup_printf (ngettext ("Are you sure you want to open %d folder?",
+					     "Are you sure you want to open %d folders?",
+					     count),
+				   count);
+
+	secondary = g_strdup_printf (ngettext ("This will open %d separate window.",
+					       "This will open %d separate windows.",
+					       count),
+				     count);
+
+	dialog = gtk_message_dialog_new (GTK_WINDOW (window),
+					 GTK_DIALOG_DESTROY_WITH_PARENT,
+					 GTK_MESSAGE_QUESTION,
+					 GTK_BUTTONS_CANCEL,
+					 primary);
+	gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+						  secondary);
+
+	gtk_window_set_title (GTK_WINDOW (dialog), "");
+	gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+	gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 14);
+
+	button = gtk_button_new_from_stock ("gtk-open");
+	GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+	gtk_widget_show (button);
+
+	gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, GTK_RESPONSE_OK);
+	gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+	response = gtk_dialog_run (GTK_DIALOG (dialog));
+	gtk_widget_destroy (dialog);
+	g_free (primary);
+	g_free (secondary);
+
+	return response;
+}
+
+void
+open_folder_cb (GtkAction * action,
+		gpointer data)
+{
+	GSearchWindow * gsearch = data;
+	GtkTreeModel * model;
+	GList * list, * tmp;
+
+	if (gtk_tree_selection_count_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection)) == 0) {
+		return;
+	}
+
+	list = gtk_tree_selection_get_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection),
+						     &model);
+
+	if (g_list_length (list) > SILENT_WINDOW_OPEN_LIMIT) {
+		gint response;
+
+		response = display_dialog_folder_open_limit (gsearch->window, g_list_length (list));
+
+		if (response == GTK_RESPONSE_CANCEL) {
+			g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
+			g_list_free (list);
+			return;
+		}
+	}
+
+	for (tmp = list; tmp; tmp = tmp->next) {
+
+		gchar * folder_locale;
+		gchar * folder_utf8;
+		GtkTreeIter iter;
+
+		gtk_tree_model_get_iter (GTK_TREE_MODEL (gsearch->search_results_list_store), &iter, tmp->data);
+
+		gtk_tree_model_get (GTK_TREE_MODEL (gsearch->search_results_list_store), &iter,
+				    COLUMN_PATH, &folder_utf8,
+				    -1);
+
+		folder_locale = g_filename_from_utf8 (folder_utf8, -1, NULL, NULL, NULL);
+
+		if (open_file_with_xdg_open (gsearch->window, folder_locale) == FALSE) {
+			if (open_file_with_nautilus (gsearch->window, folder_locale) == FALSE) {
+
+				display_dialog_could_not_open_folder (gsearch->window, folder_utf8);
+			}
+			g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
+			g_list_free (list);
+			g_free (folder_locale);
+			g_free (folder_utf8);
+			return;
+		}
+		g_free (folder_locale);
+		g_free (folder_utf8);
+	}
+	g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
+	g_list_free (list);
+}
+
+static void
+display_dialog_could_not_move_to_trash (GtkWidget * window,
+					const gchar * file,
+					const gchar * message)
+{
+	GtkWidget * dialog;
+	gchar * primary;
+
+	primary = g_strdup_printf (_("Could not move \"%s\" to trash."), file);
+
+	dialog = gtk_message_dialog_new (GTK_WINDOW (window),
+					 GTK_DIALOG_DESTROY_WITH_PARENT,
+					 GTK_MESSAGE_ERROR,
+					 GTK_BUTTONS_OK,
+					 primary);
+	gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+						  message);
+
+	gtk_window_set_title (GTK_WINDOW (dialog), "");
+	gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+	gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 14);
+
+	g_signal_connect (G_OBJECT (dialog),
+			  "response",
+			  G_CALLBACK (gtk_widget_destroy), NULL);
+	gtk_widget_show (dialog);
+	g_free (primary);
+}
+
+static gint
+display_dialog_delete_permanently (GtkWidget * window,
+				   const gchar * file)
+{
+	GtkWidget * dialog;
+	GtkWidget * button;
+	gchar * primary;
+	gchar * secondary;
+	gint response;
+
+	primary = g_strdup_printf (_("Do you want to delete \"%s\" permanently?"),
+				   g_path_get_basename (file));
+
+	secondary = g_strdup_printf (_("Trash is unavailable.  Could not move \"%s\" to the trash."),
+				     file);
+
+	dialog = gtk_message_dialog_new (GTK_WINDOW (window),
+					 GTK_DIALOG_DESTROY_WITH_PARENT,
+					 GTK_MESSAGE_QUESTION,
+					 GTK_BUTTONS_CANCEL,
+					 primary);
+	gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+						  secondary);
+
+	gtk_window_set_title (GTK_WINDOW (dialog), "");
+	gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+	gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 14);
+
+	button = gtk_button_new_from_stock ("gtk-delete");
+	GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+	gtk_widget_show (button);
+
+	gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, GTK_RESPONSE_OK);
+	gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+	response = gtk_dialog_run (GTK_DIALOG (dialog));
+
+	gtk_widget_destroy (GTK_WIDGET(dialog));
+	g_free (primary);
+	g_free (secondary);
+
+	return response;
+}
+
+static void
+display_dialog_could_not_delete (GtkWidget * window,
+				 const gchar * file,
+				 const gchar * message)
+{
+	GtkWidget * dialog;
+	gchar	  * primary;
+
+	primary = g_strdup_printf (_("Could not delete \"%s\"."), file);
+
+	dialog = gtk_message_dialog_new (GTK_WINDOW (window),
+					 GTK_DIALOG_DESTROY_WITH_PARENT,
+					 GTK_MESSAGE_ERROR,
+					 GTK_BUTTONS_OK,
+					 primary);
+	gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+						  message);
+
+	gtk_window_set_title (GTK_WINDOW (dialog), "");
+	gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+	gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 14);
+
+	g_signal_connect (G_OBJECT (dialog),
+			  "response",
+			  G_CALLBACK (gtk_widget_destroy), NULL);
+	gtk_widget_show (dialog);
+	g_free (primary);
+}
+
+static char *
+get_trash_path (const gchar * file)
+{
+	GnomeVFSURI * trash_uri;
+	GnomeVFSURI * uri;
+	gchar	    * filename;
+
+	filename = gnome_vfs_escape_path_string (file);
+	uri = gnome_vfs_uri_new (filename);
+	g_free (filename);
+
+	gnome_vfs_find_directory (uri,
+				  GNOME_VFS_DIRECTORY_KIND_TRASH,
+				  &trash_uri,
+				  TRUE,
+				  TRUE,
+				  0777);
+	gnome_vfs_uri_unref (uri);
+
+	if (trash_uri == NULL) {
+		return NULL;
+	}
+	else {
+		gchar * trash_path;
+		trash_path = gnome_vfs_uri_to_string (trash_uri, GNOME_VFS_URI_HIDE_TOPLEVEL_METHOD);
+		gnome_vfs_uri_unref (trash_uri);
+		return trash_path;
+	}
+}
+
+void
+move_to_trash_cb (GtkAction * action,
+		  gpointer data)
+{
+	GSearchWindow * gsearch = data;
+	gint total;
+	gint index;
+
+	if (gtk_tree_selection_count_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection)) == 0) {
+		return;
+	}
+
+	total = gtk_tree_selection_count_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection));
+
+	for (index = 0; index < total; index++) {
+		gboolean no_files_found = FALSE;
+		GtkTreeModel * model;
+		GtkTreeIter iter;
+		GList * list;
+		gchar * utf8_basename;
+		gchar * utf8_basepath;
+		gchar * utf8_filename;
+		gchar * locale_filename;
+		gchar * trash_path;
+
+		list = gtk_tree_selection_get_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection),
+							     &model);
+
+		gtk_tree_model_get_iter (GTK_TREE_MODEL (gsearch->search_results_list_store), &iter,
+					 g_list_nth (list, 0)->data);
+
+		gtk_tree_model_get (GTK_TREE_MODEL (gsearch->search_results_list_store), &iter,
+				    COLUMN_NAME, &utf8_basename,
+				    COLUMN_PATH, &utf8_basepath,
+				    COLUMN_NO_FILES_FOUND, &no_files_found,
+				    -1);
+
+		if (no_files_found) {
+			g_free (utf8_basename);
+			g_free (utf8_basepath);
+			return;
+		}
+
+		utf8_filename = g_build_filename (utf8_basepath, utf8_basename, NULL);
+		locale_filename = g_locale_from_utf8 (utf8_filename, -1, NULL, NULL, NULL);
+		trash_path = get_trash_path (locale_filename);
+
+		if ((!g_file_test (locale_filename, G_FILE_TEST_EXISTS)) &&
+		    (!g_file_test (locale_filename, G_FILE_TEST_IS_SYMLINK))) {
+			gtk_tree_selection_unselect_iter (GTK_TREE_SELECTION (gsearch->search_results_selection), &iter);
+			display_dialog_could_not_move_to_trash (gsearch->window, utf8_basename,
+								_("The document does not exist."));
+		}
+		else if (trash_path != NULL) {
+			GnomeVFSResult result;
+			gchar * destination;
+			gchar * basename;
+			gchar * source_uri;;
+
+			source_uri = g_filename_to_uri (locale_filename, NULL, NULL);
+			basename = g_locale_from_utf8 (utf8_basename, -1, NULL, NULL, NULL);
+			destination = g_build_filename (trash_path, basename, NULL);
+
+			result = gnome_vfs_move (source_uri, destination, TRUE);
+			gtk_tree_selection_unselect_iter (GTK_TREE_SELECTION (gsearch->search_results_selection), &iter);
+
+			if (result == GNOME_VFS_OK) {
+
+				gtk_list_store_remove (GTK_LIST_STORE (gsearch->search_results_list_store), &iter);
+			}
+			else {
+				gchar * message;
+
+				message = g_strdup_printf (_("Moving \"%s\" failed: %s."),
+							   utf8_filename,
+							   gnome_vfs_result_to_string (result));
+				display_dialog_could_not_move_to_trash (gsearch->window, utf8_basename,
+									message);
+				g_free (message);
+			}
+			g_free (source_uri);
+			g_free (basename);
+			g_free (destination);
+		}
+		else {
+			gint response;
+
+			gtk_tree_selection_unselect_iter (GTK_TREE_SELECTION (gsearch->search_results_selection), &iter);
+			response = display_dialog_delete_permanently (gsearch->window, utf8_filename);
+
+			if (response == GTK_RESPONSE_OK) {
+				GnomeVFSResult result;
+
+				if (!g_file_test (locale_filename, G_FILE_TEST_IS_DIR)) {
+					result = gnome_vfs_unlink (locale_filename);
+				}
+				else {
+					result = gnome_vfs_remove_directory (locale_filename);
+				}
+
+				if (result == GNOME_VFS_OK) {
+
+					gtk_list_store_remove (GTK_LIST_STORE (gsearch->search_results_list_store), &iter);
+				}
+				else {
+					gchar * message;
+
+					message = g_strdup_printf (_("Deleting \"%s\" failed: %s."),
+								     utf8_filename, gnome_vfs_result_to_string (result));
+
+					display_dialog_could_not_delete (gsearch->window, utf8_basename, message);
+
+					g_free (message);
+				}
+			}
+		}
+		g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
+		g_list_free (list);
+		g_free (locale_filename);
+		g_free (utf8_filename);
+		g_free (utf8_basename);
+		g_free (utf8_basepath);
+		g_free (trash_path);
+	}
+
+	if (gsearch->command_details->command_status != RUNNING) {
+		update_search_counts (gsearch);
+	}
+}
+
+gboolean
+file_button_press_event_cb (GtkWidget * widget,
+			    GdkEventButton * event,
+			    gpointer data)
+{
+	GtkTreeView * tree = data;
+	GtkTreePath * path;
+
+	row_selected_by_button_press_event = TRUE;
+
+	if (event->window != gtk_tree_view_get_bin_window (tree)) {
+		return FALSE;
+	}
+
+	if (gtk_tree_view_get_path_at_pos (tree, event->x, event->y,
+		&path, NULL, NULL, NULL)) {
+
+		if ((event->button == 1 || event->button == 2 || event->button == 3)
+			&& gtk_tree_selection_path_is_selected (gtk_tree_view_get_selection (tree), path)) {
+			row_selected_by_button_press_event = FALSE;
+		}
+		gtk_tree_path_free (path);
+	}
+	else {
+		gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (tree));
+	}
+
+	return !(row_selected_by_button_press_event);
+}
+
+gboolean
+file_key_press_event_cb (GtkWidget * widget,
+			 GdkEventKey * event,
+			 gpointer data)
+{
+	if (event->keyval == GDK_space || event->keyval == GDK_Return) {
+		if (event->state != GDK_CONTROL_MASK) {
+			open_file_cb ((GtkAction *) NULL, data);
+			return TRUE;
+		}
+	}
+	else if (event->keyval == GDK_Delete) {
+		move_to_trash_cb ((GtkAction *) NULL, data);
+		return TRUE;
+	}
+	return FALSE;
+}
+
+gboolean
+file_button_release_event_cb (GtkWidget * widget,
+			      GdkEventButton * event,
+			      gpointer data)
+{
+	GSearchWindow * gsearch = data;
+	gboolean no_files_found = FALSE;
+	GtkTreeIter iter;
+
+	if (event->window != gtk_tree_view_get_bin_window (GTK_TREE_VIEW (gsearch->search_results_tree_view))) {
+		return FALSE;
+	}
+
+	if (gtk_tree_selection_count_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection)) == 0) {
+		return FALSE;
+	}
+
+	if (event->button == 3) {
+		GtkTreeModel * model;
+		GList * list;
+
+		list = gtk_tree_selection_get_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection),
+							     &model);
+
+		gtk_tree_model_get_iter (GTK_TREE_MODEL (gsearch->search_results_list_store), &iter,
+					 g_list_first (list)->data);
+
+		gtk_tree_model_get (GTK_TREE_MODEL (gsearch->search_results_list_store), &iter,
+				    COLUMN_NO_FILES_FOUND, &no_files_found,
+				    -1);
+
+		if (!no_files_found) {
+			gtk_menu_popup (GTK_MENU (gsearch->search_results_popup_menu), NULL, NULL, NULL, NULL,
+					event->button, event->time);
+		}
+		g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
+		g_list_free (list);
+	}
+	else if (event->button == 1 || event->button == 2) {
+		if (gsearch->is_search_results_single_click_to_activate == TRUE) {
+			if (!(event->state & GDK_CONTROL_MASK) && !(event->state & GDK_SHIFT_MASK)) {
+				open_file_cb ((GtkAction *) NULL, data);
+			}
+		}
+	}
+
+	return FALSE;
+}
+
+gboolean
+file_event_after_cb (GtkWidget * widget,
+		     GdkEventButton * event,
+		     gpointer data)
+{
+	GSearchWindow * gsearch = data;
+
+	if (event->window != gtk_tree_view_get_bin_window (GTK_TREE_VIEW (gsearch->search_results_tree_view))) {
+		return FALSE;
+	}
+
+	if (gtk_tree_selection_count_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection)) == 0) {
+		return FALSE;
+	}
+
+	if (!(event->state & GDK_CONTROL_MASK) && !(event->state & GDK_SHIFT_MASK)) {
+		if (gsearch->is_search_results_single_click_to_activate == FALSE) {
+			if (event->type == GDK_2BUTTON_PRESS) {
+				open_file_cb ((GtkAction *) NULL, data);
+				return TRUE;
+			}
+		}
+	}
+	return FALSE;
+}
+
+gboolean
+file_motion_notify_cb (GtkWidget *widget,
+		       GdkEventMotion *event,
+		       gpointer user_data)
+{
+	GSearchWindow * gsearch = user_data;
+	GdkCursor * cursor;
+	GtkTreePath * last_hover_path;
+	GtkTreeIter iter;
+
+	if (gsearch->is_search_results_single_click_to_activate == FALSE) {
+		gdk_window_set_cursor (event->window, NULL);
+		return FALSE;
+	}
+
+	if (event->window != gtk_tree_view_get_bin_window (GTK_TREE_VIEW (gsearch->search_results_tree_view))) {
+		gdk_window_set_cursor (event->window, NULL);
+		return FALSE;
+	}
+
+	last_hover_path = gsearch->search_results_hover_path;
+
+	gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
+				       event->x, event->y,
+				       &gsearch->search_results_hover_path,
+				       NULL, NULL, NULL);
+
+	if (gsearch->search_results_hover_path != NULL) {
+		cursor = gdk_cursor_new (GDK_HAND2);
+	}
+	else {
+		cursor = NULL;
+	}
+
+	gdk_window_set_cursor (event->window, cursor);
+
+	/* Redraw if the hover row has changed */
+	if (!(last_hover_path == NULL && gsearch->search_results_hover_path == NULL) &&
+	    (!(last_hover_path != NULL && gsearch->search_results_hover_path != NULL) ||
+	     gtk_tree_path_compare (last_hover_path, gsearch->search_results_hover_path))) {
+		if (last_hover_path) {
+			gtk_tree_model_get_iter (GTK_TREE_MODEL (gsearch->search_results_list_store),
+						 &iter, last_hover_path);
+			gtk_tree_model_row_changed (GTK_TREE_MODEL (gsearch->search_results_list_store),
+						    last_hover_path, &iter);
+		}
+
+		if (gsearch->search_results_hover_path) {
+			gtk_tree_model_get_iter (GTK_TREE_MODEL (gsearch->search_results_list_store),
+						 &iter, gsearch->search_results_hover_path);
+			gtk_tree_model_row_changed (GTK_TREE_MODEL (gsearch->search_results_list_store),
+						    gsearch->search_results_hover_path, &iter);
+		}
+	}
+
+	gtk_tree_path_free (last_hover_path);
+
+	return FALSE;
+}
+
+gboolean
+file_leave_notify_cb (GtkWidget *widget,
+		      GdkEventCrossing *event,
+		      gpointer user_data)
+{
+	GSearchWindow * gsearch = user_data;
+	GtkTreeIter iter;
+
+	if (gsearch->is_search_results_single_click_to_activate && (gsearch->search_results_hover_path != NULL)) {
+		gtk_tree_model_get_iter (GTK_TREE_MODEL (gsearch->search_results_list_store),
+					 &iter,
+					 gsearch->search_results_hover_path);
+		gtk_tree_model_row_changed (GTK_TREE_MODEL (gsearch->search_results_list_store),
+					    gsearch->search_results_hover_path,
+					    &iter);
+
+		gtk_tree_path_free (gsearch->search_results_hover_path);
+		gsearch->search_results_hover_path = NULL;
+
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+void
+drag_begin_file_cb (GtkWidget * widget,
+		    GdkDragContext * context,
+		    gpointer data)
+{
+	GSearchWindow * gsearch = data;
+	gint number_of_selected_rows;
+
+	number_of_selected_rows = gtk_tree_selection_count_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection));
+
+	if (number_of_selected_rows > 1) {
+		gtk_drag_set_icon_stock (context, GTK_STOCK_DND_MULTIPLE, 0, 0);
+	}
+	else if (number_of_selected_rows == 1) {
+		GdkPixbuf * pixbuf;
+		GtkTreeModel * model;
+		GtkTreeIter iter;
+		GList * list;
+
+		list = gtk_tree_selection_get_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection),
+							     &model);
+
+		gtk_tree_model_get_iter (GTK_TREE_MODEL (gsearch->search_results_list_store), &iter,
+					 g_list_first (list)->data);
+
+		gtk_tree_model_get (GTK_TREE_MODEL (gsearch->search_results_list_store), &iter,
+				    COLUMN_ICON, &pixbuf,
+				    -1);
+		g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
+		g_list_free (list);
+
+		if (pixbuf) {
+			gtk_drag_set_icon_pixbuf (context, pixbuf, 0, 0);
+		}
+		else {
+			gtk_drag_set_icon_stock (context, GTK_STOCK_DND, 0, 0);
+		}
+	}
+}
+
+/* Make a desktop file in /tmp witch points to this email */
+static gchar*
+make_email_desktop_file (const gchar *utf8_uri, const gchar *utf8_name)
+{
+	GnomeDesktopItem *item = NULL;
+	gchar *exec_string = NULL;
+	gchar *save_uri = NULL;
+	time_t seconds;
+
+	item = gnome_desktop_item_new ();
+
+	exec_string = g_strdup_printf ("evolution \"%s\"",utf8_uri);
+
+	gnome_desktop_item_set_string (item, GNOME_DESKTOP_ITEM_ENCODING, "UTF-8");
+	gnome_desktop_item_set_string (item, GNOME_DESKTOP_ITEM_NAME, utf8_name);
+	gnome_desktop_item_set_string (item, GNOME_DESKTOP_ITEM_COMMENT, _("Activate to view this email"));
+	gnome_desktop_item_set_string (item, GNOME_DESKTOP_ITEM_EXEC, exec_string);
+	gnome_desktop_item_set_string (item, GNOME_DESKTOP_ITEM_ICON, "email");
+	gnome_desktop_item_set_string (item, GNOME_DESKTOP_ITEM_TERMINAL, "false");
+	gnome_desktop_item_set_string (item, GNOME_DESKTOP_ITEM_TYPE, "Application");
+
+	seconds = time (NULL);
+	save_uri = g_strdup_printf ("/tmp/tracker-email-shortcut-file%d.desktop", (int)seconds);
+	gnome_desktop_item_save (item,
+				 save_uri,
+				 TRUE,
+				 NULL);
+
+	g_free (exec_string);
+
+	return save_uri;
+}
+
+void
+drag_file_cb (GtkWidget * widget,
+	      GdkDragContext * context,
+	      GtkSelectionData * selection_data,
+	      guint info,
+	      guint time,
+	      gpointer data)
+{
+	GSearchWindow * gsearch = data;
+	gchar * uri_list = NULL;
+	GList * list, * tmp;
+	GtkTreeModel * model;
+	GtkTreeIter iter;
+
+	if (gtk_tree_selection_count_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection)) == 0) {
+		return;
+	}
+
+	list = gtk_tree_selection_get_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection),
+						     &model);
+
+	for (tmp = list; tmp; tmp = tmp->next) {
+
+		gboolean no_files_found = FALSE;
+		gchar * utf8_name;
+		gchar * utf8_path;
+		gchar * utf8_uri;
+		gchar * file;
+
+		gtk_tree_model_get_iter (GTK_TREE_MODEL (gsearch->search_results_list_store), &iter, tmp->data);
+
+		gtk_tree_model_get (GTK_TREE_MODEL (gsearch->search_results_list_store), &iter,
+				    COLUMN_NAME, &utf8_name,
+				    COLUMN_PATH, &utf8_path,
+				    COLUMN_URI, &utf8_uri,
+				    COLUMN_NO_FILES_FOUND, &no_files_found,
+				    -1);
+
+		file = g_build_filename (utf8_path, utf8_name, NULL);
+
+		if (!no_files_found) {
+			gchar * tmp_uri = NULL;
+			tmp_uri = g_filename_to_uri (file, NULL, NULL);
+
+			if (uri_list == NULL) {
+				uri_list = g_strdup (tmp_uri);
+			}
+			else {
+				uri_list = g_strconcat (uri_list, "\n", tmp_uri, NULL);
+			}
+			if (gsearch->type < 10) {
+				gtk_selection_data_set (selection_data,
+							selection_data->target,
+							8,
+							(guchar *) uri_list,
+							strlen (uri_list));
+			} else {
+				gchar *desktop_uri;
+				desktop_uri = make_email_desktop_file (utf8_uri, utf8_name);
+				gtk_selection_data_set (selection_data,
+						selection_data->target,
+						8,
+						(guchar *) desktop_uri,
+						strlen (desktop_uri));
+					g_free (desktop_uri);
+			}
+			g_free (tmp_uri);
+		}
+		else {
+			gtk_selection_data_set_text (selection_data, utf8_name, -1);
+		}
+		g_free (utf8_name);
+		g_free (utf8_path);
+		g_free (utf8_uri);
+		g_free (file);
+	}
+	g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
+	g_list_free (list);
+	g_free (uri_list);
+}
+
+void
+drag_data_animation_cb (GtkWidget * widget,
+			GdkDragContext * context,
+			GtkSelectionData * selection_data,
+			guint info,
+			guint time,
+			gpointer data)
+{
+	GSearchWindow * gsearch = data;
+	GnomeDesktopItem * ditem;
+	GString * command = g_string_new ("");
+	gchar ** argv;
+	gchar * desktop_item_name = NULL;
+	gchar * uri;
+	gchar * path;
+	gchar * disk;
+	gchar * scheme;
+	gint argc;
+	gint index;
+
+	set_clone_command (gsearch, &argc, &argv, "tracker-search-tool", TRUE);
+
+	if (argc == 0) {
+		return;
+	}
+
+	for (index = 0; index < argc; index++) {
+		command = g_string_append (command, argv[index]);
+		command = g_string_append_c (command, ' ');
+	}
+	command = g_string_append (command, "--start");
+
+	disk = g_locale_from_utf8 (command->str, -1, NULL, NULL, NULL);
+	uri = gnome_vfs_make_uri_from_input_with_dirs (disk, GNOME_VFS_MAKE_URI_DIR_HOMEDIR);
+	scheme = gnome_vfs_get_uri_scheme (uri);
+
+	ditem = gnome_desktop_item_new ();
+
+	gnome_desktop_item_set_entry_type (ditem, GNOME_DESKTOP_ITEM_TYPE_APPLICATION);
+	gnome_desktop_item_set_string (ditem, GNOME_DESKTOP_ITEM_EXEC, command->str);
+
+	desktop_item_name = get_desktop_item_name (gsearch);
+
+	gnome_desktop_item_set_string (ditem, GNOME_DESKTOP_ITEM_NAME, desktop_item_name);
+	gnome_desktop_item_set_boolean (ditem, GNOME_DESKTOP_ITEM_TERMINAL, FALSE);
+	gnome_desktop_item_set_string (ditem, GNOME_DESKTOP_ITEM_ICON, TRACKER_SEARCH_TOOL_ICON);
+	gnome_desktop_item_set_boolean (ditem, "StartupNotify", TRUE);
+
+	g_string_free (command, TRUE);
+	g_free (desktop_item_name);
+	g_free (uri);
+
+	path = tracker_search_get_unique_filename (g_get_tmp_dir (), ".desktop");
+	gnome_desktop_item_set_location (ditem, path);
+
+	uri = gnome_vfs_get_uri_from_local_path (path);
+
+	if (gnome_desktop_item_save (ditem, NULL, FALSE, NULL)) {
+		gtk_selection_data_set (selection_data,
+					selection_data->target, 8,
+					(guchar *) uri, strlen (uri));
+	}
+	gnome_desktop_item_unref (ditem);
+
+	g_free (uri);
+	g_free (path);
+	g_free (disk);
+	g_free (scheme);
+}
+
+void
+show_file_selector_cb (GtkAction * action,
+		       gpointer data)
+{
+	GSearchWindow * gsearch = data;
+	GtkWidget * file_chooser;
+
+	file_chooser = gtk_file_chooser_dialog_new (_("Save Search Results As..."),
+						    GTK_WINDOW (gsearch->window),
+						    GTK_FILE_CHOOSER_ACTION_SAVE,
+						    GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+						    GTK_STOCK_SAVE, GTK_RESPONSE_OK,
+						    NULL);
+
+#if GTK_CHECK_VERSION(2,7,3)
+	gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (file_chooser), TRUE);
+#endif
+
+	if (gsearch->save_results_as_default_filename != NULL) {
+		gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (file_chooser),
+					       gsearch->save_results_as_default_filename);
+	} else {
+		gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (file_chooser),
+					       g_get_home_dir());
+	}
+
+	g_signal_connect (G_OBJECT (file_chooser), "response",
+			  G_CALLBACK (save_results_cb), gsearch);
+
+	gtk_window_set_modal (GTK_WINDOW (file_chooser), TRUE);
+	gtk_window_set_position (GTK_WINDOW (file_chooser), GTK_WIN_POS_CENTER_ON_PARENT);
+
+	gtk_widget_show (GTK_WIDGET (file_chooser));
+}
+
+static void
+display_dialog_could_not_save_no_name (GtkWidget * window)
+{
+	GtkWidget * dialog;
+	gchar * primary;
+	gchar * secondary;
+
+	primary = g_strdup (_("Could not save document."));
+	secondary = g_strdup (_("You did not select a document name."));
+
+	dialog = gtk_message_dialog_new (GTK_WINDOW (window),
+					 GTK_DIALOG_DESTROY_WITH_PARENT,
+					 GTK_MESSAGE_ERROR,
+					 GTK_BUTTONS_OK,
+					 primary);
+	gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+						  secondary);
+
+	gtk_window_set_title (GTK_WINDOW (dialog), "");
+	gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+	gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 14);
+
+	g_signal_connect (G_OBJECT (dialog),
+			  "response",
+			  G_CALLBACK (gtk_widget_destroy), NULL);
+	gtk_widget_show (dialog);
+	g_free (primary);
+	g_free (secondary);
+}
+
+static void
+display_dialog_could_not_save_to (GtkWidget * window,
+				  const gchar * file,
+				  const gchar * message)
+{
+	GtkWidget * dialog;
+	gchar * primary;
+
+	primary = g_strdup_printf (_("Could not save \"%s\" document to \"%s\"."),
+				   g_path_get_basename (file),
+				   g_path_get_dirname (file));
+
+	dialog = gtk_message_dialog_new (GTK_WINDOW (window),
+					 GTK_DIALOG_DESTROY_WITH_PARENT,
+					 GTK_MESSAGE_ERROR,
+					 GTK_BUTTONS_OK,
+					 primary);
+	gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+						  message);
+
+	gtk_window_set_title (GTK_WINDOW (dialog), "");
+	gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+	gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 14);
+
+	g_signal_connect (G_OBJECT (dialog),
+			  "response",
+			  G_CALLBACK (gtk_widget_destroy), NULL);
+	gtk_widget_show (dialog);
+	g_free (primary);
+}
+
+#if !GTK_CHECK_VERSION(2,7,3)
+static gint
+display_dialog_could_not_save_exists (GtkWidget * window,
+				      const gchar * file)
+{
+	GtkWidget * dialog;
+	GtkWidget * button;
+	gchar * primary;
+	gchar * secondary;
+	gint response;
+
+	primary = g_strdup_printf (_("The document \"%s\" already exists.  "
+				     "Would you like to replace it?"),
+				   g_path_get_basename (file));
+
+	secondary = g_strdup (_("If you replace an existing file, "
+				"its contents will be overwritten."));
+
+	dialog = gtk_message_dialog_new (GTK_WINDOW (window),
+					 GTK_DIALOG_DESTROY_WITH_PARENT,
+					 GTK_MESSAGE_QUESTION,
+					 GTK_BUTTONS_CANCEL,
+					 primary);
+	gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+						  secondary);
+
+	gtk_window_set_title (GTK_WINDOW (dialog), "");
+	gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+	gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 14);
+
+	button = tracker_search_button_new_with_stock_icon (_("_Replace"), GTK_STOCK_OK);
+	GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+	gtk_widget_show (button);
+
+	gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, GTK_RESPONSE_OK);
+	gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+	response = gtk_dialog_run (GTK_DIALOG (dialog));
+
+	gtk_widget_destroy (GTK_WIDGET(dialog));
+	g_free (primary);
+	g_free (secondary);
+
+	return response;
+}
+#endif
+
+void
+save_results_cb (GtkWidget * chooser,
+		 gint response,
+		 gpointer data)
+{
+	GSearchWindow * gsearch = data;
+	GtkListStore * store;
+	GtkTreeIter iter;
+	FILE * fp;
+	gchar * utf8 = NULL;
+
+	if (response != GTK_RESPONSE_OK) {
+		gtk_widget_destroy (GTK_WIDGET (chooser));
+		return;
+	}
+
+	store = gsearch->search_results_list_store;
+	g_free (gsearch->save_results_as_default_filename);
+
+	gsearch->save_results_as_default_filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser));
+	gtk_widget_destroy (chooser);
+
+	if (gsearch->save_results_as_default_filename != NULL) {
+		utf8 = g_filename_to_utf8 (gsearch->save_results_as_default_filename, -1, NULL, NULL, NULL);
+	}
+
+	if (utf8 == NULL) {
+		display_dialog_could_not_save_no_name (gsearch->window);
+		return;
+	}
+
+	if (g_file_test (gsearch->save_results_as_default_filename, G_FILE_TEST_IS_DIR)) {
+		display_dialog_could_not_save_to (gsearch->window, utf8,
+						  _("The document name you selected is a folder."));
+		g_free (utf8);
+		return;
+	}
+
+#if !GTK_CHECK_VERSION(2,7,3)
+	if (g_file_test (gsearch->save_results_as_default_filename, G_FILE_TEST_EXISTS)) {
+
+		gint response;
+
+		response = display_dialog_could_not_save_exists (gsearch->window, utf8);
+
+		if (response != GTK_RESPONSE_OK) {
+			g_free (utf8);
+			return;
+		}
+	}
+#endif
+
+	if ((fp = g_fopen (gsearch->save_results_as_default_filename, "w")) != NULL) {
+
+		gint index;
+
+		for (index = 0; index < gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), NULL); index++)
+		{
+			if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (store), &iter, NULL, index) == TRUE) {
+
+				gchar * utf8_path;
+				gchar * utf8_name;
+				gchar * utf8_file;
+				gchar * locale_file;
+
+				gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, COLUMN_PATH, &utf8_path, -1);
+				gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, COLUMN_NAME, &utf8_name, -1);
+
+				utf8_file = g_build_filename (utf8_path, utf8_name, NULL);
+				locale_file = g_filename_from_utf8 (utf8_file, -1, NULL, NULL, NULL);
+				fprintf (fp, "%s\n", locale_file);
+
+				g_free (utf8_path);
+				g_free (utf8_name);
+				g_free (utf8_file);
+				g_free (locale_file);
+			}
+		}
+		fclose (fp);
+	}
+	else {
+		display_dialog_could_not_save_to (gsearch->window, utf8,
+						  _("You may not have write permissions to the document."));
+	}
+	g_free (utf8);
+}
+
+void
+save_session_cb (GnomeClient * client,
+		 gint phase,
+		 GnomeRestartStyle save_style,
+		 gint shutdown,
+		 GnomeInteractStyle interact_style,
+		 gint fast,
+		 gpointer client_data)
+{
+	GSearchWindow * gsearch = client_data;
+	char ** argv;
+	gint argc;
+
+	set_clone_command (gsearch, &argc, &argv, "tracker-search-tool", FALSE);
+	gnome_client_set_clone_command (client, argc, argv);
+	gnome_client_set_restart_command (client, argc, argv);
+}
+
+gboolean
+key_press_cb (GtkWidget * widget,
+	      GdkEventKey * event,
+	      gpointer data)
+{
+	GSearchWindow * gsearch = data;
+
+	g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
+
+	if (event->keyval == GDK_Escape) {
+		if (gsearch->command_details->command_status == RUNNING) {
+			click_stop_cb (widget, data);
+		}
+		else if (gsearch->command_details->is_command_timeout_enabled == FALSE) {
+			quit_cb (widget, (GdkEvent *) NULL, data);
+		}
+	}
+	else if (event->keyval == GDK_F10) {
+		if (event->state & GDK_SHIFT_MASK) {
+			gboolean no_files_found = FALSE;
+			GtkTreeModel * model;
+			GtkTreeIter iter;
+			GList * list;
+
+			if (gtk_tree_selection_count_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection)) == 0) {
+				return FALSE;
+			}
+
+			list = gtk_tree_selection_get_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection),
+								     &model);
+
+			gtk_tree_model_get_iter (GTK_TREE_MODEL (gsearch->search_results_list_store), &iter,
+						 g_list_first (list)->data);
+
+			gtk_tree_model_get (GTK_TREE_MODEL (gsearch->search_results_list_store), &iter,
+					    COLUMN_NO_FILES_FOUND, &no_files_found, -1);
+
+			g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
+			g_list_free (list);
+
+			if (!no_files_found) {
+				gtk_menu_popup (GTK_MENU (gsearch->search_results_popup_menu), NULL, NULL, NULL, NULL,
+						event->keyval, event->time);
+				return TRUE;
+			}
+		}
+	}
+	return FALSE;
+}
+
+gboolean
+not_running_timeout_cb (gpointer data)
+{
+	GSearchWindow * gsearch = data;
+
+	gsearch->command_details->is_command_timeout_enabled = FALSE;
+	return FALSE;
+}
+
+void
+disable_quick_search_cb (GtkWidget * dialog,
+			 gint response,
+			 gpointer data)
+{
+	gtk_widget_destroy (GTK_WIDGET (dialog));
+
+	if (response == GTK_RESPONSE_OK) {
+		tracker_search_gconf_set_boolean ("/apps/tracker-search-tool/disable_quick_search", TRUE);
+	}
+}
+
+void
+single_click_to_activate_key_changed_cb (GConfClient * client,
+					 guint cnxn_id,
+					 GConfEntry * entry,
+					 gpointer user_data)
+{
+	GSearchWindow * gsearch = user_data;
+	GConfValue * value = gconf_entry_get_value (entry);
+
+	g_return_if_fail (value->type == GCONF_VALUE_STRING);
+
+	gsearch->is_search_results_single_click_to_activate =
+		(strncmp (gconf_value_get_string (value), "single", 6) == 0);
+}
+
+void
+columns_changed_cb (GtkTreeView * treeview,
+		    gpointer user_data)
+{
+	GSList * order = tracker_search_get_columns_order (treeview);
+
+	if (g_slist_length (order) == NUM_VISIBLE_COLUMNS) {
+		tracker_search_gconf_set_list ("/apps/tracker-search-tool/columns_order", order, GCONF_VALUE_INT);
+	}
+	g_slist_free (order);
+}
+
+gboolean
+window_state_event_cb (GtkWidget * widget,
+		       GdkEventWindowState * event,
+		       gpointer data)
+{
+	GSearchWindow * gsearch = data;
+
+	if (event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) {
+		gsearch->is_window_maximized = TRUE;
+	} else {
+		gsearch->is_window_maximized = FALSE;
+	}
+	return FALSE;
+}
+
+void
+suggest_search_cb (GtkWidget * widget,
+		   gpointer data)
+{
+	GSearchWindow * gsearch = data;
+	gchar	      * suggest = g_object_get_data (G_OBJECT (widget), "suggestion");
+
+	gtk_entry_set_text (GTK_ENTRY (gsearch->search_entry), suggest);
+	gtk_button_clicked (GTK_BUTTON (gsearch->find_button));
+}

Added: trunk/src/tracker-search-tool/tracker-search-tool-callbacks.h
==============================================================================
--- (empty file)
+++ trunk/src/tracker-search-tool/tracker-search-tool-callbacks.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,211 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * GNOME Search Tool
+ *
+ *  File:  tracker_search-callbacks.h
+ *
+ *  (C) 2002 the Free Software Foundation
+ *
+ *  Authors:	Dennis Cranston  <dennis_cranston yahoo com>
+ *		George Lebl
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+
+
+#ifndef _GSEARCHTOOL_CALLBACKS_H_
+#define _GSEARCHTOOL_CALLBACKS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif
+
+
+void
+tracker_update_metadata_tile (GSearchWindow *gsearch);
+
+void
+die_cb (GnomeClient * client,
+	gpointer data);
+void
+quit_cb (GtkWidget * widget,
+	 GdkEvent * event,
+	 gpointer data);
+void
+click_close_cb (GtkWidget * widget,
+		gpointer data);
+
+void
+click_stop_cb (GtkWidget * widget,
+	       gpointer	data);
+void
+click_help_cb (GtkWidget * widget,
+	       gpointer data);
+void
+click_expander_cb (GObject * object,
+		   GParamSpec * param_spec,
+		   gpointer data);
+void
+size_allocate_cb (GtkWidget * widget,
+		  GtkAllocation * allocation,
+		  gpointer data);
+void
+add_constraint_cb (GtkWidget * widget,
+		   gpointer data);
+void
+remove_constraint_cb (GtkWidget * widget,
+		      gpointer data);
+void
+constraint_activate_cb (GtkWidget * widget,
+			gpointer data);
+void
+constraint_update_info_cb (GtkWidget * widget,
+			   gpointer data);
+void
+name_contains_activate_cb (GtkWidget * widget,
+			   gpointer data);
+
+gboolean
+text_changed_cb (GtkWidget * widget,
+		 gpointer data);
+
+void
+click_find_cb (GtkWidget * widget,
+	       gpointer data);
+void
+next_results_cb (GtkWidget * widget,
+		 gpointer data);
+void
+prev_results_cb (GtkWidget * widget,
+		 gpointer data);
+
+void
+category_changed_cb (GtkTreeSelection * treeselection,
+		     gpointer data) ;
+
+void
+open_file_cb (GtkAction * action,
+	      gpointer data);
+void
+open_folder_cb (GtkAction * action,
+		gpointer data);
+void
+file_changed_cb (GnomeVFSMonitorHandle * handle,
+		 const gchar * monitor_uri,
+		 const gchar * info_uri,
+		 GnomeVFSMonitorEventType event_type,
+		 gpointer data);
+void
+move_to_trash_cb (GtkAction * action,
+		  gpointer data);
+void
+drag_begin_file_cb (GtkWidget * widget,
+		    GdkDragContext * context,
+		    gpointer data);
+void
+drag_file_cb (GtkWidget * widget,
+	      GdkDragContext * context,
+	      GtkSelectionData * selection_data,
+	      guint info,
+	      guint time,
+	      gpointer data);
+void
+drag_data_animation_cb (GtkWidget * widget,
+			GdkDragContext * context,
+			GtkSelectionData * selection_data,
+			guint info,
+			guint time,
+			gpointer data);
+void
+show_file_selector_cb (GtkAction * action,
+		       gpointer data);
+void
+save_results_cb (GtkWidget * chooser,
+		 gint response,
+		 gpointer data);
+
+
+void
+select_changed_cb (GtkTreeSelection *treeselection, gpointer user_data);
+
+void
+save_session_cb (GnomeClient * client,
+		 gint phase,
+		 GnomeRestartStyle save_style,
+		 gint shutdown,
+		 GnomeInteractStyle interact_style,
+		 gint fast,
+		 gpointer client_data);
+gboolean
+key_press_cb (GtkWidget * widget,
+	      GdkEventKey * event,
+	      gpointer data);
+gboolean
+file_button_release_event_cb (GtkWidget * widget,
+			      GdkEventButton * event,
+			      gpointer data);
+gboolean
+file_event_after_cb (GtkWidget	* widget,
+		     GdkEventButton * event,
+		     gpointer data);
+gboolean
+file_button_press_event_cb (GtkWidget * widget,
+			    GdkEventButton * event,
+			    gpointer data);
+gboolean
+file_key_press_event_cb (GtkWidget * widget,
+			 GdkEventKey * event,
+			 gpointer data);
+gboolean
+file_motion_notify_cb (GtkWidget *widget,
+		       GdkEventMotion *event,
+		       gpointer user_data);
+gboolean
+file_leave_notify_cb (GtkWidget *widget,
+		      GdkEventCrossing *event,
+		      gpointer user_data);
+gboolean
+not_running_timeout_cb (gpointer data);
+
+void
+disable_quick_search_cb (GtkWidget * dialog,
+			 gint response,
+			 gpointer data);
+void
+single_click_to_activate_key_changed_cb (GConfClient * client,
+					 guint cnxn_id,
+					 GConfEntry * entry,
+					 gpointer user_data);
+void
+columns_changed_cb (GtkTreeView * treeview,
+		    gpointer user_data);
+gboolean
+window_state_event_cb (GtkWidget * widget,
+		       GdkEventWindowState * event,
+		       gpointer data);
+
+void
+suggest_search_cb	(GtkWidget *widget,
+			 gpointer data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GSEARCHTOOL_CALLBACKS_H_ */

Added: trunk/src/tracker-search-tool/tracker-search-tool-support.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-search-tool/tracker-search-tool-support.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,1579 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * GNOME Search Tool
+ *
+ *  File:  tracker_search-support.c
+ *
+ *  (C) 2002 the Free Software Foundation
+ *
+ *  Authors:	Dennis Cranston  <dennis_cranston yahoo com>
+ *		George Lebl
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <glib/gi18n.h>
+#include <glib/gdate.h>
+#include <regex.h>
+#include <gdk/gdkx.h>
+#include <libart_lgpl/art_rgb.h>
+#include <libgnomevfs/gnome-vfs-mime.h>
+#include <libgnomevfs/gnome-vfs-mime-handlers.h>
+#include <libgnomevfs/gnome-vfs-ops.h>
+#include <libgnomevfs/gnome-vfs-utils.h>
+#include <libgnome/gnome-desktop-item.h>
+#include <libgnomeui/gnome-thumbnail.h>
+
+#include <gnome.h>
+
+#include "tracker-search-tool.h"
+#include "tracker-search-tool-callbacks.h"
+#include "tracker-search-tool-support.h"
+
+#define C_STANDARD_STRFTIME_CHARACTERS "aAbBcdHIjmMpSUwWxXyYZ"
+#define C_STANDARD_NUMERIC_STRFTIME_CHARACTERS "dHIjmMSUwWyY"
+#define SUS_EXTENDED_STRFTIME_MODIFIERS "EO"
+#define BINARY_EXEC_MIME_TYPE	   "application/x-executable"
+#define MAX_SYMLINKS_FOLLOWED	   32
+#define GSEARCH_DATE_FORMAT_LOCALE "locale"
+#define GSEARCH_DATE_FORMAT_ISO    "iso"
+
+gboolean
+tracker_is_empty_string (const gchar *s)
+{
+	return s == NULL || s[0] == '\0';
+}
+
+/* START OF THE GCONF FUNCTIONS */
+
+static gboolean
+tracker_search_gconf_handle_error (GError ** error)
+{
+	if (error != NULL) {
+		if (*error != NULL) {
+			g_warning (_("GConf error:\n  %s"), (*error)->message);
+			g_error_free (*error);
+			*error = NULL;
+			return TRUE;
+		}
+	}
+	return FALSE;
+}
+
+static GConfClient *
+tracker_search_gconf_client_get_global (void)
+{
+	static GConfClient * global_gconf_client = NULL;
+
+	/* Initialize gconf if needed */
+	if (!gconf_is_initialized ()) {
+		gchar *argv[] = { "tracker_search-preferences", NULL };
+		GError *error = NULL;
+
+		if (!gconf_init (1, argv, &error)) {
+			if (tracker_search_gconf_handle_error (&error)) {
+				return NULL;
+			}
+		}
+	}
+
+	if (global_gconf_client == NULL) {
+		global_gconf_client = gconf_client_get_default ();
+	}
+
+	return global_gconf_client;
+}
+
+gchar *
+tracker_search_gconf_get_string (const gchar * key)
+{
+	GConfClient * client;
+	GError * error = NULL;
+	gchar * result;
+
+	g_return_val_if_fail (key != NULL, NULL);
+
+	client = tracker_search_gconf_client_get_global ();
+	g_return_val_if_fail (client != NULL, NULL);
+
+	result = gconf_client_get_string (client, key, &error);
+
+	if (tracker_search_gconf_handle_error (&error)) {
+		result = g_strdup ("");
+	}
+
+	return result;
+}
+
+GSList *
+tracker_search_gconf_get_list (const gchar * key,
+			       GConfValueType list_type)
+{
+	GConfClient * client;
+	GError * error = NULL;
+	GSList * result;
+
+	g_return_val_if_fail (key != NULL, FALSE);
+
+	client = tracker_search_gconf_client_get_global ();
+	g_return_val_if_fail (client != NULL, NULL);
+
+	result = gconf_client_get_list (client, key, list_type, &error);
+
+	if (tracker_search_gconf_handle_error (&error)) {
+		result = NULL;
+	}
+
+	return result;
+}
+
+void
+tracker_search_gconf_set_list (const gchar * key,
+			       GSList * list,
+			       GConfValueType list_type)
+{
+	GConfClient * client;
+	GError * error = NULL;
+
+	g_return_if_fail (key != NULL);
+
+	client = tracker_search_gconf_client_get_global ();
+	g_return_if_fail (client != NULL);
+
+	gconf_client_set_list (client, key, list_type, list, &error);
+
+	tracker_search_gconf_handle_error (&error);
+}
+
+gint
+tracker_search_gconf_get_int (const gchar * key)
+{
+	GConfClient * client;
+	GError * error = NULL;
+	gint result;
+
+	g_return_val_if_fail (key != NULL, FALSE);
+
+	client = tracker_search_gconf_client_get_global ();
+	g_return_val_if_fail (client != NULL, FALSE);
+
+	result = gconf_client_get_int (client, key, &error);
+
+	if (tracker_search_gconf_handle_error (&error)) {
+		result = 0;
+	}
+
+	return result;
+}
+
+void
+tracker_search_gconf_set_int (const gchar * key,
+			      const gint value)
+{
+	GConfClient * client;
+	GError * error = NULL;
+
+	g_return_if_fail (key != NULL);
+
+	client = tracker_search_gconf_client_get_global ();
+	g_return_if_fail (client != NULL);
+
+	gconf_client_set_int (client, key, value, &error);
+
+	tracker_search_gconf_handle_error (&error);
+}
+
+gboolean
+tracker_search_gconf_get_boolean (const gchar * key)
+{
+	GConfClient * client;
+	GError * error = NULL;
+	gboolean result;
+
+	g_return_val_if_fail (key != NULL, FALSE);
+
+	client = tracker_search_gconf_client_get_global ();
+	g_return_val_if_fail (client != NULL, FALSE);
+
+	result = gconf_client_get_bool (client, key, &error);
+
+	if (tracker_search_gconf_handle_error (&error)) {
+		result = FALSE;
+	}
+
+	return result;
+}
+
+void
+tracker_search_gconf_set_boolean (const gchar * key,
+				  const gboolean flag)
+{
+	GConfClient * client;
+	GError * error = NULL;
+
+	g_return_if_fail (key != NULL);
+
+	client = tracker_search_gconf_client_get_global ();
+	g_return_if_fail (client != NULL);
+
+	gconf_client_set_bool (client, key, flag, &error);
+
+	tracker_search_gconf_handle_error (&error);
+}
+
+void
+tracker_search_gconf_add_dir (const gchar * dir)
+{
+	GConfClient * client;
+	GError * error = NULL;
+
+	g_return_if_fail (dir != NULL);
+
+	client = tracker_search_gconf_client_get_global ();
+	g_return_if_fail (client != NULL);
+
+	gconf_client_add_dir (client,
+			      dir,
+			      GCONF_CLIENT_PRELOAD_RECURSIVE,
+			      &error);
+
+	tracker_search_gconf_handle_error (&error);
+}
+
+void
+tracker_search_gconf_watch_key (const gchar * dir,
+				const gchar * key,
+				GConfClientNotifyFunc callback,
+				gpointer user_data)
+{
+	GConfClient * client;
+	GError * error = NULL;
+
+	g_return_if_fail (key != NULL);
+	g_return_if_fail (dir != NULL);
+
+	client = tracker_search_gconf_client_get_global ();
+	g_return_if_fail (client != NULL);
+
+	gconf_client_add_dir (client,
+			      dir,
+			      GCONF_CLIENT_PRELOAD_NONE,
+			      &error);
+
+	tracker_search_gconf_handle_error (&error);
+
+	gconf_client_notify_add (client,
+				 key,
+				 callback,
+				 user_data,
+				 NULL,
+				 &error);
+
+	tracker_search_gconf_handle_error (&error);
+}
+
+/* START OF GENERIC GNOME-SEARCH-TOOL FUNCTIONS */
+
+gboolean
+is_path_hidden (const gchar * path)
+{
+	gint results = FALSE;
+	gchar * sub_str;
+	gchar * hidden_path_substr = g_strconcat (G_DIR_SEPARATOR_S, ".", NULL);
+
+	sub_str = g_strstr_len (path, strlen (path), hidden_path_substr);
+
+	if (sub_str != NULL) {
+		gchar * gnome_desktop_str;
+
+		gnome_desktop_str = g_strconcat (G_DIR_SEPARATOR_S, ".gnome-desktop", G_DIR_SEPARATOR_S, NULL);
+
+		/* exclude the .gnome-desktop folder */
+		if (strncmp (sub_str, gnome_desktop_str, strlen (gnome_desktop_str)) == 0) {
+			sub_str++;
+			results = (g_strstr_len (sub_str, strlen (sub_str), hidden_path_substr) != NULL);
+		}
+		else {
+			results = TRUE;
+		}
+
+		g_free (gnome_desktop_str);
+	}
+
+	g_free (hidden_path_substr);
+	return results;
+}
+
+gboolean
+is_quick_search_excluded_path (const gchar * path)
+{
+	GSList	   * exclude_path_list;
+	GSList	   * tmp_list;
+	gchar	   * dir;
+	gboolean     results = FALSE;
+
+	dir = g_strdup (path);
+
+	/* Remove trailing G_DIR_SEPARATOR. */
+	if ((strlen (dir) > 1) && (g_str_has_suffix (dir, G_DIR_SEPARATOR_S) == TRUE)) {
+		dir[strlen (dir) - 1] = '\0';
+	}
+
+	/* Always exclude a path that is symbolic link. */
+	if (g_file_test (dir, G_FILE_TEST_IS_SYMLINK)) {
+		g_free (dir);
+
+		return TRUE;
+	}
+	g_free (dir);
+
+	/* Check path against the Quick-Search-Excluded-Paths list. */
+	exclude_path_list = tracker_search_gconf_get_list ("/apps/tracker-search-tool/quick_search_excluded_paths",
+							GCONF_VALUE_STRING);
+
+	for (tmp_list = exclude_path_list; tmp_list; tmp_list = tmp_list->next) {
+
+		gchar * dir;
+
+		/* Skip empty or null values. */
+		if (tracker_is_empty_string (tmp_list->data)) {
+			continue;
+		}
+
+		dir = g_strdup (tmp_list->data);
+
+		/* Wild-card comparisons. */
+		if (g_strstr_len (dir, strlen (dir), "*") != NULL) {
+
+			if (g_pattern_match_simple (dir, path) == TRUE) {
+
+				results = TRUE;
+				g_free (dir);
+				break;
+			}
+		}
+		/* Non-wild-card comparisons. */
+		else {
+			/* Add a trailing G_DIR_SEPARATOR. */
+			if (g_str_has_suffix (dir, G_DIR_SEPARATOR_S) == FALSE) {
+
+				gchar *tmp;
+
+				tmp = dir;
+				dir = g_strconcat (dir, G_DIR_SEPARATOR_S, NULL);
+				g_free (tmp);
+			}
+
+			if (strcmp (path, dir) == 0) {
+
+				results = TRUE;
+				g_free (dir);
+				break;
+			}
+		}
+		g_free (dir);
+	}
+
+	for (tmp_list = exclude_path_list; tmp_list; tmp_list = tmp_list->next) {
+		g_free (tmp_list->data);
+	}
+	g_slist_free (exclude_path_list);
+
+	return results;
+}
+
+gboolean
+is_second_scan_excluded_path (const gchar * path)
+{
+	GSList	   * exclude_path_list;
+	GSList	   * tmp_list;
+	gchar	   * dir;
+	gboolean     results = FALSE;
+
+	dir = g_strdup (path);
+
+	/* Remove trailing G_DIR_SEPARATOR. */
+	if ((strlen (dir) > 1) && (g_str_has_suffix (dir, G_DIR_SEPARATOR_S) == TRUE)) {
+		dir[strlen (dir) - 1] = '\0';
+	}
+
+	/* Always exclude a path that is symbolic link. */
+	if (g_file_test (dir, G_FILE_TEST_IS_SYMLINK)) {
+		g_free (dir);
+
+		return TRUE;
+	}
+	g_free (dir);
+
+	/* Check path against the Quick-Search-Excluded-Paths list. */
+	exclude_path_list = tracker_search_gconf_get_list ("/apps/tracker-search-tool/quick_search_second_scan_excluded_paths",
+							GCONF_VALUE_STRING);
+
+	for (tmp_list = exclude_path_list; tmp_list; tmp_list = tmp_list->next) {
+
+		gchar * dir;
+
+		/* Skip empty or null values. */
+		if (tracker_is_empty_string (tmp_list->data)) {
+			continue;
+		}
+
+		dir = g_strdup (tmp_list->data);
+
+		/* Wild-card comparisons. */
+		if (g_strstr_len (dir, strlen (dir), "*") != NULL) {
+
+			if (g_pattern_match_simple (dir, path) == TRUE) {
+
+				results = TRUE;
+				g_free (dir);
+				break;
+			}
+		}
+		/* Non-wild-card comparisons. */
+		else {
+			/* Add a trailing G_DIR_SEPARATOR. */
+			if (g_str_has_suffix (dir, G_DIR_SEPARATOR_S) == FALSE) {
+
+				gchar *tmp;
+
+				tmp = dir;
+				dir = g_strconcat (dir, G_DIR_SEPARATOR_S, NULL);
+				g_free (tmp);
+			}
+
+			if (strcmp (path, dir) == 0) {
+
+				results = TRUE;
+				g_free (dir);
+				break;
+			}
+		}
+		g_free (dir);
+	}
+
+	for (tmp_list = exclude_path_list; tmp_list; tmp_list = tmp_list->next) {
+		g_free (tmp_list->data);
+	}
+	g_slist_free (exclude_path_list);
+
+	return results;
+}
+
+gboolean
+compare_regex (const gchar * regex,
+	       const gchar * string)
+{
+	regex_t regexec_pattern;
+
+	if (regex == NULL) {
+		return TRUE;
+	}
+
+	if (!regcomp (&regexec_pattern, regex, REG_NOSUB)) {
+		if (regexec (&regexec_pattern, string, 0, 0, 0) != REG_NOMATCH) {
+			return TRUE;
+		}
+	}
+	return FALSE;
+}
+
+gboolean
+limit_string_to_x_lines (GString * string,
+			 gint x)
+{
+	int i;
+	int count = 0;
+	for (i = 0; string->str[i] != '\0'; i++) {
+		if (string->str[i] == '\n') {
+			count++;
+			if (count == x) {
+				g_string_truncate (string, i);
+				return TRUE;
+			}
+		}
+	}
+	return FALSE;
+}
+
+gchar *
+tracker_string_replace (const gchar * haystack,
+			gchar * needle,
+			gchar * replacement)
+{
+	GString * str;
+	gint pos, needle_len;
+
+	g_return_val_if_fail (haystack && needle, NULL);
+
+	needle_len = strlen (needle);
+
+	str = g_string_new ("");
+
+	for (pos = 0; haystack[pos]; pos++)
+	{
+		if (strncmp (&haystack[pos], needle, needle_len) == 0)
+		{
+
+			if (replacement) {
+				str = g_string_append (str, replacement);
+			}
+
+			pos += needle_len - 1;
+
+		} else {
+			str = g_string_append_c (str, haystack[pos]);
+		}
+	}
+
+	return g_string_free (str, FALSE);
+}
+
+static gint
+count_of_char_in_string (const gchar * string,
+			 const gchar c)
+{
+	int cnt = 0;
+	for(; *string; string++) {
+		if (*string == c) cnt++;
+	}
+	return cnt;
+}
+
+gchar *
+escape_single_quotes (const gchar * string)
+{
+	GString * gs;
+
+	if (string == NULL) {
+		return NULL;
+	}
+
+	if (count_of_char_in_string (string, '\'') == 0) {
+		return g_strdup(string);
+	}
+	gs = g_string_new ("");
+	for(; *string; string++) {
+		if (*string == '\'') {
+			g_string_append(gs, "'\\''");
+		}
+		else {
+			g_string_append_c(gs, *string);
+		}
+	}
+	return g_string_free (gs, FALSE);
+}
+
+gchar *
+backslash_special_characters (const gchar * string)
+{
+	GString * gs;
+
+	if (string == NULL) {
+		return NULL;
+	}
+
+	if ((count_of_char_in_string (string, '\\') == 0) &&
+	    (count_of_char_in_string (string, '-') == 0)) {
+		return g_strdup(string);
+	}
+	gs = g_string_new ("");
+	for(; *string; string++) {
+		if (*string == '\\') {
+			g_string_append(gs, "\\\\");
+		}
+		else if (*string == '-') {
+			g_string_append(gs, "\\-");
+		}
+		else {
+			g_string_append_c(gs, *string);
+		}
+	}
+	return g_string_free (gs, FALSE);
+}
+
+gchar *
+remove_mnemonic_character (const gchar * string)
+{
+	GString * gs;
+	gboolean first_mnemonic = TRUE;
+
+	if (string == NULL) {
+		return NULL;
+	}
+
+	gs = g_string_new ("");
+	for(; *string; string++) {
+		if ((first_mnemonic) && (*string == '_')) {
+			first_mnemonic = FALSE;
+			continue;
+		}
+		g_string_append_c(gs, *string);
+	}
+	return g_string_free (gs, FALSE);
+}
+
+gchar *
+get_readable_date (const gchar * format_string,
+		   const time_t file_time_raw)
+{
+	struct tm * file_time;
+	gchar * format;
+	GDate * today;
+	GDate * file_date;
+	guint32 file_date_age;
+	gchar * readable_date;
+
+	file_time = localtime (&file_time_raw);
+
+	/* Base format of date column on nautilus date_format key */
+	if (format_string != NULL) {
+		if (strcmp(format_string, GSEARCH_DATE_FORMAT_LOCALE) == 0) {
+			return tracker_search_strdup_strftime ("%c", file_time);
+		} else if (strcmp (format_string, GSEARCH_DATE_FORMAT_ISO) == 0) {
+			return tracker_search_strdup_strftime ("%Y-%m-%d %H:%M:%S", file_time);
+		}
+	}
+
+	file_date = g_date_new_dmy (file_time->tm_mday,
+			       file_time->tm_mon + 1,
+			       file_time->tm_year + 1900);
+
+	today = g_date_new ();
+	g_date_set_time_t (today, time (NULL));
+
+	file_date_age = g_date_get_julian (today) - g_date_get_julian (file_date);
+
+	g_date_free (today);
+	g_date_free (file_date);
+
+	if (file_date_age == 0)	{
+	/* Translators:  Below are the strings displayed in the 'Date Modified'
+	   column of the list view.  The format of this string can vary depending
+	   on age of a file.  Please modify the format of the timestamp to match
+	   your locale.  For example, to display 24 hour time replace the '%-I'
+	   with '%-H' and remove the '%p'.  (See bugzilla report #120434.) */
+		format = g_strdup(_("today at %-I:%M %p"));
+	} else if (file_date_age == 1) {
+		format = g_strdup(_("yesterday at %-I:%M %p"));
+	} else if (file_date_age < 7) {
+		format = g_strdup(_("%A, %B %-d %Y at %-I:%M:%S %p"));
+	} else {
+		format = g_strdup(_("%A, %B %-d %Y at %-I:%M:%S %p"));
+	}
+
+	readable_date = tracker_search_strdup_strftime (format, file_time);
+	g_free (format);
+
+	return readable_date;
+}
+
+gchar *
+tracker_search_strdup_strftime (const gchar * format,
+				struct tm * time_pieces)
+{
+	/* This function is borrowed from eel's eel_strdup_strftime() */
+	GString * string;
+	const char * remainder, * percent;
+	char code[4], buffer[512];
+	char * piece, * result, * converted;
+	size_t string_length;
+	gboolean strip_leading_zeros, turn_leading_zeros_to_spaces;
+	char modifier;
+	int i;
+
+	/* Format could be translated, and contain UTF-8 chars,
+	 * so convert to locale encoding which strftime uses */
+	converted = g_locale_from_utf8 (format, -1, NULL, NULL, NULL);
+	g_return_val_if_fail (converted != NULL, NULL);
+
+	string = g_string_new ("");
+	remainder = converted;
+
+	/* Walk from % character to % character. */
+	for (;;) {
+		percent = strchr (remainder, '%');
+		if (percent == NULL) {
+			g_string_append (string, remainder);
+			break;
+		}
+		g_string_append_len (string, remainder,
+				     percent - remainder);
+
+		/* Handle the "%" character. */
+		remainder = percent + 1;
+		switch (*remainder) {
+		case '-':
+			strip_leading_zeros = TRUE;
+			turn_leading_zeros_to_spaces = FALSE;
+			remainder++;
+			break;
+		case '_':
+			strip_leading_zeros = FALSE;
+			turn_leading_zeros_to_spaces = TRUE;
+			remainder++;
+			break;
+		case '%':
+			g_string_append_c (string, '%');
+			remainder++;
+			continue;
+		case '\0':
+			g_warning ("Trailing %% passed to tracker_search_strdup_strftime");
+			g_string_append_c (string, '%');
+			continue;
+		default:
+			strip_leading_zeros = FALSE;
+			turn_leading_zeros_to_spaces = FALSE;
+			break;
+		}
+
+		modifier = 0;
+		if (strchr (SUS_EXTENDED_STRFTIME_MODIFIERS, *remainder) != NULL) {
+			modifier = *remainder;
+			remainder++;
+
+			if (*remainder == 0) {
+				g_warning ("Unfinished %%%c modifier passed to tracker_search_strdup_strftime", modifier);
+				break;
+			}
+		}
+
+		if (strchr (C_STANDARD_STRFTIME_CHARACTERS, *remainder) == NULL) {
+			g_warning ("tracker_search_strdup_strftime does not support "
+				   "non-standard escape code %%%c",
+				   *remainder);
+		}
+
+		/* Convert code to strftime format. We have a fixed
+		 * limit here that each code can expand to a maximum
+		 * of 512 bytes, which is probably OK. There's no
+		 * limit on the total size of the result string.
+		 */
+		i = 0;
+		code[i++] = '%';
+		if (modifier != 0) {
+#ifdef HAVE_STRFTIME_EXTENSION
+			code[i++] = modifier;
+#endif
+		}
+		code[i++] = *remainder;
+		code[i++] = '\0';
+		string_length = strftime (buffer, sizeof (buffer),
+					  code, time_pieces);
+		if (string_length == 0) {
+			/* We could put a warning here, but there's no
+			 * way to tell a successful conversion to
+			 * empty string from a failure.
+			 */
+			buffer[0] = '\0';
+		}
+
+		/* Strip leading zeros if requested. */
+		piece = buffer;
+		if (strip_leading_zeros || turn_leading_zeros_to_spaces) {
+			if (strchr (C_STANDARD_NUMERIC_STRFTIME_CHARACTERS, *remainder) == NULL) {
+				g_warning ("tracker_search_strdup_strftime does not support "
+					   "modifier for non-numeric escape code %%%c%c",
+					   remainder[-1],
+					   *remainder);
+			}
+			if (*piece == '0') {
+				do {
+					piece++;
+				} while (*piece == '0');
+				if (!g_ascii_isdigit (*piece)) {
+				    piece--;
+				}
+			}
+			if (turn_leading_zeros_to_spaces) {
+				memset (buffer, ' ', piece - buffer);
+				piece = buffer;
+			}
+		}
+		remainder++;
+
+		/* Add this piece. */
+		g_string_append (string, piece);
+	}
+
+	/* Convert the string back into utf-8. */
+	result = g_locale_to_utf8 (string->str, -1, NULL, NULL, NULL);
+
+	g_string_free (string, TRUE);
+	g_free (converted);
+
+	return result;
+}
+
+gchar *
+get_file_type_description (const gchar * file,
+			   const char *mime,
+			   GnomeVFSFileInfo * file_info)
+{
+	gchar * desc;
+
+	if (file == NULL || mime == NULL) {
+		return g_strdup (gnome_vfs_mime_get_description (GNOME_VFS_MIME_TYPE_UNKNOWN));
+	}
+
+	desc = g_strdup (gnome_vfs_mime_get_description (mime));
+
+	if (file_info->symlink_name != NULL) {
+
+		gchar * absolute_symlink = NULL;
+		gchar * str = NULL;
+
+		if (g_path_is_absolute (file_info->symlink_name) != TRUE) {
+			gchar *dirname;
+
+			dirname = g_path_get_dirname (file);
+			absolute_symlink = g_strconcat (dirname, G_DIR_SEPARATOR_S, file_info->symlink_name, NULL);
+			g_free (dirname);
+		}
+		else {
+			absolute_symlink = g_strdup (file_info->symlink_name);
+		}
+
+		if (g_file_test (absolute_symlink, G_FILE_TEST_EXISTS) != TRUE) {
+		       if ((g_ascii_strcasecmp (mime, "x-special/socket") != 0) &&
+			   (g_ascii_strcasecmp (mime, "x-special/fifo") != 0)) {
+				g_free (absolute_symlink);
+				g_free (desc);
+				return g_strdup (_("link (broken)"));
+			}
+		}
+
+		str = g_strdup_printf (_("link to %s"), (desc != NULL) ? desc : mime);
+		g_free (absolute_symlink);
+		g_free (desc);
+		return str;
+	}
+	return desc;
+}
+
+
+static GdkPixbuf *
+tracker_search_load_thumbnail_frame (void)
+{
+	GdkPixbuf * pixbuf = NULL;
+	gchar * image_path;
+
+	image_path = tracker_search_pixmap_file ("thumbnail_frame.png");
+
+	if (image_path != NULL) {
+		pixbuf = gdk_pixbuf_new_from_file (image_path, NULL);
+	}
+	g_free (image_path);
+	return pixbuf;
+}
+
+static void
+tracker_search_draw_frame_row (GdkPixbuf * frame_image,
+			    gint target_width,
+			    gint source_width,
+			    gint source_v_position,
+			    gint dest_v_position,
+			    GdkPixbuf * result_pixbuf,
+			    gint left_offset,
+			    gint height)
+{
+	gint remaining_width;
+	gint h_offset;
+	gint slab_width;
+
+	remaining_width = target_width;
+	h_offset = 0;
+	while (remaining_width > 0) {
+		slab_width = remaining_width > source_width ? source_width : remaining_width;
+		gdk_pixbuf_copy_area (frame_image, left_offset, source_v_position, slab_width,
+				      height, result_pixbuf, left_offset + h_offset, dest_v_position);
+		remaining_width -= slab_width;
+		h_offset += slab_width;
+	}
+}
+
+static void
+tracker_search_draw_frame_column (GdkPixbuf * frame_image,
+			       gint target_height,
+			       gint source_height,
+			       gint source_h_position,
+			       gint dest_h_position,
+			       GdkPixbuf * result_pixbuf,
+			       gint top_offset,
+			       gint width)
+{
+	gint remaining_height;
+	gint v_offset;
+	gint slab_height;
+
+	remaining_height = target_height;
+	v_offset = 0;
+	while (remaining_height > 0) {
+		slab_height = remaining_height > source_height ? source_height : remaining_height;
+		gdk_pixbuf_copy_area (frame_image, source_h_position, top_offset, width, slab_height,
+				      result_pixbuf, dest_h_position, top_offset + v_offset);
+		remaining_height -= slab_height;
+		v_offset += slab_height;
+	}
+}
+
+static GdkPixbuf *
+tracker_search_stretch_frame_image (GdkPixbuf *frame_image,
+				 gint left_offset,
+				 gint top_offset,
+				 gint right_offset,
+				 gint bottom_offset,
+				 gint dest_width,
+				 gint dest_height,
+				 gboolean fill_flag)
+{
+	GdkPixbuf * result_pixbuf;
+	guchar * pixels_ptr;
+	gint frame_width, frame_height;
+	gint y, row_stride;
+	gint target_width, target_frame_width;
+	gint target_height, target_frame_height;
+
+	frame_width = gdk_pixbuf_get_width (frame_image);
+	frame_height = gdk_pixbuf_get_height (frame_image);
+
+	if (fill_flag) {
+		result_pixbuf = gdk_pixbuf_scale_simple (frame_image, dest_width, dest_height, GDK_INTERP_NEAREST);
+	} else {
+		result_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, dest_width, dest_height);
+	}
+	row_stride = gdk_pixbuf_get_rowstride (result_pixbuf);
+	pixels_ptr = gdk_pixbuf_get_pixels (result_pixbuf);
+
+	/* clear the new pixbuf */
+	if (fill_flag == FALSE) {
+		for (y = 0; y < dest_height; y++) {
+			art_rgb_run_alpha (pixels_ptr, 255, 255, 255, 255, dest_width);
+			pixels_ptr += row_stride;
+		}
+	}
+
+	target_width  = dest_width - left_offset - right_offset;
+	target_frame_width = frame_width - left_offset - right_offset;
+
+	target_height  = dest_height - top_offset - bottom_offset;
+	target_frame_height = frame_height - top_offset - bottom_offset;
+
+	/* Draw the left top corner  and top row */
+	gdk_pixbuf_copy_area (frame_image, 0, 0, left_offset, top_offset, result_pixbuf, 0,  0);
+	tracker_search_draw_frame_row (frame_image, target_width, target_frame_width, 0, 0,
+				    result_pixbuf, left_offset, top_offset);
+
+	/* Draw the right top corner and left column */
+	gdk_pixbuf_copy_area (frame_image, frame_width - right_offset, 0, right_offset, top_offset,
+			      result_pixbuf, dest_width - right_offset,  0);
+	tracker_search_draw_frame_column (frame_image, target_height, target_frame_height, 0, 0,
+				       result_pixbuf, top_offset, left_offset);
+
+	/* Draw the bottom right corner and bottom row */
+	gdk_pixbuf_copy_area (frame_image, frame_width - right_offset, frame_height - bottom_offset,
+			      right_offset, bottom_offset, result_pixbuf, dest_width - right_offset,
+			      dest_height - bottom_offset);
+	tracker_search_draw_frame_row (frame_image, target_width, target_frame_width,
+				    frame_height - bottom_offset, dest_height - bottom_offset,
+				    result_pixbuf, left_offset, bottom_offset);
+
+	/* Draw the bottom left corner and the right column */
+	gdk_pixbuf_copy_area (frame_image, 0, frame_height - bottom_offset, left_offset, bottom_offset,
+			      result_pixbuf, 0,  dest_height - bottom_offset);
+	tracker_search_draw_frame_column (frame_image, target_height, target_frame_height,
+				       frame_width - right_offset, dest_width - right_offset,
+				       result_pixbuf, top_offset, right_offset);
+	return result_pixbuf;
+}
+
+static GdkPixbuf *
+tracker_search_embed_image_in_frame (GdkPixbuf * source_image,
+				  GdkPixbuf * frame_image,
+				  gint left_offset,
+				  gint top_offset,
+				  gint right_offset,
+				  gint bottom_offset)
+{
+	GdkPixbuf * result_pixbuf;
+	gint source_width, source_height;
+	gint dest_width, dest_height;
+
+	source_width = gdk_pixbuf_get_width (source_image);
+	source_height = gdk_pixbuf_get_height (source_image);
+
+	dest_width = source_width + left_offset + right_offset;
+	dest_height = source_height + top_offset + bottom_offset;
+
+	result_pixbuf = tracker_search_stretch_frame_image (frame_image, left_offset, top_offset, right_offset, bottom_offset,
+							 dest_width, dest_height, FALSE);
+
+	gdk_pixbuf_copy_area (source_image, 0, 0, source_width, source_height, result_pixbuf, left_offset, top_offset);
+
+	return result_pixbuf;
+}
+
+static void
+tracker_search_thumbnail_frame_image (GdkPixbuf ** pixbuf)
+{
+	GdkPixbuf * pixbuf_with_frame;
+	GdkPixbuf * frame;
+
+	frame = tracker_search_load_thumbnail_frame ();
+	if (frame == NULL) {
+		return;
+	}
+
+	pixbuf_with_frame = tracker_search_embed_image_in_frame (*pixbuf, frame, 3, 3, 6, 6);
+	g_object_unref (*pixbuf);
+	g_object_unref (frame);
+
+	*pixbuf = pixbuf_with_frame;
+}
+
+static GdkPixbuf *
+tracker_search_get_thumbnail_image (const gchar * file)
+{
+	GdkPixbuf * pixbuf = NULL;
+	gchar * thumbnail_path;
+	gchar * uri;
+
+	uri = gnome_vfs_get_uri_from_local_path (file);
+	thumbnail_path = gnome_thumbnail_path_for_uri (uri, GNOME_THUMBNAIL_SIZE_NORMAL);
+
+	if (thumbnail_path != NULL) {
+		if (g_file_test (thumbnail_path, G_FILE_TEST_EXISTS)) {
+
+			GdkPixbuf * thumbnail_pixbuf = NULL;
+			gfloat scale_factor_x = 1.0;
+			gfloat scale_factor_y = 1.0;
+			gint scale_x;
+			gint scale_y;
+
+			thumbnail_pixbuf = gdk_pixbuf_new_from_file (thumbnail_path, NULL);
+			tracker_search_thumbnail_frame_image (&thumbnail_pixbuf);
+
+			if (gdk_pixbuf_get_width (thumbnail_pixbuf) > ICON_SIZE) {
+				scale_factor_x = (gfloat) ICON_SIZE / (gfloat) gdk_pixbuf_get_width (thumbnail_pixbuf);
+			}
+			if (gdk_pixbuf_get_height (thumbnail_pixbuf) > ICON_SIZE) {
+				scale_factor_y = (gfloat) ICON_SIZE / (gfloat) gdk_pixbuf_get_height (thumbnail_pixbuf);
+			}
+
+			if (gdk_pixbuf_get_width (thumbnail_pixbuf) > gdk_pixbuf_get_height (thumbnail_pixbuf)) {
+				scale_x = ICON_SIZE;
+				scale_y = (gint) (gdk_pixbuf_get_height (thumbnail_pixbuf) * scale_factor_x);
+			}
+			else {
+				scale_x = (gint) (gdk_pixbuf_get_width (thumbnail_pixbuf) * scale_factor_y);
+				scale_y = ICON_SIZE;
+			}
+
+			pixbuf = gdk_pixbuf_scale_simple (thumbnail_pixbuf, scale_x, scale_y, GDK_INTERP_BILINEAR);
+			g_object_unref (thumbnail_pixbuf);
+		}
+		g_free (thumbnail_path);
+	}
+	g_free (uri);
+	return pixbuf;
+}
+
+static gchar *
+tracker_search_icon_lookup (GSearchWindow * gsearch,
+			    const gchar * file,
+			    const gchar * mime,
+			    GnomeVFSFileInfo * file_info,
+			    gboolean enable_thumbnails)
+{
+	GnomeIconLookupFlags lookup_flags = GNOME_ICON_LOOKUP_FLAGS_NONE;
+	gchar * icon_name = NULL;
+	gchar * uri;
+
+	uri = gnome_vfs_get_uri_from_local_path (file);
+
+	if ((strncmp (mime, "image/", 6) != 0) ||
+	    ((int)file_info->size < (int)gsearch->show_thumbnails_file_size_limit)) {
+		if (gsearch->thumbnail_factory == NULL) {
+			gsearch->thumbnail_factory = gnome_thumbnail_factory_new (GNOME_THUMBNAIL_SIZE_NORMAL);
+		}
+		lookup_flags = GNOME_ICON_LOOKUP_FLAGS_SHOW_SMALL_IMAGES_AS_THEMSELVES |
+			       GNOME_ICON_LOOKUP_FLAGS_ALLOW_SVG_AS_THEMSELVES;
+	}
+
+	icon_name = gnome_icon_lookup (gtk_icon_theme_get_default (),
+				       gsearch->thumbnail_factory,
+				       uri,
+				       NULL,
+				       file_info,
+				       mime,
+				       lookup_flags,
+				       NULL);
+	g_free (uri);
+	return icon_name;
+}
+
+GdkPixbuf *
+get_file_pixbuf (GSearchWindow * gsearch,
+		 const gchar * file,
+		 const char *mime,
+		 GnomeVFSFileInfo * file_info)
+{
+	GdkPixbuf * pixbuf = NULL;
+	gchar * icon_name = NULL;
+
+	if (file == NULL || mime == NULL) {
+		icon_name = g_strdup (ICON_THEME_REGULAR_ICON);
+	}
+
+	else if ((g_file_test (file, G_FILE_TEST_IS_EXECUTABLE)) &&
+		 (g_ascii_strcasecmp (mime, "application/x-executable-binary") == 0)) {
+		icon_name = g_strdup (ICON_THEME_EXECUTABLE_ICON);
+	}
+	else if (g_ascii_strcasecmp (mime, "x-special/device-char") == 0) {
+		icon_name = g_strdup (ICON_THEME_CHAR_DEVICE);
+	}
+	else if (g_ascii_strcasecmp (mime, "x-special/device-block") == 0) {
+		icon_name = g_strdup (ICON_THEME_BLOCK_DEVICE);
+	}
+	else if (g_ascii_strcasecmp (mime, "x-special/socket") == 0) {
+		icon_name = g_strdup (ICON_THEME_SOCKET);
+	}
+	else if (g_ascii_strcasecmp (mime, "x-special/fifo") == 0) {
+		icon_name = g_strdup (ICON_THEME_FIFO);
+	} else {
+
+		/* check for thumbnail first */
+		GdkPixbuf *thumbnail_pixbuf = tracker_search_get_thumbnail_image (file);
+
+		if (thumbnail_pixbuf != NULL) {
+
+			if ((gdk_pixbuf_get_width (thumbnail_pixbuf) > ICON_SIZE) ||
+			    (gdk_pixbuf_get_height (thumbnail_pixbuf) > ICON_SIZE)) {
+
+				gfloat scale_factor_x = 1.0;
+				gfloat scale_factor_y = 1.0;
+				gint scale_x;
+				gint scale_y;
+
+				if (gdk_pixbuf_get_width (thumbnail_pixbuf) > ICON_SIZE) {
+					scale_factor_x = (gfloat) ICON_SIZE / (gfloat) gdk_pixbuf_get_width (thumbnail_pixbuf);
+				}
+				if (gdk_pixbuf_get_height (thumbnail_pixbuf) > ICON_SIZE) {
+					scale_factor_y = (gfloat) ICON_SIZE / (gfloat) gdk_pixbuf_get_height (thumbnail_pixbuf);
+				}
+
+				if (gdk_pixbuf_get_width (thumbnail_pixbuf) > gdk_pixbuf_get_height (thumbnail_pixbuf)) {
+					scale_x = ICON_SIZE;
+					scale_y = (gint) (gdk_pixbuf_get_height (thumbnail_pixbuf) * scale_factor_x);
+				}
+				else {
+					scale_x = (gint) (gdk_pixbuf_get_width (thumbnail_pixbuf) * scale_factor_y);
+					scale_y = ICON_SIZE;
+				}
+
+				pixbuf = gdk_pixbuf_scale_simple (thumbnail_pixbuf, scale_x, scale_y, GDK_INTERP_BILINEAR);
+				g_object_unref (thumbnail_pixbuf);
+
+				return pixbuf;
+
+			} else {
+				return thumbnail_pixbuf;
+			}
+
+		}
+
+		/* check if image can be generated from file */
+
+		if ((strncmp (mime, "image/", 6) == 0)) {
+			pixbuf = gdk_pixbuf_new_from_file_at_scale (file, ICON_SIZE, ICON_SIZE, TRUE, NULL);
+		}
+
+		if (pixbuf) {
+			return pixbuf;
+		} else {
+			icon_name = tracker_search_icon_lookup (gsearch, file, mime, file_info, TRUE);
+		}
+
+
+	}
+
+	pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (), icon_name, ICON_SIZE, 0, NULL);
+
+	if (!pixbuf) {
+		gtk_icon_theme_load_icon (gtk_icon_theme_get_default (), ICON_THEME_REGULAR_ICON, ICON_SIZE, 0, NULL);
+	}
+
+
+	g_free (icon_name);
+
+
+	return pixbuf;
+}
+
+gboolean
+open_file_with_xdg_open (GtkWidget * window,
+			 const gchar * file)
+{
+	gboolean  result;
+	gchar	  *quoted_filename = g_shell_quote (file);
+	gchar	  *command = g_strconcat ("xdg-open ", quoted_filename, NULL);
+
+	g_free (quoted_filename);
+	result = g_spawn_command_line_async (command, NULL);
+	g_free (command);
+
+	return result;
+}
+
+gboolean
+open_file_with_nautilus (GtkWidget * window,
+			 const gchar * file)
+{
+	GnomeDesktopItem * ditem;
+	GdkScreen * screen;
+	GError *error = NULL;
+	gchar * command;
+	gchar * contents;
+	gchar * escaped;
+
+	escaped = g_shell_quote (file);
+
+	command = g_strconcat ("nautilus ",
+			       "--sm-disable ",
+			       "--no-desktop ",
+			       "--no-default-window ",
+			       escaped,
+			       NULL);
+
+	contents = g_strdup_printf ("[Desktop Entry]\n"
+				    "Name=Nautilus\n"
+				    "Icon=file-manager\n"
+				    "Exec=%s\n"
+				    "Terminal=false\n"
+				    "StartupNotify=true\n"
+				    "Type=Application\n",
+				    command);
+
+	ditem = gnome_desktop_item_new_from_string (NULL,
+						    contents,
+						    strlen (contents),
+						    GNOME_DESKTOP_ITEM_LOAD_NO_TRANSLATIONS ,
+						    NULL);
+
+	if (ditem == NULL) {
+		g_free (contents);
+		g_free (command);
+		g_free (escaped);
+		return FALSE;
+	}
+
+	screen = gtk_widget_get_screen (window);
+
+	gnome_desktop_item_set_launch_time (ditem,
+					    gtk_get_current_event_time ());
+
+	gnome_desktop_item_launch_on_screen (ditem, NULL,
+					     GNOME_DESKTOP_ITEM_LAUNCH_ONLY_ONE,
+					     screen, -1, &error);
+
+	gnome_desktop_item_unref (ditem);
+	g_free (contents);
+	g_free (command);
+	g_free (escaped);
+
+	if (error) {
+		g_error_free (error);
+		return FALSE;
+	}
+	return TRUE;
+}
+
+gboolean
+open_file_with_application (GtkWidget * window,
+			    const gchar * file)
+{
+	GnomeVFSMimeApplication * application;
+	const char * mime;
+
+	mime = gnome_vfs_get_file_mime_type (file, NULL, FALSE);
+	application = gnome_vfs_mime_get_default_application (mime);
+
+	if (!g_file_test (file, G_FILE_TEST_IS_DIR)) {
+		if (application) {
+			const char *desktop_file;
+			GnomeDesktopItem *ditem;
+			GdkScreen *screen;
+			GError *error = NULL;
+			GList *uris = NULL;
+			gboolean result;
+			char *uri;
+
+			desktop_file = gnome_vfs_mime_application_get_desktop_file_path (application);
+
+			uri = gnome_vfs_get_uri_from_local_path (file);
+			uris = g_list_append (uris, uri);
+
+			if (!g_file_test (desktop_file, G_FILE_TEST_EXISTS)) {
+				result = (gnome_vfs_mime_application_launch (application, uris) == GNOME_VFS_OK);
+			}
+			else {
+				result = TRUE;
+				ditem = gnome_desktop_item_new_from_file (desktop_file, 0, &error);
+				if (error) {
+					result = FALSE;
+					g_error_free (error);
+				}
+				else {
+					screen = gtk_widget_get_screen (window);
+					gnome_desktop_item_set_launch_time (ditem, gtk_get_current_event_time ());
+					gnome_desktop_item_launch_on_screen (ditem, uris,
+						GNOME_DESKTOP_ITEM_LAUNCH_APPEND_PATHS, screen, -1, &error);
+					if (error) {
+						result = FALSE;
+						g_error_free (error);
+					}
+				}
+				gnome_desktop_item_unref (ditem);
+			}
+			gnome_vfs_mime_application_free (application);
+			g_list_free (uris);
+			g_free (uri);
+
+			return result;
+		}
+	}
+	return FALSE;
+}
+
+gboolean
+launch_file (const gchar * file)
+{
+	const char * mime = gnome_vfs_get_file_mime_type (file, NULL, FALSE);
+	gboolean result = FALSE;
+
+	if ((g_file_test (file, G_FILE_TEST_IS_EXECUTABLE)) &&
+	    (g_ascii_strcasecmp (mime, BINARY_EXEC_MIME_TYPE) == 0)) {
+		result = g_spawn_command_line_async (file, NULL);
+	}
+
+	return result;
+}
+
+gchar *
+tracker_search_get_unique_filename (const gchar * path,
+				 const gchar * suffix)
+{
+	const gint num_of_words = 12;
+	gchar	  * words[] = {
+		    "foo",
+		    "bar",
+		    "blah",
+		    "cranston",
+		    "frobate",
+		    "hadjaha",
+		    "greasy",
+		    "hammer",
+		    "eek",
+		    "larry",
+		    "curly",
+		    "moe",
+		    NULL};
+	gchar * retval = NULL;
+	gboolean exists = TRUE;
+
+	while (exists) {
+		gchar * file;
+		gint rnd;
+		gint word;
+
+		rnd = rand ();
+		word = rand () % num_of_words;
+
+		file = g_strdup_printf ("%s-%010x%s",
+					    words [word],
+					    (guint) rnd,
+					    suffix);
+
+		g_free (retval);
+		retval = g_strconcat (path, G_DIR_SEPARATOR_S, file, NULL);
+		exists = g_file_test (retval, G_FILE_TEST_EXISTS);
+		g_free (file);
+	}
+	return retval;
+}
+
+GtkWidget *
+tracker_search_button_new_with_stock_icon (const gchar * string,
+					const gchar * stock_id)
+{
+	GtkWidget * align;
+	GtkWidget * button;
+	GtkWidget * hbox;
+	GtkWidget * image;
+	GtkWidget * label;
+
+	button = gtk_button_new ();
+	label = gtk_label_new_with_mnemonic (string);
+	gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (button));
+	image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
+	hbox = gtk_hbox_new (FALSE, 2);
+	align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
+	gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
+	gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+	gtk_container_add (GTK_CONTAINER (button), align);
+	gtk_container_add (GTK_CONTAINER (align), hbox);
+	gtk_widget_show_all (align);
+
+	return button;
+}
+
+GSList *
+tracker_search_get_columns_order (GtkTreeView * treeview)
+{
+	GSList *order = NULL;
+	GList * columns;
+	GList * col;
+
+	columns = gtk_tree_view_get_columns (treeview);
+
+	for (col = columns; col; col = col->next) {
+		gint id;
+
+		id = gtk_tree_view_column_get_sort_column_id (col->data);
+		order = g_slist_prepend (order, GINT_TO_POINTER (id));
+	}
+	g_list_free (columns);
+
+	order = g_slist_reverse (order);
+	return order;
+}
+
+static GtkTreeViewColumn *
+get_column_with_sort_column_id (GtkTreeView * treeview,
+				gint id)
+{
+	GtkTreeViewColumn * col = NULL;
+	GList * columns;
+	GList * it;
+
+	columns = gtk_tree_view_get_columns (treeview);
+
+	for (it = columns; it; it = it->next) {
+		if (gtk_tree_view_column_get_sort_column_id (it->data) == id) {
+			col = it->data;
+			break;
+		}
+	}
+	g_list_free (columns);
+	return col;
+}
+
+void
+tracker_search_set_columns_order (GtkTreeView * treeview)
+{
+	GtkTreeViewColumn * last = NULL;
+	GSList * order;
+	GSList * it;
+
+	order = tracker_search_gconf_get_list ("/apps/tracker-search-tool/columns_order", GCONF_VALUE_INT);
+
+	for (it = order; it; it = it->next) {
+
+		GtkTreeViewColumn * cur;
+		gint id;
+
+		id = GPOINTER_TO_INT (it->data);
+
+		if (id >= 0 && id < NUM_COLUMNS) {
+
+			cur = get_column_with_sort_column_id (treeview, id);
+
+			if (cur && cur != last) {
+				gtk_tree_view_move_column_after (treeview, cur, last);
+				last = cur;
+			}
+		}
+	}
+	g_slist_free (order);
+}
+
+gint
+tracker_get_stored_separator_position (void)
+{
+	gint saved_pos;
+
+	saved_pos = tracker_search_gconf_get_int ("/apps/tracker-search-tool/separator_position");
+
+	if (saved_pos < 1) {
+		return DEFAULT_SEPARATOR_POSITION;
+	}
+
+	return saved_pos;
+}
+
+void
+tracker_set_stored_separator_position (gint pos)
+{
+	tracker_search_gconf_set_int ("/apps/tracker-search-tool/separator_position", pos);
+}
+
+void
+tracker_search_get_stored_window_geometry (gint * width,
+					   gint * height)
+{
+	gint saved_width;
+	gint saved_height;
+
+	if (width == NULL || height == NULL) {
+		return;
+	}
+
+	saved_width = tracker_search_gconf_get_int ("/apps/tracker-search-tool/default_window_width");
+	saved_height = tracker_search_gconf_get_int ("/apps/tracker-search-tool/default_window_height");
+
+	if (saved_width < 1) {
+		saved_width = DEFAULT_WINDOW_WIDTH;
+	}
+
+	if (saved_height < 1) {
+		saved_height = DEFAULT_WINDOW_HEIGHT;
+	}
+
+	*width = saved_width;
+	*height = saved_height;
+}
+
+void
+tracker_set_atk_relationship(GtkWidget *obj1, int relation_type,
+			     GtkWidget *obj2)
+{
+	AtkObject *atk_obj1, *atk_obj2, *targets[1];
+	AtkRelationSet *atk_rel_set;
+	AtkRelation *atk_rel;
+
+	atk_obj1 = gtk_widget_get_accessible (GTK_WIDGET (obj1));
+	atk_obj2 = gtk_widget_get_accessible (GTK_WIDGET (obj2));
+	atk_rel_set = atk_object_ref_relation_set (atk_obj1);
+	targets[0] = atk_obj2;
+	atk_rel = atk_relation_new (targets, 1, relation_type);
+	atk_relation_set_add (atk_rel_set, atk_rel);
+	g_object_unref (G_OBJECT (atk_rel));
+}

Added: trunk/src/tracker-search-tool/tracker-search-tool-support.h
==============================================================================
--- (empty file)
+++ trunk/src/tracker-search-tool/tracker-search-tool-support.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,186 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * GNOME Search Tool
+ *
+ *  File:  tracker_search-support.h
+ *
+ *  (C) 2002 the Free Software Foundation
+ *
+ *  Authors:	Dennis Cranston  <dennis_cranston yahoo com>
+ *		George Lebl
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef _GSEARCHTOOL_SUPPORT_H_
+#define _GSEARCHTOOL_SUPPORT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif
+
+#include "tracker-search-tool.h"
+
+#define ICON_SIZE 40
+#define ICON_THEME_EXECUTABLE_ICON "application-x-executable"
+#define ICON_THEME_REGULAR_ICON    "gnome-fs-regular"
+#define ICON_THEME_CHAR_DEVICE	   "gnome-fs-chardev"
+#define ICON_THEME_BLOCK_DEVICE    "gnome-fs-blockdev"
+#define ICON_THEME_SOCKET	   "gnome-fs-socket"
+#define ICON_THEME_FIFO		   "gnome-fs-fifo"
+
+
+gboolean
+tracker_is_empty_string (const gchar *s);
+
+gboolean
+tracker_search_gconf_get_boolean (const gchar * key);
+
+void
+tracker_search_gconf_set_boolean (const gchar * key,
+				  const gboolean flag);
+
+gint
+tracker_search_gconf_get_int (const gchar * key);
+
+void
+tracker_search_gconf_set_int (const gchar * key,
+			      const gint value);
+
+gchar *
+tracker_search_gconf_get_string (const gchar * key);
+
+GSList *
+tracker_search_gconf_get_list (const gchar * key,
+			       GConfValueType list_type);
+
+void
+tracker_search_gconf_set_list (const gchar * key,
+			       GSList * list,
+			       GConfValueType list_type);
+
+void
+tracker_search_gconf_add_dir (const gchar * dir);
+
+void
+tracker_search_gconf_watch_key (const gchar * dir,
+				const gchar * key,
+				GConfClientNotifyFunc callback,
+				gpointer user_data);
+
+
+gboolean
+is_path_hidden (const gchar * path);
+
+gboolean
+is_quick_search_excluded_path (const gchar * path);
+
+gboolean
+is_second_scan_excluded_path (const gchar * path);
+
+gboolean
+compare_regex (const gchar * regex,
+	       const gchar * string);
+
+gboolean
+limit_string_to_x_lines (GString * string,
+			 gint x);
+
+gchar *
+tracker_string_replace (const gchar * haystack,
+			gchar * needle,
+			gchar * replacement);
+
+gchar *
+escape_single_quotes (const gchar * string);
+
+gchar *
+backslash_special_characters (const gchar * string);
+
+gchar *
+remove_mnemonic_character (const gchar * string);
+
+
+gchar *
+get_readable_date (const gchar * format_string,
+		   const time_t file_time_raw);
+
+gchar *
+tracker_search_strdup_strftime (const gchar * format,
+				struct tm * time_pieces);
+
+gchar *
+get_file_type_description (const gchar * file,
+			   const char *mime,
+			   GnomeVFSFileInfo * file_info);
+
+GdkPixbuf *
+get_file_pixbuf (GSearchWindow * gsearch,
+		 const gchar * file,
+		 const char * mime,
+		 GnomeVFSFileInfo * file_info);
+
+
+gboolean
+open_file_with_xdg_open (GtkWidget * window,
+			 const gchar * file);
+
+gboolean
+open_file_with_nautilus (GtkWidget * window,
+			 const gchar * file);
+
+gboolean
+open_file_with_application (GtkWidget * window,
+			    const gchar * file);
+
+gboolean
+launch_file (const gchar * file);
+
+gchar *
+tracker_search_get_unique_filename (const gchar * path,
+				    const gchar * suffix);
+
+GtkWidget *
+tracker_search_button_new_with_stock_icon (const gchar * string,
+					   const gchar * stock_id);
+
+GSList *
+tracker_search_get_columns_order (GtkTreeView * treeview);
+
+void
+tracker_search_set_columns_order (GtkTreeView * treeview);
+
+gint
+tracker_get_stored_separator_position (void);
+
+void
+tracker_set_stored_separator_position (gint pos);
+
+void
+tracker_search_get_stored_window_geometry (gint * width,
+					   gint * height);
+
+void
+tracker_set_atk_relationship (GtkWidget *obj1,
+			      int relation_type,
+			      GtkWidget *obj2);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GSEARCHTOOL_SUPPORT_H */

Added: trunk/src/tracker-search-tool/tracker-search-tool.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-search-tool/tracker-search-tool.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,2276 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * TRACKER Search Tool - modfied from Gnome search tool
+ *
+ *  File:  tracker_search_tool.c
+ *  (C) Me Jamie McCracken
+ *
+ *  Original Copyright:
+ *  (C) 1998,2002 the Free Software Foundation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <config.h>
+
+#include <fnmatch.h>
+#ifndef FNM_CASEFOLD
+#  define FNM_CASEFOLD 0
+#endif
+
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <glib/gi18n.h>
+#include <gdk/gdkcursor.h>
+#include <libgnomevfs/gnome-vfs-mime.h>
+#include <libgnomevfs/gnome-vfs-ops.h>
+#include <libgnomevfs/gnome-vfs-utils.h>
+#include <gtk/gtk.h>
+
+
+
+#include "tracker-search-tool.h"
+#include "tracker-search-tool-callbacks.h"
+#include "tracker-search-tool-support.h"
+#include "sexy-icon-entry.h"
+#include "../libtracker-gtk/tracker-metadata-tile.h"
+
+#define TRACKER_SEARCH_TOOL_DEFAULT_ICON_SIZE 32
+#define TRACKER_SEARCH_TOOL_STOCK "panel-searchtool"
+#define TRACKER_SEARCH_TOOL_REFRESH_DURATION  50000
+#define LEFT_LABEL_SPACING "	 "
+
+static GObjectClass * parent_class;
+static TrackerClient *tracker_client;
+
+static gchar **terms = NULL;
+static gchar *service = NULL;
+
+static GOptionEntry options[] = {
+	{"service", 's', 0, G_OPTION_ARG_STRING, &service, N_("Search from a specific service"), N_("SERVICE")},
+	{G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &terms, "search terms", NULL},
+	{NULL}
+};
+
+typedef enum {
+	SEARCH_CONSTRAINT_TYPE_BOOLEAN,
+	SEARCH_CONSTRAINT_TYPE_NUMERIC,
+	SEARCH_CONSTRAINT_TYPE_TEXT,
+	SEARCH_CONSTRAINT_TYPE_DATE_BEFORE,
+	SEARCH_CONSTRAINT_TYPE_DATE_AFTER,
+	SEARCH_CONSTRAINT_TYPE_SEPARATOR,
+	SEARCH_CONSTRAINT_TYPE_NONE
+} GSearchConstraintType;
+
+typedef struct _GSearchOptionTemplate GSearchOptionTemplate;
+
+
+
+typedef struct {
+	GSearchWindow * gsearch;
+	char  *uri;
+	ServiceType type;
+
+} SnippetRow;
+
+
+struct _GSearchOptionTemplate {
+	GSearchConstraintType type; /* The available option type */
+	gchar * option;		    /* An option string to pass to the command */
+	gchar * desc;		    /* The description for display */
+	gchar * units;		    /* Optional units for display */
+	gboolean is_selected;
+};
+
+
+static char *search_service_types[] = {
+"Files",
+"Folders",
+"Documents",
+"Images",
+"Music",
+"Videos",
+"Text",
+"Development",
+"Other",
+"VFS",
+"VFSFolders",
+"VFSDocuments",
+"VFSImages",
+"VFSMusic",
+"VFSVideos",
+"VFSText",
+"VFSDevelopment",
+"VFSOther",
+"Conversations",
+"Playlists",
+"Applications",
+"Contacts",
+"Emails",
+"EmailAttachments",
+"EvolutionEmails",
+"ModestEmails",
+"ThunderbirdEmails",
+"Appointments",
+"Tasks",
+"Bookmarks",
+"WebHistory",
+"Projects",
+NULL
+};
+
+static service_info_t services[16] = {
+	{ "Emails",	   N_("Emails"),       "stock_mail",		   NULL, SERVICE_EMAILS,	    NULL, FALSE, 0, 0},
+	{ "EvolutionEmails",
+			   N_("Emails"),       "stock_mail",		   NULL, SERVICE_EMAILS,	    NULL, FALSE, 0, 0},
+	{ "ModestEmails",  N_("Emails"),       "stock_mail",		   NULL, SERVICE_EMAILS,	    NULL, FALSE, 0, 0},
+	{ "ThunderbirdEmails",
+			   N_("Emails"),       "stock_mail",		   NULL, SERVICE_EMAILS,	    NULL, FALSE, 0, 0},
+	{ "Files",	   N_("All Files"),    "system-file-manager",	   NULL, SERVICE_FILES,		    NULL, FALSE, 0, 0},
+	{ "Folders",	   N_("Folders"),      "folder",		   NULL, SERVICE_FOLDERS,	    NULL, FALSE, 0, 0},
+	{ "Documents",	   N_("Documents"),    "x-office-document",	   NULL, SERVICE_DOCUMENTS,	    NULL, FALSE, 0, 0},
+	{ "Images",	   N_("Images"),       "image-x-generic",	   NULL, SERVICE_IMAGES,	    NULL, FALSE, 0, 0},
+	{ "Music",	   N_("Music"),        "audio-x-generic",	   NULL, SERVICE_MUSIC,		    NULL, FALSE, 0, 0},
+	{ "Videos",	   N_("Videos"),       "video-x-generic",	   NULL, SERVICE_VIDEOS,	    NULL, FALSE, 0, 0},
+	{ "Text",	   N_("Text"),	       "text-x-generic",	   NULL, SERVICE_TEXT_FILES,	    NULL, FALSE, 0, 0},
+	{ "Development",   N_("Development"),  "applications-development", NULL, SERVICE_DEVELOPMENT_FILES, NULL, FALSE, 0, 0},
+	{ "Conversations", N_("Chat Logs"),    "stock_help-chat",	   NULL, SERVICE_CONVERSATIONS,     NULL, FALSE, 0, 0},
+	{ "Applications",  N_("Applications"), "system-run",		   NULL, SERVICE_APPLICATIONS,	    NULL, FALSE, 0, 0},
+	{ "WebHistory",    N_("WebHistory"),	"text-html",		   NULL, SERVICE_WEBHISTORY,	    NULL, FALSE, 0, 0},
+	{ NULL,		   NULL,	       NULL,			   NULL, -1,			    NULL, FALSE, 0, 0},
+};
+
+static GSearchOptionTemplate GSearchOptionTemplates[] = {
+	{ SEARCH_CONSTRAINT_TYPE_TEXT, NULL, "Contains the _text", NULL, FALSE },
+	{ SEARCH_CONSTRAINT_TYPE_SEPARATOR, NULL, NULL, NULL, TRUE },
+	{ SEARCH_CONSTRAINT_TYPE_DATE_BEFORE, "-mtime -%d", "_Date modified less than", "days", FALSE },
+	{ SEARCH_CONSTRAINT_TYPE_DATE_AFTER, "\\( -mtime +%d -o -mtime %d \\)", "Date modified more than", "days", FALSE },
+	{ SEARCH_CONSTRAINT_TYPE_SEPARATOR, NULL, NULL, NULL, TRUE },
+	{ SEARCH_CONSTRAINT_TYPE_NUMERIC, "\\( -size %uc -o -size +%uc \\)", "S_ize at least", "kilobytes", FALSE },
+	{ SEARCH_CONSTRAINT_TYPE_NUMERIC, "\\( -size %uc -o -size -%uc \\)", "Si_ze at most", "kilobytes", FALSE },
+	{ SEARCH_CONSTRAINT_TYPE_BOOLEAN, "-size 0c \\( -type f -o -type d \\)", "File is empty", NULL, FALSE },
+	{ SEARCH_CONSTRAINT_TYPE_SEPARATOR, NULL, NULL, NULL, TRUE },
+	{ SEARCH_CONSTRAINT_TYPE_TEXT, "-user '%s'", "Owned by _user", NULL, FALSE },
+	{ SEARCH_CONSTRAINT_TYPE_TEXT, "-group '%s'", "Owned by _group", NULL, FALSE },
+	{ SEARCH_CONSTRAINT_TYPE_BOOLEAN, "\\( -nouser -o -nogroup \\)", "Owner is unrecognized", NULL, FALSE },
+	{ SEARCH_CONSTRAINT_TYPE_SEPARATOR, NULL, NULL, NULL, TRUE },
+	{ SEARCH_CONSTRAINT_TYPE_TEXT, "'!' -name '*%s*'", "Na_me does not contain", NULL, FALSE },
+	{ SEARCH_CONSTRAINT_TYPE_TEXT, "-regex '%s'", "Name matches regular e_xpression", NULL, FALSE },
+	{ SEARCH_CONSTRAINT_TYPE_SEPARATOR, NULL, NULL, NULL, TRUE },
+	{ SEARCH_CONSTRAINT_TYPE_BOOLEAN, "SHOW_HIDDEN_FILES", "Show hidden and backup files", NULL, FALSE },
+	{ SEARCH_CONSTRAINT_TYPE_BOOLEAN, "-follow", "Follow symbolic links", NULL, FALSE },
+	{ SEARCH_CONSTRAINT_TYPE_BOOLEAN, "INCLUDE_OTHER_FILESYSTEMS", "Include other filesystems", NULL, FALSE },
+	{ SEARCH_CONSTRAINT_TYPE_NONE, NULL, NULL, NULL, FALSE}
+};
+
+enum {
+	SEARCH_CONSTRAINT_CONTAINS_THE_TEXT,
+	SEARCH_CONSTRAINT_TYPE_SEPARATOR_00,
+	SEARCH_CONSTRAINT_DATE_MODIFIED_BEFORE,
+	SEARCH_CONSTRAINT_DATE_MODIFIED_AFTER,
+	SEARCH_CONSTRAINT_TYPE_SEPARATOR_01,
+	SEARCH_CONSTRAINT_SIZE_IS_MORE_THAN,
+	SEARCH_CONSTRAINT_SIZE_IS_LESS_THAN,
+	SEARCH_CONSTRAINT_FILE_IS_EMPTY,
+	SEARCH_CONSTRAINT_TYPE_SEPARATOR_02,
+	SEARCH_CONSTRAINT_OWNED_BY_USER,
+	SEARCH_CONSTRAINT_OWNED_BY_GROUP,
+	SEARCH_CONSTRAINT_OWNER_IS_UNRECOGNIZED,
+	SEARCH_CONSTRAINT_TYPE_SEPARATOR_03,
+	SEARCH_CONSTRAINT_FILE_IS_NOT_NAMED,
+	SEARCH_CONSTRAINT_FILE_MATCHES_REGULAR_EXPRESSION,
+	SEARCH_CONSTRAINT_TYPE_SEPARATOR_04,
+	SEARCH_CONSTRAINT_SHOW_HIDDEN_FILES_AND_FOLDERS,
+	SEARCH_CONSTRAINT_FOLLOW_SYMBOLIC_LINKS,
+	SEARCH_CONSTRAINT_SEARCH_OTHER_FILESYSTEMS,
+	SEARCH_CONSTRAINT_MAXIMUM_POSSIBLE
+};
+
+static GtkTargetEntry GSearchDndTable[] = {
+	{ "text/uri-list", 0, 1 },
+	{ "text/plain",    0, 0 },
+	{ "STRING",	   0, 0 }
+};
+
+static guint GSearchTotalDnds = sizeof (GSearchDndTable) / sizeof (GSearchDndTable[0]);
+
+
+static GtkActionEntry GSearchUiEntries[] = {
+  { "Open",	     GTK_STOCK_OPEN,	N_("_Open"),		   NULL, NULL, NULL },
+  { "OpenFolder",    GTK_STOCK_OPEN,	N_("O_pen Folder"),	   NULL, NULL, NULL },
+  { "MoveToTrash",   GTK_STOCK_DELETE,	N_("Mo_ve to Trash"),	   NULL, NULL, NULL },
+  { "SaveResultsAs", GTK_STOCK_SAVE_AS, N_("_Save Results As..."), NULL, NULL, NULL },
+};
+
+static const char * GSearchUiDescription =
+"<ui>"
+"  <popup name='PopupMenu'>"
+"      <menuitem action='Open'/>"
+"      <menuitem action='OpenFolder'/>"
+"      <separator/>"
+"      <menuitem action='MoveToTrash'/>"
+"      <separator/>"
+"      <menuitem action='SaveResultsAs'/>"
+"  </popup>"
+"</ui>";
+
+static void set_snippet (gchar * snippet, GError *error, gpointer user_data);
+
+static void
+display_dialog_character_set_conversion_error (GtkWidget * window,
+					       gchar * string,
+					       GError * error)
+{
+	GtkWidget * dialog;
+
+	dialog = gtk_message_dialog_new (GTK_WINDOW (window),
+					 GTK_DIALOG_DESTROY_WITH_PARENT,
+					 GTK_MESSAGE_ERROR,
+					 GTK_BUTTONS_OK,
+					 _("Character set conversion failed for \"%s\""),
+					 string);
+
+	gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+						  (error == NULL) ? " " : error->message);
+
+	gtk_window_set_title (GTK_WINDOW (dialog), "");
+	gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+	gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 14);
+
+	g_signal_connect (G_OBJECT (dialog),
+			  "response",
+			   G_CALLBACK (gtk_widget_destroy), NULL);
+
+	gtk_widget_show (dialog);
+}
+
+static void
+display_error_dialog (GtkWidget * window,
+		      const char *error)
+{
+	GtkWidget * dialog;
+
+	dialog = gtk_message_dialog_new (GTK_WINDOW (window),
+				 GTK_DIALOG_DESTROY_WITH_PARENT,
+				 GTK_MESSAGE_ERROR,
+				 GTK_BUTTONS_OK,
+				 _("The following error has occurred :"));
+
+	gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), error);
+
+	gtk_window_set_title (GTK_WINDOW (dialog), _("Error"));
+	gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+	gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 14);
+
+	g_signal_connect (G_OBJECT (dialog),
+		  "response",
+		   G_CALLBACK (gtk_widget_destroy), NULL);
+
+	gtk_widget_show (dialog);
+}
+
+static void
+start_animation (GSearchWindow * gsearch,
+		 gboolean first_pass)
+{
+	if (first_pass == TRUE) {
+
+		gsearch->focus = gtk_window_get_focus (GTK_WINDOW (gsearch->window));
+
+		gtk_widget_set_sensitive (gsearch->find_button, FALSE);
+		if (gsearch->type < 10) {
+			gtk_widget_set_sensitive (gsearch->search_results_save_results_as_item, FALSE);
+		}
+		gtk_widget_set_sensitive (gsearch->search_results_vbox, TRUE);
+		gtk_widget_set_sensitive (GTK_WIDGET (gsearch->search_results_tree_view), TRUE);
+
+		gtk_widget_set_sensitive (gsearch->name_and_folder_table, FALSE);
+	}
+}
+
+static void
+stop_animation (GSearchWindow * gsearch)
+{
+	gtk_window_set_default (GTK_WINDOW (gsearch->window), gsearch->find_button);
+	gtk_widget_set_sensitive (gsearch->name_and_folder_table, TRUE);
+	gtk_widget_set_sensitive (gsearch->find_button, TRUE);
+	if (gsearch->type < 10) {
+		gtk_widget_set_sensitive (gsearch->search_results_save_results_as_item, TRUE);
+	}
+	gtk_widget_show (gsearch->find_button);
+
+
+	if (gtk_window_get_focus (GTK_WINDOW (gsearch->window)) == NULL) {
+		gtk_window_set_focus (GTK_WINDOW (gsearch->window), gsearch->focus);
+	}
+}
+
+static gboolean
+process_snippets (GSearchWindow * gsearch)
+{
+	if (!gsearch->snippet_queue || g_queue_is_empty (gsearch->snippet_queue)) {
+		return FALSE;
+	}
+
+	SnippetRow *snippet = g_queue_pop_head (gsearch->snippet_queue);
+
+	tracker_search_get_snippet_async (tracker_client, snippet->type, snippet->uri, gsearch->search_term, set_snippet, snippet);
+
+	return FALSE;
+}
+
+gchar *
+build_search_command (GSearchWindow * gsearch,
+		      gboolean first_pass)
+{
+	GString * command;
+	GError * error = NULL;
+	gchar * file_is_named_utf8;
+	gchar * file_is_named_locale;
+
+
+	gsearch->show_thumbnails = TRUE;
+
+	file_is_named_utf8 = g_strdup ((gchar *) gtk_entry_get_text (GTK_ENTRY (gsearch->search_entry)));
+
+	if (!file_is_named_utf8 || !*file_is_named_utf8) {
+		g_free (file_is_named_utf8);
+		file_is_named_utf8 = g_strdup ("");
+
+	} else {
+		gchar * locale;
+
+		locale = g_locale_from_utf8 (file_is_named_utf8, -1, NULL, NULL, &error);
+		if (locale == NULL) {
+
+			display_dialog_character_set_conversion_error (gsearch->window, file_is_named_utf8, error);
+			g_free (file_is_named_utf8);
+			g_error_free (error);
+			return NULL;
+		}
+
+		g_free (locale);
+	}
+
+	file_is_named_locale = g_locale_from_utf8 (file_is_named_utf8, -1, NULL, NULL, &error);
+	if (file_is_named_locale == NULL) {
+
+		display_dialog_character_set_conversion_error (gsearch->window, file_is_named_utf8, error);
+		g_free (file_is_named_utf8);
+		g_error_free (error);
+		return NULL;
+	}
+
+	command = g_string_new (file_is_named_utf8);
+
+
+	gsearch->command_details->is_command_show_hidden_files_enabled = FALSE;
+	gsearch->command_details->name_contains_regex_string = NULL;
+	gsearch->search_results_date_format_string = NULL;
+	gsearch->command_details->name_contains_pattern_string = NULL;
+
+	gsearch->command_details->is_command_first_pass = first_pass;
+	if (gsearch->command_details->is_command_first_pass == TRUE) {
+		gsearch->command_details->is_command_using_quick_mode = FALSE;
+	}
+
+
+	gsearch->show_thumbnails = TRUE;
+	gsearch->show_thumbnails_file_size_limit = tracker_search_gconf_get_int ("/apps/nautilus/preferences/thumbnail_limit");
+
+
+	return g_string_free (command, FALSE);
+}
+
+static void
+free_snippet (SnippetRow * snippet_row)
+{
+	g_free (snippet_row->uri);
+	g_free (snippet_row);
+}
+
+static void
+set_snippet (gchar * snippet,
+	     GError * error,
+	     gpointer user_data)
+{
+	gchar *snippet_markup;
+	GtkTreeIter iter;
+	SnippetRow *snippet_row = user_data;
+
+	g_return_if_fail (error == NULL);
+
+	snippet_markup = g_strdup_printf ("<span size='small'>%s</span>", snippet);
+
+	if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (snippet_row->gsearch->search_results_list_store), &iter)) {
+
+		while (TRUE) {
+
+			gchar *uri;
+
+			gtk_tree_model_get (GTK_TREE_MODEL (snippet_row->gsearch->search_results_list_store), &iter,
+					    COLUMN_URI, &uri,
+					    -1);
+
+			if ( (strcmp (snippet_row->uri, uri) == 0)) {
+				gtk_list_store_set (GTK_LIST_STORE (snippet_row->gsearch->search_results_list_store), &iter, COLUMN_SNIPPET, snippet_markup, -1);
+				g_free (uri);
+				break;
+			} else {
+				g_free (uri);
+
+				if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (snippet_row->gsearch->search_results_list_store), &iter)) {
+					break;
+				}
+			}
+		}
+	}
+
+	if (!g_queue_is_empty (snippet_row->gsearch->snippet_queue)) {
+		g_idle_add ((GSourceFunc) process_snippets, snippet_row->gsearch);
+	}
+
+	free_snippet (snippet_row);
+
+	g_free (snippet_markup);
+}
+
+static void
+add_email_to_search_results (const gchar * uri,
+			     const gchar  * mime,
+			     const gchar  * subject,
+			     const gchar  * sender,
+			     GtkListStore * store,
+			     GtkTreeIter * iter,
+			     GSearchWindow * gsearch)
+{
+	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (gsearch->search_results_tree_view), FALSE);
+
+	gtk_list_store_append (GTK_LIST_STORE (store), iter);
+	gtk_list_store_set (GTK_LIST_STORE (store), iter,
+			    COLUMN_ICON, gsearch->email_pixbuf,
+			    COLUMN_URI, uri,
+			    COLUMN_NAME, subject,
+			    COLUMN_PATH, sender,
+			    COLUMN_MIME, mime,
+			    COLUMN_TYPE, SERVICE_EMAILS,
+			    COLUMN_NO_FILES_FOUND, FALSE,
+			    -1);
+
+	gchar * search_term;
+
+	if (gsearch->search_term) {
+		search_term = gsearch->search_term;
+	} else {
+		search_term = "";
+	}
+
+	SnippetRow * snippet_row;
+
+	snippet_row = g_new (SnippetRow, 1);
+	snippet_row->gsearch = gsearch;
+	snippet_row->uri = g_strdup (uri);
+	snippet_row->type = SERVICE_EMAILS;
+
+	g_queue_push_tail (gsearch->snippet_queue, snippet_row);
+	//tracker_search_get_snippet_async (tracker_client, SERVICE_EMAILS, uri, search_term, set_snippet, snippet_row);
+}
+
+static void
+add_file_to_search_results (const gchar * file,
+			    ServiceType service_type,
+			    const gchar * mime,
+			    GtkListStore * store,
+			    GtkTreeIter * iter,
+			    GSearchWindow * gsearch)
+{
+	GdkPixbuf * pixbuf;
+	GnomeVFSFileInfo * vfs_file_info;
+
+	gchar * description;
+	gchar * base_name;
+	gchar * dir_name;
+	gchar * escape_path_string;
+	gchar * uri;
+
+	uri = g_filename_from_utf8 (file, -1, NULL, NULL, NULL);
+
+	if (!g_file_test (uri, G_FILE_TEST_EXISTS)) {
+		g_warning ("file %s does not exist", file);
+		g_free (uri);
+		return;
+	}
+
+	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (gsearch->search_results_tree_view), FALSE);
+
+	vfs_file_info = gnome_vfs_file_info_new ();
+
+	escape_path_string = gnome_vfs_escape_path_string (uri);
+
+	gnome_vfs_get_file_info (escape_path_string, vfs_file_info,
+				 GNOME_VFS_FILE_INFO_DEFAULT |
+				 GNOME_VFS_FILE_INFO_FOLLOW_LINKS);
+
+
+	pixbuf = get_file_pixbuf (gsearch, uri, mime, vfs_file_info);
+
+	description = get_file_type_description (uri, mime, vfs_file_info);
+
+	if (!description) {
+		description = g_strdup (mime);
+	}
+
+	base_name = g_path_get_basename (file);
+	dir_name = g_path_get_dirname (file);
+
+	gchar * search_term;
+
+	if (gsearch->search_term) {
+		search_term = gsearch->search_term;
+	} else {
+		search_term = NULL;
+	}
+
+	gtk_list_store_append (GTK_LIST_STORE (store), iter);
+	gtk_list_store_set (GTK_LIST_STORE (store), iter,
+			    COLUMN_ICON, pixbuf,
+			    COLUMN_URI, file,
+			    COLUMN_NAME, base_name,
+			    COLUMN_PATH, dir_name,
+			    COLUMN_MIME, (description != NULL) ? description : mime,
+			    COLUMN_TYPE, service_type,
+			    COLUMN_NO_FILES_FOUND, FALSE,
+			    -1);
+
+	if (search_term  &&
+	    (service_type == SERVICE_DOCUMENTS ||
+	     service_type == SERVICE_TEXT_FILES ||
+	     service_type == SERVICE_DEVELOPMENT_FILES ||
+	     gsearch->type == SERVICE_CONVERSATIONS)) {
+
+		SnippetRow * snippet_row;
+
+		snippet_row = g_new (SnippetRow, 1);
+		snippet_row->gsearch = gsearch;
+		snippet_row->uri = g_strdup (uri);
+		snippet_row->type = service_type;
+
+		g_queue_push_tail (gsearch->snippet_queue, snippet_row);
+	}
+
+	gnome_vfs_file_info_unref (vfs_file_info);
+	g_free (base_name);
+	g_free (dir_name);
+	g_free (uri);
+	g_free (escape_path_string);
+	g_free (description);
+}
+
+static void
+add_application_to_search_results (const gchar * uri,
+				   gchar * display_name,
+				   const gchar * exec,
+				   const gchar * icon,
+				   GtkListStore * store,
+				   GtkTreeIter * iter,
+				   GSearchWindow * gsearch)
+{
+	GdkPixbuf * pixbuf = NULL;
+
+	if (!g_file_test (uri, G_FILE_TEST_EXISTS)) {
+		return;
+	}
+
+	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (gsearch->search_results_tree_view), FALSE);
+
+	if (icon && icon[0] && icon[1]) {
+
+		/* if icon is a full path then load it from file otherwise its an icon name in a theme */
+		if (icon[0] == '/') {
+			pixbuf = gdk_pixbuf_new_from_file_at_scale (icon, ICON_SIZE, ICON_SIZE, TRUE, NULL);
+		} else {
+			pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (), icon,
+								     ICON_SIZE, 0, NULL);
+		}
+	}
+
+	if (!pixbuf) {
+		pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (), ICON_THEME_EXECUTABLE_ICON,
+						   ICON_SIZE, 0, NULL);
+	}
+
+
+	gtk_list_store_append (GTK_LIST_STORE (store), iter);
+
+	gtk_list_store_set (GTK_LIST_STORE (store), iter,
+			    COLUMN_ICON, pixbuf,
+			    COLUMN_URI, uri,
+			    COLUMN_NAME, display_name,
+			    COLUMN_PATH, "Application",
+			    COLUMN_MIME, "",
+			    COLUMN_TYPE, SERVICE_APPLICATIONS,
+			    COLUMN_EXEC, exec,
+			    COLUMN_NO_FILES_FOUND, FALSE,
+			    -1);
+}
+
+
+static void
+set_suggestion (gchar * suggestion,
+		GError * error,
+		gpointer user_data)
+{
+	gchar	      * str;
+	GtkWidget     * label;
+	GtkWidget     * box1, * box2;
+	GtkWidget     * button;
+	GSearchWindow * gsearch = user_data;
+	gchar	      * search_term = (gchar *) gtk_entry_get_text (GTK_ENTRY (gsearch->search_entry));
+
+	if (strcmp (search_term, suggestion) == 0) {
+		return;
+	}
+
+	box1 = gtk_hbox_new (FALSE, 0);
+	box2 = gtk_hbox_new (FALSE, 0);
+	label = gtk_label_new (_("Did you mean"));
+	gtk_box_pack_start (GTK_BOX (box2), label, FALSE, TRUE, 0);
+
+	str = g_strconcat ("<b><i><u>", suggestion, "</u></i></b>?", NULL);
+	button = gtk_button_new ();
+	gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+	label = gtk_label_new (NULL);
+	gtk_label_set_markup (GTK_LABEL (label), str);
+	g_free (str);
+	gtk_container_add (GTK_CONTAINER (button), label);
+	gtk_box_pack_start (GTK_BOX (box2), button, FALSE, TRUE, 0);
+	gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, FALSE, 0);
+	gtk_box_pack_start (GTK_BOX (gsearch->no_results), box1, FALSE, FALSE, 12);
+	gtk_widget_show_all (box1);
+
+
+	g_object_set_data (G_OBJECT (button), "suggestion", suggestion);
+	g_signal_connect (G_OBJECT (button), "clicked",
+			  G_CALLBACK (suggest_search_cb), gsearch);
+}
+
+static void
+add_no_files_found_message (GSearchWindow * gsearch)
+{
+	GtkWidget * label;
+	gchar	  * search_term = (gchar *) gtk_entry_get_text (GTK_ENTRY (gsearch->search_entry));
+
+	if (!gsearch->no_results) {
+		gtk_widget_hide (gsearch->search_results_vbox);
+
+		gsearch->no_results = gtk_vbox_new (FALSE, 0);
+		label = gtk_label_new (_("Your search returned no results."));
+		gtk_box_pack_start (GTK_BOX (gsearch->no_results), label, FALSE, FALSE, 12);
+
+		gtk_box_pack_start (GTK_BOX (gsearch->message_box), gsearch->no_results, TRUE, TRUE, 12);
+		gtk_widget_show_all (gsearch->no_results);
+
+		tracker_search_suggest_async (tracker_client, search_term, 3, (TrackerStringReply) set_suggestion, gsearch);
+	}
+}
+
+void
+update_search_counts (GSearchWindow * gsearch)
+{
+	gchar * title_bar_string = NULL;
+
+	title_bar_string = g_strconcat ( _("Tracker Search Tool-"), gsearch->search_term ,NULL);
+	gtk_window_set_title (GTK_WINDOW (gsearch->window), title_bar_string);
+	g_free (title_bar_string);
+}
+
+void
+update_constraint_info (GSearchConstraint * constraint,
+			gchar * info)
+{
+	switch (GSearchOptionTemplates[constraint->constraint_id].type) {
+		case SEARCH_CONSTRAINT_TYPE_TEXT:
+			constraint->data.text = info;
+			break;
+		case SEARCH_CONSTRAINT_TYPE_NUMERIC:
+			sscanf (info, "%d", &constraint->data.number);
+			break;
+		case SEARCH_CONSTRAINT_TYPE_DATE_BEFORE:
+		case SEARCH_CONSTRAINT_TYPE_DATE_AFTER:
+			sscanf (info, "%d", &constraint->data.time);
+			break;
+		default:
+			g_warning ("Entry changed called for a non entry option!");
+			break;
+	}
+}
+
+void
+set_constraint_gconf_boolean (gint constraint_id,
+			      gboolean flag)
+{
+	switch (constraint_id) {
+		case SEARCH_CONSTRAINT_CONTAINS_THE_TEXT:
+			tracker_search_gconf_set_boolean ("/apps/tracker-search-tool/select/contains_the_text",
+						       flag);
+			break;
+		case SEARCH_CONSTRAINT_DATE_MODIFIED_BEFORE:
+			tracker_search_gconf_set_boolean ("/apps/tracker-search-tool/select/date_modified_less_than",
+						       flag);
+			break;
+		case SEARCH_CONSTRAINT_DATE_MODIFIED_AFTER:
+			tracker_search_gconf_set_boolean ("/apps/tracker-search-tool/select/date_modified_more_than",
+						       flag);
+			break;
+		case SEARCH_CONSTRAINT_SIZE_IS_MORE_THAN:
+			tracker_search_gconf_set_boolean ("/apps/tracker-search-tool/select/size_at_least",
+						       flag);
+			break;
+		case SEARCH_CONSTRAINT_SIZE_IS_LESS_THAN:
+			tracker_search_gconf_set_boolean ("/apps/tracker-search-tool/select/size_at_most",
+						       flag);
+			break;
+		case SEARCH_CONSTRAINT_FILE_IS_EMPTY:
+			tracker_search_gconf_set_boolean ("/apps/tracker-search-tool/select/file_is_empty",
+						       flag);
+			break;
+		case SEARCH_CONSTRAINT_OWNED_BY_USER:
+			tracker_search_gconf_set_boolean ("/apps/tracker-search-tool/select/owned_by_user",
+						       flag);
+			break;
+		case SEARCH_CONSTRAINT_OWNED_BY_GROUP:
+			tracker_search_gconf_set_boolean ("/apps/tracker-search-tool/select/owned_by_group",
+						       flag);
+			break;
+		case SEARCH_CONSTRAINT_OWNER_IS_UNRECOGNIZED:
+			tracker_search_gconf_set_boolean ("/apps/tracker-search-tool/select/owner_is_unrecognized",
+						       flag);
+			break;
+		case SEARCH_CONSTRAINT_FILE_IS_NOT_NAMED:
+			tracker_search_gconf_set_boolean ("/apps/tracker-search-tool/select/name_does_not_contain",
+						       flag);
+			break;
+		case SEARCH_CONSTRAINT_FILE_MATCHES_REGULAR_EXPRESSION:
+			tracker_search_gconf_set_boolean ("/apps/tracker-search-tool/select/name_matches_regular_expression",
+						       flag);
+			break;
+		case SEARCH_CONSTRAINT_SHOW_HIDDEN_FILES_AND_FOLDERS:
+			tracker_search_gconf_set_boolean ("/apps/tracker-search-tool/select/show_hidden_files_and_folders",
+						       flag);
+			break;
+		case SEARCH_CONSTRAINT_FOLLOW_SYMBOLIC_LINKS:
+			tracker_search_gconf_set_boolean ("/apps/tracker-search-tool/select/follow_symbolic_links",
+						       flag);
+			break;
+		case SEARCH_CONSTRAINT_SEARCH_OTHER_FILESYSTEMS:
+			tracker_search_gconf_set_boolean ("/apps/tracker-search-tool/select/include_other_filesystems",
+						       flag);
+			break;
+
+		default:
+			break;
+	}
+}
+
+/*
+ * add_atk_namedesc
+ * @widget    : The Gtk Widget for which @name and @desc are added.
+ * @name      : Accessible Name
+ * @desc      : Accessible Description
+ * Description: This function adds accessible name and description to a
+ *		Gtk widget.
+ */
+
+static void
+add_atk_namedesc (GtkWidget * widget,
+		  const gchar * name,
+		  const gchar * desc)
+{
+	AtkObject * atk_widget;
+
+	g_assert (GTK_IS_WIDGET (widget));
+
+	atk_widget = gtk_widget_get_accessible (widget);
+
+	if (name != NULL)
+		atk_object_set_name (atk_widget, name);
+	if (desc !=NULL)
+		atk_object_set_description (atk_widget, desc);
+}
+
+/*
+ * add_atk_relation
+ * @obj1      : The first widget in the relation @rel_type
+ * @obj2      : The second widget in the relation @rel_type.
+ * @rel_type  : Relation type which relates @obj1 and @obj2
+ * Description: This function establishes Atk Relation between two given
+ *		objects.
+ */
+
+/* static void */
+/* add_atk_relation (GtkWidget * obj1, */
+/*		  GtkWidget * obj2, */
+/*		  AtkRelationType rel_type) */
+/* { */
+/*	AtkObject * atk_obj1, * atk_obj2; */
+/*	AtkRelationSet * relation_set; */
+/*	AtkRelation * relation; */
+
+/*	g_assert (GTK_IS_WIDGET (obj1)); */
+/*	g_assert (GTK_IS_WIDGET (obj2)); */
+
+/*	atk_obj1 = gtk_widget_get_accessible (obj1); */
+
+/*	atk_obj2 = gtk_widget_get_accessible (obj2); */
+
+/*	relation_set = atk_object_ref_relation_set (atk_obj1); */
+/*	relation = atk_relation_new (&atk_obj2, 1, rel_type); */
+/*	atk_relation_set_add (relation_set, relation); */
+/*	g_object_unref (G_OBJECT (relation)); */
+
+/* } */
+
+
+gchar *
+get_desktop_item_name (GSearchWindow * gsearch)
+{
+	GString * gs;
+	gchar * file_is_named_utf8;
+	gchar * file_is_named_locale;
+	//GList * list;
+
+	gs = g_string_new ("");
+	g_string_append (gs, _("Tracker Search Tool"));
+	g_string_append (gs, " (");
+
+	file_is_named_utf8 = (gchar *) gtk_entry_get_text (GTK_ENTRY (gsearch->search_entry));
+	file_is_named_locale = g_locale_from_utf8 (file_is_named_utf8 != NULL ? file_is_named_utf8 : "" ,
+						   -1, NULL, NULL, NULL);
+	g_string_append_printf (gs, "named=%s", file_is_named_locale);
+	g_free (file_is_named_locale);
+
+	g_string_append_c (gs, ')');
+	return g_string_free (gs, FALSE);
+}
+
+static gchar *
+crop_string (gchar *str,
+	     gint max_length)
+{
+	gchar buffer[1024];
+	gint len;
+	gchar *s1, *s2;
+
+	g_return_val_if_fail (str && max_length > 0, NULL);
+
+	len = g_utf8_strlen (str, -1);
+
+	if (len < (max_length+3)) {
+		return g_strdup (str);
+	}
+
+	s1 = g_strdup (g_utf8_strncpy (buffer, str, max_length));
+
+	s2 = g_strconcat (s1, "...", NULL);
+
+	g_free (s1);
+
+	return s2;
+}
+
+static void
+filename_cell_data_func (GtkTreeViewColumn * column,
+			 GtkCellRenderer * renderer,
+			 GtkTreeModel * model,
+			 GtkTreeIter * iter,
+			 GSearchWindow * gsearch)
+{
+	GtkTreePath * path;
+	PangoUnderline underline;
+	gboolean underline_set;
+	gchar * markup, * fpath, * name, * type = NULL;
+
+	gtk_tree_model_get (model, iter, COLUMN_NAME, &name, -1);
+	gtk_tree_model_get (model, iter, COLUMN_PATH, &fpath, -1);
+	gtk_tree_model_get (model, iter, COLUMN_MIME, &type, -1);
+
+	gchar * display_name = crop_string (name, 65);
+	gchar * display_path = crop_string (fpath, 120);
+
+	gchar * mark_name = g_markup_escape_text (display_name, -1);
+	gchar * mark_dir =  g_markup_escape_text (display_path, -1);
+
+	markup = g_strconcat ("<b>", mark_name, "</b>\n", "<span  size='small'>", mark_dir,"</span>\n",
+			      "<span  size='small'>",type, "</span>", NULL);
+
+	g_free (display_name);
+	g_free (display_path);
+
+	g_free (mark_name);
+	g_free (mark_dir);
+
+	g_free (fpath);
+	g_free (name);
+	g_free (type);
+
+	if (gsearch->is_search_results_single_click_to_activate == TRUE) {
+
+		path = gtk_tree_model_get_path (model, iter);
+
+		if ((gsearch->search_results_hover_path == NULL) ||
+		    (gtk_tree_path_compare (path, gsearch->search_results_hover_path) != 0)) {
+			underline = PANGO_UNDERLINE_NONE;
+			underline_set = FALSE;
+		}
+		else {
+			underline = PANGO_UNDERLINE_SINGLE;
+			underline_set = TRUE;
+		}
+		gtk_tree_path_free (path);
+
+	} else {
+		underline = PANGO_UNDERLINE_NONE;
+		underline_set = FALSE;
+	}
+
+	g_object_set (renderer,
+		      "markup", markup,
+		      "underline", underline,
+		      "underline-set", underline_set,
+		      NULL);
+
+	g_free (markup);
+}
+
+static void
+snippet_cell_data_func (GtkTreeViewColumn * column,
+			 GtkCellRenderer * renderer,
+			 GtkTreeModel * model,
+			 GtkTreeIter * iter,
+			 GSearchWindow * gsearch)
+{
+	gchar * snippet;
+	gint width;
+
+	gtk_tree_model_get (model, iter, COLUMN_SNIPPET, &snippet, -1);
+
+	g_object_set (renderer,
+		      "markup", snippet,
+		      NULL);
+
+	g_free (snippet);
+
+	/* set length of word wrap to available col size */
+
+	width = gtk_tree_view_column_get_width (column);
+
+	if (width > 20) {
+		g_object_set (renderer, "wrap_width", width - 3, "wrap-mode",  PANGO_WRAP_WORD, NULL);
+	}
+}
+
+static gboolean
+gsearch_equal_func (GtkTreeModel * model,
+		    gint column,
+		    const gchar * key,
+		    GtkTreeIter * iter,
+		    gpointer search_data)
+{
+	gboolean results = TRUE;
+	gchar * name;
+
+	gtk_tree_model_get (model, iter, COLUMN_NAME, &name, -1);
+
+	if (name != NULL) {
+		gchar * casefold_key;
+		gchar * casefold_name;
+
+		casefold_key = g_utf8_casefold (key, -1);
+		casefold_name = g_utf8_casefold (name, -1);
+
+		if ((casefold_key != NULL) &&
+		    (casefold_name != NULL) &&
+		    (strstr (casefold_name, casefold_key) != NULL)) {
+			results = FALSE;
+		}
+		g_free (casefold_key);
+		g_free (casefold_name);
+		g_free (name);
+	}
+	return results;
+}
+
+static GtkWidget *
+create_search_results_section (GSearchWindow * gsearch)
+{
+	GtkWidget * label;
+	GtkWidget * vbox;
+	GtkWidget * hbox;
+	GtkWidget * label_box;
+	GtkWidget * align_box;
+	GtkWidget * image;
+	GtkWidget * button_prev;
+	GtkWidget * button_next;
+
+	GtkWidget * window;
+	GtkTreeViewColumn * column;
+	GtkCellRenderer * renderer;
+
+	vbox = gtk_vbox_new (FALSE, 0);
+
+	hbox = gtk_hbox_new (FALSE, 0);
+	gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
+
+	align_box = gtk_alignment_new (0.0, 1.0, 1.0, 1.0);
+	gtk_alignment_set_padding (GTK_ALIGNMENT (align_box), 18, 3, 0, 0);
+
+	gtk_box_pack_start (GTK_BOX (hbox), align_box, FALSE, TRUE, 0);
+
+	label_box = gtk_hbox_new (FALSE, 0);
+	gtk_container_add (GTK_CONTAINER (align_box), label_box);
+
+
+	label = gtk_label_new_with_mnemonic (_("Search _results: "));
+
+	gtk_box_pack_start (GTK_BOX (label_box), label, FALSE, TRUE, 0);
+	gtk_label_set_justify	(GTK_LABEL (label), GTK_JUSTIFY_LEFT);
+
+	/* Translators: this will appears as "Search results: no search performed" */
+	gsearch->count_label = gtk_label_new (_("no search performed"));
+	gtk_label_set_selectable (GTK_LABEL (gsearch->count_label), TRUE);
+	tracker_set_atk_relationship(gsearch->count_label,
+				     ATK_RELATION_LABELLED_BY,
+				     label);
+	tracker_set_atk_relationship(label, ATK_RELATION_LABEL_FOR,
+				     gsearch->count_label);
+
+	gtk_box_pack_start (GTK_BOX (label_box), gsearch->count_label, FALSE, TRUE, 0);
+
+	button_next = gtk_button_new();
+	gtk_button_set_relief (GTK_BUTTON(button_next), GTK_RELIEF_NONE);
+	image = gtk_image_new_from_stock ("gtk-go-forward", GTK_ICON_SIZE_SMALL_TOOLBAR);
+	//gtk_widget_set_tooltip_text (GTK_BUTTON(button_next), _("Add a meagniful tooltip here"));
+	/*FIXME: maybe add an a11y name for this button*/
+	gtk_container_add (GTK_CONTAINER(button_next), image);
+	gtk_box_pack_end (GTK_BOX (hbox), button_next, FALSE, TRUE, 0);
+
+	button_prev = gtk_button_new();
+	gtk_button_set_relief (GTK_BUTTON(button_prev), GTK_RELIEF_NONE);
+	image = gtk_image_new_from_stock ("gtk-go-back", GTK_ICON_SIZE_SMALL_TOOLBAR);
+	//gtk_widget_set_tooltip_text (GTK_BUTTON(button_prev), _("Add a meagniful tooltip here"));
+	/*FIXME: maybe add an a11y name for this button*/
+	gtk_container_add (GTK_CONTAINER(button_prev), image);
+	gtk_box_pack_end (GTK_BOX (hbox), button_prev, FALSE, TRUE, 0);
+
+	gsearch->back_button = button_prev;
+	g_signal_connect (G_OBJECT (gsearch->back_button), "clicked",
+			  G_CALLBACK (prev_results_cb), (gpointer) gsearch);
+
+
+	gsearch->forward_button = button_next;
+	g_signal_connect (G_OBJECT (gsearch->forward_button), "clicked",
+			  G_CALLBACK (next_results_cb), (gpointer) gsearch);
+
+	gtk_widget_show_all (hbox);
+
+
+	gtk_widget_set_sensitive (gsearch->forward_button, FALSE);
+	gtk_widget_set_sensitive (gsearch->back_button, FALSE);
+
+	gsearch->files_found_label = gtk_label_new (NULL);
+	gtk_box_pack_start (GTK_BOX (label_box), gsearch->files_found_label, FALSE, FALSE, 0);
+
+	window = gtk_scrolled_window_new (NULL, NULL);
+	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (window), GTK_SHADOW_IN);
+	gtk_container_set_border_width (GTK_CONTAINER (window), 0);
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (window),
+					GTK_POLICY_AUTOMATIC,
+					GTK_POLICY_AUTOMATIC);
+
+	gsearch->search_results_tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
+
+	gtk_tree_view_set_headers_visible (gsearch->search_results_tree_view, FALSE);
+	gtk_tree_view_set_search_equal_func (gsearch->search_results_tree_view,
+					     gsearch_equal_func, NULL, NULL);
+	gtk_tree_view_set_rules_hint (gsearch->search_results_tree_view, TRUE);
+
+
+	if (gsearch->is_window_accessible) {
+		add_atk_namedesc (GTK_WIDGET (gsearch->search_results_tree_view), _("List View"), NULL);
+	}
+
+	gsearch->search_results_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (gsearch->search_results_tree_view));
+
+	gtk_tree_selection_set_mode (GTK_TREE_SELECTION (gsearch->search_results_selection),
+				     GTK_SELECTION_MULTIPLE);
+
+	gtk_drag_source_set (GTK_WIDGET (gsearch->search_results_tree_view),
+			     GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
+			     GSearchDndTable, GSearchTotalDnds,
+			     GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_ASK);
+
+	g_signal_connect (G_OBJECT (gsearch->search_results_tree_view),
+			  "drag_data_get",
+			  G_CALLBACK (drag_file_cb),
+			  (gpointer) gsearch);
+
+	g_signal_connect (G_OBJECT (gsearch->search_results_tree_view),
+			  "drag_begin",
+			  G_CALLBACK (drag_begin_file_cb),
+			  (gpointer) gsearch);
+
+	g_signal_connect (G_OBJECT (gsearch->search_results_tree_view),
+			  "event_after",
+			  G_CALLBACK (file_event_after_cb),
+			  (gpointer) gsearch);
+
+	g_signal_connect (G_OBJECT (gsearch->search_results_tree_view),
+			  "button_release_event",
+			  G_CALLBACK (file_button_release_event_cb),
+			  (gpointer) gsearch);
+
+	g_signal_connect (G_OBJECT (gsearch->search_results_tree_view),
+			  "button_press_event",
+			  G_CALLBACK (file_button_press_event_cb),
+			  (gpointer) gsearch->search_results_tree_view);
+
+	g_signal_connect (G_OBJECT (gsearch->search_results_tree_view),
+			  "key_press_event",
+			  G_CALLBACK (file_key_press_event_cb),
+			  (gpointer) gsearch);
+
+	g_signal_connect (G_OBJECT (gsearch->search_results_tree_view),
+			  "motion_notify_event",
+			  G_CALLBACK (file_motion_notify_cb),
+			  (gpointer) gsearch);
+
+	g_signal_connect (G_OBJECT (gsearch->search_results_tree_view),
+			  "leave_notify_event",
+			  G_CALLBACK (file_leave_notify_cb),
+			  (gpointer) gsearch);
+
+	g_signal_connect (G_OBJECT (gsearch->search_results_selection),
+			  "changed",
+			  G_CALLBACK (select_changed_cb),
+			  (gpointer) gsearch);
+
+	gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (gsearch->search_results_tree_view));
+
+	gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (gsearch->search_results_tree_view));
+
+
+	/* metadata_tile */
+	gsearch->metatile = tracker_metadata_tile_new ();
+	//gtk_widget_show (gsearch->metatile);
+	gtk_box_pack_end (GTK_BOX (vbox), gsearch->metatile, FALSE, FALSE, 0);
+
+	gtk_box_pack_end (GTK_BOX (vbox), window, TRUE, TRUE, 0);
+
+	/* create the name column */
+	column = gtk_tree_view_column_new ();
+	gtk_tree_view_column_set_title (column, _("Icon"));
+
+	renderer = gtk_cell_renderer_pixbuf_new ();
+	gtk_tree_view_column_pack_start (column, renderer, FALSE);
+	gtk_tree_view_column_set_attributes (column, renderer,
+					     "pixbuf", COLUMN_ICON,
+					     NULL);
+	gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+	gtk_tree_view_column_set_reorderable (column, TRUE);
+	gtk_tree_view_append_column (GTK_TREE_VIEW (gsearch->search_results_tree_view), column);
+
+	column = gtk_tree_view_column_new ();
+	gtk_tree_view_column_set_title (column, _("Name"));
+	gsearch->search_results_name_cell_renderer = gtk_cell_renderer_text_new ();
+	gtk_tree_view_column_pack_start (column, gsearch->search_results_name_cell_renderer, TRUE);
+
+	gtk_tree_view_column_set_attributes (column, gsearch->search_results_name_cell_renderer,
+					     "text", COLUMN_NAME,
+					     NULL);
+
+	gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+
+	gtk_tree_view_column_set_sort_column_id (column, COLUMN_NAME);
+	gtk_tree_view_column_set_reorderable (column, TRUE);
+
+	gtk_tree_view_column_set_cell_data_func (column, gsearch->search_results_name_cell_renderer,
+						 (GtkTreeCellDataFunc) filename_cell_data_func,
+						 gsearch, NULL);
+
+	gtk_tree_view_append_column (GTK_TREE_VIEW (gsearch->search_results_tree_view), column);
+
+	/* create the snippet column */
+	renderer = gtk_cell_renderer_text_new ();
+
+	column = gtk_tree_view_column_new_with_attributes (_("Text"), renderer,
+							   "text", COLUMN_SNIPPET,
+							   NULL);
+
+	gtk_tree_view_column_set_expand (column, TRUE);
+
+	gtk_tree_view_column_set_cell_data_func (column, renderer,
+						 (GtkTreeCellDataFunc) snippet_cell_data_func,
+						 gsearch, NULL);
+
+	gtk_tree_view_column_set_reorderable (column, TRUE);
+
+	gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
+	gtk_tree_view_column_set_min_width (column, 0);
+	gtk_tree_view_column_set_max_width (column, 10000);
+
+	gtk_tree_view_append_column (GTK_TREE_VIEW (gsearch->search_results_tree_view), column);
+
+//	gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (gsearch->search_results_tree_view), GTK_TREE_VIEW_GRID_LINES_VERTICAL);
+	gtk_tree_view_set_enable_search (GTK_TREE_VIEW (gsearch->search_results_tree_view), FALSE);
+
+	g_signal_connect (G_OBJECT (gsearch->search_results_tree_view),
+			  "columns-changed",
+			  G_CALLBACK (columns_changed_cb),
+			  (gpointer) gsearch);
+
+	return vbox;
+}
+
+static GtkWidget *
+create_sidebar (GSearchWindow * gsearch)
+{
+	GtkWidget * window;
+	GtkTreeViewColumn * column;
+	GtkCellRenderer * renderer;
+
+	GtkWidget *vbox = gtk_vbox_new (FALSE, 11);
+
+	GtkWidget *hbox = gtk_hbox_new (FALSE, 0);
+	gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
+
+	window = gtk_scrolled_window_new (NULL, NULL);
+	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (window), GTK_SHADOW_IN);
+	gtk_container_set_border_width (GTK_CONTAINER (window), 0);
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (window),
+					GTK_POLICY_AUTOMATIC,
+					GTK_POLICY_AUTOMATIC);
+
+	gsearch->category_list =  gtk_tree_view_new ();
+
+	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (gsearch->category_list), FALSE);
+	gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (gsearch->category_list), TRUE);
+
+	if (gsearch->is_window_accessible) {
+		add_atk_namedesc (gsearch->category_list, _("List View"), NULL);
+	}
+
+	gsearch->category_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (gsearch->category_list));
+
+	gtk_tree_selection_set_mode (GTK_TREE_SELECTION (gsearch->category_selection),
+				     GTK_SELECTION_BROWSE);
+
+	g_signal_connect (G_OBJECT (gsearch->category_selection),
+			  "changed",
+			  G_CALLBACK (category_changed_cb),
+			  (gpointer) gsearch);
+
+	gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (gsearch->category_list));
+
+	/* create the  columns */
+	column = gtk_tree_view_column_new ();
+
+	gtk_tree_view_column_set_title (column, _("_Categories"));
+	gsearch->category_name_cell_renderer = gtk_cell_renderer_text_new ();
+	gtk_tree_view_column_pack_end (column, gsearch->category_name_cell_renderer, TRUE);
+
+	gtk_tree_view_column_set_attributes (column, gsearch->category_name_cell_renderer,
+					     "text", CATEGORY_TITLE,
+					     NULL);
+
+	gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+
+
+
+	renderer = gtk_cell_renderer_pixbuf_new ();
+	gtk_tree_view_column_pack_start (column, renderer, FALSE);
+	gtk_tree_view_column_set_attributes (column, renderer,
+					     "pixbuf", CATEGORY_ICON_NAME,
+					     NULL);
+
+	gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+
+	gtk_tree_view_append_column (GTK_TREE_VIEW (gsearch->category_list), column);
+
+
+	gtk_tree_view_set_model (GTK_TREE_VIEW (gsearch->category_list), GTK_TREE_MODEL (gsearch->category_store));
+
+	gtk_box_pack_end (GTK_BOX (vbox), window, TRUE, TRUE, 0);
+
+	gtk_tree_view_set_enable_search (GTK_TREE_VIEW (gsearch->category_list), FALSE);
+	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (gsearch->category_list), TRUE);
+
+	return vbox;
+}
+
+static void
+register_tracker_search_icon (GtkIconFactory * factory)
+{
+	GtkIconSource * source;
+	GtkIconSet * icon_set;
+
+	source = gtk_icon_source_new ();
+
+	gtk_icon_source_set_icon_name (source, TRACKER_SEARCH_TOOL_ICON);
+
+	icon_set = gtk_icon_set_new ();
+	gtk_icon_set_add_source (icon_set, source);
+
+	gtk_icon_factory_add (factory, TRACKER_SEARCH_TOOL_STOCK, icon_set);
+
+	gtk_icon_set_unref (icon_set);
+
+	gtk_icon_source_free (source);
+}
+
+static void
+tracker_search_init_stock_icons (void)
+{
+	GtkIconFactory * factory;
+	GtkIconSize tracker_search_icon_size;
+
+	tracker_search_icon_size = gtk_icon_size_register ("panel-menu",
+							TRACKER_SEARCH_TOOL_DEFAULT_ICON_SIZE,
+							TRACKER_SEARCH_TOOL_DEFAULT_ICON_SIZE);
+
+	factory = gtk_icon_factory_new ();
+	gtk_icon_factory_add_default (factory);
+
+	register_tracker_search_icon (factory);
+
+	g_object_unref (factory);
+}
+
+void
+set_clone_command (GSearchWindow * gsearch,
+		   gint * argcp,
+		   gchar *** argvp,
+		   gpointer client_data,
+		   gboolean escape_values)
+{
+	gchar ** argv;
+	gchar * file_is_named_utf8;
+	gchar * file_is_named_locale;
+	gchar * tmp;
+	gint  i = 0;
+
+	argv = g_new0 (gchar*, SEARCH_CONSTRAINT_MAXIMUM_POSSIBLE);
+
+	argv[i++] = (gchar *) client_data;
+
+	file_is_named_utf8 = (gchar *) gtk_entry_get_text (GTK_ENTRY (gsearch->search_entry));
+	file_is_named_locale = g_locale_from_utf8 (file_is_named_utf8 != NULL ? file_is_named_utf8 : "" ,
+						   -1, NULL, NULL, NULL);
+	if (escape_values) {
+		tmp = g_shell_quote (file_is_named_locale);
+	} else {
+		tmp = g_strdup (file_is_named_locale);
+	}
+	argv[i++] = g_strdup_printf ("--named=%s", tmp);
+	g_free (tmp);
+	g_free (file_is_named_locale);
+
+	*argvp = argv;
+	*argcp = i;
+}
+
+static void
+tracker_search_ui_manager (GSearchWindow * gsearch)
+{
+	GtkActionGroup * action_group;
+	GtkAccelGroup * accel_group;
+	GtkAction * action;
+	GError * error = NULL;
+
+	action_group = gtk_action_group_new ("PopupActions");
+	gtk_action_group_set_translation_domain (action_group, NULL);
+	gtk_action_group_add_actions (action_group, GSearchUiEntries, G_N_ELEMENTS (GSearchUiEntries), gsearch->window);
+
+	gsearch->window_ui_manager = gtk_ui_manager_new ();
+	gtk_ui_manager_insert_action_group (gsearch->window_ui_manager, action_group, 0);
+
+	accel_group = gtk_ui_manager_get_accel_group (gsearch->window_ui_manager);
+	gtk_window_add_accel_group (GTK_WINDOW (gsearch->window), accel_group);
+
+	if (!gtk_ui_manager_add_ui_from_string (gsearch->window_ui_manager, GSearchUiDescription, -1, &error)) {
+		g_message ("Building menus failed: %s", error->message);
+		g_error_free (error);
+		exit (EXIT_FAILURE);
+	}
+
+	action = gtk_ui_manager_get_action (gsearch->window_ui_manager, "/PopupMenu/Open");
+	g_signal_connect (G_OBJECT (action),
+			  "activate",
+			  G_CALLBACK (open_file_cb),
+			  (gpointer) gsearch);
+
+	action = gtk_ui_manager_get_action (gsearch->window_ui_manager, "/PopupMenu/OpenFolder");
+	g_signal_connect (G_OBJECT (action),
+			  "activate",
+			  G_CALLBACK (open_folder_cb),
+			  (gpointer) gsearch);
+
+	action = gtk_ui_manager_get_action (gsearch->window_ui_manager, "/PopupMenu/MoveToTrash");
+	g_signal_connect (G_OBJECT (action),
+			  "activate",
+			  G_CALLBACK (move_to_trash_cb),
+			  (gpointer) gsearch);
+
+	action = gtk_ui_manager_get_action (gsearch->window_ui_manager, "/PopupMenu/SaveResultsAs");
+	g_signal_connect (G_OBJECT (action),
+			  "activate",
+			  G_CALLBACK (show_file_selector_cb),
+			  (gpointer) gsearch);
+
+	gsearch->search_results_popup_menu = gtk_ui_manager_get_widget (gsearch->window_ui_manager,
+									"/PopupMenu");
+	gsearch->search_results_save_results_as_item = gtk_ui_manager_get_widget (gsearch->window_ui_manager,
+										  "/PopupMenu/SaveResultsAs");
+}
+
+static void
+gsearch_window_size_allocate (GtkWidget * widget,
+			      GtkAllocation * allocation,
+			      GSearchWindow * gsearch)
+{
+	if (gsearch->is_window_maximized == FALSE) {
+		gsearch->window_width = allocation->width;
+		gsearch->window_height = allocation->height;
+	}
+}
+
+static void
+get_meta_table_data (gpointer value,
+		     gpointer data)
+{
+	gchar **meta;
+	GSearchWindow * gsearch = data;
+
+	meta = (char **)value;
+
+	if (gsearch->type == SERVICE_EMAILS) {
+
+		if (meta[0] && meta[1] && meta[2]) {
+			gchar * subject = "Unknown email subject", * sender = "Unknown email sender";
+
+			if (meta[3]) {
+				subject = meta[3];
+				if (meta[4]) {
+					sender = meta[4];
+				}
+			}
+
+			add_email_to_search_results (meta[0], meta[2],
+						     subject, sender, gsearch->search_results_list_store, &gsearch->search_results_iter, gsearch);
+		}
+
+	} else {
+		if (meta[0] && meta[1] && meta[2]) {
+
+			if (gsearch->type == SERVICE_APPLICATIONS) {
+
+				if (!meta[3] || !meta[4]) {
+					return;
+				}
+
+				gchar *icon=NULL, *exec = meta[4], *name = meta[3];
+
+				if (meta[5]) {
+					icon = meta[5];
+				}
+
+				add_application_to_search_results (meta[0], name, exec, icon,
+								   gsearch->search_results_list_store, &gsearch->search_results_iter, gsearch);
+
+			} else {
+				add_file_to_search_results (meta[0], tracker_service_name_to_type (meta[1]), meta[2],
+							    gsearch->search_results_list_store, &gsearch->search_results_iter, gsearch);
+			}
+		}
+	}
+
+}
+
+static gint
+str_in_array (const gchar *str,
+	      gchar **array)
+{
+	gint  i;
+	gchar **st;
+
+	for (i = 0, st = array; *st; st++, i++) {
+		if (strcasecmp (*st, str) == 0) {
+			return i;
+		}
+	}
+
+	return -1;
+}
+
+static void
+populate_hit_counts (gpointer value,
+		     gpointer data)
+
+{
+	gchar **meta;
+	gint type;
+	service_info_t *service;
+
+	meta = (char **)value;
+
+	if (meta[0] && meta[1]) {
+
+		type = str_in_array (meta[0], (char**) search_service_types);
+
+		if (type != -1) {
+			for (service = services; service->service; ++service) {
+				if (strcmp(service->service,meta[0]) == 0) {
+					service->hit_count = atoi (meta[1]);
+					break;
+				}
+			}
+		}
+	}
+}
+
+static void
+update_page_count_label (GSearchWindow * gsearch)
+{
+	gint from, to, count;
+	gchar * label_str;
+
+	count = gsearch->current_service->hit_count;
+	from = gsearch->current_service->offset+1;
+
+	if (MAX_SEARCH_RESULTS + from > count) {
+		to = count;
+	} else {
+		to = MAX_SEARCH_RESULTS + from -1;
+	}
+
+	if (count > 5) {
+		/* Translators: this will appear like "Search results: 5 - 10 of 30 items" */
+		label_str = g_strdup_printf (_("%d - %d of %d items"), from, to, count);
+	} else
+		/* Translators: this will appear like "Search results: 7 items" */
+		label_str = g_strdup_printf (ngettext ("%d item", "%d items", count), count);
+
+	gtk_label_set_text (GTK_LABEL (gsearch->count_label), label_str);
+	g_free (label_str);
+
+	if (gsearch->current_service->hit_count < gsearch->current_service->offset + 1 + MAX_SEARCH_RESULTS) {
+		gtk_widget_set_sensitive (gsearch->forward_button, FALSE);
+	} else {
+		gtk_widget_set_sensitive (gsearch->forward_button, TRUE);
+	}
+
+	if (gsearch->current_service->offset > 0) {
+		gtk_widget_set_sensitive (gsearch->back_button, TRUE);
+	} else {
+		gtk_widget_set_sensitive (gsearch->back_button, FALSE);
+	}
+}
+
+static void
+init_tab (GSearchWindow * gsearch,
+	  service_info_t * service)
+{
+	gsearch->search_results_list_store = service->store;
+	gtk_tree_view_set_model (gsearch->search_results_tree_view, GTK_TREE_MODEL (service->store));
+
+	update_page_count_label (gsearch);
+
+	GtkAction * action = gtk_ui_manager_get_action (gsearch->window_ui_manager, "/PopupMenu/OpenFolder");
+	gtk_action_set_sensitive (action, (gsearch->type < 10));
+
+	action = gtk_ui_manager_get_action (gsearch->window_ui_manager, "/PopupMenu/MoveToTrash");
+	gtk_action_set_sensitive (action, (gsearch->type < 10));
+
+	action = gtk_ui_manager_get_action (gsearch->window_ui_manager, "/PopupMenu/SaveResultsAs");
+	gtk_action_set_sensitive (action, (gsearch->type < 10));
+}
+
+static void
+get_hit_count (GPtrArray *out_array,
+	       GError *error,
+	       gpointer user_data)
+{
+	service_info_t	*service;
+	gboolean	first_service = FALSE, has_hits = FALSE;
+
+	GSearchWindow *gsearch = user_data;
+
+	if (error) {
+		display_error_dialog (gsearch->window, _("Could not connect to search service as it may be busy"));
+		g_error_free (error);
+		return;
+	}
+
+	if (out_array) {
+		g_ptr_array_foreach (out_array, (GFunc) populate_hit_counts, NULL);
+		g_ptr_array_free (out_array, TRUE);
+		out_array = NULL;
+	}
+
+	/* reset and create categories with hits > 0 */
+
+	gtk_list_store_clear (gsearch->category_store);
+
+
+	for (service = services; service->service; ++service) {
+
+		if (service->hit_count == 0) {
+			continue;
+		}
+
+		has_hits = TRUE;
+
+		gtk_list_store_append (gsearch->category_store, &gsearch->category_iter);
+
+		gchar * label_tmp = g_strdup (_(service->display_name));
+		gchar * label_str = g_strdup_printf ("%s (%d)", label_tmp, service->hit_count);
+		g_free (label_tmp);
+
+
+		gtk_list_store_set (gsearch->category_store, &gsearch->category_iter,
+				    CATEGORY_ICON_NAME, service->pixbuf,
+				    CATEGORY_TITLE, label_str,
+				    CATEGORY_SERVICE, service->service,
+				    -1);
+
+		g_free (label_str);
+
+		if (gsearch->old_type == service->service_type) {
+			first_service = TRUE;
+			gsearch->current_service = service;
+			gsearch->type = service->service_type;
+			init_tab (gsearch, service);
+		}
+	}
+
+	if (!first_service) {
+
+		if (!has_hits) {
+
+			add_no_files_found_message (gsearch);
+			gsearch->page_setup_mode = FALSE;
+			gsearch->current_service = NULL;
+			gsearch->type = -1;
+			stop_animation (gsearch);
+			tracker_update_metadata_tile (gsearch);
+			return;
+		}
+
+		/* old category not found so go to first one with hits */
+		for (service = services; service->service; ++service) {
+			if (service->hit_count == 0) {
+				continue;
+			}
+
+			gsearch->current_service = service;
+			gsearch->type = service->service_type;
+			gsearch->old_type = gsearch->type;
+			init_tab (gsearch, service);
+
+			break;
+		}
+	}
+
+	gsearch->page_setup_mode = FALSE;
+
+	do_search (gsearch, gsearch->search_term, TRUE, 0);
+}
+
+void
+select_category (GtkTreeSelection * treeselection,
+		 gpointer user_data)
+{
+	GSearchWindow * gsearch = user_data;
+	GtkTreeIter iter;
+	gchar * name;
+
+	if (gtk_tree_selection_count_selected_rows (GTK_TREE_SELECTION (gsearch->category_selection)) == 0) {
+		return;
+	}
+
+	GtkTreeModel * model = gtk_tree_view_get_model (GTK_TREE_VIEW (gsearch->category_list));
+
+	gtk_tree_selection_get_selected (GTK_TREE_SELECTION (gsearch->category_selection),
+					 &model,
+					 &iter);
+
+	gtk_tree_model_get (model, &iter, 2, &name, -1);
+
+	service_info_t * service = g_hash_table_lookup (gsearch->category_table, name);
+
+	g_free (name);
+
+	if (!service) {
+		return;
+	}
+
+	gsearch->current_service = service;
+	gsearch->type = service->service_type;
+
+	g_queue_foreach (gsearch->snippet_queue, (GFunc) free_snippet, NULL);
+	g_queue_free (gsearch->snippet_queue);
+	gsearch->snippet_queue = g_queue_new ();
+
+	init_tab (gsearch, gsearch->current_service);
+
+	gsearch->old_type = gsearch->type;
+
+	do_search (gsearch, gsearch->search_term, FALSE, service->offset);
+}
+
+void
+start_new_search (GSearchWindow * gsearch,
+		  const gchar * query)
+{
+	service_info_t	* service;
+
+	if (tracker_is_empty_string (query)) {
+		return;
+	}
+
+	gtk_widget_set_sensitive (gsearch->category_list, TRUE);
+
+	/* yes, we are comparing pointer addresses here */
+	if (gsearch->search_term && gsearch->search_term != query) {
+		g_free (gsearch->search_term);
+		gsearch->search_term = NULL;
+	}
+
+	if (gsearch->search_term == NULL) {
+		gsearch->search_term = g_strdup (query);
+	}
+
+	gsearch->page_setup_mode = TRUE;
+
+	gtk_widget_show (gsearch->search_results_vbox);
+
+	if (gsearch->no_results) {
+		gtk_widget_destroy (gsearch->no_results);
+		gsearch->no_results = NULL;
+	}
+
+	for (service = services; service->service; ++service) {
+		service->has_hits = FALSE;
+
+		service->hit_count = 0;
+		service->offset = 0;
+
+		gtk_list_store_clear (service->store);
+	}
+
+	tracker_search_text_get_hit_count_all_async (tracker_client, query, (TrackerGPtrArrayReply) get_hit_count, gsearch);
+}
+
+static void
+end_refresh_count (int count, GError * error, gpointer user_data)
+{
+	GSearchWindow *gsearch = user_data;
+	service_info_t	* service;
+
+	for (service = services; service->service; ++service) {
+		if (service->service_type == gsearch->current_service->service_type) {
+			service->hit_count = count;
+			break;
+		}
+	}
+
+	update_page_count_label (gsearch);
+
+}
+
+void
+end_search (GPtrArray * out_array,
+	    GError * error,
+	    gpointer user_data)
+{
+	GSearchWindow *gsearch = user_data;
+
+	gsearch->is_locate_database_check_finished = TRUE;
+	stop_animation (gsearch);
+
+	if (error) {
+		display_error_dialog (gsearch->window,	_("Could not connect to search service as it may be busy"));
+		g_error_free (error);
+		return;
+	}
+
+	GError *error2 = NULL;
+	gchar* status = tracker_get_status (tracker_client, &error2);
+
+	if (error2) {
+		g_error_free (error2);
+		status = g_strdup ("Indexing");
+	}
+
+	if (strcmp (status, "Idle") == 0) {
+		gtk_widget_hide (gsearch->warning_label);
+	} else {
+		gtk_widget_show (gsearch->warning_label);
+	}
+
+	g_free (status);
+
+	if (out_array) {
+
+		gsearch->current_service->has_hits = TRUE;
+
+		/* update hit count after search in case of dud hits */
+
+		tracker_search_text_get_hit_count_async	(tracker_client, gsearch->current_service->service_type,
+							 gsearch->search_term,
+							 (TrackerIntReply)end_refresh_count,
+							 gsearch);
+
+
+
+		gsearch->search_results_list_store = gsearch->current_service->store;
+
+		gsearch->command_details->command_status = RUNNING;
+
+		gtk_list_store_clear (GTK_LIST_STORE (gsearch->search_results_list_store));
+
+		g_ptr_array_foreach (out_array, (GFunc)get_meta_table_data, gsearch);
+		g_ptr_array_free (out_array, TRUE);
+
+
+		GtkTreeModel *model = gtk_tree_view_get_model (gsearch->search_results_tree_view);
+
+		GtkTreeIter iter;
+		if (gtk_tree_model_get_iter_first (model, &iter)) {
+			gtk_tree_selection_select_iter (gsearch->search_results_selection, &iter);
+		}
+
+		/* process snippets */
+		g_idle_add ((GSourceFunc) process_snippets, gsearch);
+
+	} else {
+		gsearch->current_service->offset = 0;
+		gsearch->current_service->hit_count = 0;
+		gtk_widget_set_sensitive (gsearch->forward_button, FALSE);
+		gtk_widget_set_sensitive (gsearch->category_list, FALSE);
+	}
+
+	tracker_update_metadata_tile (gsearch);
+}
+
+void
+do_search (GSearchWindow * gsearch,
+	   const gchar * query,
+	   gboolean new_search,
+	   gint search_offset)
+{
+	start_animation (gsearch, TRUE);
+
+	if (!new_search) {
+
+		if (gsearch->current_service->has_hits && (gsearch->current_service->offset == search_offset)) {
+			update_page_count_label (gsearch);
+
+			GtkTreeModel *model = gtk_tree_view_get_model (gsearch->search_results_tree_view);
+
+			GtkTreeIter iter;
+			if (gtk_tree_model_get_iter_first (model, &iter)) {
+				gtk_tree_selection_select_iter (gsearch->search_results_selection, &iter);
+			}
+			stop_animation (gsearch);
+			tracker_update_metadata_tile (gsearch);
+			return;
+		}
+	}
+
+	gsearch->current_service->offset = search_offset;
+	tracker_search_text_detailed_async (tracker_client,
+					    -1,
+					    gsearch->current_service->service_type,
+					    query,
+					    search_offset, MAX_SEARCH_RESULTS,
+					    (TrackerGPtrArrayReply)end_search,
+					    gsearch);
+}
+
+static GtkWidget *
+gsearch_app_create (GSearchWindow * gsearch)
+{
+	GtkWidget * hbox;
+	GtkWidget * vbox;
+	GtkWidget * entry;
+	GtkWidget * label;
+	GtkWidget * container;
+	GtkWidget * main_container;
+	service_info_t	*service;
+
+	gsearch->snippet_queue = g_queue_new ();
+
+	gsearch->category_table = g_hash_table_new (g_str_hash, g_str_equal);
+
+	for (service = services; service->service; service++) {
+		g_hash_table_insert (gsearch->category_table,
+				     g_strdup (service->service),
+				     service);
+	}
+
+	gsearch->category_store = gtk_list_store_new (NUM_CATEGORY_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING);
+
+	GtkIconTheme * theme = gtk_icon_theme_get_default ();
+
+	for (service = services; service->service; ++service) {
+
+		service->store = gtk_list_store_new (NUM_COLUMNS,
+						     GDK_TYPE_PIXBUF,
+						     G_TYPE_STRING,
+						     G_TYPE_STRING,
+						     G_TYPE_STRING,
+						     G_TYPE_STRING,
+						     G_TYPE_STRING,
+						     G_TYPE_STRING,
+						     G_TYPE_INT,
+						     G_TYPE_BOOLEAN);
+
+		service->pixbuf = gtk_icon_theme_load_icon (theme, service->icon_name,
+							    24,
+							    GTK_ICON_LOOKUP_USE_BUILTIN,
+							    NULL);
+
+		g_object_ref (service->pixbuf);
+	}
+
+	gsearch->email_pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default(), "email",
+					 ICON_SIZE,
+					 GTK_ICON_LOOKUP_USE_BUILTIN,
+					 NULL);
+
+	gsearch->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+	gsearch->is_window_maximized = tracker_search_gconf_get_boolean ("/apps/tracker-search-tool/default_window_maximized");
+	g_signal_connect (G_OBJECT (gsearch->window), "size-allocate",
+			  G_CALLBACK (gsearch_window_size_allocate),
+			  gsearch);
+	gsearch->command_details = g_slice_new0 (GSearchCommandDetails);
+	gsearch->window_geometry.min_height = -1;
+	gsearch->window_geometry.min_width  = -1;
+	gsearch->search_term = NULL;
+
+	gtk_window_set_position (GTK_WINDOW (gsearch->window), GTK_WIN_POS_CENTER);
+	gtk_window_set_geometry_hints (GTK_WINDOW (gsearch->window), GTK_WIDGET (gsearch->window),
+				       &gsearch->window_geometry, GDK_HINT_MIN_SIZE);
+
+	tracker_search_get_stored_window_geometry (&gsearch->window_width,
+						&gsearch->window_height);
+	gtk_window_set_default_size (GTK_WINDOW (gsearch->window),
+				     gsearch->window_width,
+				     gsearch->window_height);
+
+	if (gsearch->is_window_maximized == TRUE) {
+		gtk_window_maximize (GTK_WINDOW (gsearch->window));
+	}
+
+	main_container = gtk_vbox_new (FALSE, 0);
+	gtk_container_add (GTK_CONTAINER (gsearch->window), main_container);
+	gtk_container_set_border_width (GTK_CONTAINER (main_container), 0);
+
+	container = gtk_vbox_new (FALSE, 2);
+	gtk_container_add (GTK_CONTAINER (main_container), container);
+	gtk_container_set_border_width (GTK_CONTAINER (container), 1);
+
+
+	GtkWidget * widget;
+	char *search_label;
+
+	hbox = gtk_hbox_new (FALSE, 0);
+	gtk_box_pack_start (GTK_BOX (container), hbox, FALSE, FALSE, 3);
+
+	gsearch->name_and_folder_table = gtk_table_new (2, 4, FALSE);
+	gtk_container_add (GTK_CONTAINER (hbox), gsearch->name_and_folder_table);
+
+	label = gtk_label_new (NULL);
+	search_label = g_strconcat ("<b>", _("_Search:"), "</b>", NULL);
+	gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), search_label);
+	g_free (search_label);
+	gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
+	g_object_set (G_OBJECT (label), "xalign", 0.0, NULL);
+
+	gtk_table_attach (GTK_TABLE (gsearch->name_and_folder_table), label, 0, 1, 0, 1, GTK_FILL, 0, 6, 1);
+
+	gsearch->search_entry = sexy_icon_entry_new ();
+	sexy_icon_entry_add_clear_button (SEXY_ICON_ENTRY (gsearch->search_entry));
+	gtk_table_attach (GTK_TABLE (gsearch->name_and_folder_table), gsearch->search_entry, 1, 2, 0, 1, GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 0, 0);
+	entry =  (gsearch->search_entry);
+	gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
+
+	gsearch->warning_label = gtk_label_new (_("Tracker is still indexing so not all search results are available yet"));
+	gtk_label_set_selectable (GTK_LABEL (gsearch->warning_label), TRUE);
+	gtk_label_set_justify (GTK_LABEL (gsearch->warning_label), GTK_JUSTIFY_LEFT);
+	g_object_set (G_OBJECT (gsearch->warning_label), "xalign", 0.0, NULL);
+	gtk_table_attach (GTK_TABLE (gsearch->name_and_folder_table), gsearch->warning_label, 0, 2, 1, 2, GTK_FILL, 0, 6, 1);
+
+	hbox = gtk_hbutton_box_new ();
+	gtk_table_attach (GTK_TABLE (gsearch->name_and_folder_table), hbox, 3, 4, 0, 1, GTK_FILL, 0, 6, 0);
+
+	gsearch->find_button = gtk_button_new_from_stock (GTK_STOCK_FIND);
+	gtk_container_add (GTK_CONTAINER (hbox), gsearch->find_button);
+
+	if (GTK_IS_ACCESSIBLE (gtk_widget_get_accessible (gsearch->search_entry))) {
+		gsearch->is_window_accessible = TRUE;
+		add_atk_namedesc (gsearch->search_entry, NULL, _("Enter a search term with multiple words seperated with spaces."));
+		add_atk_namedesc (entry, _("search_entry"), _("Enter a search term with multiple words seperated with spaces."));
+	}
+
+	g_signal_connect (G_OBJECT (gsearch->search_entry), "activate",
+			  G_CALLBACK (name_contains_activate_cb),
+			  (gpointer) gsearch);
+
+	g_signal_connect (G_OBJECT (gsearch->search_entry), "changed",
+			  G_CALLBACK (text_changed_cb),
+			  (gpointer) gsearch);
+
+	gsearch->show_more_options_expander = gtk_expander_new_with_mnemonic ("Select more _options");
+
+	/* paned container for search results and category sections */
+
+	gsearch->pane = gtk_hpaned_new ();
+	gtk_paned_set_position (GTK_PANED (gsearch->pane), tracker_get_stored_separator_position ());
+
+	gtk_box_pack_start (GTK_BOX (container), gsearch->pane, TRUE, TRUE, 3);
+
+	/* layout container for results section */
+
+	vbox = gtk_vbox_new (FALSE, 2);
+	gsearch->message_box = vbox;
+
+	gsearch->no_results = NULL;
+
+	gtk_paned_pack2 (GTK_PANED (gsearch->pane), vbox, TRUE, FALSE);
+
+	/* search results panel */
+
+	gsearch->search_results_vbox = create_search_results_section (gsearch);
+	gtk_tree_view_set_model (gsearch->search_results_tree_view, GTK_TREE_MODEL (services[0].store));
+	gtk_widget_set_sensitive (GTK_WIDGET (gsearch->search_results_vbox), FALSE);
+
+	gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (gsearch->search_results_vbox), TRUE, TRUE, 0);
+
+	GTK_WIDGET_SET_FLAGS (gsearch->find_button, GTK_CAN_DEFAULT);
+
+	gtk_widget_set_sensitive (gsearch->find_button, TRUE);
+
+	g_signal_connect (G_OBJECT (gsearch->find_button), "clicked",
+			  G_CALLBACK (click_find_cb),
+			  (gpointer) gsearch);
+
+	if (gsearch->is_window_accessible) {
+		add_atk_namedesc (GTK_WIDGET (gsearch->find_button), NULL, _("Click to perform a search."));
+	}
+
+	/* category sidebar */
+
+	widget = create_sidebar (gsearch);
+	gtk_paned_pack1 (GTK_PANED (gsearch->pane), widget, TRUE, FALSE);
+
+	gtk_widget_set_sensitive (gsearch->category_list, FALSE);
+
+	gtk_widget_set_sensitive (gsearch->forward_button, FALSE);
+	gtk_widget_set_sensitive (gsearch->back_button, FALSE);
+
+	gtk_window_set_focus (GTK_WINDOW (gsearch->window),
+		GTK_WIDGET (gsearch->search_entry));
+
+	gtk_window_set_default (GTK_WINDOW (gsearch->window), gsearch->find_button);
+
+
+	gtk_widget_show_all (main_container);
+
+	gtk_widget_hide (gsearch->warning_label);
+
+	return gsearch->window;
+}
+
+static void
+gsearch_window_finalize (GObject * object)
+{
+	parent_class->finalize (object);
+}
+
+static void
+gsearch_window_class_init (GSearchWindowClass * klass)
+{
+	GObjectClass * object_class = (GObjectClass *) klass;
+
+	object_class->finalize = gsearch_window_finalize;
+	parent_class = g_type_class_peek_parent (klass);
+}
+
+GType
+gsearch_window_get_type (void)
+{
+	static GType object_type = 0;
+
+	if (!object_type) {
+		static const GTypeInfo object_info = {
+			sizeof (GSearchWindowClass),
+			NULL,
+			NULL,
+			(GClassInitFunc) gsearch_window_class_init,
+			NULL,
+			NULL,
+			sizeof (GSearchWindow),
+			0,
+			(GInstanceInitFunc) gsearch_app_create
+		};
+		object_type = g_type_register_static (GTK_TYPE_WINDOW, "GSearchWindow", &object_info, 0);
+	}
+	return object_type;
+}
+
+static void
+tracker_search_setup_gconf_notifications (GSearchWindow * gsearch)
+{
+	gchar * click_to_activate_pref = NULL;
+
+	/* Get value of nautilus click behavior (single or double click to activate items) */
+/*	click_to_activate_pref = tracker_search_gconf_get_string ("/apps/nautilus/preferences/click_policy"); */
+
+	if (click_to_activate_pref == NULL) {
+		gsearch->is_search_results_single_click_to_activate = FALSE;
+		return;
+	}
+
+	gsearch->is_search_results_single_click_to_activate =
+		(strncmp (click_to_activate_pref, "single", 6) == 0) ? TRUE : FALSE;
+
+	tracker_search_gconf_watch_key ("/apps/nautilus/preferences",
+				     "/apps/nautilus/preferences/click_policy",
+				     (GConfClientNotifyFunc) single_click_to_activate_key_changed_cb,
+				     gsearch);
+
+	g_free (click_to_activate_pref);
+}
+
+gchar *
+tracker_search_pixmap_file (const gchar * partial_path)
+{
+	gchar * path;
+
+	path = g_build_filename (TRACKER_DATADIR "/tracker/icons", partial_path, NULL);
+	if (g_file_test (path, G_FILE_TEST_EXISTS)) {
+		return path;
+	} else {
+		return g_strdup (partial_path);
+	}
+	g_free (path);
+	return NULL;
+}
+
+gint
+main (gint argc,
+      gchar * argv[])
+{
+	GSearchWindow * gsearch;
+	GOptionContext * option_context;
+	GError *error = NULL;
+	GnomeProgram * program;
+	GnomeClient * client;
+	GtkWidget * window;
+	gchar * search_string;
+
+	option_context = g_option_context_new ("tracker-search-tool");
+	g_option_context_add_main_entries (option_context, options, NULL);
+
+	if (error) {
+		g_printerr ("invalid arguments: %s\n", error->message);
+		return 1;
+	}
+
+	bindtextdomain (GETTEXT_PACKAGE, TRACKER_LOCALEDIR);
+	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+	textdomain (GETTEXT_PACKAGE);
+
+	program = gnome_program_init ("tracker-search-tool",
+				      VERSION,
+				      LIBGNOMEUI_MODULE,
+				      argc, argv,
+				      GNOME_PARAM_APP_DATADIR, TRACKER_DATADIR,
+				      GNOME_PARAM_GOPTION_CONTEXT, option_context,
+				      GNOME_PARAM_NONE);
+
+	g_set_application_name (_("Tracker Search Tool"));
+
+
+	tracker_search_init_stock_icons ();
+
+	window = g_object_new (GSEARCH_TYPE_WINDOW, NULL);
+	gsearch = GSEARCH_WINDOW (window);
+
+	//gsearch->search_results_pixbuf_hash_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+
+	tracker_search_ui_manager (gsearch);
+
+	gtk_window_set_icon_name (GTK_WINDOW (gsearch->window), "system-search");
+
+	gtk_window_set_default_icon_name ("tracker");
+
+	gtk_window_set_wmclass (GTK_WINDOW (gsearch->window), "tracker-search-tool", "tracker-search-tool");
+	gtk_window_set_policy (GTK_WINDOW (gsearch->window), TRUE, TRUE, TRUE);
+
+	g_signal_connect (G_OBJECT (gsearch->window), "delete_event",
+				    G_CALLBACK (quit_cb),
+				    (gpointer) gsearch);
+	g_signal_connect (G_OBJECT (gsearch->window), "key_press_event",
+				    G_CALLBACK (key_press_cb),
+				    (gpointer) gsearch);
+	g_signal_connect (G_OBJECT (gsearch->window), "window_state_event",
+				    G_CALLBACK (window_state_event_cb),
+				    (gpointer) gsearch);
+
+	if ((client = gnome_master_client ()) != NULL) {
+		g_signal_connect (client, "save_yourself",
+				  G_CALLBACK (save_session_cb),
+				  (gpointer) gsearch);
+		g_signal_connect (client, "die",
+				  G_CALLBACK (die_cb),
+				  (gpointer) gsearch);
+	}
+
+	gtk_widget_show (gsearch->window);
+
+	tracker_client = tracker_connect (FALSE);
+
+	tracker_search_setup_gconf_notifications (gsearch);
+
+
+	if (terms) {
+		search_string = g_strjoinv (" ", terms);
+		gtk_entry_set_text (GTK_ENTRY (gsearch->search_entry), search_string);
+		g_free (search_string);
+		gtk_button_clicked (GTK_BUTTON (gsearch->find_button));
+
+	} else {
+		gtk_widget_set_sensitive (gsearch->find_button, FALSE);
+	}
+
+	gtk_main ();
+
+	return 0;
+}

Added: trunk/src/tracker-search-tool/tracker-search-tool.desktop.in.in
==============================================================================
--- (empty file)
+++ trunk/src/tracker-search-tool/tracker-search-tool.desktop.in.in	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,15 @@
+[Desktop Entry]
+Encoding=UTF-8
+_Name=Tracker Search Tool
+_Comment=Locate documents and folders on this computer by name or content using Tracker
+Exec=tracker-search-tool
+Icon=system-search
+Terminal=false
+Type=Application
+Categories=GTK;Utility;
+StartupNotify=true
+X-GNOME-Bugzilla-Bugzilla=GNOME
+X-GNOME-Bugzilla-Product=tracker
+X-GNOME-Bugzilla-Component=Tracker Search Tool
+X-GNOME-Bugzilla-Version= VERSION@
+

Added: trunk/src/tracker-search-tool/tracker-search-tool.h
==============================================================================
--- (empty file)
+++ trunk/src/tracker-search-tool/tracker-search-tool.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,309 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Tracker Search Tool
+ *
+ *  File:  tracker_search.h modified from gnome-search.h
+ *
+ *  (c) Mr Jamie McCracken
+ *
+ *  Original copyright :
+ *  (C) 1998,2002 the Free Software Foundation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef _GSEARCHTOOL_H_
+#define _GSEARCHTOOL_H_
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif
+
+
+
+#include <gconf/gconf.h>
+#include <gconf/gconf-client.h>
+
+#include <libgnomevfs/gnome-vfs-mime.h>
+#include <libgnomevfs/gnome-vfs-ops.h>
+#include <libgnomevfs/gnome-vfs-utils.h>
+
+#include <libgnomeui/libgnomeui.h>
+
+#include "../libtracker/tracker.h"
+
+#define MAX_SEARCH_RESULTS 10
+
+#define GSEARCH_TYPE_WINDOW	       (gsearch_window_get_type ())
+#define GSEARCH_WINDOW(obj)	       (GTK_CHECK_CAST((obj), GSEARCH_TYPE_WINDOW, GSearchWindow))
+#define GSEARCH_WINDOW_CLASS(klass)    (GTK_CHECK_CLASS_CAST((klass), GSEARCH_TYPE_WINDOW, GSearchWindowClass))
+#define GSEARCH_IS_WINDOW(obj)	       (GTK_CHECK_TYPE((obj), GSEARCH_TYPE_WINDOW))
+#define GSEARCH_IS_WINDOW_CLASS(klass) (GTK_CHECK_CLASS_TYPE((obj), GSEARCH_TYPE_WINDOW))
+#define GSEARCH_WINDOW_GET_CLASS(obj)  (GTK_CHECK_GET_CLASS((obj), GSEARCH_TYPE_WINDOW, GSearchWindowClass))
+
+#define TRACKER_SEARCH_TOOL_ICON "system-search"
+
+#define DEFAULT_WINDOW_WIDTH   700
+#define DEFAULT_WINDOW_HEIGHT  580
+#define WINDOW_HEIGHT_STEP	35
+#define NUM_VISIBLE_COLUMNS	 5
+#define DEFAULT_SEPARATOR_POSITION 170
+
+typedef enum {
+	STOPPED,
+	ABORTED,
+	RUNNING,
+	MAKE_IT_STOP,
+	MAKE_IT_QUIT
+} GSearchCommandStatus;
+
+typedef enum {
+	COLUMN_ICON,
+	COLUMN_URI,
+	COLUMN_NAME,
+	COLUMN_PATH,
+	COLUMN_SNIPPET,
+	COLUMN_MIME,
+	COLUMN_EXEC,
+	COLUMN_TYPE,
+	COLUMN_NO_FILES_FOUND,
+	NUM_COLUMNS
+} GSearchResultColumns;
+
+
+
+typedef enum {
+	CATEGORY_ICON_NAME,
+	CATEGORY_TITLE,
+	CATEGORY_SERVICE,
+	NUM_CATEGORY_COLUMNS
+} CategoryColumns;
+
+
+typedef struct {
+	gchar		*service;
+	gchar		*display_name;
+	gchar		*icon_name;
+	GdkPixbuf	*pixbuf;
+	ServiceType	service_type;
+	GtkListStore	*store;
+	gboolean	has_hits;
+	gint		hit_count;
+	gint		offset;
+} service_info_t;
+
+
+typedef struct _GSearchWindow GSearchWindow;
+typedef struct _GSearchWindowClass GSearchWindowClass;
+typedef struct _GSearchCommandDetails GSearchCommandDetails;
+typedef struct _GSearchConstraint GSearchConstraint;
+typedef struct _GSearchMonitor GSearchMonitor;
+
+struct _GSearchWindow {
+	GtkWindow		parent_instance;
+
+	GtkWidget	      * window;
+	GtkUIManager	      * window_ui_manager;
+	GdkGeometry		window_geometry;
+	gint			window_width;
+	gint			window_height;
+	gboolean		is_window_maximized;
+	gboolean		is_window_accessible;
+
+	gboolean		page_setup_mode;
+
+	GtkWidget		*pane;
+	GQueue			*snippet_queue;
+
+	GHashTable		*category_table;
+
+	/* sidebar category list */
+	GtkWidget		*category_list;
+	GtkListStore		*category_store;
+	GtkTreeSelection	*category_selection;
+	GtkTreeIter		category_iter;
+	GtkCellRenderer		*category_name_cell_renderer;
+
+	GdkPixbuf		*email_pixbuf;
+	gint			current_page;
+	gint			type;
+	guint			old_type;
+	service_info_t		*current_service;
+
+	GtkWidget		*metatile;
+	GtkWidget		*no_results;
+	GtkWidget		*count_label;
+	GtkWidget		*message_box;
+
+	GtkWidget	      * search_entry;
+	GtkWidget	      * warning_label;
+	GtkWidget	      * look_in_folder_button;
+	GtkWidget	      * name_and_folder_table;
+	GtkWidget	      * find_button;
+	GtkWidget	      * back_button;
+	GtkWidget	      * forward_button;
+	GtkWidget	      * stop_button;
+	GtkWidget	      * focus;
+
+	GtkWidget	      * show_more_options_expander;
+	GtkWidget	      * available_options_vbox;
+	GtkWidget	      * available_options_label;
+	GtkWidget	      * available_options_combo_box;
+	GtkWidget	      * available_options_add_button;
+	GtkSizeGroup	      * available_options_button_size_group;
+	GList		      * available_options_selected_list;
+
+	GtkWidget	      * files_found_label;
+	GtkWidget	      * search_results_vbox;
+	GtkWidget	      * search_results_popup_menu;
+	GtkWidget	      * search_results_save_results_as_item;
+	GtkTreeView	      * search_results_tree_view;
+	GtkListStore	      * search_results_list_store;
+	GtkCellRenderer       * search_results_name_cell_renderer;
+	GtkTreeSelection      * search_results_selection;
+	GtkTreeIter		search_results_iter;
+	GtkTreePath	      * search_results_hover_path;
+	GHashTable	      * search_results_pixbuf_hash_table;
+	gchar		      * search_results_date_format_string;
+	GnomeThumbnailFactory * thumbnail_factory;
+	gint			show_thumbnails_file_size_limit;
+	gboolean		show_thumbnails;
+	gboolean		is_search_results_single_click_to_activate;
+	gboolean		is_locate_database_check_finished;
+	gboolean		is_locate_database_available;
+
+	gchar		      * save_results_as_default_filename;
+
+	GSearchCommandDetails * command_details;
+	gchar		      * search_term;
+};
+
+struct _GSearchCommandDetails {
+	pid_t			command_pid;
+	GSearchCommandStatus	command_status;
+
+	gchar		      * name_contains_pattern_string;
+	gchar		      * name_contains_regex_string;
+	gchar		      * look_in_folder_string;
+
+	gboolean		is_command_first_pass;
+	gboolean		is_command_using_quick_mode;
+	gboolean		is_command_second_pass_enabled;
+	gboolean		is_command_show_hidden_files_enabled;
+	gboolean		is_command_regex_matching_enabled;
+	gboolean		is_command_timeout_enabled;
+};
+
+struct _GSearchConstraint {
+	gint constraint_id;
+	union {
+		gchar * text;
+		gint	time;
+		gint	number;
+	} data;
+};
+
+struct _GSearchWindowClass {
+	GtkWindowClass parent_class;
+};
+
+struct _GSearchMonitor {
+	GSearchWindow	      * gsearch;
+	GtkTreeRowReference   * reference;
+	GnomeVFSMonitorHandle * handle;
+};
+
+GType
+gsearch_window_get_type (void);
+
+gchar *
+tracker_search_pixmap_file (const gchar * partial_path);
+
+gchar *
+build_search_command (GSearchWindow * gsearch,
+		      gboolean first_pass);
+
+void
+select_category (GtkTreeSelection * treeselection,
+		 gpointer user_data);
+
+void
+start_new_search (GSearchWindow * gsearch,
+		  const gchar * query);
+
+void
+end_search (GPtrArray * out_array,
+	    GError * error,
+	    gpointer user_data);
+
+void
+do_search (GSearchWindow * gsearch,
+	   const gchar * query,
+	   gboolean new_search,
+	   gint search_offset);
+
+void
+spawn_search_command (GSearchWindow * gsearch,
+		      gchar * command);
+
+void
+add_constraint (GSearchWindow * gsearch,
+		gint constraint_id,
+		gchar * value,
+		gboolean show_constraint);
+void
+update_constraint_info (GSearchConstraint * constraint,
+			gchar * info);
+void
+remove_constraint (gint constraint_id);
+
+void
+set_constraint_gconf_boolean (gint constraint_id,
+			      gboolean flag);
+
+void
+set_constraint_selected_state (GSearchWindow * gsearch,
+			       gint constraint_id,
+			       gboolean state);
+void
+set_clone_command (GSearchWindow * gsearch,
+		   gint * argcp,
+		   gchar *** argvp,
+		   gpointer client_data,
+		   gboolean escape_values);
+
+gchar *
+get_desktop_item_name (GSearchWindow * gsearch);
+
+void
+update_search_counts (GSearchWindow * gsearch);
+
+gboolean
+tree_model_iter_free_monitor (GtkTreeModel * model,
+			      GtkTreePath * path,
+			      GtkTreeIter * iter,
+			      gpointer data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GSEARCHTOOL_H_ */

Added: trunk/src/tracker-thumbnailer/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/tracker-thumbnailer/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,15 @@
+include $(top_srcdir)/Makefile.decl
+
+INCLUDES = 				\
+	$(GLIB2_CFLAGS)			\
+	$(LIBPNG_CFLAGS) 		\
+	-DLIBDIR=\""$(libdir)"\" 
+
+libexec_PROGRAMS = tracker-thumbnailer
+
+tracker_thumbnailer_SOURCES = 		\
+	tracker-thumbnailer.c
+
+tracker_thumbnailer_LDADD = 		\
+	$(GLIB2_LIBS) 			\
+	$(LIBPNG_LIBS)

Added: trunk/src/tracker-thumbnailer/tracker-thumbnailer.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-thumbnailer/tracker-thumbnailer.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,388 @@
+
+/* tracker-thumbnailer
+ * Copyright (C) 2006, Edward Duffy <eduffy gmail com>
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <png.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#ifdef G_OS_WIN32
+#include <Windows.h>
+
+#define _fullpath_internal(res,path,size) \
+  (GetFullPathName ((path), (size), (res), NULL) ? (res) : NULL)
+#define realpath(path,resolved_path) _fullpath_internal(resolved_path, path, MAX_PATH)
+#endif
+
+#ifndef LIBDIR
+#define LIBDIR "/usr/lib"
+#endif
+
+static gboolean
+create_thumbnails_dir (const gchar * const subdir)
+{
+
+	gchar *thumbnails_dir = NULL;
+	const gchar *home_dir;
+
+	home_dir = g_get_home_dir ();
+
+	thumbnails_dir = g_build_filename (home_dir, ".thumbnails", NULL);
+
+	/* Ensure that ~/.thumbnails is not a file if it exists */
+	if (g_file_test (thumbnails_dir, G_FILE_TEST_EXISTS) &&
+			!g_file_test (thumbnails_dir, G_FILE_TEST_IS_DIR)) {
+		g_printerr ("%s exists but is not a directory.\n", thumbnails_dir);
+		g_free (thumbnails_dir);
+		return FALSE;
+	}
+
+	g_free (thumbnails_dir);
+
+	thumbnails_dir = g_build_filename (home_dir, ".thumbnails", subdir, NULL);
+
+	if (g_mkdir_with_parents (thumbnails_dir, 00775) == -1) {
+		g_printerr ("failed: g_mkdir_with_parents (%s)\n", thumbnails_dir);
+		g_free (thumbnails_dir);
+		return FALSE;
+	}
+
+	g_free (thumbnails_dir);
+	return TRUE;
+
+}
+
+static gboolean
+valid_thumbnail_exists (const gchar *thumbnail_filename, const gchar *uri, const gchar *mtime)
+{
+	FILE	    *fp;
+	png_structp  png_ptr;
+	png_infop    info_ptr;
+	gint	     i, count, tests_passed;
+	png_textp    pngtext;
+
+	if (g_file_test (thumbnail_filename, G_FILE_TEST_EXISTS)) {
+
+		g_printerr ("%s exists ", thumbnail_filename);
+
+		/* thumbnail exists; but is it valid? */
+		g_assert ((fp = g_fopen (thumbnail_filename, "r")));
+		g_assert ((png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)));
+		g_assert ((info_ptr = png_create_info_struct (png_ptr)));
+		png_init_io (png_ptr, fp);
+		png_read_info (png_ptr, info_ptr);
+		i = png_get_text (png_ptr, info_ptr, &pngtext, &count);
+		tests_passed = 0;
+		for (i = 0; i < count; i++) {
+			if (strcmp (pngtext[i].key, "Thumb::URI") == 0 && strcmp (pngtext[i].text, uri) == 0) {
+				++tests_passed;
+			}
+			else if (strcmp (pngtext[i].key, "Thumb::MTime") == 0 && strcmp (pngtext[i].text, mtime) == 0) {
+				++tests_passed;
+			}
+		}
+		fclose (fp);
+
+		png_destroy_info_struct (png_ptr, &info_ptr);
+		png_destroy_read_struct (&png_ptr, NULL, NULL);
+
+
+		if (tests_passed == 2) {
+			g_printerr ("and it is valid\n");
+			return TRUE;
+		}
+		g_printerr ("but it is not valid. Recreating thumbnail...\n");
+	}
+
+	return FALSE;
+}
+
+static gboolean
+generate_thumbnail (const gchar *filename, const gchar *thumbnail_filename, const gchar *mime, gint size)
+{
+
+	gchar	 *thumbnailer;
+	gchar	 *args[5];
+	gint	  i;
+	gboolean  error = FALSE;
+
+	/* do we have a thumbnailer for this mime type? */
+	thumbnailer = g_strconcat (LIBDIR "/tracker/thumbnailers/",
+				   mime, "_thumbnailer", NULL);
+
+	if (!g_file_test (thumbnailer, G_FILE_TEST_EXISTS)) {
+		g_printerr ("Thumbnailer '%s' for mime %s not found\n", thumbnailer, mime);
+		g_free (thumbnailer);
+		return FALSE;
+	}
+
+	/* execute the thumbnailer */
+	args[0] = g_strdup (thumbnailer);
+	args[1] = g_filename_from_utf8 (filename, -1, NULL, NULL, NULL);
+	args[2] = g_strdup (thumbnail_filename);
+	args[3] = g_strdup_printf ("%d", size);
+	args[4] = NULL;
+
+	if (!g_spawn_sync (NULL, args, NULL,
+			   G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL,
+			   NULL, NULL, NULL, NULL, NULL, NULL)) {
+
+		g_printerr ("%s failed to create %s\n", thumbnailer, thumbnail_filename);
+		g_remove (thumbnail_filename);
+		error = TRUE;
+	}
+
+	for (i = 0; i < 4; i++) {
+		g_free (args[i]);
+	}
+	g_free (thumbnailer);
+
+	return !error;
+}
+
+
+static gboolean
+ensure_correct_thumbnail (const gchar *uri, const gchar *thumbnail_filename, const gchar *mtime) {
+
+	FILE	      *fp;
+	png_structp    png_ptr;
+	png_infop      info_ptr;
+	png_textp      pngtext;
+	png_uint_32    width, height;
+	gint	       bit_depth, color_type, interlace_method;
+	gint	       compression_method, filter_method;
+	png_colorp     palette = NULL;
+	gint	       num_palette;
+	png_bytepp     row_pointers;
+	guint	       i;
+
+	/* the fd.o spec requires us to set certain PNG keys.  As far as I can
+	 * see, libpng doesn't allow you to just set metadata, you have to do
+	 * that when you create the file.  Therefore, we need to copy the newly
+	 * created thumbnail into a new one while setting the required attributes
+	 */
+	g_assert ((fp = g_fopen (thumbnail_filename, "r")));
+	g_assert ((png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)));
+	g_assert ((info_ptr = png_create_info_struct (png_ptr)));
+
+	if (setjmp (png_jmpbuf (png_ptr))) {
+		g_printerr ("Error reading thumbnail\n");
+		png_destroy_info_struct (png_ptr, &info_ptr);
+		png_destroy_read_struct (&png_ptr, NULL, NULL);
+		fclose (fp);
+		g_remove (thumbnail_filename);
+		return FALSE;
+	}
+
+	png_init_io (png_ptr, fp);
+	png_read_info (png_ptr, info_ptr);
+
+	g_assert ((1 == png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_method, &compression_method, &filter_method)));
+
+	png_get_PLTE (png_ptr, info_ptr, &palette, &num_palette);
+
+	row_pointers = g_new (png_bytep, height);
+	for (i = 0; i < height; i++) {
+		row_pointers[i] = (png_bytep) g_malloc0 (png_get_rowbytes (png_ptr, info_ptr));
+	}
+	png_read_image (png_ptr, row_pointers);
+	png_read_end (png_ptr, NULL);
+
+	png_destroy_info_struct (png_ptr, &info_ptr);
+	png_destroy_read_struct (&png_ptr, NULL, NULL);
+	fclose (fp);
+
+	/* write the new thumbnail (overwrites the old) */
+	g_assert ((fp = g_fopen (thumbnail_filename, "w")));
+	g_assert ((png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)));
+	g_assert ((info_ptr = png_create_info_struct (png_ptr)));
+
+	if (setjmp (png_jmpbuf (png_ptr))) {
+		g_printerr ("Error creating thumbnail\n");
+	} else {
+
+		png_init_io (png_ptr, fp);
+
+		png_set_IHDR (png_ptr, info_ptr, width, height, bit_depth,
+			      color_type, interlace_method, compression_method, filter_method);
+
+		if (palette && num_palette > 0)
+			png_set_PLTE (png_ptr, info_ptr, palette, num_palette);
+
+		/* set the required fields */
+		pngtext = g_new0 (png_text, 3);
+		pngtext[0].key = "Thumb::URI";
+		pngtext[0].text = g_strdup (uri);
+		pngtext[0].text_length = strlen (uri);
+		pngtext[0].compression = PNG_TEXT_COMPRESSION_NONE;
+		pngtext[1].key = "Thumb::MTime";
+		pngtext[1].text = g_strdup (mtime);
+		pngtext[1].text_length = strlen (mtime);
+		pngtext[1].compression = PNG_TEXT_COMPRESSION_NONE;
+		/* set some optional fields */
+		pngtext[2].key = "Software";
+		pngtext[2].text = "Tracker thumbnail factory";
+		pngtext[2].compression = PNG_TEXT_COMPRESSION_NONE;
+		png_set_text(png_ptr, info_ptr, pngtext, 3);
+
+		png_write_info (png_ptr, info_ptr);
+		png_write_image (png_ptr, row_pointers);
+		png_write_end (png_ptr, info_ptr);
+
+		g_free (pngtext[0].text);
+		g_free (pngtext[1].text);
+		g_free (pngtext);
+	}
+	fclose (fp);
+
+	for (i = 0; i < height; i++) {
+		g_free (row_pointers[i]);
+	}
+
+	g_free (row_pointers);
+
+	png_destroy_info_struct (png_ptr, &info_ptr);
+	png_destroy_write_struct (&png_ptr, NULL);
+
+	return TRUE;
+}
+
+static gboolean
+validate_args (gint argc, gchar **argv)
+{
+	if (argc < 4) {
+		return FALSE;
+	}
+
+	if (!strcmp (argv[3], "normal")
+	    || !strcmp (argv[3], "large")
+	    || !strcmp (argv[3], "preview")) {
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+
+/* argv[1] == full path of file to be nailed
+ * argv[2] == mime type of said file
+ * argv[3] == requested size: "normal", "large", "preview"
+ */
+
+gint
+main (gint argc, gchar *argv[])
+{
+	gchar	      *uri;
+	gchar	       realname[MAXPATHLEN];
+	struct stat    stat_info;
+	gchar	      *mtime;
+	gchar	      *uri_hash;
+	GChecksum     *checksum;
+	gchar	      *thumbnail_filename;
+
+	if (!validate_args (argc, argv)) {
+		g_printerr ("Usage: ./tracker-thumbnailer filename mimetype [normal|large|preview]\n");
+		return EXIT_FAILURE;
+	}
+
+	/* only make normal size thumbnails for now */
+	if (strcmp (argv[3], "normal") != 0) {
+		g_printerr ("Only normal sized thumbnails are supported\n");
+		return EXIT_FAILURE;
+	}
+
+	if (!create_thumbnails_dir (argv[3]))
+		return EXIT_FAILURE;
+
+	/* make sure the actual file exists */
+	if (!g_file_test (argv[1], G_FILE_TEST_EXISTS)) {
+		g_printerr ("File '%s' does not exist\n", argv[1]);
+		return EXIT_FAILURE;
+	}
+
+	/* g_filename_to_uri needs an absolute filename */
+	if (!realpath (argv[1], realname)) {
+		g_printerr("could not resolve absolute pathname for %s\n", argv[1]);
+		return EXIT_FAILURE;
+	}
+
+	/* convert file name to URI */
+	uri = g_filename_to_uri (realname, NULL, NULL);
+
+
+	/* create path to thumbnail */
+	checksum = g_checksum_new (G_CHECKSUM_MD5);
+	g_checksum_update (checksum, (guchar *)uri, strlen (uri));
+	uri_hash = g_strconcat (g_checksum_get_string (checksum), ".png", NULL);
+	g_checksum_free (checksum);
+
+
+	thumbnail_filename = g_build_filename (g_get_home_dir (), ".thumbnails", argv[3], uri_hash, NULL);
+	g_free (uri_hash);
+
+	/* get stat information on the file */
+	if (g_stat (argv[1], &stat_info) == -1) {
+		g_printerr ("stat () failed for %s\n", argv[1]);
+		g_free (thumbnail_filename);
+		g_free (uri);
+		return EXIT_FAILURE;
+	}
+	mtime = g_strdup_printf ("%lu", stat_info.st_mtime);
+
+	/* check to see if the thumbnail already exists */
+	if (valid_thumbnail_exists (thumbnail_filename, uri, mtime)) {
+		g_print ("%s\n", thumbnail_filename);
+		g_free (mtime);
+		g_free (thumbnail_filename);
+		g_free (uri);
+		return EXIT_SUCCESS;
+	}
+
+	if (!generate_thumbnail (uri, thumbnail_filename, argv[2], 128)) {
+		g_free (mtime);
+		g_free (thumbnail_filename);
+		g_free (uri);
+		return EXIT_FAILURE;
+	}
+
+	if (g_file_test (thumbnail_filename, G_FILE_TEST_EXISTS)) {
+		ensure_correct_thumbnail (uri, thumbnail_filename, mtime);
+	} else {
+		g_free (mtime);
+		g_free (thumbnail_filename);
+		g_free (uri);
+		return EXIT_FAILURE;
+	}
+
+	/* if we get here, everything must have succeeded */
+	g_print ("%s\n", thumbnail_filename);
+	g_free (thumbnail_filename);
+	g_free (uri);
+	g_free (mtime);
+	return EXIT_SUCCESS;
+}

Added: trunk/src/tracker-utils/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/tracker-utils/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,48 @@
+include $(top_srcdir)/Makefile.decl
+
+INCLUDES =							\
+	-DLOCALEDIR=\""$(localedir)"\" 				\
+	-DG_LOG_DOMAIN=\"Tracker\"				\
+	-I$(top_srcdir)/src					\
+	$(DBUS_CFLAGS)						\
+	$(GIO_CFLAGS)						\
+	$(GLIB2_CFLAGS)
+
+libs = 								\
+	$(top_builddir)/src/libtracker/libtrackerclient.la	\
+	$(GIO_LIBS)						\
+	$(GLIB2_LIBS)
+
+bin_PROGRAMS = 							\
+	tracker-search 						\
+	tracker-query 						\
+	tracker-meta-folder					\
+	tracker-stats						\
+	tracker-tag						\
+	tracker-files						\
+	tracker-status						\
+	tracker-unique
+
+tracker_search_SOURCES = tracker-search.c
+tracker_search_LDADD = $(libs)
+
+tracker_query_SOURCES = tracker-query.c
+tracker_query_LDADD = $(libs)
+
+tracker_meta_folder_SOURCES = tracker-meta-folder.c
+tracker_meta_folder_LDADD = $(libs)
+
+tracker_stats_SOURCES = tracker-stats.c
+tracker_stats_LDADD = $(libs)
+
+tracker_tag_SOURCES = tracker-tag.c
+tracker_tag_LDADD = $(libs)
+
+tracker_files_SOURCES = tracker-files.c
+tracker_files_LDADD = $(libs)
+
+tracker_status_SOURCES = tracker-status.c
+tracker_status_LDADD = $(libs)
+
+tracker_unique_SOURCES = tracker-unique.c
+tracker_unique_LDADD = $(libs)

Added: trunk/src/tracker-utils/tracker-files.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-utils/tracker-files.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,178 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <locale.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <locale.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <libtracker/tracker.h>
+
+static gchar	   *service;
+static gchar	  **mimes;
+static gint	    limit = 512;
+static gint	    offset;
+
+static GOptionEntry entries[] = {
+	{ "service", 's', 0, G_OPTION_ARG_STRING, &service,
+	  N_("Search from a specific service"),
+	  NULL
+	},
+	{ "limit", 'l', 0, G_OPTION_ARG_INT, &limit,
+	  N_("Limit the number of results shown"),
+	  N_("512")
+	},
+	{ "offset", 'o', 0, G_OPTION_ARG_INT, &offset,
+	  N_("Offset the results"),
+	  N_("0")
+	},
+	{ "add-mime", 'm', 0, G_OPTION_ARG_STRING_ARRAY, &mimes,
+	  N_("MIME types (can be used multiple times)"),
+	  NULL
+	},
+	{ NULL }
+};
+
+int
+main (int argc, char **argv)
+{
+
+	TrackerClient  *client;
+	ServiceType	type;
+	GOptionContext *context;
+	GError	       *error = NULL;
+
+	bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+	textdomain (GETTEXT_PACKAGE);
+
+	context = g_option_context_new (_("- Search for files by service or by MIME type"));
+	g_option_context_add_main_entries (context, entries, "tracker-files");
+	g_option_context_parse (context, &argc, &argv, NULL);
+
+	if (!service && !mimes) {
+		gchar *help;
+
+		help = g_option_context_get_help (context, TRUE, NULL);
+		g_option_context_free (context);
+		g_printerr (help);
+		g_free (help);
+
+		return EXIT_FAILURE;
+	}
+
+	g_option_context_free (context);
+
+	client = tracker_connect (FALSE);
+
+	if (!client) {
+		g_printerr ("%s\n",
+			    _("Could not establish a DBus connection to Tracker"));
+		return EXIT_FAILURE;
+	}
+
+	if (service) {
+		gchar **array;
+		gchar **p_strarray;
+
+		type = tracker_service_name_to_type (service);
+
+		array = tracker_files_get_by_service_type (client,
+							   time (NULL),
+							   type,
+							   offset,
+							   limit,
+							   &error);
+
+		if (error) {
+			g_printerr ("%s:'%s', %s\n",
+				    _("Could not get files by service type"),
+				    service,
+				    error->message);
+			g_error_free (error);
+
+			return EXIT_FAILURE;
+		}
+
+		if (!array) {
+			g_print ("%s\n",
+				 _("No files found by that service type"));
+
+			return EXIT_FAILURE;
+		}
+
+		g_print ("%s:\n",
+			 _("Results"));
+
+		for (p_strarray = array; *p_strarray; p_strarray++) {
+			g_print ("  %s\n", *p_strarray);
+		}
+
+		g_strfreev (array);
+	}
+
+	if (mimes) {
+		gchar **array;
+		gchar **p;
+
+		array = tracker_files_get_by_mime_type (client,
+							time (NULL),
+							mimes,
+							offset,
+							limit,
+							&error);
+
+		if (error) {
+			g_printerr ("%s, %s\n",
+				    _("Could not get files by MIME type"),
+				    error->message);
+			g_error_free (error);
+
+			return EXIT_FAILURE;
+		}
+
+		if (!array) {
+			g_print ("%s\n",
+				 _("No files found by those MIME types"));
+
+			return EXIT_FAILURE;
+		}
+
+		g_print ("%s:\n",
+			 _("Results"));
+
+		for (p = array; *p; p++) {
+			g_print ("  %s\n", *p);
+		}
+
+		g_strfreev (array);
+	}
+
+	tracker_disconnect (client);
+
+	return EXIT_SUCCESS;
+}

Added: trunk/src/tracker-utils/tracker-meta-folder.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-utils/tracker-meta-folder.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,265 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <time.h>
+#include <locale.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <libtracker/tracker.h>
+
+#define MAX_FILENAME_WIDTH 35
+
+static gchar	     *path;
+static gchar	    **fields;
+
+static GOptionEntry   entries[] = {
+	{ "path", 'p', 0, G_OPTION_ARG_STRING, &path,
+	  N_("Path to use for directory to get metadata information about"),
+	  NULL,
+	},
+	{ G_OPTION_REMAINING, 0, 0,
+	  G_OPTION_ARG_STRING_ARRAY, &fields,
+	  NULL,
+	  NULL
+	},
+	{ NULL }
+};
+
+static void
+print_header (gchar **fields_resolved)
+{
+	gint cols;
+	gint width;
+	gint i;
+
+	/* Headers */
+	g_print ("  %-*.*s ",
+		 MAX_FILENAME_WIDTH,
+		 MAX_FILENAME_WIDTH,
+		 _("Filename"));
+
+	width  = MAX_FILENAME_WIDTH;
+	width += 1;
+
+	cols = g_strv_length (fields_resolved);
+
+	for (i = 0; i < cols; i++) {
+		g_print ("%s%s",
+			 fields_resolved[i],
+			 i < cols - 1 ? ", " : "");
+
+		width += g_utf8_strlen (fields_resolved[i], -1);
+		width += i < cols - 1 ? 2 : 0;
+	}
+	g_print ("\n");
+
+	/* Line under header */
+	g_print ("  ");
+	for (i = 0; i < width; i++) {
+		g_print ("-");
+	}
+	g_print ("\n");
+}
+
+static void
+get_meta_table_data (gpointer data,
+		     gpointer user_data)
+{
+	gchar **meta;
+	gchar **p;
+	gchar **fields;
+	gchar  *basename;
+	gint	i;
+	gint	len;
+	gint	cols;
+
+	meta = data;
+	fields = user_data;
+
+	basename = g_path_get_basename (*meta);
+	len = g_utf8_strlen (basename, -1);
+	cols = g_strv_length (meta);
+
+	for (p = meta, i = 0; *p; p++, i++) {
+		if (i == 0) {
+			g_print ("  %-*.*s",
+				 MAX (len, MAX_FILENAME_WIDTH),
+				 MAX (len, MAX_FILENAME_WIDTH),
+				 basename);
+
+			if (len > MAX_FILENAME_WIDTH) {
+				gint i = 0;
+
+				g_print ("\n");
+				while (i++ < MAX_FILENAME_WIDTH + 2) {
+					g_print (" ");
+				}
+			}
+
+			g_print (" (");
+		} else {
+			g_print ("%s%s",
+				 *p,
+				 i < cols - 1 ? ", " : "");
+		}
+	}
+
+	g_free (basename);
+
+	g_print (")\n");
+}
+
+int
+main (int argc, char **argv)
+{
+	TrackerClient	*client;
+	GOptionContext	*context;
+	GError		*error = NULL;
+	gchar		*summary;
+	const gchar	*failed = NULL;
+	gchar	       **fields_resolved = NULL;
+	gchar		*path_in_utf8;
+	GPtrArray	*array;
+	gint		 i, j;
+
+	bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+	textdomain (GETTEXT_PACKAGE);
+
+	/* Translators: this messagge will apper immediately after the
+	 * usage string - Usage: COMMAND [OPTION]... <THIS_MESSAGE>
+	 */
+	context = g_option_context_new (_("Retrieve meta-data information about files in a directory"));
+
+	/* Translators: this message will appear after the usage string
+	 * and before the list of options, showing an usage example.
+	 */
+	summary = g_strconcat (_("To use multiple meta-data types simply list them, for example:"),
+			       "\n"
+			       "\n"
+			       "  -p ", _("PATH"), " File:Size File:Type",
+			       NULL);
+
+	g_option_context_set_summary (context, summary);
+	g_option_context_add_main_entries (context, entries, NULL);
+	g_option_context_parse (context, &argc, &argv, NULL);
+	g_free (summary);
+
+	if (!path) {
+		failed = _("No path was given");
+	} else if (!fields) {
+		failed = _("No fields were given");
+	}
+
+	if (failed) {
+		gchar *help;
+
+		g_printerr ("%s\n\n", failed);
+
+		help = g_option_context_get_help (context, TRUE, NULL);
+		g_option_context_free (context);
+		g_printerr (help);
+		g_free (help);
+
+		return EXIT_FAILURE;
+	}
+
+	g_option_context_free (context);
+
+	client = tracker_connect (FALSE);
+
+	if (!client) {
+		g_printerr ("%s\n",
+			    _("Could not establish a DBus connection to Tracker"));
+		return EXIT_FAILURE;
+	}
+
+	fields_resolved = g_new0 (gchar*, g_strv_length (fields) + 1);
+
+	for (i = 0, j = 0; fields && fields[i] != NULL; i++) {
+		gchar *field;
+
+		field = g_locale_to_utf8 (fields[i], -1, NULL, NULL, NULL);
+
+		if (field) {
+			fields_resolved[j++] = field;
+		}
+	}
+
+	fields_resolved[j] = NULL;
+
+	path_in_utf8 = g_filename_to_utf8 (path, -1, NULL, NULL, &error);
+	if (error) {
+		g_printerr ("%s:'%s', %s\n",
+			    _("Could not get UTF-8 path from path"),
+			    path,
+			    error->message);
+		g_error_free (error);
+		tracker_disconnect (client);
+
+		return EXIT_FAILURE;
+	}
+
+	array = tracker_files_get_metadata_for_files_in_folder (client,
+								time (NULL),
+								path_in_utf8,
+								fields_resolved,
+								&error);
+
+	g_free (path_in_utf8);
+
+	if (error) {
+		g_printerr ("%s:'%s', %s\n",
+			    _("Could not get meta-data for files in directory"),
+			    path,
+			    error->message);
+		g_error_free (error);
+		g_strfreev (fields_resolved);
+		tracker_disconnect (client);
+
+		return EXIT_FAILURE;
+	}
+
+	if (!array) {
+		g_print ("%s\n",
+			 _("No meta-data found for files in that directory"));
+	} else {
+		g_print ("%s:\n",
+			 _("Results"));
+
+		print_header (fields_resolved);
+
+		g_ptr_array_foreach (array,
+				     get_meta_table_data,
+				     fields_resolved);
+		g_ptr_array_free (array, TRUE);
+	}
+
+	g_strfreev (fields_resolved);
+	tracker_disconnect (client);
+
+	return EXIT_SUCCESS;
+}

Added: trunk/src/tracker-utils/tracker-query.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-utils/tracker-query.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,239 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <sys/param.h>
+#include <stdlib.h>
+#include <time.h>
+#include <locale.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <libtracker/tracker.h>
+
+#ifdef G_OS_WIN32
+#include <trackerd/mingw-compat.h>
+#endif /* G_OS_WIN32 */
+
+static gchar	     *path;
+static gchar	     *search;
+static gchar	    **fields;
+static gchar	     *service;
+static gchar	     *keyword;
+static gint	      limit = 512;
+static gint	      offset;
+
+static GOptionEntry   entries[] = {
+	{ "path", 'p', 0, G_OPTION_ARG_STRING, &path,
+	  N_("Path to use in query"),
+	  NULL,
+	},
+	{ "service", 's', 0, G_OPTION_ARG_STRING, &service,
+	  N_("Search from a specific service"),
+	  NULL
+	},
+	{ "limit", 'l', 0, G_OPTION_ARG_INT, &limit,
+	  N_("Limit the number of results shown"),
+	  N_("512")
+	},
+	{ "offset", 'o', 0, G_OPTION_ARG_INT, &offset,
+	  N_("Offset the results"),
+	  N_("0")
+	},
+	{ "search-term", 't', 0, G_OPTION_ARG_STRING, &search,
+	  N_("Adds a fulltext search filter"),
+	  NULL,
+	},
+	{ "keyword", 'k', 0, G_OPTION_ARG_STRING, &keyword,
+	  N_("Adds a keyword filter"),
+	  NULL
+	},
+	{ G_OPTION_REMAINING, 0, 0,
+	  G_OPTION_ARG_STRING_ARRAY, &fields,
+	  N_("Metadata Fields"),
+	  NULL
+	},
+	{ NULL }
+};
+
+static void
+get_meta_table_data (gpointer value)
+{
+	gchar **meta;
+	gchar **p;
+	gint	i;
+
+	meta = value;
+
+	for (p = meta, i = 0; *p; p++, i++) {
+		if (i == 0) {
+			g_print ("  %s", *p);
+		} else {
+			g_print (", %s", *p);
+		}
+	}
+
+	g_print ("\n");
+}
+
+int
+main (int argc, char **argv)
+{
+	TrackerClient	*client;
+	ServiceType	 type;
+	GOptionContext	*context;
+	GError		*error = NULL;
+	gchar		*path_in_utf8;
+	gchar		*content;
+	gchar		*buffer;
+	gsize		 size;
+	GPtrArray	*array;
+
+	bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+	textdomain (GETTEXT_PACKAGE);
+
+	context = g_option_context_new (_("- Query using RDF and return results "
+					  "with specified metadata fields"));
+
+	g_option_context_add_main_entries (context, entries, NULL);
+	g_option_context_parse (context, &argc, &argv, NULL);
+
+	if (!fields || !path) {
+		gchar *help;
+
+		g_printerr ("%s\n\n",
+			    _("Path or fields are missing"));
+
+		help = g_option_context_get_help (context, TRUE, NULL);
+		g_option_context_free (context);
+		g_printerr (help);
+		g_free (help);
+
+		return EXIT_FAILURE;
+	}
+
+	g_option_context_free (context);
+
+	client = tracker_connect (FALSE);
+
+	if (!client) {
+		g_printerr ("%s\n",
+			    _("Could not establish a DBus connection to Tracker"));
+		return EXIT_FAILURE;
+	}
+
+	if (limit <= 0) {
+		limit = 512;
+	}
+
+	if (!service) {
+		g_print ("%s\n",
+			 _("Defaulting to 'files' service"));
+
+		type = SERVICE_FILES;
+	} else {
+		type = tracker_service_name_to_type (service);
+
+		if (type == SERVICE_OTHER_FILES && g_ascii_strcasecmp (service, "Other")) {
+			g_printerr ("%s\n",
+				    _("Service not recognized, searching in other files..."));
+		}
+	}
+
+	path_in_utf8 = g_filename_to_utf8 (path, -1, NULL, NULL, &error);
+	if (error) {
+		g_printerr ("%s:'%s', %s\n",
+			    _("Could not get UTF-8 path from path"),
+			    path,
+			    error->message);
+		g_error_free (error);
+		tracker_disconnect (client);
+
+		return EXIT_FAILURE;
+	}
+
+	g_file_get_contents (path_in_utf8, &content, &size, &error);
+	if (error) {
+		g_printerr ("%s:'%s', %s\n",
+			    _("Could not read file"),
+			    path_in_utf8,
+			    error->message);
+		g_error_free (error);
+		g_free (path_in_utf8);
+		tracker_disconnect (client);
+
+		return EXIT_FAILURE;
+	}
+
+	g_free (path_in_utf8);
+
+	buffer = g_locale_to_utf8 (content, size, NULL, NULL, &error);
+	g_free (content);
+
+	if (error) {
+		g_printerr ("%s, %s\n",
+			    _("Could not convert query file to UTF-8"),
+			    error->message);
+		g_error_free (error);
+		tracker_disconnect (client);
+
+		return EXIT_FAILURE;
+	}
+
+	array = tracker_search_query (client,
+				      time (NULL),
+				      type,
+				      fields,
+				      search,
+				      keyword,
+				      buffer,
+				      offset,
+				      limit,
+				      FALSE,
+				      NULL,
+				      FALSE,
+				      &error);
+	g_free (buffer);
+
+	if (error) {
+		g_printerr ("%s, %s\n",
+			    _("Could not query search"),
+			    error->message);
+		g_error_free (error);
+
+		return EXIT_FAILURE;
+	}
+
+	if (!array) {
+		g_print ("%s\n",
+			 _("No results found matching your query"));
+	} else {
+		g_ptr_array_foreach (array, (GFunc) get_meta_table_data, NULL);
+		g_ptr_array_free (array, TRUE);
+	}
+
+	tracker_disconnect (client);
+
+	return EXIT_SUCCESS;
+}

Added: trunk/src/tracker-utils/tracker-search.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-utils/tracker-search.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,271 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <locale.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <libtracker/tracker.h>
+
+static gint	      limit = 512;
+static gint	      offset;
+static gchar	    **terms;
+static gchar	     *service;
+static gboolean       detailed;
+
+static GOptionEntry   entries[] = {
+	{ "service", 's', 0, G_OPTION_ARG_STRING, &service,
+	  N_("Search from a specific service"),
+	  NULL
+	},
+	{ "limit", 'l', 0, G_OPTION_ARG_INT, &limit,
+	  N_("Limit the number of results shown"),
+	  N_("512")
+	},
+	{ "offset", 'o', 0, G_OPTION_ARG_INT, &offset,
+	  N_("Offset the results"),
+	  N_("0")
+	},
+	{ "detailed", 'd', 0, G_OPTION_ARG_NONE, &detailed,
+	  N_("Show more detailed results with service and mime type"),
+	  NULL
+	},
+	{ G_OPTION_REMAINING, 0, 0,
+	  G_OPTION_ARG_STRING_ARRAY, &terms,
+	  N_("search terms"),
+	  NULL
+	},
+	{ NULL }
+};
+
+static void
+get_meta_table_data (gpointer value)
+{
+	gchar **meta;
+	gchar **p;
+	gchar  *str;
+	gint	i;
+
+	meta = value;
+
+	for (p = meta, i = 0; *p; p++, i++) {
+		switch (i) {
+		case 0:
+			str = g_filename_from_utf8 (*p, -1, NULL, NULL, NULL);
+			g_print ("  %s:'%s'", _("Path"), str);
+			g_free (str);
+			break;
+		case 1:
+			g_print (", %s:'%s'", _("Service"), *p);
+			break;
+		case 2:
+			g_print (", %s:'%s'", _("MIME-type"), *p);
+			break;
+		default:
+			break;
+		}
+	}
+
+	g_print ("\n");
+}
+
+int
+main (int argc, char **argv)
+{
+	TrackerClient	*client;
+	ServiceType	 type;
+	GOptionContext	*context;
+	GError		*error = NULL;
+	gchar		*search;
+	gchar		*summary;
+	gchar	       **strv;
+	gchar	       **p;
+	GPtrArray	*array;
+
+	bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+	textdomain (GETTEXT_PACKAGE);
+
+	/* Translators: this messagge will apper immediately after the	*/
+	/* usage string - Usage: COMMAND [OPTION]... <THIS_MESSAGE>	*/
+	context = g_option_context_new (_("- Search files for certain terms"));
+
+	/* Translators: this message will appear after the usage string */
+	/* and before the list of options.				*/
+	summary = g_strconcat (_("Specifying multiple terms apply an AND "
+				 "operator to the search performed"),
+			       "\n",
+			       _("This means if you search for 'foo' and 'bar', "
+				 "they must BOTH exist"),
+			       "\n",
+			       "\n",
+			       _("Recognized services include:"),
+			       "\n"
+			       "\n",
+			       "  Documents\n"
+			       "  Emails\n"
+			       "  EmailAttachments\n"
+			       "  Music\n"
+			       "  Images\n"
+			       "  Videos\n"
+			       "  Text\n"
+			       "  Development\n"
+			       "  Applications\n"
+			       "  Conversations\n"
+			       "  Folders\n"
+			       "  Files",
+			       NULL);
+	g_option_context_set_summary (context, summary);
+	g_option_context_add_main_entries (context, entries, NULL);
+	g_option_context_parse (context, &argc, &argv, NULL);
+
+	g_free (summary);
+
+	if (!terms) {
+		gchar *help;
+
+		g_printerr ("%s\n\n",
+			    _("Search terms are missing"));
+
+		help = g_option_context_get_help (context, TRUE, NULL);
+		g_option_context_free (context);
+		g_printerr (help);
+		g_free (help);
+
+		return EXIT_FAILURE;
+	}
+
+	g_option_context_free (context);
+
+	client = tracker_connect (FALSE);
+
+	if (!client) {
+		g_printerr ("%s\n",
+			    _("Could not establish a DBus connection to Tracker"));
+		return EXIT_FAILURE;
+	}
+
+	if (limit <= 0) {
+		limit = 512;
+	}
+
+	if (!service) {
+		g_print ("%s\n",
+			 _("Defaulting to 'files' service"));
+
+		type = SERVICE_FILES;
+	} else {
+		type = tracker_service_name_to_type (service);
+
+		if (type == SERVICE_OTHER_FILES && g_ascii_strcasecmp (service, "Other")) {
+			g_printerr ("%s\n",
+				    _("Service not recognized, searching in other files..."));
+		}
+	}
+
+	search = g_strjoinv (" ", terms);
+
+	if (detailed) {
+		array = tracker_search_text_detailed (client,
+						      time (NULL),
+						      type,
+						      search,
+						      offset,
+						      limit,
+						      &error);
+		g_free (search);
+
+		if (error) {
+			g_printerr ("%s, %s\n",
+				    _("Could not get find detailed results by text"),
+				    error->message);
+
+			g_error_free (error);
+			tracker_disconnect (client);
+
+			return EXIT_FAILURE;
+		}
+
+		if (!array) {
+			g_print ("%s\n",
+				 _("No results found matching your query"));
+		} else {
+			g_print ("%s\n",
+				 _("Results:"));
+
+			g_ptr_array_foreach (array, (GFunc) get_meta_table_data, NULL);
+			g_ptr_array_free (array, TRUE);
+		}
+	} else {
+		strv = tracker_search_text (client,
+					    time (NULL),
+					    type,
+					    search,
+					    offset,
+					    limit,
+					    &error);
+		g_free (search);
+
+		if (error) {
+			g_printerr ("%s, %s\n",
+				    _("Could not get find results by text"),
+				    error->message);
+
+			g_error_free (error);
+			tracker_disconnect (client);
+
+			return EXIT_FAILURE;
+		}
+
+		if (!strv) {
+			g_print ("%s\n",
+				 _("No results found matching your query"));
+		} else {
+			g_print ("%s:\n",
+				 _("Results"));
+
+			for (p = strv; *p; p++) {
+				gchar *s;
+
+				s = g_locale_from_utf8 (*p, -1, NULL, NULL, NULL);
+
+				if (!s) {
+					continue;
+				}
+
+				g_print ("  %s\n", s);
+				g_free (s);
+			}
+
+			g_free (strv);
+		}
+	}
+
+	tracker_disconnect (client);
+
+	return EXIT_SUCCESS;
+}

Added: trunk/src/tracker-utils/tracker-stats.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-utils/tracker-stats.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,105 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <locale.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <libtracker/tracker.h>
+
+static void
+get_meta_table_data (gpointer value)
+{
+	gchar **meta;
+	gchar **p;
+	gint	i;
+
+	meta = value;
+
+	for (p = meta, i = 0; *p; p++, i++) {
+		if (i == 0) {
+			g_print ("  %s", *p);
+		} else {
+			g_print (" = %s", *p);
+		}
+	}
+
+	g_print ("\n");
+}
+
+int
+main (int argc, char **argv)
+{
+	TrackerClient  *client;
+	GOptionContext *context;
+	GPtrArray      *array;
+	GError	       *error = NULL;
+
+	bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+	textdomain (GETTEXT_PACKAGE);
+
+	/* Translators: this messagge will apper immediately after the	*/
+	/* usage string - Usage: COMMAND [OPTION]... <THIS_MESSAGE>	*/
+	context = g_option_context_new (_(" - Show number of indexed files for each service"));
+	g_option_context_parse (context, &argc, &argv, NULL);
+	g_option_context_free (context);
+
+	client = tracker_connect (FALSE);
+
+	if (!client) {
+		g_printerr ("%s\n",
+			    _("Could not establish a DBus connection to Tracker"));
+		return EXIT_FAILURE;
+	}
+
+	array = tracker_get_stats (client, &error);
+
+	if (error) {
+		g_printerr ("%s, %s\n",
+			    _("Could not get Tracker statistics"),
+			    error->message);
+		g_error_free (error);
+
+		return EXIT_FAILURE;
+	}
+
+	if (!array) {
+		g_print ("%s\n",
+			 _("No statistics available"));
+	} else {
+		g_print ("%s\n",
+			 _("Statistics:"));
+
+		g_ptr_array_foreach (array, (GFunc) get_meta_table_data, NULL);
+		g_ptr_array_free (array, TRUE);
+	}
+
+	tracker_disconnect (client);
+
+	return EXIT_SUCCESS;
+}

Added: trunk/src/tracker-utils/tracker-status.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-utils/tracker-status.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,76 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <locale.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <libtracker/tracker.h>
+
+gint
+main (gint argc, gchar *argv[])
+{
+	TrackerClient *client;
+	GError	      *error = NULL;
+	gchar	      *status;
+
+	bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+	textdomain (GETTEXT_PACKAGE);
+
+	client = tracker_connect (FALSE);
+
+	if (!client) {
+		g_printerr ("%s\n",
+			    _("Could not establish a DBus connection to Tracker"));
+
+		return EXIT_FAILURE;
+	}
+
+	status = tracker_get_status (client, &error);
+
+	if (error) {
+		g_printerr ("%s, %s\n",
+			    _("Could not get Tracker status"),
+			    error->message);
+		g_error_free (error);
+
+		return EXIT_FAILURE;
+	}
+
+	if (status) {
+		gchar *str;
+
+		str = g_strdup_printf (_("Tracker status is '%s'"), status);
+		g_print ("%s\n", str);
+		g_free (str);
+	}
+
+	tracker_disconnect (client);
+
+	return EXIT_SUCCESS;
+}

Added: trunk/src/tracker-utils/tracker-tag.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-utils/tracker-tag.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,403 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <time.h>
+#include <locale.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+
+#include <libtracker/tracker.h>
+
+#ifdef G_OS_WIN32
+#include <trackerd/mingw-compat.h>
+#endif /* G_OS_WIN32 */
+
+static gint	     limit = 512;
+static gint	     offset;
+static gchar	   **add;
+static gchar	   **remove;
+static gchar	   **search;
+static gchar	   **files;
+static gboolean      remove_all;
+static gboolean      list;
+
+static GOptionEntry entries[] = {
+	{ "add", 'a', 0, G_OPTION_ARG_STRING_ARRAY, &add,
+	  N_("Add specified tag to a file"),
+	  N_("TAG")
+	},
+	{ "remove", 'r', 0, G_OPTION_ARG_STRING_ARRAY, &remove,
+	  N_("Remove specified tag from a file"),
+	  N_("TAG")
+	},
+	{ "remove-all", 'R', 0, G_OPTION_ARG_NONE, &remove_all,
+	  N_("Remove all tags from a file"),
+	  NULL
+	},
+	{ "list", 't', 0, G_OPTION_ARG_NONE, &list,
+	  N_("List all defined tags"),
+	  NULL
+	},
+	{ "limit", 'l', 0, G_OPTION_ARG_INT, &limit,
+	  N_("Limit the number of results shown"),
+	  N_("512")
+	},
+	{ "offset", 'o', 0, G_OPTION_ARG_INT, &offset,
+	  N_("Offset the results"),
+	  N_("0")
+	},
+	{ "search", 's', 0, G_OPTION_ARG_STRING_ARRAY, &search,
+	  N_("Search for files with specified tag"),
+	  N_("TAG")
+	},
+	{ G_OPTION_REMAINING, 0,
+	  G_OPTION_FLAG_FILENAME, G_OPTION_ARG_STRING_ARRAY, &files,
+	  N_("FILE..."),
+	  NULL},
+	{ NULL }
+};
+
+static void
+get_meta_table_data (gpointer value)
+{
+	gchar **meta;
+	gchar **p;
+	gint	i;
+
+	meta = value;
+
+	for (p = meta, i = 0; *p; p++, i++) {
+		if (i == 0) {
+			g_print ("  %s", *p);
+		} else {
+			g_print (", %s", *p);
+		}
+	}
+
+	g_print ("\n");
+}
+
+int
+main (int argc, char **argv)
+{
+	TrackerClient	*client;
+	GOptionContext	*context;
+	GError		*error = NULL;
+	const gchar	*failed = NULL;
+	gchar		*summary;
+	gchar	       **files_resolved = NULL;
+	gchar	       **search_resolved = NULL;
+	gchar	       **tags_to_add = NULL;
+	gchar	       **tags_to_remove = NULL;
+	gchar		*error_str = NULL;
+	gint		 i, j;
+
+	bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+	textdomain (GETTEXT_PACKAGE);
+
+	/* Translators: this messagge will apper immediately after the
+	 * usage string - Usage: COMMAND [OPTION]... <THIS_MESSAGE>
+	 */
+	context = g_option_context_new (_("Add, remove or search for tags"));
+
+	/* Translators: this message will appear after the usage string
+	 * and before the list of options, showing an usage example.
+	 */
+	summary = g_strconcat (_("To add, remove, or search for multiple tags "
+				 "at the same time, join multiple options, for example:"),
+			       "\n"
+			       "\n"
+			       "  -a ", _("TAG"), " -a ", _("TAG"), " -a ", _("TAG"),
+			       NULL);
+
+	g_option_context_set_summary (context, summary);
+	g_option_context_add_main_entries (context, entries, NULL);
+	g_option_context_parse (context, &argc, &argv, NULL);
+	g_free (summary);
+
+	/* Check arguments */
+	if ((add || remove || remove_all) && !files) {
+		failed = _("No files were specified");
+	} else if ((add || remove) && remove_all) {
+		failed = _("Add and delete actions can not be used with remove all actions");
+	} else if (search && files) {
+		failed = _("Files are not needed with searching");
+	} else if (!add && !remove && !remove_all && !files && !search && !list) {
+		failed = _("No arguments were provided");
+	}
+
+	if (failed) {
+		gchar *help;
+
+		g_printerr ("%s\n\n", failed);
+
+		help = g_option_context_get_help (context, TRUE, NULL);
+		g_option_context_free (context);
+		g_printerr (help);
+		g_free (help);
+
+		return EXIT_FAILURE;
+	}
+
+	g_option_context_free (context);
+
+	client = tracker_connect (FALSE);
+
+	if (!client) {
+		g_printerr ("%s\n",
+			    _("Could not establish a DBus connection to Tracker"));
+		return EXIT_FAILURE;
+	}
+
+	if (files) {
+		files_resolved = g_new0 (gchar*, g_strv_length (files) + 1);
+
+		for (i = 0, j = 0; files[i] != NULL; i++) {
+			/* GFile *file; */
+			/* gchar *path; */
+
+			/* file = g_file_new_for_commandline_arg (files[i]); */
+			/* path = g_file_get_path (file); */
+			/* g_object_unref (file); */
+
+			files_resolved[j++] = g_strdup (files[i]);
+		}
+
+		files_resolved[j] = NULL;
+	}
+
+	if (add || remove || remove_all) {
+		if (add) {
+			tags_to_add = g_new0 (gchar*, g_strv_length (add) + 1);
+
+			for (i = 0, j = 0; add[i] != NULL; i++) {
+				gchar *path;
+
+				path = g_locale_to_utf8 (add[i], -1, NULL, NULL, NULL);
+				if (path) {
+					tags_to_add[j++] = path;
+				}
+			}
+		}
+
+		if (remove) {
+			tags_to_remove = g_new0 (gchar*, g_strv_length (remove) + 1);
+
+			for (i = 0, j = 0; remove[i] != NULL; i++) {
+				gchar *path;
+
+				path = g_locale_to_utf8 (remove[i], -1, NULL, NULL, NULL);
+				if (path) {
+					tags_to_remove[j++] = path;
+				}
+			}
+		}
+
+		for (i = 0; files_resolved[i] != NULL; i++) {
+			if (remove_all) {
+				tracker_keywords_remove_all (client,
+							     SERVICE_FILES,
+							     files_resolved[i],
+							     &error);
+
+				if (error) {
+					gchar *str;
+
+					str = g_strdup_printf (_("Could not remove all tags for '%s'"),
+							       files_resolved[i]);
+					g_printerr ("%s, %s\n",
+						    str,
+						    error->message);
+					g_free (str);
+					g_clear_error (&error);
+				}
+			}
+
+			if (tags_to_add) {
+				tracker_keywords_add (client,
+						      SERVICE_FILES,
+						      files_resolved[i],
+						      tags_to_add,
+						      &error);
+
+				if (error) {
+					gchar *str;
+
+					str = g_strdup_printf (_("Could not add tags for '%s'"),
+							       files_resolved[i]);
+					g_printerr ("%s, %s\n",
+						    str,
+						    error->message);
+					g_free (str);
+					g_clear_error (&error);
+				}
+			}
+
+			if (tags_to_remove) {
+				tracker_keywords_remove (client,
+							 SERVICE_FILES,
+							 files_resolved[i],
+							 tags_to_remove,
+							 &error);
+
+				if (error) {
+					gchar *str;
+
+					str = g_strdup_printf (_("Could not remove tags for '%s'"),
+							       files_resolved[i]);
+					g_printerr ("%s, %s\n",
+						    str,
+						    error->message);
+					g_free (str);
+					g_clear_error (&error);
+				}
+			}
+		}
+	}
+
+	if (((!files && list) ||
+	     (!files && (!add && !remove && !remove_all))) && !search) {
+		GPtrArray *array;
+
+		array = tracker_keywords_get_list (client,
+						   SERVICE_FILES,
+						   &error);
+
+		if (error) {
+			error_str = g_strdup (_("Could not get tag list"));
+			goto finish;
+		}
+
+		if (!array) {
+			g_print ("%s\n",
+				 _("No tags found"));
+		} else {
+			g_print ("%s:\n",
+				 _("All tags"));
+
+			g_ptr_array_foreach (array, (GFunc) get_meta_table_data, NULL);
+			g_ptr_array_free (array, TRUE);
+		}
+	}
+
+	if ((files && list) ||
+	    (files && (!add && !remove && !remove_all))) {
+		g_print ("%s:\n",
+			 _("Tags found"));
+
+		for (i = 0, j = 0; files_resolved[i] != NULL; i++) {
+			gchar **tags;
+
+			tags = tracker_keywords_get (client,
+						     SERVICE_FILES,
+						     files_resolved[i],
+						     &error);
+
+			if (error) {
+				error_str = g_strdup_printf (_("Could not get tags for file:'%s'"),
+							     files_resolved[i]);
+				goto finish;
+			}
+
+			if (!tags) {
+				continue;
+			}
+
+			g_print ("  '%s': ", files_resolved[i]);
+
+			for (j = 0; tags[j] != NULL; j++) {
+				if (j > 0) {
+					g_print ("|");
+				}
+
+				g_print ("%s", tags[j]);
+			}
+
+			g_print ("\n");
+
+			g_strfreev (tags);
+		}
+	}
+
+	if (search) {
+		gchar **results;
+
+		search_resolved = g_new0 (gchar*, g_strv_length (search) + 1);
+
+		for (i = 0, j = 0; search[i] != NULL; i++) {
+			gchar *str;
+
+			str = g_locale_to_utf8 (search[i], -1, NULL, NULL, NULL);
+			search_resolved[j++] = str;
+		}
+
+		search_resolved[j] = NULL;
+
+		results = tracker_keywords_search (client, -1,
+						   SERVICE_FILES,
+						   search_resolved,
+						   offset,
+						   limit,
+						   &error);
+
+		if (error) {
+			error_str = g_strdup (_("Could not search tags"));
+			goto finish;
+		}
+
+		if (!results) {
+			g_print ("%s\n",
+				 _("No tags found"));
+		} else {
+			for (i = 0; results[i] != NULL; i++) {
+				g_print ("  %s\n", results[i]);
+			}
+		}
+
+		g_strfreev (results);
+	}
+
+finish:
+	g_strfreev (tags_to_remove);
+	g_strfreev (tags_to_add);
+	g_strfreev (search_resolved);
+	g_strfreev (files_resolved);
+
+	tracker_disconnect (client);
+
+	if (error_str) {
+		g_printerr ("%s, %s\n",
+			    error_str,
+			    error->message);
+		g_free (error_str);
+		g_clear_error (&error);
+
+		return EXIT_FAILURE;
+	}
+
+	return EXIT_SUCCESS;
+}

Added: trunk/src/tracker-utils/tracker-unique.c
==============================================================================
--- (empty file)
+++ trunk/src/tracker-utils/tracker-unique.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,226 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <sys/param.h>
+#include <stdlib.h>
+#include <locale.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <libtracker/tracker.h>
+
+#ifdef G_OS_WIN32
+#include <trackerd/mingw-compat.h>
+#endif /* G_OS_WIN32 */
+
+static gchar	    **fields;
+static gchar	     *service;
+static gchar	     *path;
+static gchar	     *count;
+static gchar	     *sum;
+static gboolean       descending;
+
+static GOptionEntry   entries[] = {
+	{ "path", 'p', 0, G_OPTION_ARG_STRING, &path,
+	  N_("Path to use in query"),
+	  NULL
+	},
+	{ "service", 's', 0, G_OPTION_ARG_STRING, &service,
+	  N_("Search from a specific service"),
+	  NULL
+	},
+	{ "count", 'c', 0, G_OPTION_ARG_STRING, &count,
+	  N_("Count instances of unique fields of this type"),
+	  "e.g. File:Mime"
+	},
+	{ "sum", 'u', 0, G_OPTION_ARG_STRING, &sum,
+	  N_("Sum the values of this field"),
+	  "e.g. File:Mime"
+	},
+	{ "desc", 'o', 0, G_OPTION_ARG_NONE, &descending,
+	  N_("Sort to descending order"),
+	  NULL},
+	{ G_OPTION_REMAINING, 0, 0,
+	  G_OPTION_ARG_STRING_ARRAY, &fields,
+	  N_("Required fields"), NULL},
+	{ NULL }
+};
+
+static void
+get_meta_table_data (gpointer value)
+{
+	gchar **meta;
+	gchar **p;
+	gint	i;
+
+	meta = value;
+
+	for (p = meta, i = 0; *p; p++, i++) {
+		if (i == 0) {
+			g_print ("  %s", *p);
+		} else {
+			g_print (", %s", *p);
+		}
+	}
+
+	g_print ("\n");
+}
+
+int
+main (int argc, char **argv)
+{
+	TrackerClient	*client;
+	ServiceType	 type;
+	GOptionContext	*context;
+	GError		*error = NULL;
+	gchar		*content;
+	gchar		*buffer = NULL;
+	gsize		 size;
+	GPtrArray	*array;
+
+	bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+	textdomain (GETTEXT_PACKAGE);
+
+	context = g_option_context_new ("- Get unique values with an optional RDF query filter");
+	g_option_context_add_main_entries (context, entries, NULL);
+	g_option_context_parse (context, &argc, &argv, NULL);
+
+	if (!fields) {
+		gchar *help;
+
+		g_printerr ("%s\n\n",
+			    _("Fields are missing"));
+
+		help = g_option_context_get_help (context, TRUE, NULL);
+		g_option_context_free (context);
+		g_printerr (help);
+		g_free (help);
+
+		return EXIT_FAILURE;
+	}
+
+	g_option_context_free (context);
+
+	client = tracker_connect (FALSE);
+
+	if (!client) {
+		g_printerr ("%s\n",
+			    _("Could not establish a DBus connection to Tracker"));
+		return EXIT_FAILURE;
+	}
+
+	if (!service) {
+		g_print ("%s\n",
+			 _("Defaulting to 'files' service"));
+
+		type = SERVICE_FILES;
+	} else {
+		type = tracker_service_name_to_type (service);
+
+		if (type == SERVICE_OTHER_FILES && g_ascii_strcasecmp (service, "Other")) {
+			g_printerr ("%s\n",
+				    _("Service not recognized, searching in other files..."));
+		}
+	}
+
+	if (path) {
+		gchar *path_in_utf8;
+
+		path_in_utf8 = g_filename_to_utf8 (path, -1, NULL, NULL, &error);
+		if (error) {
+			g_printerr ("%s:'%s', %s\n",
+				    _("Could not get UTF-8 path from path"),
+				    path,
+				    error->message);
+			g_error_free (error);
+			tracker_disconnect (client);
+
+			return EXIT_FAILURE;
+		}
+
+		g_file_get_contents (path_in_utf8, &content, &size, &error);
+		if (error) {
+			g_printerr ("%s:'%s', %s\n",
+				    _("Could not read file"),
+				    path_in_utf8,
+				    error->message);
+			g_error_free (error);
+			g_free (path_in_utf8);
+			tracker_disconnect (client);
+
+			return EXIT_FAILURE;
+		}
+
+		g_free (path_in_utf8);
+
+		buffer = g_locale_to_utf8 (content, size, NULL, NULL, &error);
+		g_free (content);
+
+		if (error) {
+			g_printerr ("%s, %s\n",
+				    _("Could not convert query file to UTF-8"),
+				    error->message);
+			g_error_free (error);
+			tracker_disconnect (client);
+
+			return EXIT_FAILURE;
+		}
+	}
+
+	array = tracker_metadata_get_unique_values_with_count (client,
+							       type,
+							       fields,
+							       buffer,
+							       count,
+							       descending,
+							       0,
+							       512,
+							       &error);
+	g_free (buffer);
+
+	if (error) {
+		g_printerr ("%s, %s\n",
+			    _("Could not query search"),
+			    error->message);
+		g_error_free (error);
+
+		return EXIT_FAILURE;
+	}
+
+	if (!array) {
+		g_print ("%s\n",
+			 _("No results found matching your query"));
+	} else {
+		g_print ("%s:\n",
+			 _("Results"));
+
+		g_ptr_array_foreach (array, (GFunc) get_meta_table_data, NULL);
+		g_ptr_array_free (array, TRUE);
+	}
+
+	tracker_disconnect (client);
+
+	return EXIT_SUCCESS;
+}

Added: trunk/src/trackerd/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,132 @@
+include $(top_srcdir)/Makefile.decl
+
+INCLUDES =								\
+	-DSHAREDIR=\""$(datadir)"\"					\
+	-DLIBDIR=\""$(libdir)"\"					\
+	-DLOCALEDIR=\""$(localedir)"\" 					\
+	-DMAIL_MODULES_DIR=\""$(libdir)"/tracker/mail-modules\"         \
+	-DG_LOG_DOMAIN=\"Tracker\"					\
+	-I$(top_srcdir)/src						\
+	$(GMIME_CFLAGS)							\
+	$(FAM_CFLAGS)							\
+	$(SQLITE3_CFLAGS)						\
+	$(QDBM_CFLAGS)							\
+	$(DBUS_CFLAGS)							\
+	$(PANGO_CFLAGS)							\
+	$(GIO_CFLAGS)							\
+	$(GMODULE_CFLAGS)						\
+	$(GTHREAD_CFLAGS)						\
+	$(GLIB2_CFLAGS)
+
+if HAVE_INOTIFY
+inotify_libs = $(top_builddir)/src/libinotify/libinotify.la
+endif
+
+#
+# Daemon sources
+#
+libexec_PROGRAMS = trackerd
+
+trackerd_SOURCES =							\
+	tracker-crawler.c						\
+	tracker-crawler.h						\
+	tracker-daemon.c						\
+	tracker-daemon.h						\
+	tracker-db.c							\
+	tracker-db.h							\
+	tracker-dbus.c							\
+	tracker-dbus.h							\
+	tracker-files.c							\
+	tracker-files.h							\
+	tracker-keywords.c						\
+	tracker-keywords.h						\
+	tracker-processor.c						\
+	tracker-processor.h						\
+	tracker-main.c							\
+	tracker-main.h							\
+	tracker-marshal-main.c						\
+	tracker-metadata.c						\
+	tracker-metadata.h						\
+	tracker-monitor.c						\
+	tracker-monitor.h						\
+	tracker-query-tree.c						\
+	tracker-query-tree.h						\
+	tracker-rdf-query.c						\
+	tracker-rdf-query.h						\
+	tracker-search.c						\
+	tracker-search.h						\
+	tracker-status.c						\
+	tracker-status.h						\
+	tracker-utils.c							\
+	tracker-utils.h							\
+	tracker-xesam.c							\
+	tracker-xesam.h							\
+	tracker-xesam-manager.c						\
+	tracker-xesam-manager.h						\
+	tracker-xesam-query.c						\
+	tracker-xesam-query.h						\
+	tracker-xesam-session.c						\
+	tracker-xesam-session.h						\
+	tracker-xesam-live-search.c					\
+	tracker-xesam-live-search.h
+
+if OS_WIN32
+trackerd_win_libs = -lws2_32 -lkernel32
+endif
+
+trackerd_LDADD =							\
+	$(top_builddir)/src/libtracker-db/libtracker-db.la 		\
+	$(top_builddir)/src/libtracker-common/libtracker-common.la 	\
+	$(top_builddir)/src/libstemmer/libstemmer.la	 		\
+	$(inotify_libs)							\
+	$(GMIME_LIBS)							\
+	$(FAM_LIBS)							\
+	$(SQLITE3_LIBS)							\
+	$(QDBM_LIBS)							\
+	$(DBUS_LIBS)							\
+	$(PANGO_LIBS)							\
+	$(GIO_LIBS)							\
+	$(GMODULE_LIBS)							\
+	$(GTHREAD_LIBS)							\
+	$(GLIB2_LIBS)							\
+	$(trackerd_win_libs)						\
+	-lz								\
+	-lm
+
+tracker-marshal.h: tracker-marshal.list
+	$(GLIB_GENMARSHAL) $< --prefix=tracker_marshal --header > $@
+
+tracker-marshal.c: tracker-marshal.list
+	$(GLIB_GENMARSHAL) $< --prefix=tracker_marshal --body > $@
+
+tracker-marshal-main.c: tracker-marshal.c tracker-marshal.h
+
+marshal_sources =                                         		\
+        tracker-marshal.h                             			\
+        tracker-marshal.c
+
+dbus_sources = 								\
+	tracker-daemon-glue.h						\
+	tracker-files-glue.h						\
+	tracker-keywords-glue.h						\
+	tracker-metadata-glue.h						\
+	tracker-search-glue.h						\
+	tracker-xesam-glue.h						\
+	tracker-indexer-client.h
+
+%-glue.h: $(top_srcdir)/data/dbus/%.xml
+	$(DBUSBINDINGTOOL) --mode=glib-server --output=$@ --prefix=$(subst -,_,$*) $^
+
+%-client.h: $(top_srcdir)/data/dbus/%.xml
+	$(DBUSBINDINGTOOL) --mode=glib-client --output=$@ --prefix=$(subst -,_,$*) $^
+
+BUILT_SOURCES = 							\
+	$(dbus_sources)							\
+	$(marshal_sources)
+
+CLEANFILES = $(BUILT_SOURCES)
+
+EXTRA_DIST = 								\
+	$(BUILT_SOURCES)						\
+	tracker-marshal.list
+

Added: trunk/src/trackerd/mingw-compat.h
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/mingw-compat.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,109 @@
+#ifndef MINGW_COMPAT_H
+#define MINGW_COMPAT_H
+
+#include <Windows.h>
+
+#define _fullpath_internal(res,path,size) \
+  (GetFullPathName ((path), (size), (res), NULL) ? (res) : NULL)
+
+#define realpath(path,resolved_path) _fullpath_internal(resolved_path, path, MAX_PATH)
+
+#define getc_unlocked(s) getc(s)
+
+#define RLIMIT_CPU	0		/* CPU time in seconds */
+#define RLIMIT_AS	6		/* address space (virt. memory) limit */
+typedef unsigned long rlim_t;
+
+struct rlimit {
+	rlim_t	rlim_cur;
+	rlim_t	rlim_max;
+};
+
+#define localtime_r( _clock, _result ) \
+	( *(_result) = *localtime( (_clock) ), \
+	  (_result) )
+
+#include <time.h>
+#include <sys/time.h>
+#include <sys/timeb.h>
+
+struct timezone {
+       int tz_minuteswest; /* minutes west of Greenwich */
+       int tz_dsttime;	   /* type of dst correction */
+     };
+
+static int gettimeofday (struct timeval *tv, struct timezone *tz)
+{
+   struct _timeb tb;
+
+   if (!tv)
+      return (-1);
+
+  _ftime (&tb);
+  tv->tv_sec  = tb.time;
+  tv->tv_usec = tb.millitm * 1000 + 500;
+  if (tz)
+  {
+    tz->tz_minuteswest = -60 * _timezone;
+    tz->tz_dsttime = _daylight;
+  }
+  return (0);
+}
+
+// Does not exist in a windows filesystem
+#define S_IFLNK 0
+#define S_IFSOCK 0
+#define S_ISVTX 0
+#define S_ISLNK(X) 0
+#define S_ISUID 0
+#define S_ISGID 0
+#define S_ISGRP 0
+#define S_IXOTH 0
+#define S_IXGRP 0
+
+#define link(from, to) 0
+
+#define	_LK_UNLCK	0	/* Unlock */
+#define	_LK_LOCK	1	/* Lock */
+#define	_LK_NBLCK	2	/* Non-blocking lock */
+#define	_LK_RLCK	3	/* Lock for read only */
+#define	_LK_NBRLCK	4	/* Non-blocking lock for read only */
+
+#define F_TLOCK _LK_NBLCK /* Test and lock a section for exclusive use */
+
+#ifndef SIGHUP
+#define SIGHUP 1 /* hangup */
+#endif
+#ifndef SIGBUS
+#define SIGBUS 7 /* bus error */
+#endif
+#ifndef SIGKILL
+#define SIGKILL 9 /* kill (cannot be caught or ignored) */
+#endif
+#ifndef SIGSEGV
+#define SIGSEGV 11 /* segment violation */
+#endif
+#ifndef SIGPIPE
+#define SIGPIPE 13 /* write on a pipe with no one to read it */
+#endif
+#ifndef SIGCHLD
+#define SIGCHLD 20 /* to parent on child stop or exit */
+#endif
+#ifndef SIGUSR1
+#define SIGUSR1 30 /* user defined signal 1 */
+#endif
+#ifndef SIGUSR2
+#define SIGUSR2 31 /* user defined signal 2 */
+#endif
+
+#define sigemptyset(pset)    (*(pset) = 0)
+#define sigfillset(pset)     (*(pset) = (unsigned int)-1)
+#define sigaddset(pset, num) (*(pset) |= (1L<<(num)))
+#define sigdelset(pset, num) (*(pset) &= ~(1L<<(num)))
+#define sigismember(pset, num) (*(pset) & (1L<<(num)))
+
+#define lockf _locking
+
+#define nice(nice_level) 0
+
+#endif

Added: trunk/src/trackerd/tracker-crawler.c
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-crawler.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,1000 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gio/gio.h>
+
+#include <libtracker-common/tracker-dbus.h>
+#include <libtracker-common/tracker-file-utils.h>
+#include <libtracker-common/tracker-type-utils.h>
+#include <libtracker-common/tracker-utils.h>
+#include <libtracker-common/tracker-module-config.h>
+
+#include "tracker-crawler.h"
+#include "tracker-dbus.h"
+#include "tracker-indexer-client.h"
+#include "tracker-monitor.h"
+#include "tracker-marshal.h"
+#include "tracker-status.h"
+
+#define TRACKER_CRAWLER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TRACKER_TYPE_CRAWLER, TrackerCrawlerPrivate))
+
+#define FILE_ATTRIBUTES				\
+	G_FILE_ATTRIBUTE_STANDARD_NAME ","	\
+	G_FILE_ATTRIBUTE_STANDARD_TYPE
+
+#define FILES_QUEUE_PROCESS_INTERVAL 2000
+#define FILES_QUEUE_PROCESS_MAX      5000
+
+/* This is the number of files to be called back with from GIO at a
+ * time so we don't get called back for every file.
+ */
+#define FILES_GROUP_SIZE	     100
+
+struct _TrackerCrawlerPrivate {
+	TrackerConfig  *config;
+
+	gchar	       *module_name;
+
+	/* Found data */
+	GQueue	       *directories;
+	GQueue	       *files;
+
+	/* Idle handler for processing found data */
+	guint		idle_id;
+
+	/* Options */
+	gboolean	use_module_paths;
+
+	/* Actual paths that exist which we are crawling */
+	GSList	       *paths;
+	GSList	       *current_path;
+	gboolean	handled_paths;
+
+	GSList	       *recurse_paths;
+	GSList	       *current_recurse_path;
+	gboolean	handled_recurse_paths;
+
+	GList	       *ignored_directory_patterns;
+	GList	       *ignored_file_patterns;
+	GList	       *index_file_patterns;
+
+	/* Legacy NoWatchDirectoryRoots */
+	GSList	       *no_watch_directory_roots;
+	GSList	       *watch_directory_roots;
+	GSList	       *crawl_directory_roots;
+
+	/* Statistics */
+	GTimer	       *timer;
+	guint		enumerations;
+	guint		directories_found;
+	guint		directories_ignored;
+	guint		files_found;
+	guint		files_ignored;
+
+	/* Status */
+	gboolean	running;
+	gboolean	finished;
+};
+
+enum {
+	PROCESSING_DIRECTORY,
+	PROCESSING_FILE,
+	FINISHED,
+	LAST_SIGNAL
+};
+
+typedef struct {
+	TrackerCrawler *crawler;
+	GFile	       *parent;
+} EnumeratorData;
+
+static void tracker_crawler_finalize (GObject	      *object);
+static void file_enumerate_next      (GFileEnumerator *enumerator,
+				      EnumeratorData  *ed);
+static void file_enumerate_children  (TrackerCrawler  *crawler,
+				      GFile	      *file);
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE(TrackerCrawler, tracker_crawler, G_TYPE_OBJECT)
+
+static void
+tracker_crawler_class_init (TrackerCrawlerClass *klass)
+{
+	GObjectClass *object_class;
+
+	object_class = G_OBJECT_CLASS (klass);
+
+	object_class->finalize = tracker_crawler_finalize;
+
+	signals[PROCESSING_DIRECTORY] =
+		g_signal_new ("processing-directory",
+			      G_TYPE_FROM_CLASS (klass),
+			      G_SIGNAL_RUN_LAST,
+			      0,
+			      NULL, NULL,
+			      tracker_marshal_VOID__STRING_OBJECT,
+			      G_TYPE_NONE,
+			      2,
+			      G_TYPE_STRING,
+			      G_TYPE_OBJECT);
+	signals[PROCESSING_FILE] =
+		g_signal_new ("processing-file",
+			      G_TYPE_FROM_CLASS (klass),
+			      G_SIGNAL_RUN_LAST,
+			      0,
+			      NULL, NULL,
+			      tracker_marshal_VOID__STRING_OBJECT,
+			      G_TYPE_NONE,
+			      2,
+			      G_TYPE_STRING,
+			      G_TYPE_OBJECT);
+	signals[FINISHED] =
+		g_signal_new ("finished",
+			      G_TYPE_FROM_CLASS (klass),
+			      G_SIGNAL_RUN_LAST,
+			      0,
+			      NULL, NULL,
+			      tracker_marshal_VOID__STRING_UINT_UINT_UINT_UINT,
+			      G_TYPE_NONE,
+			      5,
+			      G_TYPE_STRING,
+			      G_TYPE_UINT,
+			      G_TYPE_UINT,
+			      G_TYPE_UINT,
+			      G_TYPE_UINT);
+
+	g_type_class_add_private (object_class, sizeof (TrackerCrawlerPrivate));
+}
+
+static void
+tracker_crawler_init (TrackerCrawler *object)
+{
+	TrackerCrawlerPrivate *priv;
+
+	object->private = TRACKER_CRAWLER_GET_PRIVATE (object);
+
+	priv = object->private;
+
+	priv->directories = g_queue_new ();
+	priv->files = g_queue_new ();
+}
+
+static void
+tracker_crawler_finalize (GObject *object)
+{
+	TrackerCrawlerPrivate *priv;
+
+	priv = TRACKER_CRAWLER_GET_PRIVATE (object);
+
+	if (priv->timer) {
+		g_timer_destroy (priv->timer);
+	}
+
+	g_slist_foreach (priv->no_watch_directory_roots, (GFunc) g_free, NULL);
+	g_slist_free (priv->no_watch_directory_roots);
+
+	g_slist_foreach (priv->watch_directory_roots, (GFunc) g_free, NULL);
+	g_slist_free (priv->watch_directory_roots);
+
+	g_slist_foreach (priv->crawl_directory_roots, (GFunc) g_free, NULL);
+	g_slist_free (priv->crawl_directory_roots);
+
+	if (priv->index_file_patterns) {
+		g_list_free (priv->index_file_patterns);
+	}
+
+	if (priv->ignored_file_patterns) {
+		g_list_free (priv->ignored_file_patterns);
+	}
+
+	/* Don't free the 'current_' variant of these, they are just
+	 * place holders so we know our status.
+	 */
+	g_slist_foreach (priv->paths, (GFunc) g_free, NULL);
+	g_slist_free (priv->paths);
+
+	g_slist_foreach (priv->recurse_paths, (GFunc) g_free, NULL);
+	g_slist_free (priv->recurse_paths);
+
+	if (priv->idle_id) {
+		g_source_remove (priv->idle_id);
+	}
+
+	g_queue_foreach (priv->files, (GFunc) g_object_unref, NULL);
+	g_queue_free (priv->files);
+
+	g_queue_foreach (priv->directories, (GFunc) g_object_unref, NULL);
+	g_queue_free (priv->directories);
+
+	g_free (priv->module_name);
+
+	g_object_unref (priv->config);
+
+	G_OBJECT_CLASS (tracker_crawler_parent_class)->finalize (object);
+}
+
+TrackerCrawler *
+tracker_crawler_new (TrackerConfig *config,
+		     const gchar   *module_name)
+{
+	TrackerCrawler *crawler;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), NULL);
+	g_return_val_if_fail (module_name != NULL, NULL);
+
+	crawler = g_object_new (TRACKER_TYPE_CRAWLER, NULL);
+
+	crawler->private->config = g_object_ref (config);
+
+	crawler->private->module_name = g_strdup (module_name);
+
+	/* Set up crawl data */
+	crawler->private->ignored_directory_patterns =
+		tracker_module_config_get_ignored_directory_patterns (module_name);
+	crawler->private->ignored_file_patterns =
+		tracker_module_config_get_ignored_file_patterns (module_name);
+	crawler->private->index_file_patterns =
+		tracker_module_config_get_index_file_patterns (module_name);
+
+	/* Should we use module config paths? If true, when we
+	 * _start() the module config paths are used to import paths
+	 * to crawl. By default this is TRUE.
+	 */
+	crawler->private->use_module_paths = TRUE;
+
+	return crawler;
+}
+
+/*
+ * Functions
+ */
+
+static gboolean
+is_path_ignored (TrackerCrawler *crawler,
+		 const gchar	*path,
+		 gboolean	 is_directory)
+{
+	GList	 *l;
+	gchar	 *basename;
+	gboolean  ignore;
+
+	if (tracker_is_empty_string (path)) {
+		return TRUE;
+	}
+
+	if (!g_utf8_validate (path, -1, NULL)) {
+		g_message ("Ignoring path:'%s', not valid UTF-8", path);
+		return TRUE;
+	}
+
+	if (is_directory) {
+		GSList *sl;
+
+		/* Most common things to ignore */
+		if (strcmp (path, "/dev") == 0 ||
+		    strcmp (path, "/lib") == 0 ||
+		    strcmp (path, "/proc") == 0 ||
+		    strcmp (path, "/sys") == 0) {
+			return TRUE;
+		}
+
+		if (g_str_has_prefix (path, g_get_tmp_dir ())) {
+			return TRUE;
+		}
+
+		/* Check ignored directories in config */
+		for (sl = crawler->private->no_watch_directory_roots; sl; sl = sl->next) {
+			if (strcmp (sl->data, path) == 0) {
+				return TRUE;
+			}
+		}
+	}
+
+	/* Check basename against pattern matches */
+	basename = g_path_get_basename (path);
+	ignore = TRUE;
+
+	if (!basename) {
+		goto done;
+	}
+
+	/* Test ignore types */
+	if (is_directory) {
+		GSList *sl;
+
+		/* If directory begins with ".", check it isn't one of
+		 * the top level directories to watch/crawl if it
+		 * isn't we ignore it. If it is, we don't.
+		 */
+		if (basename[0] == '.') {
+			for (sl = crawler->private->watch_directory_roots; sl; sl = sl->next) {
+				if (strcmp (sl->data, path) == 0) {
+					ignore = FALSE;
+					goto done;
+				}
+			}
+
+			for (sl = crawler->private->crawl_directory_roots; sl; sl = sl->next) {
+				if (strcmp (sl->data, path) == 0) {
+					ignore = FALSE;
+					goto done;
+				}
+			}
+
+			goto done;
+		}
+
+		/* Check module directory ignore patterns */
+		for (l = crawler->private->ignored_directory_patterns; l; l = l->next) {
+			if (g_pattern_match_string (l->data, basename)) {
+				goto done;
+			}
+		}
+	} else {
+		if (basename[0] == '.') {
+			goto done;
+		}
+
+		/* Check module file ignore patterns */
+		for (l = crawler->private->ignored_file_patterns; l; l = l->next) {
+			if (g_pattern_match_string (l->data, basename)) {
+				goto done;
+			}
+		}
+
+		/* Check module file match patterns */
+		for (l = crawler->private->index_file_patterns; l; l = l->next) {
+			if (!g_pattern_match_string (l->data, basename)) {
+				goto done;
+			}
+		}
+	}
+
+	ignore = FALSE;
+
+done:
+	g_free (basename);
+
+	return ignore;
+}
+
+static void
+add_file (TrackerCrawler *crawler,
+	  GFile		 *file)
+{
+	gchar *path;
+
+	g_return_if_fail (G_IS_FILE (file));
+
+	path = g_file_get_path (file);
+
+	if (is_path_ignored (crawler, path, FALSE)) {
+		crawler->private->files_ignored++;
+
+		g_debug ("Ignored:'%s' (%d)",
+			 path,
+			 crawler->private->enumerations);
+	} else {
+		crawler->private->files_found++;
+
+		g_debug ("Found  :'%s' (%d)",
+			 path,
+			 crawler->private->enumerations);
+
+		g_queue_push_tail (crawler->private->files, g_object_ref (file));
+	}
+
+	g_free (path);
+}
+
+static void
+add_directory (TrackerCrawler *crawler,
+	       GFile	      *file)
+{
+	gchar *path;
+
+	g_return_if_fail (G_IS_FILE (file));
+
+	path = g_file_get_path (file);
+
+	if (is_path_ignored (crawler, path, TRUE)) {
+		crawler->private->directories_ignored++;
+
+		g_debug ("Ignored:'%s' (%d)",
+			 path,
+			 crawler->private->enumerations);
+	} else {
+		crawler->private->directories_found++;
+
+		g_debug ("Found  :'%s' (%d)",
+			 path,
+			 crawler->private->enumerations);
+
+		g_queue_push_tail (crawler->private->directories, g_object_ref (file));
+	}
+
+	g_free (path);
+}
+
+static void
+process_file (TrackerCrawler *crawler,
+	      const gchar    *module_name,
+	      GFile	     *file)
+{
+	g_signal_emit (crawler, signals[PROCESSING_FILE], 0, module_name, file);
+}
+
+static void
+process_directory (TrackerCrawler *crawler,
+		   const gchar	  *module_name,
+		   GFile	  *file)
+{
+	g_signal_emit (crawler, signals[PROCESSING_DIRECTORY], 0, module_name, file);
+
+	file_enumerate_children (crawler, file);
+}
+
+static gboolean
+process_func (gpointer data)
+{
+	TrackerCrawler	      *crawler;
+	TrackerCrawlerPrivate *priv;
+	GFile		      *file;
+
+	crawler = TRACKER_CRAWLER (data);
+	priv = crawler->private;
+
+	/* If manually paused, we hold off until unpaused */
+	if (tracker_status_get_is_paused_manually () ||
+	    tracker_status_get_is_paused_for_io ()) {
+		return TRUE;
+	}
+
+	/* Throttle the crawler, with testing, throttling every item
+	 * took the time to crawl 130k files from 7 seconds up to 68
+	 * seconds. So it is important to get this figure right.
+	 */
+	tracker_throttle (priv->config, 25);
+
+	/* Crawler files */
+	file = g_queue_pop_head (priv->files);
+
+	if (file) {
+		process_file (crawler, priv->module_name, file);
+		g_object_unref (file);
+
+		return TRUE;
+	}
+
+	/* Crawler directories */
+	file = g_queue_pop_head (priv->directories);
+
+	if (file) {
+		process_directory (crawler, priv->module_name, file);
+		g_object_unref (file);
+
+		return TRUE;
+	}
+
+	/* If we still have some async operations in progress, wait
+	 * for them to finish, if not, we are truly done.
+	 */
+	if (priv->enumerations > 0) {
+		return TRUE;
+	}
+
+	/* Process next path in list */
+	if (!priv->handled_paths) {
+		/* This is done so we don't go over the list again
+		 * when we get to the end and the current item = NULL.
+		 */
+		priv->handled_paths = TRUE;
+
+		if (!priv->current_path) {
+			priv->current_path = priv->paths;
+		}
+	} else {
+		if (priv->current_path) {
+			priv->current_path = priv->current_path->next;
+		}
+	}
+
+	if (priv->current_path) {
+		g_message ("  Searching directory:'%s'",
+			   (gchar*) priv->current_path->data);
+
+		file = g_file_new_for_path (priv->current_path->data);
+		add_directory (crawler, file);
+		g_object_unref (file);
+
+		return TRUE;
+	}
+
+	/* Process next recursive path in list */
+	if (!priv->handled_recurse_paths) {
+		/* This is done so we don't go over the list again
+		 * when we get to the end and the current item = NULL.
+		 */
+		priv->handled_recurse_paths = TRUE;
+
+		if (!priv->current_recurse_path) {
+			priv->current_recurse_path = priv->recurse_paths;
+		}
+	} else {
+		if (priv->current_recurse_path) {
+			priv->current_recurse_path = priv->current_recurse_path->next;
+		}
+	}
+
+	if (priv->current_recurse_path) {
+		g_message ("  Searching directory:'%s' (recursively)",
+			   (gchar *) priv->current_recurse_path->data);
+
+		file = g_file_new_for_path (priv->current_recurse_path->data);
+		add_directory (crawler, file);
+		g_object_unref (file);
+
+		return TRUE;
+	}
+
+	priv->idle_id = 0;
+	priv->finished = TRUE;
+
+	tracker_crawler_stop (crawler);
+
+	return FALSE;
+}
+
+static EnumeratorData *
+enumerator_data_new (TrackerCrawler *crawler,
+		     GFile	    *parent)
+{
+	EnumeratorData *ed;
+
+	ed = g_slice_new0 (EnumeratorData);
+	ed->crawler = g_object_ref (crawler);
+	ed->parent = g_object_ref (parent);
+
+	return ed;
+}
+
+static void
+enumerator_data_free (EnumeratorData *ed)
+{
+	g_object_unref (ed->parent);
+	g_object_unref (ed->crawler);
+	g_slice_free (EnumeratorData, ed);
+}
+
+static void
+file_enumerator_close_cb (GObject      *enumerator,
+			  GAsyncResult *result,
+			  gpointer	user_data)
+{
+	TrackerCrawler *crawler;
+
+	crawler = TRACKER_CRAWLER (user_data);
+	crawler->private->enumerations--;
+
+	if (!g_file_enumerator_close_finish (G_FILE_ENUMERATOR (enumerator),
+					     result,
+					     NULL)) {
+		g_warning ("Couldn't close GFileEnumerator:%p", enumerator);
+	}
+}
+
+static void
+file_enumerate_next_cb (GObject      *object,
+			GAsyncResult *result,
+			gpointer      user_data)
+{
+	TrackerCrawler	*crawler;
+	EnumeratorData	*ed;
+	GFileEnumerator *enumerator;
+	GFile		*parent, *child;
+	GFileInfo	*info;
+	GList		*files, *l;
+
+	enumerator = G_FILE_ENUMERATOR (object);
+
+	ed = (EnumeratorData*) user_data;
+	crawler = ed->crawler;
+	parent = ed->parent;
+
+	files = g_file_enumerator_next_files_finish (enumerator,
+						     result,
+						     NULL);
+
+	if (!files || !crawler->private->running) {
+		/* No more files or we are stopping anyway, so clean
+		 * up and close all file enumerators.
+		 */
+		if (files) {
+			g_list_foreach (files, (GFunc) g_object_unref, NULL);
+			g_list_free (files);
+		}
+
+		enumerator_data_free (ed);
+		g_file_enumerator_close_async (enumerator,
+					       G_PRIORITY_DEFAULT,
+					       NULL,
+					       file_enumerator_close_cb,
+					       crawler);
+		g_object_unref (enumerator);
+
+		return;
+	}
+
+	for (l = files; l; l = l->next) {
+		info = l->data;
+		child = g_file_get_child (parent, g_file_info_get_name (info));
+
+		if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
+			/* This is a bit of a hack, but we assume this is a
+			 * recursive lookup because the current non-recursive
+			 * path is NULL, meaning they have all been traversed
+			 * already.
+			 */
+			if (crawler->private->handled_paths) {
+				add_directory (crawler, child);
+			}
+		} else {
+			add_file (crawler, child);
+		}
+
+		g_object_unref (child);
+		g_object_unref (info);
+	}
+
+	g_list_free (files);
+
+	/* Get next files */
+	file_enumerate_next (enumerator, ed);
+}
+
+static void
+file_enumerate_next (GFileEnumerator *enumerator,
+		     EnumeratorData  *ed)
+{
+	g_file_enumerator_next_files_async (enumerator,
+					    FILES_GROUP_SIZE,
+					    G_PRIORITY_LOW,
+					    NULL,
+					    file_enumerate_next_cb,
+					    ed);
+}
+
+static void
+file_enumerate_children_cb (GObject	 *file,
+			    GAsyncResult *result,
+			    gpointer	  user_data)
+{
+	TrackerCrawler	*crawler;
+	EnumeratorData	*ed;
+	GFileEnumerator *enumerator;
+	GFile		*parent;
+
+	parent = G_FILE (file);
+	crawler = TRACKER_CRAWLER (user_data);
+	enumerator = g_file_enumerate_children_finish (parent, result, NULL);
+
+	if (!enumerator) {
+		crawler->private->enumerations--;
+		return;
+	}
+
+	ed = enumerator_data_new (crawler, parent);
+
+	/* Start traversing the directory's files */
+	file_enumerate_next (enumerator, ed);
+}
+
+static void
+file_enumerate_children (TrackerCrawler *crawler,
+			 GFile		*file)
+{
+	crawler->private->enumerations++;
+
+	g_file_enumerate_children_async (file,
+					 FILE_ATTRIBUTES,
+					 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+					 G_PRIORITY_DEFAULT,
+					 NULL,
+					 file_enumerate_children_cb,
+					 crawler);
+}
+
+static void
+prune_none_existing_paths (TrackerCrawler *crawler)
+{
+	TrackerCrawlerPrivate *priv;
+
+	priv = crawler->private;
+
+	if (priv->recurse_paths) {
+		GSList	 *new_list;
+		GSList	 *l;
+		GFile	 *file;
+		gchar	 *path;
+		gboolean  exists;
+
+		new_list = NULL;
+
+		/* Check the currently set recurse paths are real */
+		for (l = priv->recurse_paths; l; l = l->next) {
+			path = l->data;
+
+			/* Check location exists before we do anything */
+			file = g_file_new_for_path (path);
+			exists = g_file_query_exists (file, NULL);
+
+			if (exists) {
+				g_message ("  Directory:'%s' added to list to crawl (recursively)",
+					   path);
+				new_list = g_slist_prepend (new_list, g_strdup (path));
+			} else {
+				g_message ("  Directory:'%s' does not exist",
+					   path);
+			}
+
+			g_object_unref (file);
+		}
+
+		new_list = g_slist_reverse (new_list);
+		g_slist_foreach (priv->recurse_paths, (GFunc) g_free, NULL);
+		g_slist_free (priv->recurse_paths);
+		priv->recurse_paths = new_list;
+	}
+}
+
+gboolean
+tracker_crawler_start (TrackerCrawler *crawler)
+{
+	TrackerCrawlerPrivate *priv;
+	GFile		      *file;
+	GSList		      *paths = NULL;
+	GList		      *recurse_directories;
+	GList		      *directories;
+	GList		      *l;
+	GSList		      *sl;
+	gchar		      *path;
+	gboolean	       exists;
+
+	g_return_val_if_fail (TRACKER_IS_CRAWLER (crawler), FALSE);
+
+	priv = crawler->private;
+
+	g_message ("Crawling directories for module:'%s'",
+		   crawler->private->module_name);
+
+	prune_none_existing_paths (crawler);
+
+	if (priv->use_module_paths) {
+		recurse_directories =
+			tracker_module_config_get_monitor_recurse_directories (priv->module_name);
+		directories =
+			tracker_module_config_get_monitor_directories (priv->module_name);
+
+		if (recurse_directories || directories) {
+			/* First we do non-recursive directories */
+			for (l = directories; l; l = l->next) {
+				path = l->data;
+
+				/* Check location exists before we do anything */
+				file = g_file_new_for_path (path);
+				exists = g_file_query_exists (file, NULL);
+
+				if (!exists) {
+					g_message ("  Directory:'%s' does not exist",
+						   path);
+					g_object_unref (file);
+					continue;
+				}
+
+				g_message ("  Directory:'%s' added to list to crawl",
+					   path);
+
+				priv->paths = g_slist_append (priv->paths, g_strdup (l->data));
+				g_object_unref (file);
+			}
+
+			g_list_free (directories);
+
+			/* Second we do recursive directories */
+			for (l = recurse_directories; l; l = l->next) {
+				path = l->data;
+
+				/* Check location exists before we do anything */
+				file = g_file_new_for_path (path);
+				exists = g_file_query_exists (file, NULL);
+
+				if (!exists) {
+					g_message ("  Directory:'%s' does not exist",
+						   path);
+					g_object_unref (file);
+					continue;
+				}
+
+				g_message ("  Directory:'%s' added to list to crawl (recursively)",
+					   path);
+
+				priv->recurse_paths = g_slist_append (priv->recurse_paths, g_strdup (l->data));
+				g_object_unref (file);
+			}
+
+			g_list_free (recurse_directories);
+		} else {
+			g_message ("  No directories from module config");
+		}
+	} else {
+		g_message ("  Not using module config paths, using special paths added");
+	}
+
+	if (!priv->paths && !priv->recurse_paths) {
+		g_message ("  No directories that actually exist to iterate, doing nothing");
+		return FALSE;
+	}
+
+	/* Filter duplicates */
+	sl = priv->paths;
+	priv->paths = tracker_path_list_filter_duplicates (priv->paths);
+
+	g_slist_foreach (sl, (GFunc) g_free, NULL);
+	g_slist_free (sl);
+
+	sl = priv->recurse_paths;
+	priv->recurse_paths = tracker_path_list_filter_duplicates (priv->recurse_paths);
+
+	g_slist_foreach (sl, (GFunc) g_free, NULL);
+	g_slist_free (sl);
+
+	/* Set up legacy NoWatchDirectoryRoots so we don't have to get
+	 * them from the config for EVERY file we traverse.
+	 */
+	g_slist_foreach (priv->no_watch_directory_roots, (GFunc) g_free, NULL);
+	g_slist_free (priv->no_watch_directory_roots);
+	sl = tracker_config_get_no_watch_directory_roots (priv->config);
+	priv->no_watch_directory_roots = tracker_gslist_copy_with_string_data (sl);
+
+	g_slist_foreach (priv->watch_directory_roots, (GFunc) g_free, NULL);
+	g_slist_free (priv->watch_directory_roots);
+	sl = tracker_config_get_watch_directory_roots (priv->config);
+	priv->watch_directory_roots = tracker_gslist_copy_with_string_data (sl);
+
+	g_slist_foreach (priv->crawl_directory_roots, (GFunc) g_free, NULL);
+	g_slist_free (priv->crawl_directory_roots);
+	sl = tracker_config_get_crawl_directory_roots (priv->config);
+	priv->crawl_directory_roots = tracker_gslist_copy_with_string_data (sl);
+
+	/* Time the event */
+	if (priv->timer) {
+		g_timer_destroy (priv->timer);
+	}
+
+	priv->timer = g_timer_new ();
+
+	/* Set idle handler to process directories and files found */
+	priv->idle_id = g_idle_add (process_func, crawler);
+
+	/* Set as running now */
+	priv->running = TRUE;
+	priv->finished = FALSE;
+
+	/* Reset stats */
+	priv->directories_found = 0;
+	priv->directories_ignored = 0;
+	priv->files_found = 0;
+	priv->files_ignored = 0;
+
+	g_slist_free (paths);
+
+	return TRUE;
+}
+
+void
+tracker_crawler_stop (TrackerCrawler *crawler)
+{
+	TrackerCrawlerPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CRAWLER (crawler));
+
+	priv = crawler->private;
+
+	g_message ("  %s crawling files in %4.4f seconds",
+		   priv->finished ? "Finished" : "Stopped",
+		   g_timer_elapsed (priv->timer, NULL));
+	g_message ("  Found %d directories, ignored %d directories",
+		   priv->directories_found,
+		   priv->directories_ignored);
+	g_message ("  Found %d files, ignored %d files",
+		   priv->files_found,
+		   priv->files_ignored);
+
+	priv->running = FALSE;
+
+	if (priv->idle_id) {
+		g_source_remove (priv->idle_id);
+	}
+
+	g_timer_destroy (priv->timer);
+	priv->timer = NULL;
+
+	g_signal_emit (crawler, signals[FINISHED], 0,
+		       priv->module_name,
+		       priv->directories_found,
+		       priv->directories_ignored,
+		       priv->files_found,
+		       priv->files_ignored);
+}
+
+/* This is a convenience function to add extra locations because
+ * sometimes we want to add locations like the MMC or others to the
+ * "Files" module, for example.
+ */
+void
+tracker_crawler_add_path (TrackerCrawler *crawler,
+			  const gchar	 *path)
+{
+	TrackerCrawlerPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CRAWLER (crawler));
+	g_return_if_fail (path != NULL);
+
+	priv = crawler->private;
+
+	g_return_if_fail (priv->running == FALSE);
+
+	priv->recurse_paths = g_slist_append (priv->recurse_paths, g_strdup (path));
+}
+
+void
+tracker_crawler_set_use_module_paths (TrackerCrawler *crawler,
+				      gboolean	      use_paths)
+{
+	TrackerCrawlerPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CRAWLER (crawler));
+
+	priv = crawler->private;
+
+	priv->use_module_paths = use_paths;
+}
+
+gboolean
+tracker_crawler_is_path_ignored (TrackerCrawler *crawler,
+				 const gchar	*path,
+				 gboolean	 is_directory)
+{
+	g_return_val_if_fail (TRACKER_IS_CRAWLER (crawler), TRUE);
+
+	/* We have an internal function here we call. The reason for
+	 * this is that it is expensive to type check the Crawler
+	 * object for EVERY file we process. Internally, we don't do
+	 * that. Externally we do. Externally this is used by the
+	 * processor when we get new monitor events to know if we
+	 * should be sending new files to the indexer.
+	 */
+	return is_path_ignored (crawler, path, is_directory);
+}

Added: trunk/src/trackerd/tracker-crawler.h
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-crawler.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,67 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKERD_CRAWLER_H__
+#define __TRACKERD_CRAWLER_H__
+
+#include <glib-object.h>
+
+#include <libtracker-common/tracker-config.h>
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_CRAWLER		(tracker_crawler_get_type ())
+#define TRACKER_CRAWLER(object)		(G_TYPE_CHECK_INSTANCE_CAST ((object), TRACKER_TYPE_CRAWLER, TrackerCrawler))
+#define TRACKER_CRAWLER_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), TRACKER_TYPE_CRAWLER, TrackerCrawlerClass))
+#define TRACKER_IS_CRAWLER(object)	(G_TYPE_CHECK_INSTANCE_TYPE ((object), TRACKER_TYPE_CRAWLER))
+#define TRACKER_IS_CRAWLER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TRACKER_TYPE_CRAWLER))
+#define TRACKER_CRAWLER_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj), TRACKER_TYPE_CRAWLER, TrackerCrawlerClass))
+
+typedef struct _TrackerCrawler	       TrackerCrawler;
+typedef struct _TrackerCrawlerClass    TrackerCrawlerClass;
+typedef struct _TrackerCrawlerPrivate  TrackerCrawlerPrivate;
+
+struct _TrackerCrawler {
+	GObject		       parent;
+	TrackerCrawlerPrivate *private;
+};
+
+struct _TrackerCrawlerClass {
+	GObjectClass	       parent;
+};
+
+GType		tracker_crawler_get_type	     (void);
+TrackerCrawler *tracker_crawler_new		     (TrackerConfig  *config,
+						      const gchar    *module_name);
+gboolean	tracker_crawler_start		     (TrackerCrawler *crawler);
+void		tracker_crawler_stop		     (TrackerCrawler *crawler);
+gboolean	tracker_crawler_is_path_ignored      (TrackerCrawler *crawler,
+						      const gchar    *path,
+						      gboolean	      is_directory);
+
+/* Convenience API for old .cfg file */
+void		tracker_crawler_add_path	     (TrackerCrawler *crawler,
+						      const gchar    *path);
+void		tracker_crawler_set_use_module_paths (TrackerCrawler *crawler,
+						      gboolean	      use_paths);
+
+G_END_DECLS
+
+#endif /* __TRACKERD_CRAWLER_H__ */

Added: trunk/src/trackerd/tracker-daemon.c
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-daemon.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,728 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libtracker-common/tracker-log.h>
+#include <libtracker-common/tracker-config.h>
+#include <libtracker-common/tracker-dbus.h>
+
+#include <libtracker-db/tracker-db-dbus.h>
+#include <libtracker-db/tracker-db-index.h>
+#include <libtracker-db/tracker-db-manager.h>
+
+#include "tracker-dbus.h"
+#include "tracker-daemon.h"
+#include "tracker-db.h"
+#include "tracker-indexer-client.h"
+#include "tracker-main.h"
+#include "tracker-status.h"
+#include "tracker-marshal.h"
+
+#define TRACKER_DAEMON_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TRACKER_TYPE_DAEMON, TrackerDaemonPrivate))
+
+typedef struct {
+	TrackerConfig	 *config;
+	TrackerProcessor *processor;
+	DBusGProxy	 *indexer_proxy;
+	GPtrArray	 *last_stats;
+} TrackerDaemonPrivate;
+
+enum {
+	INDEX_STATE_CHANGE,
+	INDEX_FINISHED,
+	INDEX_PROGRESS,
+	SERVICE_STATISTICS_UPDATED,
+	LAST_SIGNAL
+};
+
+static void tracker_daemon_finalize (GObject	*object);
+static void indexer_pause_cb	    (DBusGProxy *proxy,
+				     GError	*error,
+				     gpointer	 user_data);
+static void indexer_continue_cb     (DBusGProxy *proxy,
+				     GError	*error,
+				     gpointer	 user_data);
+static void indexer_paused_cb	    (DBusGProxy *proxy,
+				     GError	*error,
+				     gpointer	 user_data);
+static void indexer_continued_cb    (DBusGProxy *proxy,
+				     GError	*error,
+				     gpointer	 user_data);
+
+static guint signals[LAST_SIGNAL] = {0};
+
+G_DEFINE_TYPE(TrackerDaemon, tracker_daemon, G_TYPE_OBJECT)
+
+static void
+tracker_daemon_class_init (TrackerDaemonClass *klass)
+{
+	GObjectClass *object_class;
+
+	object_class = G_OBJECT_CLASS (klass);
+
+	object_class->finalize = tracker_daemon_finalize;
+
+	signals[INDEX_STATE_CHANGE] =
+		g_signal_new ("index-state-change",
+			      G_TYPE_FROM_CLASS (klass),
+			      G_SIGNAL_RUN_LAST,
+			      0,
+			      NULL, NULL,
+			      tracker_marshal_VOID__STRING_BOOLEAN_BOOLEAN_BOOLEAN_BOOLEAN_BOOLEAN_BOOLEAN,
+			      G_TYPE_NONE,
+			      7,
+			      G_TYPE_STRING,
+			      G_TYPE_BOOLEAN,
+			      G_TYPE_BOOLEAN,
+			      G_TYPE_BOOLEAN,
+			      G_TYPE_BOOLEAN,
+			      G_TYPE_BOOLEAN,
+			      G_TYPE_BOOLEAN);
+	signals[INDEX_FINISHED] =
+		g_signal_new ("index-finished",
+			      G_TYPE_FROM_CLASS (klass),
+			      G_SIGNAL_RUN_LAST,
+			      0,
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__DOUBLE,
+			      G_TYPE_NONE,
+			      1,
+			      G_TYPE_DOUBLE);
+	signals[INDEX_PROGRESS] =
+		g_signal_new ("index-progress",
+			      G_TYPE_FROM_CLASS (klass),
+			      G_SIGNAL_RUN_LAST,
+			      0,
+			      NULL, NULL,
+			      tracker_marshal_VOID__STRING_STRING_INT_INT_INT_DOUBLE,
+			      G_TYPE_NONE,
+			      6,
+			      G_TYPE_STRING,
+			      G_TYPE_STRING,
+			      G_TYPE_INT,
+			      G_TYPE_INT,
+			      G_TYPE_INT,
+			      G_TYPE_DOUBLE);
+	signals[SERVICE_STATISTICS_UPDATED] =
+		g_signal_new ("service-statistics-updated",
+			      G_TYPE_FROM_CLASS (klass),
+			      G_SIGNAL_RUN_LAST,
+			      0,
+			      NULL, NULL,
+			      tracker_marshal_VOID__BOXED,
+			      G_TYPE_NONE,
+			      1,
+			      G_TYPE_STRV);
+
+	g_type_class_add_private (object_class, sizeof (TrackerDaemonPrivate));
+}
+
+static void
+clean_last_stats (TrackerDaemonPrivate *priv)
+{
+	if (priv->last_stats) {
+		g_ptr_array_foreach (priv->last_stats, (GFunc) g_strfreev, NULL);
+		g_ptr_array_free (priv->last_stats, TRUE);
+	}
+}
+
+static void
+indexer_finished_cb (DBusGProxy *proxy,
+		     gdouble	 seconds_elapsed,
+		     guint	 items_done,
+		     gboolean	 interrupted,
+		     gpointer	 user_data)
+{
+	GObject		     *daemon;
+	TrackerDaemonPrivate *priv;
+	TrackerDBInterface   *iface;
+	TrackerDBResultSet   *result_set;
+	GPtrArray	     *values = NULL;
+	GPtrArray	     *new_stats;
+	GStrv		      strv;
+	guint		      i;
+
+	daemon = tracker_dbus_get_object (TRACKER_TYPE_DAEMON);
+	priv = TRACKER_DAEMON_GET_PRIVATE (daemon);
+	iface = tracker_db_manager_get_db_interface_by_service (TRACKER_DB_FOR_FILE_SERVICE);
+
+	/* GetStats has asc in its query. Therefore we don't have to
+	 * lookup the in a to compare in b, just compare index based.
+	 * Maybe we want to change this nonetheless later?
+	 */
+	result_set = tracker_db_exec_proc (iface, "GetStats", 0);
+	new_stats = tracker_dbus_query_result_to_ptr_array (result_set);
+
+	if (result_set) {
+		g_object_unref (result_set);
+	}
+
+	if (priv->last_stats && new_stats) {
+		for (i = 0; i < priv->last_stats->len && i < new_stats->len; i++) {
+			GStrv str1;
+			GStrv str2;
+
+			str1 = g_ptr_array_index (priv->last_stats, i);
+			str2 = g_ptr_array_index (new_stats, i);
+
+			if (!str1[0] || !str1[1] || !str2[1]) {
+				continue;
+			}
+
+			if (strcmp (str1[1], str2[1]) != 0) {
+				if (!values) {
+					values = g_ptr_array_new ();
+				}
+
+				g_ptr_array_add (values, g_strdup (str1[0]));
+			}
+		}
+	} else if (new_stats) {
+		for (i = 0; i < new_stats->len; i++) {
+			GStrv str;
+
+			str = g_ptr_array_index (new_stats, i);
+
+			if (!str[0]) {
+				continue;
+			}
+
+			g_ptr_array_add (values, g_strdup (str[0]));
+		}
+	}
+
+	clean_last_stats (priv);
+	priv->last_stats = new_stats;
+
+	if (values && values->len > 0) {
+		strv = g_new0 (gchar*, values->len + 1);
+
+		for (i = 0 ; i < values->len; i++) {
+			strv[i] = g_ptr_array_index (values, i);
+		}
+
+		g_ptr_array_free (values, TRUE);
+		strv[i] = NULL;
+	} else {
+		strv = g_new0 (gchar*, 1);
+		strv[0] = NULL;
+	}
+
+	g_signal_emit (daemon, signals[SERVICE_STATISTICS_UPDATED], 0, strv);
+	g_strfreev (strv);
+}
+
+static void
+tracker_daemon_init (TrackerDaemon *object)
+{
+	TrackerDaemonPrivate *priv;
+	TrackerDBInterface   *iface;
+	TrackerDBResultSet   *result_set;
+	DBusGProxy	     *proxy;
+
+	priv = TRACKER_DAEMON_GET_PRIVATE (object);
+
+	proxy = tracker_dbus_indexer_get_proxy ();
+	priv->indexer_proxy = g_object_ref (proxy);
+
+	dbus_g_proxy_connect_signal (proxy, "Paused",
+				     G_CALLBACK (indexer_paused_cb),
+				     object,
+				     NULL);
+	dbus_g_proxy_connect_signal (proxy, "Continued",
+				     G_CALLBACK (indexer_continued_cb),
+				     object,
+				     NULL);
+	dbus_g_proxy_connect_signal (proxy, "Finished",
+				     G_CALLBACK (indexer_finished_cb),
+				     object,
+				     NULL);
+
+	iface = tracker_db_manager_get_db_interface_by_service (TRACKER_DB_FOR_FILE_SERVICE);
+
+	result_set = tracker_db_exec_proc (iface, "GetStats", 0);
+	priv->last_stats = tracker_dbus_query_result_to_ptr_array (result_set);
+
+	if (result_set) {
+		g_object_unref (result_set);
+	}
+}
+
+static void
+tracker_daemon_finalize (GObject *object)
+{
+	TrackerDaemon	     *daemon;
+	TrackerDaemonPrivate *priv;
+
+	daemon = TRACKER_DAEMON (object);
+	priv = TRACKER_DAEMON_GET_PRIVATE (daemon);
+
+	clean_last_stats (priv);
+
+	dbus_g_proxy_disconnect_signal (priv->indexer_proxy, "Continued",
+					G_CALLBACK (indexer_continued_cb),
+					NULL);
+	dbus_g_proxy_disconnect_signal (priv->indexer_proxy, "Paused",
+					G_CALLBACK (indexer_paused_cb),
+					NULL);
+	dbus_g_proxy_disconnect_signal (priv->indexer_proxy, "Finished",
+					G_CALLBACK (indexer_continued_cb),
+					NULL);
+
+	g_object_unref (priv->indexer_proxy);
+
+	g_object_unref (priv->processor);
+	g_object_unref (priv->config);
+
+	G_OBJECT_CLASS (tracker_daemon_parent_class)->finalize (object);
+}
+
+TrackerDaemon *
+tracker_daemon_new (TrackerConfig    *config,
+		    TrackerProcessor *processor)
+{
+	TrackerDaemon	     *object;
+	TrackerDaemonPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), NULL);
+	g_return_val_if_fail (TRACKER_IS_PROCESSOR (processor), NULL);
+
+	object = g_object_new (TRACKER_TYPE_DAEMON, NULL);
+
+	priv = TRACKER_DAEMON_GET_PRIVATE (object);
+
+	priv->config = g_object_ref (config);
+	priv->processor = g_object_ref (processor);
+
+	return object;
+}
+
+static void
+indexer_pause_cb (DBusGProxy *proxy,
+		  GError     *error,
+		  gpointer    user_data)
+{
+	if (error) {
+		g_message ("Could not pause the indexer, %s",
+			   error->message);
+	}
+}
+
+static void
+indexer_continue_cb (DBusGProxy *proxy,
+		     GError	*error,
+		     gpointer	 user_data)
+{
+	if (error) {
+		g_message ("Could not continue the indexer, %s",
+			   error->message);
+	}
+}
+
+static void
+indexer_paused_cb (DBusGProxy *proxy,
+		   GError     *error,
+		   gpointer    user_data)
+{
+	g_message ("The indexer has paused");
+}
+
+static void
+indexer_continued_cb (DBusGProxy *proxy,
+		      GError	 *error,
+		      gpointer	  user_data)
+{
+	g_message ("The indexer has continued");
+}
+
+/*
+ * Functions
+ */
+void
+tracker_daemon_get_version (TrackerDaemon	   *object,
+			    DBusGMethodInvocation  *context,
+			    GError		  **error)
+{
+	guint  request_id;
+	gint   major = 0;
+	gint   minor = 0;
+	gint   revision = 0;
+	gint   version;
+	gchar *str;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to get daemon version");
+
+
+	sscanf (VERSION, "%d.%d.%d", &major, &minor, &revision);
+	str = g_strdup_printf ("%d%d%d", major, minor, revision);
+	version = atoi (str);
+	g_free (str);
+
+	dbus_g_method_return (context, version);
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_daemon_get_status (TrackerDaemon	  *object,
+			   DBusGMethodInvocation  *context,
+			   GError		 **error)
+{
+	TrackerDaemonPrivate *priv;
+	gchar		     *status;
+	guint		      request_id;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	priv = TRACKER_DAEMON_GET_PRIVATE (object);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to get daemon status");
+
+	status = g_strdup (tracker_status_get_as_string ());
+
+	dbus_g_method_return (context, status);
+
+	g_free (status);
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_daemon_get_services (TrackerDaemon	    *object,
+			     gboolean		     main_services_only,
+			     DBusGMethodInvocation  *context,
+			     GError		   **error)
+{
+	TrackerDBInterface *iface;
+	TrackerDBResultSet *result_set;
+	guint		    request_id;
+	GHashTable	   *values = NULL;
+
+	/* FIXME: Note, the main_services_only variable is redundant */
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to get daemon services");
+
+	/* Here it doesn't matter which one we ask, as long as it has common.db
+	 * attached. The service ones are cached connections, so we can use
+	 * those instead of asking for an individual-file connection (like what
+	 * the original code had)
+	 */
+
+	/* iface = tracker_db_manager_get_db_interfaceX (TRACKER_DB_COMMON); */
+
+	iface = tracker_db_manager_get_db_interface_by_service (TRACKER_DB_FOR_FILE_SERVICE);
+
+	result_set = tracker_db_exec_proc (iface, "GetServices", 0);
+	values = tracker_dbus_query_result_to_hash_table (result_set);
+
+	if (result_set) {
+		g_object_unref (result_set);
+	}
+
+	dbus_g_method_return (context, values);
+
+	g_hash_table_destroy (values);
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_daemon_get_stats (TrackerDaemon		 *object,
+			  DBusGMethodInvocation  *context,
+			  GError		**error)
+{
+	TrackerDBInterface *iface;
+	TrackerDBResultSet *result_set;
+	guint		    request_id;
+	GPtrArray	   *values = NULL;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to get daemon service stats");
+
+	/* Here it doesn't matter which one we ask, as long as it has common.db
+	 * attached. The service ones are cached connections, so we can use
+	 * those instead of asking for an individual-file connection (like what
+	 * the original code had)
+	 */
+
+	/* iface = tracker_db_manager_get_db_interfaceX (TRACKER_DB_COMMON); */
+
+	iface = tracker_db_manager_get_db_interface_by_service (TRACKER_DB_FOR_FILE_SERVICE);
+
+	result_set = tracker_db_exec_proc (iface, "GetStats", 0);
+	values = tracker_dbus_query_result_to_ptr_array (result_set);
+
+	if (result_set) {
+		g_object_unref (result_set);
+	}
+
+	dbus_g_method_return (context, values);
+
+	g_ptr_array_foreach (values, (GFunc) g_strfreev, NULL);
+	g_ptr_array_free (values, TRUE);
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_daemon_set_bool_option (TrackerDaemon	       *object,
+				const gchar	       *option,
+				gboolean		value,
+				DBusGMethodInvocation  *context,
+				GError		      **error)
+{
+	TrackerDaemonPrivate *priv;
+	guint		      request_id;
+	GError		     *actual_error = NULL;
+
+	/* FIXME: Shouldn't we just make the TrackerConfig module a
+	 * DBus object instead so values can be tweaked in real time
+	 * over the bus?
+	 */
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (option != NULL, context);
+
+	priv = TRACKER_DAEMON_GET_PRIVATE (object);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to set daemon boolean option, "
+				  "key:'%s', value:%s",
+				  option,
+				  value ? "true" : "false");
+
+	if (strcasecmp (option, "Pause") == 0) {
+		/* We do it here and not in the callback because we
+		 * don't know if something else paused us or if it
+		 * was the signal from our request.
+		 */
+		tracker_status_set_is_paused_manually (value);
+
+		if (value) {
+			org_freedesktop_Tracker_Indexer_pause_async (priv->indexer_proxy,
+								     indexer_pause_cb,
+								     NULL);
+		} else {
+			/* Don't continue if we are paused from IO */
+			if (!tracker_status_get_is_paused_for_io ()) {
+				org_freedesktop_Tracker_Indexer_continue_async (priv->indexer_proxy,
+										indexer_continue_cb,
+										NULL);
+			}
+		}
+	} else if (strcasecmp (option, "FastMerges") == 0) {
+		tracker_config_set_fast_merges (priv->config, value);
+		g_message ("Fast merges set to %d", value);
+	} else if (strcasecmp (option, "EnableIndexing") == 0) {
+		/* FIXME: Ideally we should be picking up the
+		 * "nofify::enable-indexing" change on the
+		 * priv->config in the tracker-main.c module to do
+		 * the signal change and to set the daemon to
+		 * readonly mode.
+		 */
+		tracker_config_set_enable_indexing (priv->config, value);
+		tracker_status_set_is_readonly (value);
+		g_message ("Enable indexing set to %d", value);
+	} else if (strcasecmp (option, "EnableWatching") == 0) {
+		tracker_config_set_enable_watches (priv->config, value);
+		g_message ("Enable Watching set to %d", value);
+	} else if (strcasecmp (option, "LowMemoryMode") == 0) {
+		tracker_config_set_low_memory_mode (priv->config, value);
+		g_message ("Extra memory usage set to %d", !value);
+	} else if (strcasecmp (option, "IndexFileContents") == 0) {
+		tracker_config_set_enable_content_indexing (priv->config, value);
+		g_message ("Index file contents set to %d", value);
+	} else if (strcasecmp (option, "GenerateThumbs") == 0) {
+		tracker_config_set_enable_thumbnails (priv->config, value);
+		g_message ("Generate thumbnails set to %d", value);
+	} else if (strcasecmp (option, "IndexMountedDirectories") == 0) {
+		tracker_config_set_index_mounted_directories (priv->config, value);
+		g_message ("Indexing mounted directories set to %d", value);
+	} else if (strcasecmp (option, "IndexRemovableDevices") == 0) {
+		tracker_config_set_index_removable_devices (priv->config, value);
+		g_message ("Indexing removable devices set to %d", value);
+	} else if (strcasecmp (option, "BatteryIndex") == 0) {
+		tracker_config_set_disable_indexing_on_battery (priv->config, !value);
+		g_message ("Disable index on battery set to %d", !value);
+	} else if (strcasecmp (option, "BatteryIndexInitial") == 0) {
+		tracker_config_set_disable_indexing_on_battery_init (priv->config, !value);
+		g_message ("Disable initial index sweep on battery set to %d", !value);
+	} else {
+		g_set_error (&actual_error,
+			     TRACKER_DBUS_ERROR,
+			     0,
+			     "Option does not exist");
+	}
+
+	if (!actual_error) {
+		dbus_g_method_return (context);
+	} else {
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+	}
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_daemon_set_int_option (TrackerDaemon	      *object,
+			       const gchar	      *option,
+			       gint		       value,
+			       DBusGMethodInvocation  *context,
+			       GError		     **error)
+{
+	TrackerDaemonPrivate *priv;
+	guint		      request_id;
+	GError		     *actual_error = NULL;
+
+	/* FIXME: Shouldn't we just make the TrackerConfig module a
+	 * DBus object instead so values can be tweaked in real time
+	 * over the bus?
+	 */
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (option != NULL, context);
+
+	priv = TRACKER_DAEMON_GET_PRIVATE (object);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to set daemon integer option, "
+				  "key:'%s', value:%d",
+				  option,
+				  value);
+
+	if (strcasecmp (option, "Throttle") == 0) {
+		tracker_config_set_throttle (priv->config, value);
+		g_message ("throttle set to %d", value);
+	} else if (strcasecmp (option, "MaxText") == 0) {
+		tracker_config_set_max_text_to_index (priv->config, value);
+		g_message ("Maxinum amount of text set to %d", value);
+	} else if (strcasecmp (option, "MaxWords") == 0) {
+		tracker_config_set_max_words_to_index (priv->config, value);
+		g_message ("Maxinum number of unique words set to %d", value);
+	} else {
+		g_set_error (&actual_error,
+			     TRACKER_DBUS_ERROR,
+			     0,
+			     "Option does not exist");
+	}
+
+	if (!actual_error) {
+		dbus_g_method_return (context);
+	} else {
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+	}
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_daemon_shutdown (TrackerDaemon		*object,
+			 gboolean		 reindex,
+			 DBusGMethodInvocation	*context,
+			 GError		       **error)
+{
+	TrackerDaemonPrivate *priv;
+	guint		      request_id;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to shutdown daemon, "
+				  "reindex:%s",
+				  reindex ? "yes" : "no");
+
+	priv = TRACKER_DAEMON_GET_PRIVATE (object);
+
+	g_message ("Tracker daemon attempting to shutdown");
+
+	tracker_set_reindex_on_shutdown (reindex);
+
+	g_timeout_add (500, (GSourceFunc) tracker_shutdown, NULL);
+
+	dbus_g_method_return (context);
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_daemon_prompt_index_signals (TrackerDaemon	    *object,
+				     DBusGMethodInvocation  *context,
+				     GError		   **error)
+{
+	TrackerDaemonPrivate *priv;
+	guint		      request_id;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to daemon to signal progress/state");
+
+	priv = TRACKER_DAEMON_GET_PRIVATE (object);
+
+	/* Signal state change */
+	tracker_status_signal ();
+
+	/* Signal progress */
+	g_signal_emit_by_name (object,
+			       "index-progress",
+			       "Files",
+			       "",
+			       tracker_processor_get_files_total (priv->processor),
+			       tracker_processor_get_directories_found (priv->processor),
+			       tracker_processor_get_directories_total (priv->processor),
+			       tracker_processor_get_seconds_elapsed (priv->processor));
+
+#if 1
+	/* FIXME: We need a way of knowing WHAT service we have a
+	 * count for, i.e. emails, files, etc.
+	 */
+	g_signal_emit_by_name (object,
+			       "index-progress",
+			       "Emails",
+			       "",
+			       0,  /* priv->tracker->index_count, */
+			       0,  /* priv->tracker->mbox_processed, */
+			       0,  /* priv->tracker->mbox_count); */
+			       0); /* seconds elapsed */
+#endif
+
+	dbus_g_method_return (context);
+
+	tracker_dbus_request_success (request_id);
+}

Added: trunk/src/trackerd/tracker-daemon.h
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-daemon.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,92 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKERD_DBUS_DAEMON_H__
+#define __TRACKERD_DBUS_DAEMON_H__
+
+#include <glib-object.h>
+
+#include <libtracker-common/tracker-config.h>
+
+#include "tracker-processor.h"
+
+#define TRACKER_DAEMON_SERVICE	       "org.freedesktop.Tracker"
+#define TRACKER_DAEMON_PATH	       "/org/freedesktop/Tracker"
+#define TRACKER_DAEMON_INTERFACE       "org.freedesktop.Tracker"
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_DAEMON	       (tracker_daemon_get_type ())
+#define TRACKER_DAEMON(object)	       (G_TYPE_CHECK_INSTANCE_CAST ((object), TRACKER_TYPE_DAEMON, TrackerDaemon))
+#define TRACKER_DAEMON_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), TRACKER_TYPE_DBUS_DAEMON, TrackerDaemonClass))
+#define TRACKER_IS_DAEMON(object)      (G_TYPE_CHECK_INSTANCE_TYPE ((object), TRACKER_TYPE_DAEMON))
+#define TRACKER_IS_DAEMON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TRACKER_TYPE_DAEMON))
+#define TRACKER_DAEMON_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), TRACKER_TYPE_DAEMON, TrackerDaemonClass))
+
+typedef struct TrackerDaemon	  TrackerDaemon;
+typedef struct TrackerDaemonClass TrackerDaemonClass;
+
+struct TrackerDaemon {
+	GObject parent;
+};
+
+struct TrackerDaemonClass {
+	GObjectClass parent;
+};
+
+GType	       tracker_daemon_get_type		   (void);
+TrackerDaemon *tracker_daemon_new		   (TrackerConfig	  *config,
+						    TrackerProcessor	  *processor);
+void	       tracker_daemon_get_version	   (TrackerDaemon	  *object,
+						    DBusGMethodInvocation *context,
+						    GError **error);
+void	       tracker_daemon_get_status	   (TrackerDaemon	  *object,
+						    DBusGMethodInvocation *context,
+						    GError **error);
+void	       tracker_daemon_get_services	   (TrackerDaemon	  *object,
+						    gboolean		   main_services_only,
+						    DBusGMethodInvocation *context,
+						    GError **error);
+void	       tracker_daemon_get_stats		   (TrackerDaemon	  *object,
+						    DBusGMethodInvocation *context,
+						    GError **error);
+void	       tracker_daemon_set_bool_option	   (TrackerDaemon	  *object,
+						    const gchar		  *option,
+						    gboolean		   value,
+						    DBusGMethodInvocation *context,
+						    GError **error);
+void	       tracker_daemon_set_int_option	   (TrackerDaemon	  *object,
+						    const gchar		  *option,
+						    gint		   value,
+						    DBusGMethodInvocation *context,
+						    GError **error);
+void	       tracker_daemon_shutdown		   (TrackerDaemon	  *object,
+						    gboolean		   reindex,
+						    DBusGMethodInvocation *context,
+						    GError **error);
+void	       tracker_daemon_prompt_index_signals (TrackerDaemon	  *object,
+						    DBusGMethodInvocation *context,
+						    GError **error);
+
+
+G_END_DECLS
+
+#endif /* __TRACKERD_DAEMON_H__ */

Added: trunk/src/trackerd/tracker-db.c
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-db.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,3051 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2007, Jason Kivlighn (jkivlighn gmail com)
+ * Copyright (C) 2007, Creative Commons (http://creativecommons.org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <zlib.h>
+
+#include <glib/gstdio.h>
+
+#include <libtracker-common/tracker-file-utils.h>
+#include <libtracker-common/tracker-nfs-lock.h>
+#include <libtracker-common/tracker-parser.h>
+#include <libtracker-common/tracker-type-utils.h>
+#include <libtracker-common/tracker-utils.h>
+
+#include <libtracker-db/tracker-db-index.h>
+#include <libtracker-db/tracker-db-interface-sqlite.h>
+#include <libtracker-db/tracker-db-index-manager.h>
+#include <libtracker-db/tracker-db-manager.h>
+
+#include "tracker-db.h"
+#include "tracker-query-tree.h"
+#include "tracker-monitor.h"
+#include "tracker-xesam-manager.h"
+#include "tracker-main.h"
+
+#define ZLIBBUFSIZ 8192
+
+typedef struct {
+	TrackerConfig	*config;
+	TrackerLanguage *language;
+} TrackerDBPrivate;
+
+/* Private */
+static GStaticPrivate private_key = G_STATIC_PRIVATE_INIT;
+
+static void
+private_free (gpointer data)
+{
+	TrackerDBPrivate *private;
+
+	private = data;
+
+	if (private->config) {
+		g_object_unref (private->config);
+	}
+
+	if (private->language) {
+		g_object_unref (private->language);
+	}
+
+	g_free (private);
+}
+
+static gchar *
+compress_string (const gchar *ptr,
+		 gint	      size,
+		 gint	     *compressed_size)
+{
+	z_stream       zs;
+	gchar	      *buf, *swap;
+	unsigned char  obuf[ZLIBBUFSIZ];
+	gint	       rv, asiz, bsiz, osiz;
+
+	if (size < 0) {
+		size = strlen (ptr);
+	}
+
+	zs.zalloc = Z_NULL;
+	zs.zfree = Z_NULL;
+	zs.opaque = Z_NULL;
+
+	if (deflateInit2 (&zs, 6, Z_DEFLATED, 15, 6, Z_DEFAULT_STRATEGY) != Z_OK) {
+		return NULL;
+	}
+
+	asiz = size + 16;
+
+	if (asiz < ZLIBBUFSIZ) {
+		asiz = ZLIBBUFSIZ;
+	}
+
+	if (!(buf = malloc (asiz))) {
+		deflateEnd (&zs);
+		return NULL;
+	}
+
+	bsiz = 0;
+	zs.next_in = (unsigned char *)ptr;
+	zs.avail_in = size;
+	zs.next_out = obuf;
+	zs.avail_out = ZLIBBUFSIZ;
+
+	while ((rv = deflate (&zs, Z_FINISH)) == Z_OK) {
+		osiz = ZLIBBUFSIZ - zs.avail_out;
+
+		if (bsiz + osiz > asiz) {
+			asiz = asiz * 2 + osiz;
+
+			if (!(swap = realloc (buf, asiz))) {
+				free (buf);
+				deflateEnd (&zs);
+				return NULL;
+			}
+
+			buf = swap;
+		}
+
+		memcpy (buf + bsiz, obuf, osiz);
+		bsiz += osiz;
+		zs.next_out = obuf;
+		zs.avail_out = ZLIBBUFSIZ;
+	}
+
+	if (rv != Z_STREAM_END) {
+		free (buf);
+		deflateEnd (&zs);
+		return NULL;
+	}
+
+	osiz = ZLIBBUFSIZ - zs.avail_out;
+
+	if (bsiz + osiz + 1 > asiz) {
+		asiz = asiz * 2 + osiz;
+
+		if (!(swap = realloc (buf, asiz))) {
+			free (buf);
+			deflateEnd (&zs);
+			return NULL;
+		}
+
+		buf = swap;
+	}
+
+	memcpy (buf + bsiz, obuf, osiz);
+	bsiz += osiz;
+	buf[bsiz] = '\0';
+
+	*compressed_size = bsiz;
+
+	deflateEnd (&zs);
+
+	return buf;
+}
+
+static gboolean
+db_exec_proc_no_reply (TrackerDBInterface *iface,
+		       const gchar	  *procedure,
+		       ...)
+{
+	TrackerDBResultSet *result_set;
+	va_list args;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (iface), FALSE);
+	g_return_val_if_fail (procedure != NULL, FALSE);
+
+	va_start (args, procedure);
+	result_set = tracker_db_interface_execute_vprocedure (iface,
+							      NULL,
+							      procedure,
+							      args);
+	va_end (args);
+
+	if (result_set) {
+		g_object_unref (result_set);
+	}
+
+	return TRUE;
+}
+
+static void
+db_save_full_text (TrackerDBInterface *iface,
+		   const gchar	      *str_file_id,
+		   const gchar	      *text,
+		   gint		       length)
+{
+	gchar	    *compressed, *value = NULL;
+	gint	     bytes_compressed;
+	const gchar *field_id;
+
+	compressed = compress_string (text, length, &bytes_compressed);
+
+	if (compressed) {
+		g_debug ("Compressed full text size of %d to %d",
+			 length, bytes_compressed);
+		value = compressed;
+	} else {
+		g_warning ("Could not compress text'%*s...', length:%d",
+			   16, text, length);
+		value = g_strdup (text);
+		bytes_compressed = length;
+	}
+
+	field_id = tracker_ontology_field_get_id ("File:Contents");
+
+	if (!field_id) {
+		g_warning ("Metadata not found for type:'File:Contents'");
+		g_free (value);
+		return;
+	}
+
+	tracker_db_interface_execute_procedure_len (iface,
+						    NULL,
+						    "SaveServiceContents",
+						    str_file_id,
+						    -1,
+						    field_id,
+						    -1,
+						    value,
+						    bytes_compressed,
+						    NULL);
+	g_free (value);
+}
+
+static void
+update_metadata_index (const gchar  *id,
+		       const gchar  *service,
+		       TrackerField *def,
+		       const gchar  *old_value,
+		       const gchar  *new_value)
+{
+	TrackerDBPrivate *private;
+	GHashTable	 *old_table;
+	GHashTable	 *new_table;
+	gint		  sid;
+
+	g_return_if_fail (TRACKER_IS_FIELD (def));
+
+	private = g_static_private_get (&private_key);
+	g_return_if_fail (private != NULL);
+
+	old_table = NULL;
+	new_table = NULL;
+
+	if (old_value) {
+		old_table = tracker_parser_text (old_table,
+						 old_value,
+						 tracker_field_get_weight (def),
+						 private->language,
+						 tracker_config_get_max_words_to_index (private->config),
+						 tracker_config_get_max_word_length (private->config),
+						 tracker_config_get_min_word_length (private->config),
+						 tracker_field_get_filtered (def),
+						 tracker_field_get_delimited (def));
+	}
+
+	/* Parse new metadata value */
+	if (new_value) {
+		new_table = tracker_parser_text (new_table,
+						 new_value,
+						 tracker_field_get_weight (def),
+						 private->language,
+						 tracker_config_get_max_words_to_index (private->config),
+						 tracker_config_get_max_word_length (private->config),
+						 tracker_config_get_min_word_length (private->config),
+						 tracker_field_get_filtered (def),
+						 tracker_field_get_delimited (def));
+	}
+
+	/* We only do differential updates so only changed words scores are updated */
+	sid = tracker_ontology_get_service_id_by_name (service);
+#if 0
+	tracker_db_update_differential_index (old_table, new_table, id, sid);
+#endif
+
+	g_hash_table_unref (old_table);
+	g_hash_table_unref (new_table);
+}
+
+static gchar *
+get_backup_id (TrackerDBInterface *iface,
+	       const gchar	  *id)
+{
+	TrackerDBResultSet *result_set;
+	gchar		   *backup_id = NULL;
+
+	result_set = tracker_db_exec_proc (iface,
+					   "GetBackupServiceByID",
+					   id,
+					   NULL);
+
+	if (result_set) {
+		tracker_db_result_set_get (result_set, 0, &backup_id, -1);
+		g_object_unref (result_set);
+	}
+
+	if (!backup_id) {
+		gint64 last_insert_id;
+
+		tracker_db_exec_proc (iface,
+				      "InsertBackupService",
+				      id,
+				      NULL);
+		last_insert_id = tracker_db_interface_sqlite_get_last_insert_id (TRACKER_DB_INTERFACE_SQLITE (iface));
+		backup_id = tracker_gint_to_string (last_insert_id);
+	}
+
+	return backup_id;
+}
+
+static inline void
+backup_non_embedded_metadata (TrackerDBInterface *iface,
+			      const gchar	 *id,
+			      const gchar	 *key_id,
+			      const gchar	 *value)
+{
+	gchar *backup_id;
+
+	backup_id = get_backup_id (iface, id);
+
+	if (backup_id) {
+		tracker_db_exec_proc (iface,
+				      "SetBackupMetadata",
+				      backup_id,
+				      key_id,
+				      value,
+				      NULL);
+		g_free (backup_id);
+	}
+}
+
+static inline void
+backup_delete_non_embedded_metadata_value (TrackerDBInterface *iface,
+					   const gchar	      *id,
+					   const gchar	      *key_id,
+					   const gchar	      *value)
+{
+	gchar *backup_id;
+
+	backup_id = get_backup_id (iface, id);
+
+	if (backup_id) {
+		tracker_db_exec_proc (iface,
+				      "DeleteBackupMetadataValue",
+				      backup_id,
+				      key_id,
+				      value,
+				      NULL);
+		g_free (backup_id);
+	}
+}
+
+static inline void
+backup_delete_non_embedded_metadata (TrackerDBInterface *iface,
+				     const gchar	*id,
+				     const gchar	*key_id)
+{
+	gchar *backup_id;
+
+	backup_id = get_backup_id (iface, id);
+
+	if (backup_id) {
+		tracker_db_exec_proc (iface,
+				      "DeleteBackupMetadata",
+				      backup_id,
+				      key_id,
+				      NULL);
+		g_free (backup_id);
+	}
+}
+
+static gchar *
+remove_value (const gchar *str,
+	      const gchar *del_str)
+{
+	GString  *s = NULL;
+	gchar	**p;
+	gchar	**strv;
+
+	strv = g_strsplit (str, "|", -1);
+
+	for (p = strv; *p; p++) {
+		if (tracker_is_empty_string (*p)) {
+			continue;
+		}
+
+		if (strcmp (del_str, *p) != 0) {
+			if (!s) {
+				s = g_string_new (*p);
+			} else {
+				g_string_append_printf (s, "%s%s", "|", *p);
+			}
+		}
+	}
+
+	g_strfreev (strv);
+
+	if (!s) {
+		return NULL;
+	}
+
+	return g_string_free (s, FALSE);
+}
+
+/* Deprecated */
+static guint32
+db_create_event (TrackerDBInterface *iface,
+		 const gchar	    *service_id_str,
+		 const gchar	    *type)
+{
+	TrackerDBResultSet *result_set;
+	gchar		   *eid;
+	gint		    i;
+	guint32		    id = 0;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (iface), 0);
+
+	result_set = tracker_db_exec_proc (iface,
+					   "GetNewEventID",
+					   NULL);
+
+	if (!result_set) {
+		g_critical ("Could not create event, GetNewEventID failed");
+		return 0;
+	}
+
+	tracker_db_result_set_get (result_set, 0, &eid, -1);
+	i = atoi (eid);
+	g_free (eid);
+
+	i++;
+	eid = tracker_gint_to_string (i);
+
+	result_set = tracker_db_exec_proc (iface,
+					   "UpdateNewEventID",
+					   eid,
+					   NULL);
+	if (result_set)
+		g_object_unref (result_set);
+
+	/* Uses the Events table */
+	result_set = tracker_db_exec_proc (iface,
+					   "CreateEvent",
+					   eid,
+					   service_id_str,
+					   type,
+					   NULL);
+
+	id = tracker_db_interface_sqlite_get_last_insert_id (TRACKER_DB_INTERFACE_SQLITE (iface));
+
+	if (result_set) {
+		g_object_unref (result_set);
+	}
+
+	g_free (eid);
+
+	tracker_xesam_manager_wakeup ();
+
+	return id;
+}
+
+GArray *
+tracker_db_create_array_of_services (const gchar *service,
+				     gboolean	  basic_services)
+{
+	GArray	 *array;
+	gint	  services[12];
+	gint	  count;
+	gboolean  add_files;
+	gboolean  add_emails;
+	gboolean  add_conversations;
+
+	if (service) {
+		if (g_ascii_strcasecmp (service, "Files") == 0) {
+			add_files = TRUE;
+			add_emails = FALSE;
+			add_conversations = FALSE;
+		} else if (g_ascii_strcasecmp (service, "Emails") == 0) {
+			add_files = FALSE;
+			add_emails = TRUE;
+			add_conversations = FALSE;
+		} else if (g_ascii_strcasecmp (service, "Conversations") == 0) {
+			add_files = FALSE;
+			add_emails = FALSE;
+			add_conversations = TRUE;
+		} else {
+			/* Maybe set them all to TRUE? */
+			add_files = FALSE;
+			add_emails = FALSE;
+			add_conversations = FALSE;
+		}
+	} else if (basic_services) {
+		add_files = TRUE;
+		add_emails = FALSE;
+		add_conversations = FALSE;
+	} else {
+		add_files = TRUE;
+		add_emails = TRUE;
+		add_conversations = TRUE;
+	}
+
+	count = 0;
+
+	if (add_files) {
+		services[count++] = tracker_ontology_get_service_id_by_name ("Files");
+		services[count++] = tracker_ontology_get_service_id_by_name ("Applications");
+		services[count++] = tracker_ontology_get_service_id_by_name ("Folders");
+		services[count++] = tracker_ontology_get_service_id_by_name ("Documents");
+		services[count++] = tracker_ontology_get_service_id_by_name ("Images");
+		services[count++] = tracker_ontology_get_service_id_by_name ("Videos");
+		services[count++] = tracker_ontology_get_service_id_by_name ("Music");
+		services[count++] = tracker_ontology_get_service_id_by_name ("Text");
+		services[count++] = tracker_ontology_get_service_id_by_name ("Development");
+		services[count++] = tracker_ontology_get_service_id_by_name ("Other");
+	}
+
+	if (add_emails) {
+		services[count++] = tracker_ontology_get_service_id_by_name ("EvolutionEmails");
+		services[count++] = tracker_ontology_get_service_id_by_name ("KMailEmails");
+		services[count++] = tracker_ontology_get_service_id_by_name ("ThunderbirdEmails");
+	}
+
+	if (add_conversations) {
+		services[count++] = tracker_ontology_get_service_id_by_name ("GaimConversations");
+	}
+
+	services[count] = 0;
+
+	array = g_array_new (TRUE, TRUE, sizeof (gint));
+	g_array_append_vals (array, services, count);
+
+	return array;
+}
+
+void
+tracker_db_init (TrackerConfig	 *config,
+		 TrackerLanguage *language,
+		 TrackerDBIndex  *file_index,
+		 TrackerDBIndex  *email_index)
+{
+	TrackerDBPrivate *private;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+	g_return_if_fail (TRACKER_IS_LANGUAGE (language));
+	g_return_if_fail (TRACKER_IS_DB_INDEX (file_index));
+	g_return_if_fail (TRACKER_IS_DB_INDEX (email_index));
+
+	private = g_static_private_get (&private_key);
+	if (private) {
+		g_warning ("Already initialized (%s)",
+			   __FUNCTION__);
+		return;
+	}
+
+	private = g_new0 (TrackerDBPrivate, 1);
+
+	private->config = g_object_ref (config);
+	private->language = g_object_ref (language);
+
+	g_static_private_set (&private_key,
+			      private,
+			      private_free);
+}
+
+void
+tracker_db_shutdown (void)
+{
+	TrackerDBPrivate *private;
+
+	private = g_static_private_get (&private_key);
+	if (!private) {
+		g_warning ("Not initialized (%s)",
+			   __FUNCTION__);
+		return;
+	}
+
+	g_static_private_free (&private_key);
+}
+
+gboolean
+tracker_db_exec_no_reply (TrackerDBInterface *iface,
+			  const gchar	     *query,
+			  ...)
+{
+	TrackerDBResultSet *result_set;
+	va_list		    args;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (iface), FALSE);
+	g_return_val_if_fail (query != NULL, FALSE);
+
+	tracker_nfs_lock_obtain ();
+
+	va_start (args, query);
+	result_set = tracker_db_interface_execute_vquery (iface, NULL, query, args);
+	va_end (args);
+
+	if (result_set) {
+		g_object_unref (result_set);
+	}
+
+	tracker_nfs_lock_release ();
+
+	return TRUE;
+}
+
+TrackerDBResultSet *
+tracker_db_exec (TrackerDBInterface *iface,
+		 const gchar	    *query,
+		 ...)
+{
+	TrackerDBResultSet *result_set;
+	va_list		    args;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (iface), NULL);
+	g_return_val_if_fail (query != NULL, NULL);
+
+	tracker_nfs_lock_obtain ();
+
+	va_start (args, query);
+	result_set = tracker_db_interface_execute_vquery (iface,
+							  NULL,
+							  query,
+							  args);
+	va_end (args);
+
+	tracker_nfs_lock_release ();
+
+	return result_set;
+}
+
+TrackerDBResultSet *
+tracker_db_exec_proc (TrackerDBInterface *iface,
+		      const gchar	 *procedure,
+		      ...)
+{
+	TrackerDBResultSet *result_set;
+	va_list		    args;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (iface), NULL);
+	g_return_val_if_fail (procedure != NULL, NULL);
+
+	va_start (args, procedure);
+	result_set = tracker_db_interface_execute_vprocedure (iface,
+							      NULL,
+							      procedure,
+							      args);
+	va_end (args);
+
+	return result_set;
+}
+
+gchar *
+tracker_db_get_field_name (const gchar *service,
+			   const gchar *meta_name)
+{
+	gint key_field;
+
+	/* Replace with tracker_ontology_get_field_name_by_service_name */
+	key_field = tracker_ontology_service_get_key_metadata (service, meta_name);
+
+	if (key_field > 0) {
+		return g_strdup_printf ("KeyMetadata%d", key_field);
+	}
+
+	if (strcasecmp (meta_name, "File:Path") == 0)	  return g_strdup ("Path");
+	if (strcasecmp (meta_name, "File:Name") == 0)	  return g_strdup ("Name");
+	if (strcasecmp (meta_name, "File:Mime") == 0)	  return g_strdup ("Mime");
+	if (strcasecmp (meta_name, "File:Size") == 0)	  return g_strdup ("Size");
+	if (strcasecmp (meta_name, "File:Rank") == 0)	  return g_strdup ("Rank");
+	if (strcasecmp (meta_name, "File:Modified") == 0) return g_strdup ("IndexTime");
+
+	return NULL;
+}
+
+TrackerDBResultSet *
+tracker_db_search_text (TrackerDBInterface *iface,
+			const gchar	   *service,
+			const gchar	   *search_string,
+			gint		    offset,
+			gint		    limit,
+			gboolean	    save_results,
+			gboolean	    detailed)
+{
+	TrackerDBPrivate    *private;
+	TrackerQueryTree    *tree;
+	TrackerDBResultSet  *result_set, *result;
+	gchar		   **array;
+	GArray		    *hits;
+	gint		     count;
+	gboolean	     detailed_emails = FALSE, detailed_apps = FALSE;
+	gint		     service_array[255];
+	const gchar	    *procedure;
+	GArray		    *services = NULL;
+	GSList		    *duds = NULL;
+	guint		     i = 0;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (iface), NULL);
+	g_return_val_if_fail (service != NULL, NULL);
+	g_return_val_if_fail (search_string != NULL, NULL);
+	g_return_val_if_fail (offset >= 0, NULL);
+
+	private = g_static_private_get (&private_key);
+	g_return_val_if_fail (private != NULL, NULL);
+
+	array = tracker_parser_text_into_array (search_string,
+						private->language,
+						tracker_config_get_max_word_length (private->config),
+						tracker_config_get_min_word_length (private->config));
+
+	result_set = tracker_db_exec_proc (iface,
+					   "GetRelatedServiceIDs",
+					   service,
+					   service,
+					   NULL);
+
+	if (result_set) {
+		gboolean valid = TRUE;
+		gint	 type_id;
+
+		while (valid) {
+			tracker_db_result_set_get (result_set, 0, &type_id, -1);
+			service_array[i] = type_id;
+			i++;
+
+			valid = tracker_db_result_set_iter_next (result_set);
+		}
+
+		service_array[i] = 0;
+		services = g_array_new (TRUE, TRUE, sizeof (gint));
+		g_array_append_vals (services, service_array, i);
+		g_object_unref (result_set);
+	}
+
+	/* FIXME: Do we need both index and services here? We used to have it */
+	tree = tracker_query_tree_new (search_string,
+				       private->config,
+				       private->language,
+				       services);
+	hits = tracker_query_tree_get_hits (tree, offset, limit);
+	result = NULL;
+
+	if (save_results) {
+		tracker_db_interface_start_transaction (iface);
+		tracker_db_exec_proc (iface,
+				      "DeleteSearchResults1",
+				      NULL);
+	}
+
+	count = 0;
+
+	for (i = 0; i < hits->len; i++) {
+		TrackerDBIndexItemRank	rank;
+		gchar		       *str_id;
+
+		if (count >= limit) {
+			break;
+		}
+
+		rank = g_array_index (hits, TrackerDBIndexItemRank, i);
+		str_id = tracker_guint_to_string (rank.service_id);
+
+		/* We save results into SearchResults table instead of
+		 * returing an array of array of strings
+		 */
+		if (save_results) {
+			gchar *str_score;
+
+			str_score = tracker_gint_to_string (rank.score);
+			tracker_db_exec_proc (iface,
+					      "InsertSearchResult1",
+					      str_id,
+					      str_score,
+					      NULL);
+			g_free (str_id);
+			g_free (str_score);
+
+			continue;
+		}
+
+		if (detailed) {
+			if (strcmp (service, "Emails") == 0) {
+				detailed_emails = TRUE;
+				procedure = "GetEmailByID";
+			} else if (strcmp (service, "Applications") == 0) {
+				detailed_apps = TRUE;
+				procedure = "GetApplicationByID";
+			} else {
+				procedure = "GetFileByID2";
+			}
+		} else {
+			procedure = "GetFileByID";
+		}
+
+		result_set = tracker_db_exec_proc (iface,
+						   procedure,
+						   str_id,
+						   NULL);
+		g_free (str_id);
+
+		if (result_set) {
+			gchar *path;
+
+			tracker_db_result_set_get (result_set, 0, &path, -1);
+
+			if (!detailed || detailed_emails || detailed_apps ||
+			    (detailed && g_file_test (path, G_FILE_TEST_EXISTS))) {
+				guint columns, i;
+
+				columns = tracker_db_result_set_get_n_columns (result_set);
+
+				if (G_UNLIKELY (!result)) {
+					guint columns;
+
+					columns = tracker_db_result_set_get_n_columns (result_set);
+					result = _tracker_db_result_set_new (columns);
+				}
+
+				_tracker_db_result_set_append (result);
+
+				for (i = 0; i < columns; i++) {
+					GValue value = { 0, };
+
+					_tracker_db_result_set_get_value (result_set, i, &value);
+					_tracker_db_result_set_set_value (result, i, &value);
+					g_value_unset (&value);
+				}
+			}
+
+			g_free (path);
+			g_object_unref (result_set);
+		} else {
+			g_message ("Dud hit for search detected");
+			duds = g_slist_prepend (duds, &rank);
+		}
+	}
+
+	if (save_results) {
+		tracker_db_interface_end_transaction (iface);
+	}
+
+	/* Delete duds */
+	if (duds) {
+		TrackerDBIndex *file_index;
+		TrackerDBIndex *email_index;
+		GSList	       *words, *w;
+
+		words = tracker_query_tree_get_words (tree);
+		file_index = tracker_db_index_manager_get_index (TRACKER_DB_INDEX_FILE);
+		email_index = tracker_db_index_manager_get_index (TRACKER_DB_INDEX_EMAIL);
+
+		for (w = words; w; w = w->next) {
+			tracker_db_index_remove_dud_hits (file_index,
+							  (const gchar *) w->data,
+							  duds);
+			tracker_db_index_remove_dud_hits (email_index,
+							  (const gchar *) w->data,
+							  duds);
+		}
+
+		g_slist_free (words);
+	}
+
+	g_object_unref (tree);
+	g_array_free (hits, TRUE);
+	g_array_free (services, TRUE);
+
+	if (!result) {
+		return NULL;
+	}
+
+	if (tracker_db_result_set_get_n_rows (result) == 0) {
+		g_object_unref (result);
+		return NULL;
+	}
+
+	tracker_db_result_set_rewind (result);
+
+	return result;
+}
+
+TrackerDBResultSet *
+tracker_db_search_text_and_mime (TrackerDBInterface  *iface,
+				 const gchar	     *text,
+				 gchar		    **mime_array)
+{
+	TrackerDBPrivate   *private;
+	TrackerQueryTree   *tree;
+	TrackerDBResultSet *result_set1;
+	GArray		   *hits;
+	GArray		   *services;
+	gint		    count = 0;
+	guint		    i;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (iface), NULL);
+	g_return_val_if_fail (text != NULL, NULL);
+	g_return_val_if_fail (mime_array != NULL, NULL);
+
+	private = g_static_private_get (&private_key);
+	g_return_val_if_fail (private != NULL, NULL);
+
+	result_set1 = NULL;
+	services = tracker_db_create_array_of_services (NULL, TRUE);
+
+	tree = tracker_query_tree_new (text,
+				       private->config,
+				       private->language,
+				       services);
+	hits = tracker_query_tree_get_hits (tree, 0, 0);
+
+	for (i = 0, count = 0; i < hits->len; i++) {
+		TrackerDBResultSet     *result_set2;
+		TrackerDBIndexItemRank	rank;
+		gchar		       *str_id, *mimetype;
+
+		rank = g_array_index (hits, TrackerDBIndexItemRank, i);
+
+		str_id = tracker_guint_to_string (rank.service_id);
+		result_set2 = tracker_db_exec_proc (iface,
+						    "GetFileByID",
+						    str_id,
+						    NULL);
+		g_free (str_id);
+
+		if (result_set2) {
+			tracker_db_result_set_get (result_set2, 2, &mimetype, -1);
+
+			if (tracker_string_in_string_list (mimetype, mime_array) != -1) {
+				GValue value = { 0, };
+
+				if (G_UNLIKELY (!result_set1)) {
+					result_set1 = _tracker_db_result_set_new (2);
+				}
+
+				_tracker_db_result_set_append (result_set1);
+
+				/* copy value in column 0 */
+				_tracker_db_result_set_get_value (result_set2, 0, &value);
+				_tracker_db_result_set_set_value (result_set1, 0, &value);
+				g_value_unset (&value);
+
+				/* copy value in column 1 */
+				_tracker_db_result_set_get_value (result_set2, 1, &value);
+				_tracker_db_result_set_set_value (result_set1, 1, &value);
+				g_value_unset (&value);
+
+				count++;
+			}
+
+			g_free (mimetype);
+			g_object_unref (result_set2);
+		}
+
+		if (count > 2047) {
+			g_warning ("Count is > 2047? Breaking for loop in %s, why?",
+				   __FUNCTION__);
+			break;
+		}
+	}
+
+	g_object_unref (tree);
+	g_array_free (hits, TRUE);
+	g_array_free (services, TRUE);
+
+	if (!result_set1) {
+		return NULL;
+	}
+
+	if (tracker_db_result_set_get_n_rows (result_set1) == 0) {
+		g_object_unref (result_set1);
+		return NULL;
+	}
+
+	tracker_db_result_set_rewind (result_set1);
+
+	return result_set1;
+}
+
+TrackerDBResultSet *
+tracker_db_search_text_and_location (TrackerDBInterface *iface,
+				     const gchar	*text,
+				     const gchar	*location)
+{
+	TrackerDBPrivate   *private;
+	TrackerDBResultSet *result_set1;
+	TrackerQueryTree   *tree;
+	GArray		   *hits;
+	GArray		   *services;
+	gchar		   *location_prefix;
+	gint		    count;
+	guint		    i;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (iface), NULL);
+	g_return_val_if_fail (text != NULL, NULL);
+	g_return_val_if_fail (location != NULL, NULL);
+
+	private = g_static_private_get (&private_key);
+	g_return_val_if_fail (private != NULL, NULL);
+
+	result_set1 = NULL;
+	location_prefix = g_strconcat (location, G_DIR_SEPARATOR_S, NULL);
+	services = tracker_db_create_array_of_services (NULL, TRUE);
+
+	tree = tracker_query_tree_new (text,
+				       private->config,
+				       private->language,
+				       services);
+	hits = tracker_query_tree_get_hits (tree, 0, 0);
+
+	for (i = 0, count = 0; i < hits->len; i++) {
+		TrackerDBResultSet     *result_set2;
+		TrackerDBIndexItemRank	rank;
+		gchar		       *str_id, *path;
+
+		rank = g_array_index (hits, TrackerDBIndexItemRank, i);
+
+		str_id = tracker_guint_to_string (rank.service_id);
+		result_set2 = tracker_db_exec_proc (iface,
+						    "GetFileByID",
+						    str_id,
+						    NULL);
+		g_free (str_id);
+
+		if (result_set2) {
+			tracker_db_result_set_get (result_set2, 0, &path, -1);
+
+			if (g_str_has_prefix (path, location_prefix) ||
+			    strcmp (path, location) == 0) {
+				GValue value = { 0, };
+
+				if (G_UNLIKELY (!result_set1)) {
+					result_set1 = _tracker_db_result_set_new (2);
+				}
+
+				_tracker_db_result_set_append (result_set1);
+
+				/* copy value in column 0 */
+				_tracker_db_result_set_get_value (result_set2, 0, &value);
+				_tracker_db_result_set_set_value (result_set1, 0, &value);
+				g_value_unset (&value);
+
+				/* copy value in column 1 */
+				_tracker_db_result_set_get_value (result_set2, 1, &value);
+				_tracker_db_result_set_set_value (result_set1, 1, &value);
+				g_value_unset (&value);
+
+				count++;
+			}
+
+			g_object_unref (result_set2);
+		}
+
+		if (count > 2047) {
+			g_warning ("Count is > 2047? Breaking for loop in %s, why?",
+				   __FUNCTION__);
+			break;
+		}
+	}
+
+	g_free (location_prefix);
+	g_object_unref (tree);
+	g_array_free (hits, TRUE);
+	g_array_free (services, TRUE);
+
+	if (!result_set1) {
+		return NULL;
+	}
+
+	if (tracker_db_result_set_get_n_rows (result_set1) == 0) {
+		g_object_unref (result_set1);
+		return NULL;
+	}
+
+	tracker_db_result_set_rewind (result_set1);
+
+	return result_set1;
+}
+
+TrackerDBResultSet *
+tracker_db_search_text_and_mime_and_location (TrackerDBInterface  *iface,
+					      const gchar	  *text,
+					      gchar		 **mime_array,
+					      const gchar	  *location)
+{
+	TrackerDBPrivate   *private;
+	TrackerDBResultSet *result_set1;
+	TrackerQueryTree   *tree;
+	GArray		   *hits;
+	GArray		   *services;
+	gchar		   *location_prefix;
+	gint		    count;
+	guint		    i;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (iface), NULL);
+	g_return_val_if_fail (text != NULL, NULL);
+	g_return_val_if_fail (location != NULL, NULL);
+
+	private = g_static_private_get (&private_key);
+	g_return_val_if_fail (private != NULL, NULL);
+
+	result_set1 = NULL;
+	location_prefix = g_strconcat (location, G_DIR_SEPARATOR_S, NULL);
+	services = tracker_db_create_array_of_services (NULL, TRUE);
+
+	tree = tracker_query_tree_new (text,
+				       private->config,
+				       private->language,
+				       services);
+	hits = tracker_query_tree_get_hits (tree, 0, 0);
+
+	for (i = 0, count = 0; i < hits->len; i++) {
+		TrackerDBResultSet     *result_set2;
+		TrackerDBIndexItemRank	rank;
+		gchar		       *str_id, *path, *mimetype;
+
+		rank = g_array_index (hits, TrackerDBIndexItemRank, i);
+
+		str_id = tracker_guint_to_string (rank.service_id);
+		result_set2 = tracker_db_exec_proc (iface,
+						    "GetFileByID",
+						    str_id,
+						    NULL);
+		g_free (str_id);
+
+		if (result_set2) {
+			tracker_db_result_set_get (result_set2,
+						   0, &path,
+						   2, &mimetype,
+						   -1);
+
+			if ((g_str_has_prefix (path, location_prefix) ||
+			     strcmp (path, location) == 0) &&
+			    tracker_string_in_string_list (mimetype, mime_array) != -1) {
+				GValue value = { 0, };
+
+				if (G_UNLIKELY (!result_set1)) {
+					result_set1 = _tracker_db_result_set_new (2);
+				}
+
+				_tracker_db_result_set_append (result_set1);
+
+				/* copy value in column 0 */
+				_tracker_db_result_set_get_value (result_set2, 0, &value);
+				_tracker_db_result_set_set_value (result_set1, 0, &value);
+				g_value_unset (&value);
+
+				/* copy value in column 1 */
+				_tracker_db_result_set_get_value (result_set2, 1, &value);
+				_tracker_db_result_set_set_value (result_set1, 1, &value);
+				g_value_unset (&value);
+
+				count++;
+			}
+
+			g_free (path);
+			g_free (mimetype);
+			g_object_unref (result_set2);
+		}
+
+		if (count > 2047) {
+			g_warning ("Count is > 2047? Breaking for loop in %s, why?",
+				   __FUNCTION__);
+			break;
+		}
+	}
+
+	g_free (location_prefix);
+	g_object_unref (tree);
+	g_array_free (hits, TRUE);
+	g_array_free (services, TRUE);
+
+	if (!result_set1) {
+		return NULL;
+	}
+
+	if (tracker_db_result_set_get_n_rows (result_set1) == 0) {
+		g_object_unref (result_set1);
+		return NULL;
+	}
+
+	tracker_db_result_set_rewind (result_set1);
+
+	return result_set1;
+}
+
+TrackerDBResultSet *
+tracker_db_metadata_get (TrackerDBInterface *iface,
+			 const gchar	    *id,
+			 const gchar	    *key)
+{
+	TrackerField *def;
+	const gchar  *proc = NULL;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (iface), NULL);
+	g_return_val_if_fail (id, NULL);
+	g_return_val_if_fail (key, NULL);
+
+	def = tracker_ontology_get_field_by_name (key);
+
+	if (!def) {
+		g_warning ("Metadata not found for id:'%s' and type:'%s'", id, key);
+		return NULL;
+	}
+
+	switch (tracker_field_get_data_type (def)) {
+	case TRACKER_FIELD_TYPE_INDEX:
+	case TRACKER_FIELD_TYPE_STRING:
+	case TRACKER_FIELD_TYPE_DOUBLE:
+		proc = "GetMetadata";
+		break;
+
+	case TRACKER_FIELD_TYPE_INTEGER:
+	case TRACKER_FIELD_TYPE_DATE:
+		proc = "GetMetadataNumeric";
+		break;
+
+	case TRACKER_FIELD_TYPE_FULLTEXT:
+		proc = "GetContents";
+		break;
+
+	case TRACKER_FIELD_TYPE_KEYWORD:
+		proc = "GetMetadataKeyword";
+		break;
+
+	default:
+		g_warning ("Metadata could not be retrieved as type:%d is not supported",
+			   tracker_field_get_data_type (def));
+		return NULL;
+	}
+
+	return tracker_db_exec_proc (iface,
+				     proc,
+				     id,
+				     tracker_field_get_id (def),
+				     NULL);
+}
+
+TrackerDBResultSet *
+tracker_db_metadata_get_array (TrackerDBInterface *iface,
+			       const gchar	  *service_type,
+			       const gchar	  *service_id,
+			       gchar		 ** keys)
+{
+	TrackerDBResultSet *result_set;
+	GString		   *sql, *sql_join;
+	gchar		   *query;
+	guint		    i;
+
+	/* Build SQL select clause */
+	sql = g_string_new (" SELECT DISTINCT ");
+	sql_join = g_string_new (" FROM Services S ");
+
+	for (i = 0; i < g_strv_length (keys); i++) {
+		TrackerFieldData *field_data;
+
+		field_data = tracker_db_get_metadata_field (iface,
+							    service_type,
+							    keys[i],
+							    i,
+							    TRUE,
+							    FALSE);
+
+		if (!field_data) {
+			g_string_free (sql_join, TRUE);
+			g_string_free (sql, TRUE);
+			return NULL;
+		}
+
+		if (i == 0) {
+			g_string_append_printf (sql, " %s",
+						tracker_field_data_get_select_field (field_data));
+		} else {
+			g_string_append_printf (sql, ", %s",
+						tracker_field_data_get_select_field (field_data));
+		}
+
+		if (tracker_field_data_get_needs_join (field_data)) {
+			g_string_append_printf (sql_join,
+						"\n LEFT OUTER JOIN %s %s ON (S.ID = %s.ServiceID and %s.MetaDataID = %s) ",
+						tracker_field_data_get_table_name (field_data),
+						tracker_field_data_get_alias (field_data),
+						tracker_field_data_get_alias (field_data),
+						tracker_field_data_get_alias (field_data),
+						tracker_field_data_get_id_field (field_data));
+		}
+
+		g_object_unref (field_data);
+	}
+
+	g_string_append (sql, sql_join->str);
+	g_string_free (sql_join, TRUE);
+
+	/* Build SQL where clause */
+	g_string_append_printf (sql, " WHERE S.ID = %s", service_id);
+
+	query = g_string_free (sql, FALSE);
+
+	g_debug (query);
+
+	result_set = tracker_db_interface_execute_query (iface, NULL, query);
+
+	g_free (query);
+
+	return result_set;
+}
+
+
+/* Gets specified metadata value as a single row (multple values for a
+ * metadata type are returned delimited by  "|" )
+ */
+gchar *
+tracker_db_metadata_get_delimited (TrackerDBInterface *iface,
+				   const gchar	      *id,
+				   const gchar	      *key)
+{
+	TrackerDBResultSet *result_set;
+	GString		   *s = NULL;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (iface), NULL);
+	g_return_val_if_fail (id != NULL, NULL);
+	g_return_val_if_fail (key != NULL, NULL);
+
+	result_set = tracker_db_metadata_get (iface, id, key);
+
+	if (result_set) {
+		gchar	 *str;
+		gboolean  valid = TRUE;
+
+		while (valid) {
+			tracker_db_result_set_get (result_set, 0, &str, -1);
+
+			if (s) {
+				g_string_append_printf (s, "|%s", str);
+			} else {
+				s = g_string_new (str);
+			}
+
+			g_free (str);
+			valid = tracker_db_result_set_iter_next (result_set);
+		}
+
+		g_object_unref (result_set);
+	}
+
+	if (s) {
+		return g_string_free (s, FALSE);
+	} else {
+		return NULL;
+	}
+}
+
+gchar *
+tracker_db_metadata_get_related_names (TrackerDBInterface *iface,
+				       const gchar	  *name)
+{
+	TrackerDBResultSet *result_set;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (iface), NULL);
+	g_return_val_if_fail (name != NULL, NULL);
+
+	result_set = tracker_db_exec_proc (iface,
+					   "GetMetadataAliasesForName",
+					   name,
+					   name,
+					   NULL);
+
+	if (result_set) {
+		GString  *s = NULL;
+		gboolean  valid = TRUE;
+		gint	  id;
+
+		while (valid) {
+			tracker_db_result_set_get (result_set, 1, &id, -1);
+
+			if (s) {
+				g_string_append_printf (s, ", %d", id);
+			} else {
+				s = g_string_new ("");
+				g_string_append_printf (s, "%d", id);
+			}
+
+			valid = tracker_db_result_set_iter_next (result_set);
+		}
+
+		g_object_unref (result_set);
+
+		return g_string_free (s, FALSE);
+	}
+
+	return NULL;
+}
+
+TrackerDBResultSet *
+tracker_db_xesam_get_metadata_names (TrackerDBInterface *iface,
+				     const gchar	*name)
+{
+	TrackerDBResultSet *result_set;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (iface), NULL);
+	g_return_val_if_fail (name != NULL, NULL);
+
+	result_set = tracker_db_exec_proc (iface,
+					   "GetXesamMetaDataLookups",
+					   name,
+					   NULL);
+
+	return result_set;
+}
+
+TrackerDBResultSet *
+tracker_db_xesam_get_all_text_metadata_names (TrackerDBInterface *iface)
+{
+	TrackerDBResultSet *result_set;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (iface), NULL);
+
+	result_set = tracker_db_exec_proc (iface,
+					   "GetXesamMetaDataTextLookups",
+					   NULL);
+
+	return result_set;
+}
+
+TrackerDBResultSet *
+tracker_db_xesam_get_service_names (TrackerDBInterface *iface,
+				    const gchar        *name)
+{
+	TrackerDBResultSet *result_set;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (iface), NULL);
+	g_return_val_if_fail (name != NULL, NULL);
+
+	result_set = tracker_db_exec_proc (iface,
+					   "GetXesamServiceLookups",
+					   name,
+					   NULL);
+
+	return result_set;
+}
+
+const gchar *
+tracker_db_metadata_get_table (TrackerFieldType type)
+{
+	switch (type) {
+	case TRACKER_FIELD_TYPE_INDEX:
+	case TRACKER_FIELD_TYPE_STRING:
+	case TRACKER_FIELD_TYPE_DOUBLE:
+		return "ServiceMetaData";
+
+	case TRACKER_FIELD_TYPE_INTEGER:
+	case TRACKER_FIELD_TYPE_DATE:
+		return "ServiceNumericMetaData";
+
+	case TRACKER_FIELD_TYPE_BLOB:
+		return "ServiceBlobMetaData";
+
+	case TRACKER_FIELD_TYPE_KEYWORD:
+		return "ServiceKeywordMetaData";
+
+	default:
+		break;
+	}
+
+	return NULL;
+}
+
+void
+tracker_db_metadata_set_single (TrackerDBInterface *iface,
+				const gchar	   *service_type,
+				const gchar	   *service_id,
+				const gchar	   *key,
+				const gchar	   *value,
+				gboolean	    do_backup)
+{
+	gchar *array[2];
+
+	g_return_if_fail (TRACKER_IS_DB_INTERFACE (iface));
+	g_return_if_fail (service_type != NULL);
+	g_return_if_fail (service_id != NULL);
+	g_return_if_fail (key != NULL);
+	g_return_if_fail (value != NULL);
+
+	array[0] = (gchar*) value;
+	array[1] = NULL;
+
+	tracker_db_metadata_set (iface, service_type, service_id, key, array, do_backup);
+}
+
+gchar *
+tracker_db_metadata_set (TrackerDBInterface  *iface,
+			 const gchar	     *service_type,
+			 const gchar	     *service_id,
+			 const gchar	     *key,
+			 gchar		    **values,
+			 gboolean	      do_backup)
+{
+	TrackerDBPrivate *private;
+	TrackerField	 *def;
+	gchar		 *old_value;
+	gchar		 *new_value;
+	gchar		 *res_service;
+	gboolean	  update_index;
+	gint		  key_field;
+	guint		  i;
+	guint		  length;
+	GString		 *str;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (iface), NULL);
+	g_return_val_if_fail (service_type != NULL, NULL);
+	g_return_val_if_fail (service_id != NULL, NULL);
+	g_return_val_if_fail (key != NULL, NULL);
+	g_return_val_if_fail (values != NULL, NULL);
+
+	private = g_static_private_get (&private_key);
+	g_return_val_if_fail (private != NULL, NULL);
+
+	if (strcmp (service_id, "0") == 0) {
+		return NULL;
+	}
+
+	def = tracker_ontology_get_field_by_name (key);
+
+	if (!def) {
+		g_warning ("Metadata type:'%s' not found", key);
+		return NULL;
+	}
+
+	res_service = tracker_db_service_get_by_entity (iface, service_id);
+
+	if (!res_service) {
+		g_warning ("Service not found for service_id:'%s'", service_id);
+		return NULL;
+	}
+
+	old_value = NULL;
+	new_value = NULL;
+	key_field = 0;
+	str = NULL;
+
+	length = g_strv_length (values);
+
+	if (tracker_field_get_multiple_values (def) && length > 1) {
+		str = g_string_new ("");
+	}
+
+	key_field = tracker_ontology_service_get_key_metadata (res_service, key);
+
+	update_index =
+		tracker_field_get_data_type (def) == TRACKER_FIELD_TYPE_INDEX ||
+		tracker_field_get_data_type (def) == TRACKER_FIELD_TYPE_KEYWORD ||
+		tracker_field_get_data_type (def) == TRACKER_FIELD_TYPE_FULLTEXT;
+
+	if (update_index) {
+		old_value = tracker_db_metadata_get_delimited (iface, service_id, key);
+	}
+
+	/* Delete old value if metadata does not support multiple values */
+	if (!tracker_field_get_multiple_values (def)) {
+		tracker_db_metadata_delete (iface, service_type, service_id, key, FALSE);
+	}
+
+	switch (tracker_field_get_data_type (def)) {
+	case TRACKER_FIELD_TYPE_KEYWORD:
+		for (i = 0; i < length; i++) {
+			if (!values[i] || !values[i][0]) {
+				continue;
+			}
+
+			tracker_db_exec_proc (iface,
+					      "SetMetadataKeyword",
+					      service_id,
+					      tracker_field_get_id (def),
+					      values[i],
+					      NULL);
+
+			/* Backup non-embedded data for embedded services */
+			if (do_backup &&
+			    !tracker_field_get_embedded (def) &&
+			    tracker_ontology_service_has_embedded (service_type)) {
+				backup_non_embedded_metadata (iface,
+							      service_id,
+							      tracker_field_get_id (def),
+							      values[i]);
+			}
+
+			if (str) {
+				g_string_append_printf (str, " %s ", values[i]);
+			} else {
+				new_value = values[i];
+			}
+
+			g_message ("Saving keyword:'%s'", values[i]);
+		}
+		break;
+
+	case TRACKER_FIELD_TYPE_INDEX:
+		for (i = 0; i < length; i++) {
+			gchar *mvalue;
+
+			if (!values[i] || !values[i][0]) {
+				continue;
+			}
+
+			if (str) {
+				g_string_append_printf (str, " %s ", values[i]);
+			} else {
+				new_value = values[i];
+			}
+
+			/* Backup non-embedded data for embedded services */
+			if (do_backup &&
+			    !tracker_field_get_embedded (def) &&
+			    tracker_ontology_service_has_embedded (service_type)) {
+				backup_non_embedded_metadata (iface, service_id, tracker_field_get_id (def), values[i]);
+			}
+
+			mvalue = tracker_parser_text_to_string (values[i],
+								private->language,
+								tracker_config_get_max_word_length (private->config),
+								tracker_config_get_min_word_length (private->config),
+								tracker_field_get_filtered (def),
+								tracker_field_get_filtered (def),
+								tracker_field_get_delimited (def));
+			tracker_db_exec_proc (iface,
+					      "SetMetadata",
+					      service_id,
+					      tracker_field_get_id (def),
+					      mvalue,
+					      values[i],
+					      NULL);
+			g_free (mvalue);
+		}
+		break;
+
+	case TRACKER_FIELD_TYPE_FULLTEXT:
+		/* We do not support multiple values for fulltext clobs */
+		if (values[0]) {
+			/* FIXME: is the blog the metadata blob/email
+			 * blob or something else?
+			 */
+			db_save_full_text (iface,
+					   service_id,
+					   values[0],
+					   strlen (values[0]));
+			new_value = values[0];
+		}
+		break;
+
+	case TRACKER_FIELD_TYPE_STRING:
+		for (i = 0; i < length; i++) {
+			gchar *mvalue;
+
+			if (!values[i] || !values[i][0]) {
+				continue;
+			}
+
+			/* Backup non-embedded data for embedded services */
+			if (do_backup &&
+			    !tracker_field_get_embedded (def) &&
+			    tracker_ontology_service_has_embedded (service_type)) {
+				backup_non_embedded_metadata (iface,
+							      service_id,
+							      tracker_field_get_id (def),
+							      values[i]);
+			}
+
+			mvalue = tracker_parser_text_to_string (values[i],
+								private->language,
+								tracker_config_get_max_word_length (private->config),
+								tracker_config_get_min_word_length (private->config),
+								tracker_field_get_filtered (def),
+								tracker_field_get_filtered (def),
+								tracker_field_get_delimited (def));
+			tracker_db_exec_proc (iface,
+					      "SetMetadata",
+					      service_id,
+					      tracker_field_get_id (def),
+					      mvalue,
+					      values[i],
+					      NULL);
+			g_free (mvalue);
+		}
+		break;
+
+	case TRACKER_FIELD_TYPE_DOUBLE:
+		for (i = 0; i < length; i++) {
+			if (!values[i] || !values[i][0]) {
+				continue;
+			}
+
+			/* Backup non-embedded data for embedded services */
+			if (do_backup &&
+			    !tracker_field_get_embedded (def) &&
+			    tracker_ontology_service_has_embedded (service_type)) {
+				backup_non_embedded_metadata (iface,
+							      service_id,
+							      tracker_field_get_id (def),
+							      values[i]);
+			}
+
+			tracker_db_exec_proc (iface,
+					      "SetMetadata",
+					      service_id,
+					      tracker_field_get_id (def),
+					      " ",
+					      values[i],
+					      NULL);
+		}
+		break;
+
+	case TRACKER_FIELD_TYPE_INTEGER:
+		for (i = 0; i < length; i++) {
+			if (!values[i] || !values[i][0]) {
+				continue;
+			}
+
+			/* Backup non-embedded data for embedded services */
+			if (do_backup &&
+			    !tracker_field_get_embedded (def) &&
+			    tracker_ontology_service_has_embedded (service_type)) {
+				backup_non_embedded_metadata (iface,
+							      service_id,
+							      tracker_field_get_id (def),
+							      values[i]);
+			}
+
+			tracker_db_exec_proc (iface,
+					      "SetMetadataNumeric",
+					      service_id,
+					      tracker_field_get_id (def),
+					      values[i],
+					      NULL);
+		}
+		break;
+
+	case TRACKER_FIELD_TYPE_DATE:
+		for (i = 0; i < length; i++) {
+			gchar *mvalue;
+
+			if (!values[i] || !values[i][0]) {
+				continue;
+			}
+
+			mvalue = tracker_date_to_time_string (values[i]);
+
+			if (!mvalue) {
+				g_warning ("Could not format date:'%s'", values[i]);
+				continue;
+			}
+
+			tracker_db_exec_proc (iface,
+					      "SetMetadataNumeric",
+					      service_id,
+					      tracker_field_get_id (def),
+					      mvalue,
+					      NULL);
+
+			/* backup non-embedded data for embedded services */
+			if (do_backup &&
+			    !tracker_field_get_embedded (def) &&
+			    tracker_ontology_service_has_embedded (service_type)) {
+				backup_non_embedded_metadata (iface,
+							      service_id,
+							      tracker_field_get_id (def),
+							      mvalue);
+			}
+
+
+			g_free (mvalue);
+		}
+		break;
+
+	default:
+		g_warning ("Metadata could not be set as type:%d for "
+			   "metadata:'%s' is not supported",
+			   tracker_field_get_data_type (def),
+			   key);
+		break;
+	}
+
+	if (key_field > 0) {
+		if (values[0]) {
+			gchar *esc_value = NULL;
+
+			if (tracker_field_get_data_type (def) == TRACKER_FIELD_TYPE_DATE) {
+				esc_value = tracker_date_to_time_string (values[0]);
+
+				if (!esc_value) {
+					return NULL;
+				}
+			} else {
+				gchar *str;
+
+				str = tracker_string_list_to_string (values, length, '|');
+				esc_value = tracker_escape_string (str);
+				g_free (str);
+			}
+
+			tracker_db_exec_no_reply (iface,
+						  "update Services set KeyMetadata%d = '%s' where id = %s",
+						  key_field,
+						  esc_value,
+						  service_id);
+
+			g_free (esc_value);
+		}
+
+	}
+
+	/* Update fulltext index differentially with current and new
+	 * values.
+	 */
+	if (update_index) {
+		if (str) {
+			update_metadata_index (service_id, res_service, def, old_value, str->str);
+			g_string_free (str, TRUE);
+		} else {
+			update_metadata_index (service_id, res_service, def, old_value, new_value);
+		}
+	}
+
+	g_free (old_value);
+	g_free (res_service);
+
+	return NULL;
+}
+
+void
+tracker_db_metadata_delete_value (TrackerDBInterface *iface,
+				  const gchar	     *service,
+				  const gchar	     *id,
+				  const gchar	     *key,
+				  const gchar	     *value)
+{
+	TrackerDBPrivate *private;
+	TrackerField	 *def;
+	gchar		 *old_value;
+	gchar		 *new_value;
+	gchar		 *mvalue;
+	gchar		 *res_service;
+	gboolean	  update_index;
+	gint		  key_field;
+
+	g_return_if_fail (TRACKER_IS_DB_INTERFACE (iface));
+	g_return_if_fail (service != NULL);
+	g_return_if_fail (id != NULL);
+	g_return_if_fail (key != NULL);
+
+	private = g_static_private_get (&private_key);
+	g_return_if_fail (private != NULL);
+
+	/* Get type details */
+	def = tracker_ontology_get_field_by_name (key);
+
+	if (!def) {
+		return;
+	}
+
+	old_value = NULL;
+	new_value = NULL;
+
+	if (!tracker_field_get_embedded (def) &&
+	    tracker_ontology_service_has_embedded (service)) {
+		backup_delete_non_embedded_metadata_value (iface,
+							   id,
+							   tracker_field_get_id (def),
+							   value);
+	}
+
+	res_service = tracker_db_service_get_by_entity (iface, id);
+
+	if (!res_service) {
+		g_warning ("Entity not found");
+		return;
+	}
+
+	key_field = tracker_ontology_service_get_key_metadata (res_service, key);
+
+	update_index =
+		tracker_field_get_data_type (def) == TRACKER_FIELD_TYPE_INDEX ||
+		tracker_field_get_data_type (def) == TRACKER_FIELD_TYPE_KEYWORD;
+
+	if (update_index) {
+		/* Get current value and claculate the new value */
+		old_value = tracker_db_metadata_get_delimited (iface, id, key);
+
+		if (old_value) {
+			new_value = remove_value (old_value, value);
+		} else {
+			g_free (res_service);
+			return;
+		}
+	}
+
+	/* Perform deletion */
+	switch (tracker_field_get_data_type (def)) {
+	case TRACKER_FIELD_TYPE_INDEX:
+	case TRACKER_FIELD_TYPE_STRING:
+		mvalue = tracker_parser_text_to_string (value,
+							private->language,
+							tracker_config_get_max_word_length (private->config),
+							tracker_config_get_min_word_length (private->config),
+							tracker_field_get_filtered (def),
+							tracker_field_get_filtered (def),
+							tracker_field_get_delimited (def));
+		tracker_db_exec_proc (iface,
+				      "DeleteMetadataValue",
+				      id,
+				      tracker_field_get_id (def),
+				      mvalue,
+				      NULL);
+		g_free (mvalue);
+		break;
+
+	case TRACKER_FIELD_TYPE_DOUBLE:
+		tracker_db_exec_proc (iface,
+				      "DeleteMetadataValue",
+				      id,
+				      tracker_field_get_id (def),
+				      value,
+				      NULL);
+		break;
+
+	case TRACKER_FIELD_TYPE_INTEGER:
+	case TRACKER_FIELD_TYPE_DATE:
+		tracker_db_exec_proc (iface,
+				      "DeleteMetadataNumericValue",
+				      id,
+				      tracker_field_get_id (def),
+				      value,
+				      NULL);
+		break;
+
+	case TRACKER_FIELD_TYPE_KEYWORD:
+		tracker_db_exec_proc (iface,
+				      "DeleteMetadataKeywordValue",
+				      id,
+				      tracker_field_get_id (def),
+				      value,
+				      NULL);
+		break;
+
+	default:
+		g_warning ("Metadata could not be deleted as type:%d for "
+			   "metadata:'%s' is not supported",
+			   tracker_field_get_data_type (def),
+			   key);
+		break;
+	}
+
+	if (key_field > 0) {
+		TrackerDBResultSet *result_set;
+		gchar		   *value;
+
+		result_set = tracker_db_metadata_get (iface, id, key);
+
+		if (result_set) {
+			tracker_db_result_set_get (result_set, 0, &value, -1);
+
+			if (value) {
+				gchar *esc_value;
+
+				esc_value = tracker_escape_string (value);
+				tracker_db_exec_no_reply (iface,
+							 "update Services set KeyMetadata%d = '%s' where id = %s",
+							  key_field,
+							  esc_value,
+							  id);
+
+				g_free (esc_value);
+				g_free (value);
+			} else {
+				tracker_db_exec_no_reply (iface,
+							  "update Services set KeyMetadata%d = NULL where id = %s",
+							  key_field,
+							  id);
+			}
+
+			g_object_unref (result_set);
+		} else {
+			tracker_db_exec_no_reply (iface,
+						  "update Services set KeyMetadata%d = NULL where id = %s",
+						  key_field,
+						  id);
+		}
+	}
+
+	/* Update fulltext index differentially with old and new values */
+	if (update_index) {
+		update_metadata_index (id,
+				       service,
+				       def,
+				       old_value,
+				       new_value);
+	}
+
+	g_free (new_value);
+	g_free (old_value);
+	g_free (res_service);
+}
+
+void
+tracker_db_metadata_delete (TrackerDBInterface *iface,
+			    const gchar        *service,
+			    const gchar        *id,
+			    const gchar        *key,
+			    gboolean		update_indexes)
+{
+	TrackerField *def;
+	gchar	     *old_value = NULL;
+	gchar	     *res_service;
+	gboolean      update_index;
+	gint	      key_field;
+
+	g_return_if_fail (TRACKER_IS_DB_INTERFACE (iface));
+	g_return_if_fail (service != NULL);
+	g_return_if_fail (id != NULL);
+	g_return_if_fail (key != NULL);
+
+	/* Get type details */
+	def = tracker_ontology_get_field_by_name(key);
+
+	if (!def) {
+		return;
+	}
+
+	if (!tracker_field_get_embedded (def) &&
+	     tracker_ontology_service_has_embedded (service)) {
+		backup_delete_non_embedded_metadata (iface,
+						     id,
+						     tracker_field_get_id (def));
+	}
+
+	res_service = tracker_db_service_get_by_entity (iface, id);
+
+	if (!res_service) {
+		g_warning ("Entity not found");
+		return;
+	}
+
+	key_field = tracker_ontology_service_get_key_metadata (res_service, key);
+
+	update_index =
+		update_indexes &&
+		(tracker_field_get_data_type (def) == TRACKER_FIELD_TYPE_INDEX ||
+		 tracker_field_get_data_type (def) == TRACKER_FIELD_TYPE_KEYWORD);
+
+	if (update_index) {
+		/* Get current value */
+		old_value = tracker_db_metadata_get_delimited (iface, id, key);
+	}
+
+	if (key_field > 0) {
+		tracker_db_exec_no_reply (iface,
+					  "update Services set KeyMetadata%d = NULL where id = %s",
+					  key_field, id);
+	}
+
+	/* Perform deletion */
+	switch (tracker_field_get_data_type (def)) {
+	case TRACKER_FIELD_TYPE_INDEX:
+	case TRACKER_FIELD_TYPE_STRING:
+	case TRACKER_FIELD_TYPE_DOUBLE:
+		tracker_db_exec_proc (iface,
+				      "DeleteMetadata",
+				      id,
+				      tracker_field_get_id (def),
+				      NULL);
+		break;
+
+	case TRACKER_FIELD_TYPE_INTEGER:
+	case TRACKER_FIELD_TYPE_DATE:
+		tracker_db_exec_proc (iface,
+				      "DeleteMetadataNumeric",
+				      id,
+				      tracker_field_get_id (def),
+				      NULL);
+		break;
+
+	case TRACKER_FIELD_TYPE_KEYWORD:
+		tracker_db_exec_proc (iface,
+				      "DeleteMetadataKeyword",
+				      id,
+				      tracker_field_get_id (def),
+				      NULL);
+		break;
+
+	case TRACKER_FIELD_TYPE_FULLTEXT:
+		tracker_db_exec_proc (iface,
+				      "DeleteContent",
+				      id,
+				      tracker_field_get_id (def),
+				      NULL);
+		break;
+
+	default:
+		g_warning ("Metadata could not be deleted as this "
+			   "operation is not supported by type:%d "
+			   "for metadata:'%s'",
+			   tracker_field_get_data_type (def),
+			   key);
+		break;
+	}
+
+
+	/* Update fulltext index differentially with old values and NULL */
+	if (update_index && old_value) {
+		update_metadata_index (id, service, def, old_value, " ");
+	}
+
+	g_free (old_value);
+	g_free (res_service);
+}
+
+TrackerDBResultSet *
+tracker_db_live_search_get_hit_count (TrackerDBInterface *iface,
+				      const gchar	 *search_id)
+{
+	/* SELECT count(*)
+	 * FROM LiveSearches
+	 * WHERE SearchID = ? */
+
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (iface), NULL);
+	g_return_val_if_fail (search_id != NULL, NULL);
+
+	return tracker_db_exec_proc (iface,
+				     "GetLiveSearchHitCount",
+				     search_id,
+				     NULL);
+}
+
+void
+tracker_db_live_search_start (TrackerDBInterface *iface,
+			      const gchar	 *from_query,
+			      const gchar	 *join_query,
+			      const gchar	 *where_query,
+			      const gchar	 *search_id)
+{
+	/* INSERT
+	 * INTO LiveSearches
+	 * SELECT ID, SEARCH_ID FROM_QUERY WHERE_QUERY */
+
+	g_return_if_fail (TRACKER_IS_DB_INTERFACE (iface));
+	g_return_if_fail (from_query != NULL);
+	g_return_if_fail (join_query != NULL);
+	g_return_if_fail (where_query != NULL);
+	g_return_if_fail (search_id != NULL);
+
+	g_message ("INSERT INTO cache.LiveSearches SELECT S.ID, '%s' %s %s %s",
+				  search_id,
+				  from_query,
+				  join_query,
+				  where_query);
+
+	tracker_db_exec_no_reply (iface,
+				  "INSERT INTO cache.LiveSearches SELECT S.ID, '%s' %s %s %s",
+				  search_id,
+				  from_query,
+				  join_query,
+				  where_query);
+}
+
+void
+tracker_db_live_search_stop (TrackerDBInterface *iface,
+			     const gchar	*search_id)
+{
+	/* DELETE
+	 * FROM LiveSearches as X
+	 * WHERE E.SearchID = ? */
+
+	g_return_if_fail (TRACKER_IS_DB_INTERFACE (iface));
+	g_return_if_fail (search_id != NULL);
+
+	db_exec_proc_no_reply (iface,
+			       "LiveSearchStopSearch",
+			       search_id,
+			       NULL);
+}
+
+TrackerDBResultSet *
+tracker_db_live_search_get_all_ids (TrackerDBInterface *iface,
+				    const gchar        *search_id)
+{
+	/* Contract, in @result:
+	 * ServiceID is #1 */
+
+	/*
+	 * SELECT X.ServiceID
+	 * FROM LiveSearches as X
+	 * WHERE X.SearchID = SEARCH_ID
+	 */
+
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (iface), NULL);
+	g_return_val_if_fail (search_id != NULL, NULL);
+
+	return tracker_db_exec_proc (iface,
+				     "GetLiveSearchAllIDs",
+				     search_id,
+				     NULL);
+}
+
+TrackerDBResultSet *
+tracker_db_live_search_get_new_ids (TrackerDBInterface *iface,
+				    const gchar        *search_id,
+				    const gchar        *from_query,
+				    const gchar        *query_joins,
+				    const gchar        *where_query)
+{
+	TrackerDBResultSet *result_set;
+
+	/* Contract, in @result:
+	 * ServiceID is #1
+	 * EventType is #2 */
+
+	/*
+	 * SELECT E.ServiceID, E.EventType
+	 * FROM_QUERY, LiveSearches as X, Events as E
+	 * QUERY_JOINS
+	 * WHERE_QUERY
+	 * AND X.ServiceID = E.ServiceID
+	 * AND X.SearchID = 'SEARCH_ID'
+	 * AND E.EventType = 'Update'
+	 * UNION
+	 * SELECT E.ServiceID, E.EventType
+	 * FROM_QUERY, Events as E
+	 * QUERY_JOINS
+	 * WHERE_QUERY
+	 * AND E.ServiceID = S.ID
+	 * AND E.EventType = 'Create'
+	 */
+
+	/*
+	 * INSERT INTO LiveSearches
+	 * SELECT E.ServiceID, 'SEARCH_ID'
+	 * FROM_QUERY, Events as E
+	 * QUERY_JOINS
+	 * WHERE_QUERY
+	 * AND E.ServiceID = S.ID
+	 * AND E.EventType = 'Create'
+	 */
+
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (iface), NULL);
+	g_return_val_if_fail (search_id != NULL, NULL);
+	g_return_val_if_fail (from_query != NULL, NULL);
+	g_return_val_if_fail (query_joins != NULL, NULL);
+	g_return_val_if_fail (where_query != NULL, NULL);
+
+	// We need to add 'file-meta' and 'email-meta' here
+
+	result_set = tracker_db_exec (iface,
+				      "SELECT E.ServiceID, E.EventType "
+				      "%s%s cache.LiveSearches as X, Events as E " /* FROM   A1 */
+				       "%s"				     /* JOINS  A2 */
+				       "%s"				     /* WHERE  A3 */
+				      "%sX.ServiceID = E.ServiceID "
+				      "AND X.SearchID = '%s' "		     /*        A4 */
+				      "AND E.EventType = 'Update' "
+				      "UNION "
+				      "SELECT E.ServiceID, E.EventType "
+				      "%s%s Events as E "		     /* FROM   B1 */
+				      "%s"				     /* JOINS  B2 */
+				      "%s"				     /* WHERE  B3 */
+				      "%sE.ServiceID = S.ID "
+				      "AND E.EventType = 'Create' ",
+				      from_query ? from_query : "FROM",      /*        A1 */
+				      from_query ? "," : "",		     /*        A1 */
+				      query_joins,			     /*        A2 */
+				      where_query ? where_query : "WHERE",   /*        A3 */
+				      where_query ? "AND " : "",	     /*        A3 */
+				      search_id,			     /*        A4 */
+				      from_query ? from_query : "FROM",      /*        B1 */
+				      from_query ? "," : "",		     /*        B1 */
+				      query_joins,			     /*        B2 */
+				      where_query ? where_query : "WHERE",   /*        B3 */
+				      where_query ? "AND " : "");	     /*        B3 */
+
+	tracker_db_exec_no_reply (iface,
+				  "INSERT INTO cache.LiveSearches "
+				   "SELECT E.ServiceID, '%s' "		     /*        B0 */
+				  "%s%s Events as E "			     /* FROM   B1 */
+				  "%s"					     /* JOINS  B2 */
+				   "%s"					     /* WHERE  B3 */
+				  "%sE.ServiceID = S.ID"
+				  "AND E.EventType = 'Create' ",
+				  search_id,				     /*        B0 */
+				  from_query ? from_query : "FROM",	     /*        B1 */
+				  from_query ? "," : "",		     /*        B1 */
+				  query_joins,				     /*        B2 */
+				  where_query ? where_query : "WHERE",	     /*        B3 */
+				  where_query ? "AND " : "");		     /*        B3 */
+
+	return result_set;
+}
+
+TrackerDBResultSet*
+tracker_db_live_search_get_deleted_ids (TrackerDBInterface *iface,
+					const gchar	   *search_id)
+{
+	/* SELECT E.ServiceID
+	 * FROM Events as E, LiveSearches as X
+	 * WHERE E.ServiceID = X.ServiceID
+	 * AND X.SearchID = ?
+	 * AND E.EventType IS 'Delete' */
+
+	/* DELETE FROM LiveSearches AS Y WHERE Y.ServiceID IN
+	 * SELECT ServiceID FROM Events as E, LiveSearches as X
+	 * WHERE E.ServiceID = X.ServiceID
+	 * AND X.SearchID = ?
+	 * AND E.EventType IS 'Delete' */
+
+	TrackerDBResultSet *result_set;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (iface), NULL);
+	g_return_val_if_fail (search_id != NULL, NULL);
+
+	result_set = tracker_db_exec_proc (iface,
+					   "GetLiveSearchDeletedIDs",
+					   search_id,
+					   NULL);
+
+	db_exec_proc_no_reply (iface,
+			       "DeleteLiveSearchDeletedIDs",
+			       search_id,
+			       NULL);
+	return result_set;
+}
+
+/* FIXME This function should be moved with other help-functions somewhere.
+ * It is used by xesam_live_search parsing. */
+
+static GList *
+add_live_search_metadata_field (TrackerDBInterface *iface,
+				GSList **fields,
+				const char *xesam_name)
+{
+	TrackerDBResultSet *result_set;
+	TrackerFieldData   *field_data;
+	gboolean	    field_exists;
+	const GSList	   *l;
+	GList		   *reply;
+	gboolean	    valid;
+
+	reply = NULL;
+	field_exists = FALSE;
+	field_data = NULL;
+	valid = TRUE;
+
+	/* Do the xesam mapping */
+
+	g_debug ("add metadata field");
+
+	result_set = tracker_db_exec_proc (iface, "GetXesamMetaDataMappings",xesam_name, NULL);
+
+	if (!result_set) {
+		return NULL;
+	}
+
+	while (valid) {
+		gchar *field_name;
+
+		tracker_db_result_set_get (result_set, 0, &field_name, -1);
+
+		/* Check if field is already in list */
+		for (l = *fields; l; l = l->next) {
+			const gchar *this_field_name;
+
+			this_field_name = tracker_field_data_get_field_name (l->data);
+
+			if (!this_field_name) {
+				continue;
+			}
+
+			if (strcasecmp (this_field_name, field_name) != 0) {
+				continue;
+			}
+
+			field_exists = TRUE;
+
+			break;
+		}
+
+		if (!field_exists) {
+			field_data = tracker_db_get_metadata_field (iface,
+								    "Files",
+								    field_name,
+								    g_slist_length (*fields),
+								    FALSE,
+								    FALSE);
+
+			if (field_data) {
+				*fields = g_slist_prepend (*fields, field_data);
+			}
+		}
+
+		reply = g_list_append (reply, field_data);
+		valid = tracker_db_result_set_iter_next (result_set);
+		g_free (field_name);
+	}
+
+	return reply;
+}
+
+
+
+TrackerDBResultSet *
+tracker_db_live_search_get_hit_data (TrackerDBInterface *iface,
+				     const gchar	*search_id,
+				     GStrv		 field_names)
+{
+	TrackerDBResultSet *result;
+	GSList		   *fields = NULL;
+	GSList		   *l = NULL;
+	GString		   *sql_select;
+	GString		   *sql_join;
+	gint		    i = 0;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (iface), NULL);
+	g_return_val_if_fail (search_id != NULL, NULL);
+
+	sql_select = g_string_new ("X.ServiceID, ");
+	sql_join = g_string_new ("");
+
+	while (field_names[i]) {
+		GList *field_data_list = NULL;
+
+		field_data_list = add_live_search_metadata_field (iface,
+								  &fields,
+								  field_names[i]);
+
+		if (!field_data_list) {
+			g_warning ("Asking for a non-mapped xesam field: %s", field_names[i]);
+			g_string_free (sql_select, TRUE);
+			g_string_free (sql_join, TRUE);
+			return NULL;
+		}
+
+		if (i) {
+			g_string_append_printf (sql_select, ",");
+		}
+
+		g_string_append_printf (sql_select, " %s",
+					tracker_field_data_get_select_field (field_data_list->data) );
+
+		i++;
+	}
+
+	for (l = fields; l; l = l->next) {
+		gchar *field_name;
+
+		field_name = tracker_db_metadata_get_related_names (iface,
+								    tracker_field_data_get_field_name (l->data));
+		g_string_append_printf (sql_join,
+					"INNER JOIN 'files-meta'.%s %s ON (X.ServiceID = %s.ServiceID AND %s.MetaDataID in (%s))\n ",
+					tracker_field_data_get_table_name (l->data),
+					tracker_field_data_get_alias (l->data),
+					tracker_field_data_get_alias (l->data),
+					tracker_field_data_get_alias (l->data),
+					field_name);
+		g_free (field_name);
+	}
+
+	g_debug("Query : SELECT %s FROM cache.LiveSearches as X \n"
+		"%s"
+		"WHERE X.SearchID = '%s'",
+		sql_select->str, sql_join->str, search_id);
+
+	result = tracker_db_exec (iface,
+				  "SELECT %s FROM cache.LiveSearches as X \n"
+				  "%s"
+				  "WHERE X.SearchID = '%s'",
+				  sql_select->str, sql_join->str, search_id);
+
+	g_string_free (sql_select, TRUE);
+	g_string_free (sql_join, TRUE);
+
+	return result;
+}
+
+void
+tracker_db_xesam_delete_handled_events (TrackerDBInterface *iface)
+{
+	g_return_if_fail (TRACKER_IS_DB_INTERFACE (iface));
+
+	tracker_db_exec (iface, "DELETE FROM Events WHERE BeingHandled = 1");
+}
+
+/* Deprecated */
+guint32
+tracker_db_service_create (TrackerDBInterface *iface,
+			   const gchar	      *service,
+			   TrackerDBFileInfo  *info)
+{
+	TrackerDBPrivate   *private;
+	TrackerDBResultSet *result_set;
+	TrackerDBResultSet *result_set_proc;
+	gint		    i;
+	guint32		    id = 0;
+	gchar		   *sid;
+	gchar		   *str_mtime;
+	const gchar	   *str_is_dir;
+	const gchar	   *str_is_link;
+	gchar		   *str_filesize;
+	gchar		   *str_offset;
+	gchar		   *str_aux;
+	gint		    service_type_id;
+	gchar		   *str_service_type_id;
+	gchar		   *path = NULL;
+	gchar		   *name = NULL;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (iface), 0);
+	g_return_val_if_fail (info, 0);
+	g_return_val_if_fail (info->uri, 0);
+	g_return_val_if_fail (info->uri[0], 0);
+	g_return_val_if_fail (service, 0);
+
+	private = g_static_private_get (&private_key);
+	g_return_val_if_fail (private != NULL, 0);
+
+	tracker_file_get_path_and_name (info->uri, &path, &name);
+
+	/* Get a new unique ID for the service - use mutex to prevent race conditions */
+	result_set = tracker_db_exec_proc (iface, "GetNewID", NULL);
+
+	if (!result_set) {
+		g_critical ("Could not create service, GetNewID failed");
+		return 0;
+	}
+
+	tracker_db_result_set_get (result_set, 0, &sid, -1);
+	i = atoi (sid);
+	g_free (sid);
+	i++;
+
+	sid = tracker_gint_to_string (i);
+	result_set_proc = tracker_db_exec_proc (iface, "UpdateNewID", sid, NULL);
+
+	if (result_set_proc) {
+		g_object_unref (result_set_proc);
+	}
+
+	if (result_set) {
+		g_object_unref (result_set);
+	}
+
+	if (info->is_directory) {
+		str_is_dir = "1";
+	} else {
+		str_is_dir = "0";
+	}
+
+	if (info->is_link) {
+		str_is_link = "1";
+	} else {
+		str_is_link = "0";
+	}
+
+	str_filesize = tracker_guint32_to_string (info->file_size);
+	str_mtime = tracker_gint32_to_string (info->mtime);
+	str_offset = tracker_gint32_to_string (info->offset);
+
+	service_type_id = tracker_ontology_get_service_id_by_name (service);
+
+	if (info->mime) {
+		g_debug ("service id for %s is %d and sid is %s with mime %s",
+			 service, service_type_id, sid, info->mime);
+	} else {
+		g_debug ("service id for %s is %d and sid is %s",
+			 service, service_type_id, sid);
+	}
+
+	str_service_type_id = tracker_gint_to_string (service_type_id);
+	str_aux = tracker_gint_to_string (info->aux_id);
+
+	if (service_type_id != -1) {
+		gchar *parent;
+
+		result_set_proc = tracker_db_exec_proc (iface,
+							"CreateService",
+							sid,
+							path,
+							name,
+							str_service_type_id,
+							info->mime,
+							str_filesize,
+							str_is_dir,
+							str_is_link,
+							str_offset,
+							str_mtime,
+							str_aux,
+							NULL);
+
+		if (result_set_proc) {
+			g_object_unref (result_set_proc);
+		}
+
+		id = tracker_db_interface_sqlite_get_last_insert_id (TRACKER_DB_INTERFACE_SQLITE (iface));
+
+		if (info->is_hidden) {
+			tracker_db_exec_no_reply (iface,
+						  "Update services set Enabled = 0 where ID = %d",
+						  (int) id);
+		}
+
+		result_set_proc = tracker_db_exec_proc (iface,
+							"IncStat",
+							service,
+							NULL);
+
+		if (result_set_proc) {
+			g_object_unref (result_set_proc);
+		}
+
+		parent = tracker_ontology_get_service_parent (service);
+
+		if (parent) {
+			result_set_proc = tracker_db_exec_proc (iface,
+								"IncStat",
+								parent,
+								NULL);
+			if (result_set_proc) {
+				g_object_unref (result_set_proc);
+			}
+
+			g_free (parent);
+		}
+
+		if (tracker_config_get_enable_xesam (private->config)) {
+			/* FIXME: Shouldn't this be the common interface? */
+			db_create_event (iface, sid, "Create");
+		}
+	}
+
+	g_free (name);
+	g_free (path);
+	g_free (str_aux);
+	g_free (str_service_type_id);
+	g_free (sid);
+	g_free (str_filesize);
+	g_free (str_mtime);
+	g_free (str_offset);
+
+	return id;
+}
+
+/*
+ * Obtain the concrete service type name for the file id.
+ */
+gchar *
+tracker_db_service_get_by_entity (TrackerDBInterface *iface,
+				  const gchar	     *id)
+{
+	TrackerDBResultSet *result_set;
+	gint		    service_type_id;
+	gchar		   *result = NULL;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (iface), NULL);
+	g_return_val_if_fail (id != NULL, NULL);
+
+	result_set = tracker_db_exec_proc (iface,
+					   "GetFileByID",
+					   id,
+					   NULL);
+
+	if (result_set) {
+		tracker_db_result_set_get (result_set, 3, &service_type_id, -1);
+		g_object_unref (result_set);
+
+		result = tracker_ontology_get_service_by_id (service_type_id);
+	}
+
+	return result;
+}
+
+
+guint32
+tracker_db_file_get_id (TrackerDBInterface *iface,
+			const gchar	   *uri)
+{
+	TrackerDBResultSet *result_set;
+	gchar		   *path, *name;
+	guint32		    id;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (iface), 0);
+	g_return_val_if_fail (uri != NULL, 0);
+
+	tracker_file_get_path_and_name (uri, &path, &name);
+
+	result_set = tracker_db_exec_proc (iface,
+					   "GetServiceID",
+					   path,
+					   name,
+					   NULL);
+
+	g_free (path);
+	g_free (name);
+
+	id = 0;
+
+	if (result_set) {
+		tracker_db_result_set_get (result_set, 0, &id, -1);
+		g_object_unref (result_set);
+	}
+
+	return id;
+}
+
+gchar *
+tracker_db_file_get_id_as_string (TrackerDBInterface *iface,
+				  const gchar	     *service,
+				  const gchar	     *uri)
+{
+	gint	service_id;
+	guint32	id;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (iface), NULL);
+	g_return_val_if_fail (service != NULL, NULL);
+
+	/* Do we really need service here? */
+	service_id = tracker_ontology_get_service_id_by_name (service);
+
+	if (service_id == -1) {
+		return NULL;
+	}
+
+	id = tracker_db_file_get_id (iface, uri);
+
+	if (id > 0) {
+		return tracker_guint_to_string (id);
+	}
+
+	return NULL;
+}
+
+gchar **
+tracker_db_files_get (TrackerDBInterface *iface,
+		      const gchar	 *uri)
+{
+	TrackerDBResultSet *result_set;
+	GPtrArray	   *array;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (iface), NULL);
+	g_return_val_if_fail (uri != NULL, NULL);
+
+	result_set = tracker_db_exec_proc (iface,
+					   "SelectFileChild",
+					   uri,
+					   NULL);
+	array = g_ptr_array_new ();
+
+	if (result_set) {
+		gchar	 *name, *prefix;
+		gboolean  valid = TRUE;
+
+		while (valid) {
+			tracker_db_result_set_get (result_set,
+						   1, &prefix,
+						   2, &name,
+						   -1);
+
+			g_ptr_array_add (array, g_build_filename (prefix, name, NULL));
+
+			g_free (prefix);
+			g_free (name);
+			valid = tracker_db_result_set_iter_next (result_set);
+		}
+
+		g_object_unref (result_set);
+	}
+
+	g_ptr_array_add (array, NULL);
+
+	return (gchar**) g_ptr_array_free (array, FALSE);
+}
+
+TrackerDBResultSet *
+tracker_db_files_get_by_service (TrackerDBInterface *iface,
+				 const gchar	    *service,
+				 gint		     offset,
+				 gint		     limit)
+{
+	TrackerDBResultSet *result_set;
+	gchar		   *str_limit;
+	gchar		   *str_offset;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (iface), NULL);
+	g_return_val_if_fail (service != NULL, NULL);
+
+	str_limit = tracker_gint_to_string (limit);
+	str_offset = tracker_gint_to_string (offset);
+
+	result_set = tracker_db_exec_proc (iface,
+					   "GetByServiceType",
+					   service,
+					   service,
+					   str_offset,
+					   str_limit,
+					   NULL);
+
+	g_free (str_offset);
+	g_free (str_limit);
+
+	return result_set;
+}
+
+TrackerDBResultSet *
+tracker_db_files_get_by_mime (TrackerDBInterface  *iface,
+			      gchar		 **mimes,
+			      gint		   n,
+			      gint		   offset,
+			      gint		   limit,
+			      gboolean		   vfs)
+{
+	TrackerDBResultSet *result_set;
+	gint		    i;
+	gchar		   *service;
+	gchar		   *query;
+	GString		   *str;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (iface), NULL);
+	g_return_val_if_fail (mimes != NULL, NULL);
+	g_return_val_if_fail (offset >= 0, NULL);
+
+	if (vfs) {
+		service = "VFS";
+	} else {
+		service = "Files";
+	}
+
+	str = g_string_new ("SELECT  DISTINCT F.Path || '/' || F.Name AS uri FROM Services F INNER JOIN ServiceKeywordMetaData M ON F.ID = M.ServiceID WHERE M.MetaDataID = (SELECT ID FROM MetaDataTypes WHERE MetaName ='File:Mime') AND (M.MetaDataValue IN ");
+
+	g_string_append_printf (str, "('%s'", mimes[0]);
+
+	for (i = 1; i < n; i++) {
+		g_string_append_printf (str, ", '%s'", mimes[i]);
+	}
+
+	g_string_append_printf (str,
+				")) AND (F.ServiceTypeID in (select TypeId from ServiceTypes where TypeName = '%s' or Parent = '%s')) LIMIT %d,%d",
+				service,
+				service,
+				offset,
+				limit);
+
+	query = g_string_free (str, FALSE);
+	result_set = tracker_db_interface_execute_query (iface, NULL, query);
+	g_free (query);
+
+	return result_set;
+}
+
+TrackerDBResultSet *
+tracker_db_metadata_get_types (TrackerDBInterface *iface,
+			       const gchar	  *class,
+			       gboolean		   writeable)
+{
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (iface), NULL);
+	g_return_val_if_fail (class != NULL, NULL);
+
+	if (strcmp (class, "*") == 0) {
+		if (writeable) {
+			return tracker_db_exec_proc (iface,
+						     "GetWriteableMetadataTypes",
+						     NULL);
+		} else {
+			return tracker_db_exec_proc (iface,
+						     "GetMetadataTypes",
+						     NULL);
+		}
+	} else {
+		if (writeable) {
+			return tracker_db_exec_proc (iface,
+						     "GetWriteableMetadataTypesLike",
+						     class,
+						     NULL);
+		} else {
+			return tracker_db_exec_proc (iface,
+						     "GetMetadataTypesLike",
+						     class,
+						     NULL);
+		}
+	}
+}
+
+TrackerDBResultSet *
+tracker_db_keywords_get_list (TrackerDBInterface *iface,
+			      const gchar	 *service)
+{
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (iface), NULL);
+	g_return_val_if_fail (service != NULL, NULL);
+
+	return tracker_db_exec_proc (iface,
+				     "GetKeywordList",
+				     service,
+				     service,
+				     NULL);
+}
+
+TrackerFieldData *
+tracker_db_get_metadata_field (TrackerDBInterface *iface,
+			       const gchar	  *service,
+			       const gchar	  *field_name,
+			       gint		   field_count,
+			       gboolean		   is_select,
+			       gboolean		   is_condition)
+{
+	TrackerFieldData *field_data = NULL;
+	TrackerField	 *def;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (iface), NULL);
+	g_return_val_if_fail (service != NULL, NULL);
+	g_return_val_if_fail (field_name != NULL, NULL);
+
+	def = tracker_ontology_get_field_by_name (field_name);
+
+	if (def) {
+		gchar	    *alias;
+		const gchar *table_name;
+		gchar	    *this_field_name;
+		gchar	    *where_field;
+
+		field_data = g_object_new (TRACKER_TYPE_FIELD_DATA,
+					   "is-select", is_select,
+					   "is-condition", is_condition,
+					   "field-name", field_name,
+					   NULL);
+
+		alias = g_strdup_printf ("M%d", field_count);
+		table_name = tracker_db_metadata_get_table (tracker_field_get_data_type (def));
+
+		g_debug ("Field_name: %s :table_name is: %s for data_type: %i",
+			 field_name,
+			 table_name,
+			 tracker_field_get_data_type(def));
+
+		tracker_field_data_set_alias (field_data, alias);
+		tracker_field_data_set_table_name (field_data, table_name);
+		tracker_field_data_set_id_field (field_data, tracker_field_get_id (def));
+		tracker_field_data_set_data_type (field_data, tracker_field_get_data_type (def));
+		tracker_field_data_set_multiple_values (field_data, tracker_field_get_multiple_values (def));
+
+		this_field_name = tracker_db_get_field_name (service, field_name);
+
+		if (this_field_name) {
+			gchar *str;
+
+			str = g_strdup_printf (" S.%s ", this_field_name);
+			tracker_field_data_set_select_field (field_data, str);
+			tracker_field_data_set_needs_join (field_data, FALSE);
+			g_free (str);
+			g_free (this_field_name);
+		} else {
+			gchar *str;
+			gchar *display_field;
+
+			display_field = tracker_ontology_field_get_display_name (def);
+			str = g_strdup_printf ("M%d.%s", field_count, display_field);
+			tracker_field_data_set_select_field (field_data, str);
+			tracker_field_data_set_needs_join (field_data, TRUE);
+			g_free (str);
+			g_free (display_field);
+		}
+
+		if ((tracker_field_get_data_type (def) == TRACKER_FIELD_TYPE_DOUBLE) ||
+		    (tracker_field_get_data_type (def) == TRACKER_FIELD_TYPE_INDEX)  ||
+		    (tracker_field_get_data_type (def) == TRACKER_FIELD_TYPE_STRING)) {
+			where_field = g_strdup_printf ("M%d.MetaDataDisplay", field_count);
+		} else {
+			where_field = g_strdup_printf ("M%d.MetaDataValue", field_count);
+		}
+
+		tracker_field_data_set_where_field (field_data, where_field);
+		g_free (where_field);
+	}
+
+	return field_data;
+}
+
+gint
+tracker_db_get_option_int (const gchar *option)
+{
+	TrackerDBInterface *iface;
+	TrackerDBResultSet *result_set;
+	gchar		   *str;
+	gint		    value = 0;
+
+	g_return_val_if_fail (option != NULL, 0);
+
+	/* Here it doesn't matter which one we ask, as long as it has common.db
+	 * attached. The service ones are cached connections, so we can use
+	 * those instead of asking for an individual-file connection (like what
+	 * the original code had) */
+
+	/* iface = tracker_db_manager_get_db_interfaceX (TRACKER_DB_COMMON); */
+
+	iface = tracker_db_manager_get_db_interface_by_service (TRACKER_DB_FOR_FILE_SERVICE);
+
+	result_set = tracker_db_exec_proc (iface, "GetOption", option, NULL);
+
+	if (result_set) {
+		tracker_db_result_set_get (result_set, 0, &str, -1);
+
+		if (str) {
+			value = atoi (str);
+			g_free (str);
+		}
+
+		g_object_unref (result_set);
+	}
+
+	return value;
+}
+
+void
+tracker_db_set_option_int (const gchar *option,
+			   gint		value)
+{
+	TrackerDBInterface *iface;
+	TrackerDBResultSet *result_set;
+	gchar		   *str;
+
+	g_return_if_fail (option != NULL);
+
+	/* Here it doesn't matter which one we ask, as long as it has common.db
+	 * attached. The service ones are cached connections, so we can use
+	 * those instead of asking for an individual-file connection (like what
+	 * the original code had) */
+
+	/* iface = tracker_db_manager_get_db_interfaceX (TRACKER_DB_COMMON); */
+
+	iface = tracker_db_manager_get_db_interface_by_service (TRACKER_DB_FOR_FILE_SERVICE);
+
+	str = tracker_gint_to_string (value);
+	result_set = tracker_db_exec_proc (iface, "SetOption", str, option, NULL);
+	g_free (str);
+
+	if (result_set) {
+		g_object_unref (result_set);
+	}
+}

Added: trunk/src/trackerd/tracker-db.h
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-db.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,204 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2007, Jason Kivlighn (jkivlighn gmail com)
+ * Copyright (C) 2007, Creative Commons (http://creativecommons.org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKERD_DB_H__
+#define __TRACKERD_DB_H__
+
+#include <glib.h>
+
+#include <libtracker-common/tracker-config.h>
+#include <libtracker-common/tracker-field.h>
+#include <libtracker-common/tracker-field-data.h>
+#include <libtracker-common/tracker-language.h>
+#include <libtracker-common/tracker-ontology.h>
+
+#include <libtracker-db/tracker-db-interface.h>
+#include <libtracker-db/tracker-db-file-info.h>
+#include <libtracker-db/tracker-db-index.h>
+
+#include "tracker-utils.h"
+
+G_BEGIN_DECLS
+
+void		    tracker_db_init				 (TrackerConfig       *config,
+								  TrackerLanguage     *language,
+								  TrackerDBIndex      *file_index,
+								  TrackerDBIndex      *email_index);
+void		    tracker_db_shutdown				 (void);
+
+GArray *	    tracker_db_create_array_of_services		 (const gchar	      *service,
+								  gboolean	       basic_services);
+
+/* Operations for TrackerDBInterface */
+TrackerDBResultSet *tracker_db_exec_proc			 (TrackerDBInterface  *iface,
+								  const gchar	      *procedure,
+								  ...);
+gboolean	    tracker_db_exec_no_reply			 (TrackerDBInterface  *iface,
+								  const gchar	      *query,
+								  ...);
+TrackerDBResultSet *tracker_db_exec				 (TrackerDBInterface  *iface,
+								  const char	      *query,
+								  ...);
+gint		    tracker_db_get_option_int			 (const gchar	      *option);
+void		    tracker_db_set_option_int			 (const gchar	      *option,
+								  gint		       value);
+
+/* Metadata API */
+gchar *		    tracker_db_metadata_get_related_names	 (TrackerDBInterface  *iface,
+								  const gchar	      *name);
+const gchar *	    tracker_db_metadata_get_table		 (TrackerFieldType     type);
+TrackerDBResultSet *tracker_db_metadata_get			 (TrackerDBInterface  *iface,
+								  const gchar	      *id,
+								  const gchar	      *key);
+TrackerDBResultSet *tracker_db_metadata_get_array		 (TrackerDBInterface *iface,
+								  const gchar	     *service_type,
+								  const gchar	     *service_id,
+								  gchar		    **keys);
+gchar *		    tracker_db_metadata_get_delimited		 (TrackerDBInterface  *iface,
+								  const gchar	      *id,
+								  const gchar	      *key);
+gchar *		    tracker_db_metadata_set			 (TrackerDBInterface  *iface,
+								  const gchar	      *service,
+								  const gchar	      *id,
+								  const gchar	      *key,
+								  gchar		     **values,
+								  gboolean	       do_backup);
+void		    tracker_db_metadata_set_single		 (TrackerDBInterface  *iface,
+								  const gchar	      *service,
+								  const gchar	      *id,
+								  const gchar	      *key,
+								  const gchar	      *value,
+								  gboolean	       do_backup);
+void		    tracker_db_metadata_delete_value		 (TrackerDBInterface  *iface,
+								  const gchar	      *service,
+								  const gchar	      *id,
+								  const gchar	      *key,
+								  const gchar	      *value);
+void		    tracker_db_metadata_delete			 (TrackerDBInterface  *iface,
+								  const gchar	      *service,
+								  const gchar	      *id,
+								  const gchar	      *key,
+								  gboolean	       update_indexes);
+TrackerDBResultSet *tracker_db_metadata_get_types		 (TrackerDBInterface  *iface,
+								  const gchar	      *class,
+								  gboolean	       writeable);
+
+/* Search API */
+TrackerDBResultSet *tracker_db_search_text			 (TrackerDBInterface  *iface,
+								  const gchar	      *service,
+								  const gchar	      *search_string,
+								  gint		       offset,
+								  gint		       limit,
+								  gboolean	       save_results,
+								  gboolean	       detailed);
+TrackerDBResultSet *tracker_db_search_text_and_mime		 (TrackerDBInterface  *iface,
+								  const gchar	      *text,
+								  gchar		     **mime_array);
+TrackerDBResultSet *tracker_db_search_text_and_location		 (TrackerDBInterface  *iface,
+								  const gchar	      *text,
+								  const gchar	      *location);
+TrackerDBResultSet *tracker_db_search_text_and_mime_and_location (TrackerDBInterface  *iface,
+								  const gchar	      *text,
+								  gchar		     **mime_array,
+								  const gchar	      *location);
+
+/* Service API */
+guint32		    tracker_db_service_create			 (TrackerDBInterface  *iface,
+								  const gchar	      *service,
+								  TrackerDBFileInfo   *info);
+gchar *		    tracker_db_service_get_by_entity		 (TrackerDBInterface  *iface,
+								  const gchar	      *id);
+
+/* Files API */
+gchar **	    tracker_db_files_get			 (TrackerDBInterface  *iface,
+								  const gchar	      *folder_uri);
+TrackerDBResultSet *tracker_db_files_get_by_service		 (TrackerDBInterface  *iface,
+								  const gchar	      *service,
+								  gint		       offset,
+								  gint		       limit);
+TrackerDBResultSet *tracker_db_files_get_by_mime		 (TrackerDBInterface  *iface,
+								  gchar		     **mimes,
+								  gint		       n,
+								  gint		       offset,
+								  gint		       limit,
+								  gboolean	       vfs);
+guint32		    tracker_db_file_get_id			 (TrackerDBInterface  *iface,
+								  const gchar	      *uri);
+gchar *		    tracker_db_file_get_id_as_string		 (TrackerDBInterface  *iface,
+								  const gchar	      *service,
+								  const gchar	      *uri);
+
+/* Keywords API */
+TrackerDBResultSet *tracker_db_keywords_get_list		 (TrackerDBInterface  *iface,
+								  const gchar	      *service);
+
+/* Miscellaneous API */
+GHashTable *	    tracker_db_get_file_contents_words		 (TrackerDBInterface  *iface,
+								  guint32	       id,
+								  GHashTable	      *old_table);
+GHashTable *	    tracker_db_get_indexable_content_words	 (TrackerDBInterface  *iface,
+								  guint32	       id,
+								  GHashTable	      *table,
+								  gboolean	       embedded_only);
+gchar *		    tracker_db_get_field_name			 (const gchar	      *service,
+								  const gchar	      *meta_name);
+TrackerFieldData *  tracker_db_get_metadata_field		 (TrackerDBInterface  *iface,
+								  const gchar	      *service,
+								  const gchar	      *field_name,
+								  gint		       field_count,
+								  gboolean	       is_select,
+								  gboolean	       is_condition);
+
+/* Live Search API */
+void		    tracker_db_live_search_start		 (TrackerDBInterface  *iface,
+								  const gchar	      *from_query,
+								  const gchar	      *join_query,
+								  const gchar	      *where_query,
+								  const gchar	      *search_id);
+void		    tracker_db_live_search_stop			 (TrackerDBInterface  *iface,
+								  const gchar	      *search_id);
+TrackerDBResultSet *tracker_db_live_search_get_all_ids		 (TrackerDBInterface  *iface,
+								  const gchar	      *search_id);
+TrackerDBResultSet *tracker_db_live_search_get_new_ids		 (TrackerDBInterface  *iface,
+								  const gchar	      *search_id,
+								  const gchar	      *from_query,
+								  const gchar	      *query_joins,
+								  const gchar	      *where_query);
+TrackerDBResultSet *tracker_db_live_search_get_deleted_ids	 (TrackerDBInterface  *iface,
+								  const gchar	      *search_id);
+TrackerDBResultSet *tracker_db_live_search_get_hit_data		 (TrackerDBInterface  *iface,
+								  const gchar	      *search_id,
+								  GStrv		       fields);
+TrackerDBResultSet *tracker_db_live_search_get_hit_count	 (TrackerDBInterface  *iface,
+								  const gchar	      *search_id);
+
+/* XESAM API */
+void		    tracker_db_xesam_delete_handled_events     (TrackerDBInterface  *iface);
+TrackerDBResultSet *tracker_db_xesam_get_metadata_names        (TrackerDBInterface  *iface,
+								const char	    *name);
+TrackerDBResultSet *tracker_db_xesam_get_all_text_metadata_names (TrackerDBInterface  *iface);
+TrackerDBResultSet *tracker_db_xesam_get_service_names	       (TrackerDBInterface  *iface,
+								const char	    *name);
+G_END_DECLS
+
+#endif /* __TRACKERD_DB_H__ */

Added: trunk/src/trackerd/tracker-dbus.c
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-dbus.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,508 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <libtracker-common/tracker-config.h>
+#include <libtracker-common/tracker-dbus.h>
+#include <libtracker-common/tracker-log.h>
+#include <libtracker-common/tracker-utils.h>
+
+#include <libtracker-db/tracker-db-manager.h>
+
+#include "tracker-db.h"
+#include "tracker-dbus.h"
+#include "tracker-daemon.h"
+#include "tracker-daemon-glue.h"
+#include "tracker-files.h"
+#include "tracker-files-glue.h"
+#include "tracker-keywords.h"
+#include "tracker-keywords-glue.h"
+#include "tracker-metadata.h"
+#include "tracker-metadata-glue.h"
+#include "tracker-search.h"
+#include "tracker-search-glue.h"
+#include "tracker-xesam.h"
+#include "tracker-xesam-glue.h"
+#include "tracker-indexer-client.h"
+#include "tracker-utils.h"
+#include "tracker-marshal.h"
+#include "tracker-status.h"
+#include "tracker-main.h"
+
+#define INDEXER_PAUSE_TIME_FOR_REQUESTS 10 /* seconds */
+
+static DBusGConnection *connection;
+static DBusGProxy      *proxy;
+static DBusGProxy      *proxy_for_indexer;
+static GSList	       *objects;
+static guint		indexer_resume_timeout_id;
+
+static gboolean
+dbus_register_service (DBusGProxy  *proxy,
+		       const gchar *name)
+{
+	GError *error = NULL;
+	guint	result;
+
+	g_message ("Registering DBus service...\n"
+		   "  Name:'%s'",
+		   name);
+
+	if (!org_freedesktop_DBus_request_name (proxy,
+						name,
+						DBUS_NAME_FLAG_DO_NOT_QUEUE,
+						&result, &error)) {
+		g_critical ("Could not aquire name:'%s', %s",
+			    name,
+			    error ? error->message : "no error given");
+		g_error_free (error);
+
+		return FALSE;
+	}
+
+	if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+		g_critical ("DBus service name:'%s' is already taken, "
+			    "perhaps the daemon is already running?",
+			    name);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+dbus_register_object (DBusGConnection	    *connection,
+		      DBusGProxy	    *proxy,
+		      GObject		    *object,
+		      const DBusGObjectInfo *info,
+		      const gchar	    *path)
+{
+	g_message ("Registering DBus object...");
+	g_message ("  Path:'%s'", path);
+	g_message ("  Type:'%s'", G_OBJECT_TYPE_NAME (object));
+
+	dbus_g_object_type_install_info (G_OBJECT_TYPE (object), info);
+	dbus_g_connection_register_g_object (connection, path, object);
+}
+
+static void
+dbus_name_owner_changed (gpointer  data,
+			 GClosure *closure)
+{
+	g_object_unref (data);
+}
+
+static gboolean
+dbus_register_names (TrackerConfig *config)
+{
+	GError *error = NULL;
+
+	if (connection) {
+		g_critical ("The DBusGConnection is already set, have we already initialized?");
+		return FALSE;
+	}
+
+	if (proxy) {
+		g_critical ("The DBusGProxy is already set, have we already initialized?");
+		return FALSE;
+	}
+
+	connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
+
+	if (!connection) {
+		g_critical ("Could not connect to the DBus session bus, %s",
+			    error ? error->message : "no error given.");
+		g_clear_error (&error);
+		return FALSE;
+	}
+
+	/* The definitions below (DBUS_SERVICE_DBUS, etc) are
+	 * predefined for us to just use (dbus_g_proxy_...)
+	 */
+	proxy = dbus_g_proxy_new_for_name (connection,
+					   DBUS_SERVICE_DBUS,
+					   DBUS_PATH_DBUS,
+					   DBUS_INTERFACE_DBUS);
+
+	/* Register the service name for org.freedesktop.Tracker */
+	if (!dbus_register_service (proxy, TRACKER_DAEMON_SERVICE)) {
+		return FALSE;
+	}
+
+	/* Register the service name for org.freedesktop.xesam if XESAM is enabled */
+	if (tracker_config_get_enable_xesam (config)) {
+		if (!dbus_register_service (proxy, TRACKER_XESAM_SERVICE)) {
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+static void
+indexer_continue_async_cb (DBusGProxy *proxy,
+			   GError     *error,
+			   gpointer    user_data)
+{
+	if (error) {
+		g_message ("Couldn't resume the indexer: %s", error->message);
+		g_error_free (error);
+	}
+
+	g_object_unref (proxy);
+}
+
+static gboolean
+indexer_resume_cb (gpointer user_data)
+{
+	DBusGProxy *proxy;
+
+	proxy = user_data;
+
+	if (!tracker_status_get_is_paused_manually () &&
+	    !tracker_status_get_is_paused_for_io ()) {
+		org_freedesktop_Tracker_Indexer_continue_async (g_object_ref (proxy),
+								indexer_continue_async_cb,
+								NULL);
+	}
+
+	return FALSE;
+}
+
+static void
+indexer_resume_destroy_notify_cb (gpointer user_data)
+{
+	g_object_unref (user_data);
+	indexer_resume_timeout_id = 0;
+}
+
+static void
+dbus_request_new_cb (guint    request_id,
+		     gpointer user_data)
+{
+	DBusGProxy *proxy;
+	GError	   *error = NULL;
+	gboolean    set_paused = TRUE;
+
+	if (tracker_status_get () != TRACKER_STATUS_INDEXING) {
+		return;
+	}
+
+	g_message ("New DBus request, checking indexer is paused...");
+
+	/* First remove the timeout */
+	if (indexer_resume_timeout_id != 0) {
+		set_paused = FALSE;
+
+		g_source_remove (indexer_resume_timeout_id);
+		indexer_resume_timeout_id = 0;
+	}
+
+	/* Second reset it so we have another 10 seconds before
+	 * continuing.
+	 */
+	proxy = tracker_dbus_indexer_get_proxy ();
+	indexer_resume_timeout_id =
+		g_timeout_add_seconds_full (G_PRIORITY_DEFAULT,
+					    INDEXER_PAUSE_TIME_FOR_REQUESTS,
+					    indexer_resume_cb,
+					    g_object_ref (proxy),
+					    indexer_resume_destroy_notify_cb);
+
+	/* Third check if we are already paused, if we are there is
+	 * no need to tell the indexer.
+	 */
+	if (tracker_status_get_is_paused_manually ()) {
+		g_message ("Tracker is already manually paused, doing nothing");
+		return;
+	}
+
+	/* We really only do this because of the chance that we tell
+	 * the indexer to pause but don't get notified until the next
+	 * request. When we are notified of being paused,
+	 * tracker_get_is_paused_manually() returns TRUE.
+	 */
+	if (!set_paused) {
+		return;
+	}
+
+	/* We use the blocking call here because this function
+	 * proceeds a call which needs the database to be available.
+	 * Therefore the indexer must reply to tell us it has paused
+	 * so we can actually use the database.
+	 */
+	org_freedesktop_Tracker_Indexer_pause (proxy, &error);
+
+	if (error) {
+		g_message ("Couldn't pause the indexer, "
+			   "we may have to wait for it to finish");
+		g_error_free (error);
+	}
+}
+
+gboolean
+tracker_dbus_init (TrackerConfig *config)
+{
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), FALSE);
+
+	/* Don't reinitialize */
+	if (objects) {
+		return TRUE;
+	}
+
+	/* Register names and get proxy/connection details */
+	if (!dbus_register_names (config)) {
+		return FALSE;
+	}
+
+	/* Register request handler so we can pause the indexer */
+	tracker_dbus_request_add_hook (dbus_request_new_cb,
+				       NULL,
+				       NULL);
+
+	return TRUE;
+}
+
+void
+tracker_dbus_shutdown (void)
+{
+	if (objects) {
+		g_slist_foreach (objects, (GFunc) g_object_unref, NULL);
+		g_slist_free (objects);
+		objects = NULL;
+	}
+
+	if (proxy) {
+		g_object_unref (proxy);
+		proxy = NULL;
+	}
+
+	if (proxy_for_indexer) {
+		g_object_unref (proxy_for_indexer);
+		proxy_for_indexer = NULL;
+	}
+
+	connection = NULL;
+}
+
+gboolean
+tracker_dbus_register_objects (TrackerConfig	*config,
+			       TrackerLanguage	*language,
+			       TrackerDBIndex	*file_index,
+			       TrackerDBIndex	*email_index,
+			       TrackerProcessor *processor)
+{
+	gpointer object;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), FALSE);
+	g_return_val_if_fail (TRACKER_IS_LANGUAGE (language), FALSE);
+	g_return_val_if_fail (TRACKER_IS_DB_INDEX (file_index), FALSE);
+	g_return_val_if_fail (TRACKER_IS_DB_INDEX (email_index), FALSE);
+
+	if (!connection || !proxy) {
+		g_critical ("DBus support must be initialized before registering objects!");
+		return FALSE;
+	}
+
+	/* Add org.freedesktop.Tracker */
+	object = tracker_daemon_new (config, processor);
+	if (!object) {
+		g_critical ("Could not create TrackerDaemon object to register");
+		return FALSE;
+	}
+
+	dbus_register_object (connection,
+			      proxy,
+			      G_OBJECT (object),
+			      &dbus_glib_tracker_daemon_object_info,
+			      TRACKER_DAEMON_PATH);
+	objects = g_slist_prepend (objects, object);
+
+	/* Add org.freedesktop.Tracker.Files */
+	object = tracker_files_new (processor);
+	if (!object) {
+		g_critical ("Could not create TrackerFiles object to register");
+		return FALSE;
+	}
+
+	dbus_register_object (connection,
+			      proxy,
+			      G_OBJECT (object),
+			      &dbus_glib_tracker_files_object_info,
+			      TRACKER_FILES_PATH);
+	objects = g_slist_prepend (objects, object);
+
+	/* Add org.freedesktop.Tracker.Keywords */
+	object = tracker_keywords_new ();
+	if (!object) {
+		g_critical ("Could not create TrackerKeywords object to register");
+		return FALSE;
+	}
+
+	dbus_register_object (connection,
+			      proxy,
+			      G_OBJECT (object),
+			      &dbus_glib_tracker_keywords_object_info,
+			      TRACKER_KEYWORDS_PATH);
+	objects = g_slist_prepend (objects, object);
+
+	/* Add org.freedesktop.Tracker.Metadata */
+	object = tracker_metadata_new ();
+	if (!object) {
+		g_critical ("Could not create TrackerMetadata object to register");
+		return FALSE;
+	}
+
+	dbus_register_object (connection,
+			      proxy,
+			      G_OBJECT (object),
+			      &dbus_glib_tracker_metadata_object_info,
+			      TRACKER_METADATA_PATH);
+	objects = g_slist_prepend (objects, object);
+
+	/* Add org.freedesktop.Tracker.Search */
+	object = tracker_search_new (config, language, file_index, email_index);
+	if (!object) {
+		g_critical ("Could not create TrackerSearch object to register");
+		return FALSE;
+	}
+
+	dbus_register_object (connection,
+			      proxy,
+			      G_OBJECT (object),
+			      &dbus_glib_tracker_search_object_info,
+			      TRACKER_SEARCH_PATH);
+	objects = g_slist_prepend (objects, object);
+
+	/* Register the XESAM object if enabled */
+	if (tracker_config_get_enable_xesam (config)) {
+		object = tracker_xesam_new ();
+		if (!object) {
+			g_critical ("Could not create TrackerXesam object to register");
+			return FALSE;
+		}
+
+		dbus_register_object (connection,
+				      proxy,
+				      G_OBJECT (object),
+				      &dbus_glib_tracker_xesam_object_info,
+				      TRACKER_XESAM_PATH);
+		objects = g_slist_prepend (objects, object);
+
+		dbus_g_proxy_add_signal (proxy, "NameOwnerChanged",
+					 G_TYPE_STRING, G_TYPE_STRING,
+					 G_TYPE_STRING, G_TYPE_INVALID);
+
+		dbus_g_proxy_connect_signal (proxy, "NameOwnerChanged",
+					     G_CALLBACK (tracker_xesam_name_owner_changed),
+					     g_object_ref (G_OBJECT (object)),
+					     dbus_name_owner_changed);
+	}
+
+	/* Reverse list since we added objects at the top each time */
+	objects = g_slist_reverse (objects);
+
+	return TRUE;
+}
+
+GObject *
+tracker_dbus_get_object (GType type)
+{
+	GSList *l;
+
+	for (l = objects; l; l = l->next) {
+		if (G_OBJECT_TYPE (l->data) == type) {
+			return l->data;
+		}
+	}
+
+	return NULL;
+}
+
+DBusGProxy *
+tracker_dbus_indexer_get_proxy (void)
+{
+	if (!connection) {
+		g_critical ("DBus support must be initialized before starting the indexer!");
+		return NULL;
+	}
+
+	if (!proxy_for_indexer) {
+		/* Get proxy for Service / Path / Interface of the indexer */
+		proxy_for_indexer = dbus_g_proxy_new_for_name (connection,
+							       "org.freedesktop.Tracker.Indexer",
+							       "/org/freedesktop/Tracker/Indexer",
+							       "org.freedesktop.Tracker.Indexer");
+
+		if (!proxy_for_indexer) {
+			g_critical ("Couldn't create a DBusGProxy to the indexer service");
+			return NULL;
+		}
+
+		/* Add marshallers */
+		dbus_g_object_register_marshaller (tracker_marshal_VOID__DOUBLE_STRING_UINT_UINT,
+						   G_TYPE_NONE,
+						   G_TYPE_DOUBLE,
+						   G_TYPE_STRING,
+						   G_TYPE_UINT,
+						   G_TYPE_UINT,
+						   G_TYPE_INVALID);
+		dbus_g_object_register_marshaller (tracker_marshal_VOID__DOUBLE_UINT_BOOL,
+						   G_TYPE_NONE,
+						   G_TYPE_DOUBLE,
+						   G_TYPE_UINT,
+						   G_TYPE_BOOLEAN,
+						   G_TYPE_INVALID);
+
+		/* Add signals, why can't we use introspection for this? */
+		dbus_g_proxy_add_signal (proxy_for_indexer,
+					 "Status",
+					 G_TYPE_DOUBLE,
+					 G_TYPE_STRING,
+					 G_TYPE_UINT,
+					 G_TYPE_UINT,
+					 G_TYPE_INVALID);
+		dbus_g_proxy_add_signal (proxy_for_indexer,
+					 "Started",
+					 G_TYPE_INVALID);
+		dbus_g_proxy_add_signal (proxy_for_indexer,
+					 "Paused",
+					 G_TYPE_INVALID);
+		dbus_g_proxy_add_signal (proxy_for_indexer,
+					 "Continued",
+					 G_TYPE_INVALID);
+		dbus_g_proxy_add_signal (proxy_for_indexer,
+					 "Finished",
+					 G_TYPE_DOUBLE,
+					 G_TYPE_UINT,
+					 G_TYPE_BOOLEAN,
+					 G_TYPE_INVALID);
+		dbus_g_proxy_add_signal (proxy_for_indexer,
+					 "ModuleStarted",
+					 G_TYPE_STRING,
+					 G_TYPE_INVALID);
+		dbus_g_proxy_add_signal (proxy_for_indexer,
+					 "ModuleFinished",
+					 G_TYPE_STRING,
+					 G_TYPE_INVALID);
+	}
+
+	return proxy_for_indexer;
+}

Added: trunk/src/trackerd/tracker-dbus.h
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-dbus.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,50 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKERD_DBUS_H__
+#define __TRACKERD_DBUS_H__
+
+#include <glib.h>
+
+#include <dbus/dbus-glib-bindings.h>
+
+#include <libtracker-common/tracker-config.h>
+#include <libtracker-common/tracker-language.h>
+
+#include <libtracker-db/tracker-db-index.h>
+
+#include "tracker-processor.h"
+
+G_BEGIN_DECLS
+
+gboolean    tracker_dbus_init		   (TrackerConfig    *config);
+void	    tracker_dbus_shutdown	   (void);
+gboolean    tracker_dbus_register_objects  (TrackerConfig    *config,
+					    TrackerLanguage  *language,
+					    TrackerDBIndex   *file_index,
+					    TrackerDBIndex   *email_index,
+					    TrackerProcessor *processor);
+GObject    *tracker_dbus_get_object	   (GType	      type);
+DBusGProxy *tracker_dbus_indexer_get_proxy (void);
+
+G_END_DECLS
+
+#endif /* __TRACKERD_DBUS_H__ */

Added: trunk/src/trackerd/tracker-files.c
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-files.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,1033 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libtracker-common/tracker-dbus.h>
+#include <libtracker-common/tracker-log.h>
+#include <libtracker-common/tracker-utils.h>
+#include <libtracker-common/tracker-file-utils.h>
+#include <libtracker-common/tracker-ontology.h>
+#include <libtracker-common/tracker-type-utils.h>
+
+#include <libtracker-db/tracker-db-dbus.h>
+#include <libtracker-db/tracker-db-manager.h>
+
+#include "tracker-dbus.h"
+#include "tracker-files.h"
+#include "tracker-db.h"
+#include "tracker-marshal.h"
+#include "tracker-indexer-client.h"
+
+#define TRACKER_FILES_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TRACKER_TYPE_FILES, TrackerFilesPrivate))
+
+typedef struct {
+	TrackerProcessor *processor;
+} TrackerFilesPrivate;
+
+static void tracker_files_finalize (GObject *object);
+
+G_DEFINE_TYPE(TrackerFiles, tracker_files, G_TYPE_OBJECT)
+
+static void
+tracker_files_class_init (TrackerFilesClass *klass)
+{
+	GObjectClass *object_class;
+
+	object_class = G_OBJECT_CLASS (klass);
+
+	object_class->finalize = tracker_files_finalize;
+
+	g_type_class_add_private (object_class, sizeof (TrackerFilesPrivate));
+}
+
+static void
+tracker_files_init (TrackerFiles *object)
+{
+}
+
+static void
+tracker_files_finalize (GObject *object)
+{
+	TrackerFilesPrivate *priv;
+
+	priv = TRACKER_FILES_GET_PRIVATE (object);
+
+	g_object_unref (priv->processor);
+
+	G_OBJECT_CLASS (tracker_files_parent_class)->finalize (object);
+}
+
+TrackerFiles *
+tracker_files_new (TrackerProcessor *processor)
+{
+	TrackerFiles	    *object;
+	TrackerFilesPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_PROCESSOR (processor), NULL);
+
+	object = g_object_new (TRACKER_TYPE_FILES, NULL);
+
+	priv = TRACKER_FILES_GET_PRIVATE (object);
+
+	priv->processor = g_object_ref (processor);
+
+	return object;
+}
+
+/*
+ * Functions
+ */
+void
+tracker_files_exist (TrackerFiles	    *object,
+		     const gchar	    *uri,
+		     gboolean		     auto_create,
+		     DBusGMethodInvocation  *context,
+		     GError		   **error)
+{
+	TrackerDBInterface *iface;
+	guint		    request_id;
+	guint32		    file_id;
+	gboolean	    exists;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (uri != NULL, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to see if files exist, "
+				  "uri:'%s' auto-create:'%d'",
+				  uri, auto_create);
+
+	/* This API is broken. The Daemon doesn't do anything in the
+	 * database except read from it.
+	 */
+	if (auto_create) {
+		GError *actual_error = NULL;
+
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "Request to check existence of file '%s' failed, "
+					     "the 'auto-create' variable can not be TRUE, "
+					     "this feature is no longer supported.",
+					     uri);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+
+		return;
+	}
+
+	iface = tracker_db_manager_get_db_interface_by_service (TRACKER_DB_FOR_FILE_SERVICE);
+
+	file_id = tracker_db_file_get_id (iface, uri);
+	exists = file_id > 0;
+
+	dbus_g_method_return (context, exists);
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_files_create (TrackerFiles	     *object,
+		      const gchar	     *uri,
+		      gboolean		      is_directory,
+		      const gchar	     *mime,
+		      gint		      size,
+		      gint		      mtime,
+		      DBusGMethodInvocation  *context,
+		      GError		    **error)
+{
+	TrackerFilesPrivate *priv;
+	GFile		    *file;
+	const gchar	    *module_name = "files";
+	guint		     request_id;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (uri != NULL, context);
+	tracker_dbus_async_return_if_fail (mime != NULL, context);
+	tracker_dbus_async_return_if_fail (size >= 0, context);
+	tracker_dbus_async_return_if_fail (mtime >= 0, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to create file, "
+				  "uri:'%s', is directory:%s, mime:'%s', "
+				  "size:%d, mtime:%d",
+				  uri,
+				  is_directory ? "yes" : "no",
+				  mime,
+				  size,
+				  mtime);
+
+	priv = TRACKER_FILES_GET_PRIVATE (object);
+
+	file = g_file_new_for_path (uri);
+	tracker_processor_files_check (priv->processor, module_name, file, is_directory);
+	g_object_unref (file);
+
+	dbus_g_method_return (context);
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_files_delete (TrackerFiles	     *object,
+		      const gchar	     *uri,
+		      DBusGMethodInvocation  *context,
+		      GError		    **error)
+{
+	TrackerFilesPrivate *priv;
+	GFile		    *file;
+	const gchar	    *module_name = "files";
+	guint		     request_id;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (uri != NULL, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to delete file, "
+				  "uri:'%s'",
+				  uri);
+
+	priv = TRACKER_FILES_GET_PRIVATE (object);
+
+	/* Check the file exists, if not delete, this is broken, we
+	 * really don't know with the API if it is a file or
+	 * directory we are dealing with so we check both.
+	 */
+	file = g_file_new_for_path (uri);
+	tracker_processor_files_check (priv->processor, module_name, file, TRUE);
+	tracker_processor_files_check (priv->processor, module_name, file, FALSE);
+	g_object_unref (file);
+
+	dbus_g_method_return (context);
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_files_get_service_type (TrackerFiles	       *object,
+				const gchar	       *uri,
+				DBusGMethodInvocation  *context,
+				GError		      **error)
+{
+	TrackerDBInterface *iface;
+	TrackerDBResultSet *result_set;
+	guint		    request_id;
+	guint32		    file_id;
+	gchar		   *file_id_str;
+	gchar		   *value = NULL;
+	const gchar	   *mime = NULL;
+	GError		   *actual_error = NULL;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (uri != NULL, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to get service type ",
+				  "uri:'%s'",
+				  uri);
+
+	iface = tracker_db_manager_get_db_interface_by_service (TRACKER_DB_FOR_FILE_SERVICE);
+
+	/* FIXME why dont obtain the service type directly from the DB??? */
+
+	file_id = tracker_db_file_get_id (iface, uri);
+
+	if (file_id < 1) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "File '%s' was not found in the database",
+					     uri);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	/* Get mime */
+	file_id_str = tracker_guint_to_string (file_id);
+
+	mime = NULL;
+	result_set = tracker_db_metadata_get (iface,
+					      file_id_str,
+					      "File:Mime");
+
+	if (result_set) {
+		tracker_db_result_set_get (result_set, 0, &mime, -1);
+		g_object_unref (result_set);
+	}
+
+	g_free (file_id_str);
+
+	if (!mime) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "Metadata 'File:Mime' for '%s' doesn't exist",
+					     uri);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	/* Get service from mime */
+	value = tracker_ontology_get_service_by_mime (mime);
+
+	if (value) {
+
+		tracker_dbus_request_comment (request_id,
+					      "Info for file '%s', "
+					      "id:%d, mime:'%s', service:'%s'",
+					      uri,
+					      file_id,
+					      mime,
+					      value);
+		dbus_g_method_return (context, value);
+		g_free (value);
+
+		tracker_dbus_request_success (request_id);
+	} else {
+
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "Unable to find service to mime '%s'",
+					     mime);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+	}
+
+}
+
+void
+tracker_files_get_text_contents (TrackerFiles		*object,
+				 const gchar		*uri,
+				 gint			 offset,
+				 gint			 max_length,
+				 DBusGMethodInvocation	*context,
+				 GError		       **error)
+{
+	TrackerDBInterface *iface;
+	TrackerDBResultSet *result_set;
+	guint		    request_id;
+	gchar		   *service_id;
+	gchar		   *offset_str;
+	gchar		   *max_length_str;
+	gchar		   *value;
+	GError		   *actual_error = NULL;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (uri != NULL, context);
+	tracker_dbus_async_return_if_fail (offset >= 0, context);
+	tracker_dbus_async_return_if_fail (max_length >= 0, context);
+	tracker_dbus_async_return_if_fail (value != NULL, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to get text contents, "
+				  "uri:'%s', offset:%d, max length:%d",
+				  uri,
+				  offset,
+				  max_length);
+
+	iface = tracker_db_manager_get_db_interface_by_service (TRACKER_DB_FOR_FILE_SERVICE);
+
+	/* FIXME iface is already for "Files". Makes no sense to try Files and Emails */
+	service_id = tracker_db_file_get_id_as_string (iface, "Files", uri);
+	if (!service_id) {
+
+		service_id = tracker_db_file_get_id_as_string (iface, "Emails", uri);
+
+		if (!service_id) {
+			tracker_dbus_request_failed (request_id,
+						     &actual_error,
+						     "Unable to retrieve service ID for uri '%s'",
+						     uri);
+			dbus_g_method_return_error (context, actual_error);
+			g_error_free (actual_error);
+			return;
+		}
+	}
+
+	offset_str = tracker_gint_to_string (offset);
+	max_length_str = tracker_gint_to_string (max_length);
+
+	result_set = tracker_db_exec_proc (iface,
+					   "GetFileContents",
+					   offset_str,
+					   max_length_str,
+					   service_id,
+					   NULL);
+
+	g_free (max_length_str);
+	g_free (offset_str);
+	g_free (service_id);
+
+	if (result_set) {
+		tracker_db_result_set_get (result_set, 0, &value, -1);
+		g_object_unref (result_set);
+
+		if (value == NULL) {
+			value = g_strdup ("");
+		}
+
+		dbus_g_method_return (context, value);
+		g_free (value);
+
+		tracker_dbus_request_success (request_id);
+
+	} else {
+
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "The contents of the uri '%s' are not stored",
+					     uri);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+	}
+}
+
+void
+tracker_files_search_text_contents (TrackerFiles	   *object,
+				    const gchar		   *uri,
+				    const gchar		   *text,
+				    gint		    max_length,
+				    DBusGMethodInvocation  *context,
+				    GError		  **error)
+{
+	TrackerDBInterface *iface;
+	TrackerDBResultSet *result_set = NULL;
+	guint		    request_id;
+	gchar		   *name = NULL;
+	gchar		   *path = NULL;
+	gchar		   *max_length_str;
+	gchar		   *value = NULL;
+	GError		   *actual_error = NULL;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (uri != NULL, context);
+	tracker_dbus_async_return_if_fail (text != NULL, context);
+	tracker_dbus_async_return_if_fail (max_length >= 0, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to search text contents, "
+				  "in uri:'%s' for text:'%s' with max length:%d",
+				  uri,
+				  text,
+				  max_length);
+
+	iface = tracker_db_manager_get_db_interface_by_service (TRACKER_DB_FOR_FILE_SERVICE);
+
+	tracker_file_get_path_and_name (uri, &path, &name);
+
+	max_length_str = tracker_gint_to_string (max_length);
+
+	/* result_set = tracker_exec_proc (iface, */
+	/*				"SearchFileContents", */
+	/*				4, */
+	/*				path, */
+	/*				name, */
+	/*				text, */
+	/*				max_length_str); */
+
+
+	g_free (max_length_str);
+	g_free (path);
+	g_free (name);
+
+	if (result_set) {
+		tracker_db_result_set_get (result_set, 0, value, -1);
+		g_object_unref (result_set);
+	} else {
+		value = g_strdup ("");
+	}
+
+	/* Fixme: when this is implemented, we should return TRUE and
+	 * change this function to the success variant.
+	 */
+	tracker_dbus_request_failed (request_id,
+				     &actual_error,
+				     "%s not implemented yet",
+				     __PRETTY_FUNCTION__);
+	dbus_g_method_return_error (context, actual_error);
+	g_error_free (actual_error);
+}
+
+void
+tracker_files_get_by_service_type (TrackerFiles		  *object,
+				   gint			   live_query_id,
+				   const gchar		  *service,
+				   gint			   offset,
+				   gint			   max_hits,
+				   DBusGMethodInvocation  *context,
+				   GError		 **error)
+{
+	TrackerDBInterface  *iface;
+	TrackerDBResultSet  *result_set;
+	guint		     request_id;
+	gchar		   **values = NULL;
+	GError		    *actual_error = NULL;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (service != NULL, context);
+	tracker_dbus_async_return_if_fail (offset >= 0, context);
+	tracker_dbus_async_return_if_fail (max_hits >= 0, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to get files by service type, "
+				  "query id:%d, service:'%s', offset:%d, max hits:%d, ",
+				  live_query_id,
+				  service,
+				  offset,
+				  max_hits);
+
+	if (!tracker_ontology_service_is_valid (service)) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "Service '%s' is invalid or has not been implemented yet",
+					     service);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	iface = tracker_db_manager_get_db_interface_by_service (TRACKER_DB_FOR_FILE_SERVICE);
+
+	result_set = tracker_db_files_get_by_service (iface,
+						      service,
+						      offset,
+						      max_hits);
+
+	values = tracker_dbus_query_result_to_strv (result_set, 0, NULL);
+
+	if (result_set) {
+		g_object_unref (result_set);
+	}
+
+	dbus_g_method_return (context, values);
+	if (values) {
+		g_strfreev (values);
+	}
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_files_get_by_mime_type (TrackerFiles	       *object,
+				gint			live_query_id,
+				gchar		      **mime_types,
+				gint			offset,
+				gint			max_hits,
+				DBusGMethodInvocation  *context,
+				GError		      **error)
+{
+	TrackerDBInterface  *iface;
+	TrackerDBResultSet  *result_set;
+	guint		     request_id;
+	gchar		   **values = NULL;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (mime_types != NULL, context);
+	tracker_dbus_async_return_if_fail (g_strv_length (mime_types) > 0, context);
+	tracker_dbus_async_return_if_fail (offset >= 0, context);
+	tracker_dbus_async_return_if_fail (max_hits >= 0, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to get files by mime types, "
+				  "query id:%d, mime types:%d, offset:%d, max hits:%d, ",
+				  live_query_id,
+				  g_strv_length (mime_types),
+				  offset,
+				  max_hits);
+
+	iface = tracker_db_manager_get_db_interface_by_service (TRACKER_DB_FOR_FILE_SERVICE);
+
+	result_set = tracker_db_files_get_by_mime (iface,
+						   mime_types,
+						   g_strv_length (mime_types),
+						   offset,
+						   max_hits,
+						   FALSE);
+
+	values = tracker_dbus_query_result_to_strv (result_set, 0, NULL);
+
+	if (result_set) {
+		g_object_unref (result_set);
+	}
+
+	dbus_g_method_return (context, values);
+	if (values) {
+		g_strfreev (values);
+	}
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_files_get_by_mime_type_vfs (TrackerFiles	   *object,
+				    gint		    live_query_id,
+				    gchar		  **mime_types,
+				    gint		    offset,
+				    gint		    max_hits,
+				    DBusGMethodInvocation  *context,
+				    GError		  **error)
+{
+	TrackerDBInterface  *iface;
+	TrackerDBResultSet  *result_set;
+	guint		     request_id;
+	gchar		   **values = NULL;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (mime_types != NULL, context);
+	tracker_dbus_async_return_if_fail (g_strv_length (mime_types) > 0, context);
+	tracker_dbus_async_return_if_fail (offset >= 0, context);
+	tracker_dbus_async_return_if_fail (max_hits >= 0, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to get files by mime types (VFS), "
+				  "query id:%d, mime types:%d, offset:%d, max hits:%d, ",
+				  live_query_id,
+				  g_strv_length (mime_types),
+				  offset,
+				  max_hits);
+
+	iface = tracker_db_manager_get_db_interface_by_service (TRACKER_DB_FOR_FILE_SERVICE);
+
+	/* NOTE: The only difference between this function and the
+	 * non-VFS version is the boolean in this function call:
+	 */
+	result_set = tracker_db_files_get_by_mime (iface,
+						   mime_types,
+						   g_strv_length (mime_types),
+						   offset,
+						   max_hits,
+						   TRUE);
+
+	values = tracker_dbus_query_result_to_strv (result_set, 0, NULL);
+
+	if (result_set) {
+		g_object_unref (result_set);
+	}
+
+	dbus_g_method_return (context, values);
+	if (values) {
+		g_strfreev (values);
+	}
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_files_get_mtime (TrackerFiles		*object,
+			 const gchar		*uri,
+			 DBusGMethodInvocation	*context,
+			 GError		       **error)
+{
+	TrackerDBInterface *iface;
+	TrackerDBResultSet *result_set;
+	guint		    request_id;
+	gchar		   *path = NULL;
+	gchar		   *name = NULL;
+	gint		    mtime;
+	GError		   *actual_error = NULL;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (uri != NULL, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request for mtime, "
+				  "uri:'%s'",
+				  uri);
+
+	iface = tracker_db_manager_get_db_interface_by_service (TRACKER_DB_FOR_FILE_SERVICE);
+
+	tracker_file_get_path_and_name (uri, &path, &name);
+
+	result_set = tracker_db_exec_proc (iface,
+					   "GetFileMTime",
+					   path,
+					   name,
+					   NULL);
+	g_free (path);
+	g_free (name);
+
+	if (!result_set) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "There is no file mtime in the database for '%s'",
+					     uri);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	tracker_db_result_set_get (result_set, 0, &mtime, -1);
+	g_object_unref (result_set);
+
+	dbus_g_method_return (context, mtime);
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_files_get_metadata_for_files_in_folder (TrackerFiles	       *object,
+						gint			live_query_id,
+						const gchar	       *uri,
+						gchar		      **fields,
+						DBusGMethodInvocation  *context,
+						GError		      **error)
+{
+	TrackerDBInterface *iface;
+	TrackerDBResultSet *result_set;
+	TrackerField	   *defs[255];
+	guint		    request_id;
+	guint		    i;
+	gchar		   *uri_filtered;
+	guint32		    file_id;
+	GString		   *sql;
+	gboolean	    needs_join[255];
+	gchar		   *query;
+	GPtrArray	   *values;
+	GError		   *actual_error = NULL;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (uri != NULL, context);
+	tracker_dbus_async_return_if_fail (fields != NULL, context);
+	tracker_dbus_async_return_if_fail (g_strv_length (fields) > 0, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request for metadata for files in folder, "
+				  "query id:%d, uri:'%s', fields:%d",
+				  live_query_id,
+				  uri,
+				  g_strv_length (fields));
+
+
+	/* Get fields for metadata list provided */
+	for (i = 0; i < g_strv_length (fields); i++) {
+		defs[i] = tracker_ontology_get_field_by_name (fields[i]);
+
+		if (!defs[i]) {
+			tracker_dbus_request_failed (request_id,
+						     &actual_error,
+						     "Metadata field '%s' was not found",
+						     fields[i]);
+			dbus_g_method_return_error (context, actual_error);
+			g_error_free (actual_error);
+			return;
+		}
+
+	}
+	defs [g_strv_length (fields)] = NULL;
+
+
+	if (g_str_has_suffix (uri, G_DIR_SEPARATOR_S)) {
+		/* Remove trailing 'G_DIR_SEPARATOR' */
+		uri_filtered = g_strndup (uri, strlen (uri) - 1);
+	} else {
+		uri_filtered = g_strdup (uri);
+	}
+
+	iface = tracker_db_manager_get_db_interface_by_service (TRACKER_DB_FOR_FILE_SERVICE);
+
+	/* Get file ID in database */
+	file_id = tracker_db_file_get_id (iface, uri_filtered);
+	if (file_id == 0) {
+		g_free (uri_filtered);
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "File or directory was not in database, uri:'%s'",
+					     uri);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	/* Build SELECT clause */
+	sql = g_string_new (" ");
+	g_string_append_printf (sql,
+				"SELECT (F.Path || '%s' || F.Name) as PathName ",
+				G_DIR_SEPARATOR_S);
+
+	for (i = 1; i <= g_strv_length (fields); i++) {
+		gchar *field;
+
+		field = tracker_db_get_field_name ("Files", fields[i-1]);
+
+		if (field) {
+			g_string_append_printf (sql, ", F.%s ", field);
+			g_free (field);
+			needs_join[i - 1] = FALSE;
+		} else {
+			gchar *display_field;
+
+			display_field = tracker_ontology_field_get_display_name (defs[i-1]);
+			g_string_append_printf (sql, ", M%d.%s ", i, display_field);
+			g_free (display_field);
+			needs_join[i - 1] = TRUE;
+		}
+	}
+
+	/* Build FROM clause */
+	g_string_append (sql,
+			 " FROM Services F ");
+
+	for (i = 0; i < g_strv_length (fields); i++) {
+		const gchar *table;
+
+		if (!needs_join[i]) {
+			continue;
+		}
+
+		table = tracker_db_metadata_get_table (tracker_field_get_data_type (defs[i]));
+
+		g_string_append_printf (sql,
+					" LEFT OUTER JOIN %s M%d ON "
+					"F.ID = M%d.ServiceID AND "
+					"M%d.MetaDataID = %s ",
+					table,
+					i+1,
+					i+1,
+					i+1,
+					tracker_field_get_id (defs[i]));
+	}
+
+	/* Build WHERE clause */
+	g_string_append_printf (sql,
+				" WHERE F.Path = '%s' ",
+				uri_filtered);
+	g_free (uri_filtered);
+
+	query = g_string_free (sql, FALSE);
+	result_set = tracker_db_interface_execute_query (iface, NULL, query);
+	values = tracker_dbus_query_result_to_ptr_array (result_set);
+
+	if (result_set) {
+		g_object_unref (result_set);
+	}
+
+	g_free (query);
+
+	dbus_g_method_return (context, values);
+
+	tracker_dbus_results_ptr_array_free (&values);
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_files_search_by_text_and_mime (TrackerFiles	      *object,
+				       const gchar	      *text,
+				       gchar		     **mime_types,
+				       DBusGMethodInvocation  *context,
+				       GError		     **error)
+{
+	TrackerDBInterface  *iface;
+	TrackerDBResultSet  *result_set;
+	guint		     request_id;
+	gchar		   **values = NULL;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (text != NULL, context);
+	tracker_dbus_async_return_if_fail (mime_types != NULL, context);
+	tracker_dbus_async_return_if_fail (g_strv_length (mime_types) > 0, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to search files by text & mime types, "
+				  "text:'%s', mime types:%d",
+				  text,
+				  g_strv_length (mime_types));
+
+	iface = tracker_db_manager_get_db_interface_by_service (TRACKER_DB_FOR_FILE_SERVICE);
+
+	result_set = tracker_db_search_text_and_mime (iface, text, mime_types);
+
+	if (result_set) {
+		gboolean  valid = TRUE;
+		gchar	 *prefix, *name;
+		gint	  row_count = 0;
+		gint	  i = 0;
+
+		row_count = tracker_db_result_set_get_n_rows (result_set);
+		values = g_new0 (gchar *, row_count);
+
+		while (valid) {
+			tracker_db_result_set_get (result_set,
+						   0, &prefix,
+						   1, &name,
+						   -1);
+
+			values[i++] = g_build_filename (prefix, name, NULL);
+			valid = tracker_db_result_set_iter_next (result_set);
+
+			g_free (prefix);
+			g_free (name);
+		}
+
+		g_object_unref (result_set);
+	} else {
+		values = g_new0 (gchar *, 1);
+		values[0] = NULL;
+	}
+
+	dbus_g_method_return (context, values);
+
+	g_strfreev (values);
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_files_search_by_text_and_location (TrackerFiles		  *object,
+					   const gchar		  *text,
+					   const gchar		  *uri,
+					   DBusGMethodInvocation  *context,
+					   GError		 **error)
+{
+	TrackerDBInterface  *iface;
+	TrackerDBResultSet  *result_set;
+	guint		     request_id;
+	gchar		   **values = NULL;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (text != NULL, context);
+	tracker_dbus_async_return_if_fail (uri != NULL, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to search files by text & location, "
+				  "text:'%s', uri:'%s'",
+				  text,
+				  uri);
+
+	iface = tracker_db_manager_get_db_interface_by_service (TRACKER_DB_FOR_FILE_SERVICE);
+
+	result_set = tracker_db_search_text_and_location (iface, text, uri);
+
+	if (result_set) {
+		gboolean  valid = TRUE;
+		gchar	 *prefix, *name;
+		gint	  row_count;
+		gint	  i = 0;
+
+		row_count = tracker_db_result_set_get_n_rows (result_set);
+		values = g_new0 (gchar *, row_count);
+
+		while (valid) {
+			tracker_db_result_set_get (result_set,
+						   0, &prefix,
+						   1, &name,
+						   -1);
+
+			values[i++] = g_build_filename (prefix, name, NULL);
+			valid = tracker_db_result_set_iter_next (result_set);
+
+			g_free (prefix);
+			g_free (name);
+		}
+
+		g_object_unref (result_set);
+	} else {
+		values = g_new0 (gchar *, 1);
+		values[0] = NULL;
+	}
+
+	dbus_g_method_return (context, values);
+
+	g_strfreev (values);
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_files_search_by_text_and_mime_and_location (TrackerFiles	   *object,
+						    const gchar		   *text,
+						    gchar		  **mime_types,
+						    const gchar		   *uri,
+						    DBusGMethodInvocation  *context,
+						    GError		  **error)
+{
+	TrackerDBInterface  *iface;
+	TrackerDBResultSet  *result_set;
+	guint		     request_id;
+	gchar		   **values = NULL;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (text != NULL, context);
+	tracker_dbus_async_return_if_fail (mime_types != NULL, context);
+	tracker_dbus_async_return_if_fail (g_strv_length (mime_types) > 0, context);
+	tracker_dbus_async_return_if_fail (uri != NULL, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to search files by text & mime types & location, "
+				  "text:'%s', mime types:%d, uri:'%s'",
+				  text,
+				  g_strv_length (mime_types),
+				  uri);
+
+	iface = tracker_db_manager_get_db_interface_by_service (TRACKER_DB_FOR_FILE_SERVICE);
+
+	result_set = tracker_db_search_text_and_mime_and_location (iface, text, mime_types, uri);
+
+	if (result_set) {
+		gboolean  valid = TRUE;
+		gchar	 *prefix, *name;
+		gint	  row_count;
+		gint	  i = 0;
+
+		row_count = tracker_db_result_set_get_n_rows (result_set);
+		values = g_new0 (gchar *, row_count);
+
+		while (valid) {
+			tracker_db_result_set_get (result_set,
+						   0, &prefix,
+						   1, &name,
+						   -1);
+
+			values[i++] = g_build_filename (prefix, name, NULL);
+			valid = tracker_db_result_set_iter_next (result_set);
+
+			g_free (prefix);
+			g_free (name);
+		}
+
+		g_object_unref (result_set);
+	} else {
+		values = g_new0 (gchar *, 1);
+		values[0] = NULL;
+	}
+
+	dbus_g_method_return (context, values);
+
+	g_strfreev (values);
+
+	tracker_dbus_request_success (request_id);
+}

Added: trunk/src/trackerd/tracker-files.h
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-files.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,138 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKERD_FILES_H__
+#define __TRACKERD_FILES_H__
+
+#include <glib-object.h>
+
+#define TRACKER_FILES_SERVICE	      "org.freedesktop.Tracker"
+#define TRACKER_FILES_PATH	      "/org/freedesktop/Tracker/Files"
+#define TRACKER_FILES_INTERFACE       "org.freedesktop.Tracker.Files"
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_FILES	      (tracker_files_get_type ())
+#define TRACKER_FILES(object)	      (G_TYPE_CHECK_INSTANCE_CAST ((object), TRACKER_TYPE_FILES, TrackerFiles))
+#define TRACKER_FILES_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), TRACKER_TYPE_FILES, TrackerFilesClass))
+#define TRACKER_IS_FILES(object)      (G_TYPE_CHECK_INSTANCE_TYPE ((object), TRACKER_TYPE_FILES))
+#define TRACKER_IS_FILES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TRACKER_TYPE_FILES))
+#define TRACKER_FILES_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), TRACKER_TYPE_FILES, TrackerFilesClass))
+
+typedef struct TrackerFiles	 TrackerFiles;
+typedef struct TrackerFilesClass TrackerFilesClass;
+
+struct TrackerFiles {
+	GObject parent;
+};
+
+struct TrackerFilesClass {
+	GObjectClass parent;
+};
+
+GType	      tracker_files_get_type				 (void);
+TrackerFiles *tracker_files_new					 (TrackerProcessor	 *processor);
+void	      tracker_files_exist				 (TrackerFiles		 *object,
+								  const gchar		 *uri,
+								  gboolean		  auto_create,
+								  DBusGMethodInvocation  *context,
+								  GError		**error);
+void	      tracker_files_create				 (TrackerFiles		 *object,
+								  const gchar		 *uri,
+								  gboolean		  is_directory,
+								  const gchar		 *mime,
+								  gint			  size,
+								  gint			  mtime,
+								  DBusGMethodInvocation  *context,
+								  GError		**error);
+void	      tracker_files_delete				 (TrackerFiles		 *object,
+								  const gchar		 *uri,
+								  DBusGMethodInvocation  *context,
+								  GError		**error);
+void	      tracker_files_get_service_type			 (TrackerFiles		 *object,
+								  const gchar		 *uri,
+								  DBusGMethodInvocation  *context,
+								  GError		**error);
+void	      tracker_files_get_text_contents			 (TrackerFiles		 *object,
+								  const gchar		 *uri,
+								  gint			  offset,
+								  gint			  max_length,
+								  DBusGMethodInvocation  *context,
+								  GError		**error);
+void	      tracker_files_search_text_contents		 (TrackerFiles		 *object,
+								  const gchar		 *uri,
+								  const gchar		 *text,
+								  gint			  max_length,
+								  DBusGMethodInvocation  *context,
+								  GError		**error);
+void	      tracker_files_get_by_service_type			 (TrackerFiles		 *object,
+								  gint			  live_query_id,
+								  const gchar		 *service,
+								  gint			  offset,
+								  gint			  max_hits,
+								  DBusGMethodInvocation  *context,
+								  GError		**error);
+void	      tracker_files_get_by_mime_type			 (TrackerFiles		 *object,
+								  gint			  live_query_id,
+								  gchar			**mime_types,
+								  gint			  offset,
+								  gint			  max_hits,
+								  DBusGMethodInvocation  *context,
+								  GError		**error);
+void	      tracker_files_get_by_mime_type_vfs		 (TrackerFiles		 *object,
+								  gint			  live_query_id,
+								  gchar			**mime_types,
+								  gint			  offset,
+								  gint			  max_hits,
+								  DBusGMethodInvocation  *context,
+								  GError		**error);
+void	      tracker_files_get_mtime				 (TrackerFiles		 *object,
+								  const gchar		 *uri,
+								  DBusGMethodInvocation  *context,
+								  GError		**error);
+void	      tracker_files_get_metadata_for_files_in_folder	 (TrackerFiles		 *object,
+								  gint			  live_query_id,
+								  const gchar		 *uri,
+								  gchar			**fields,
+								  DBusGMethodInvocation  *context,
+								  GError		**error);
+void	      tracker_files_search_by_text_and_mime		 (TrackerFiles		 *object,
+								  const gchar		 *text,
+								  gchar			**mime_types,
+								  DBusGMethodInvocation  *context,
+								  GError		**error);
+void	      tracker_files_search_by_text_and_location		 (TrackerFiles		 *object,
+								  const gchar		 *text,
+								  const gchar		 *uri,
+								  DBusGMethodInvocation  *context,
+								  GError		**error);
+void	      tracker_files_search_by_text_and_mime_and_location (TrackerFiles		 *object,
+								  const gchar		 *text,
+								  gchar			**mime_types,
+								  const gchar		 *uri,
+								  DBusGMethodInvocation  *context,
+								  GError		**error);
+
+
+
+G_END_DECLS
+
+#endif /* __TRACKERD_FILES_H__ */

Added: trunk/src/trackerd/tracker-keywords.c
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-keywords.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,592 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <libtracker-common/tracker-dbus.h>
+#include <libtracker-common/tracker-log.h>
+#include <libtracker-common/tracker-utils.h>
+
+#include <libtracker-db/tracker-db-dbus.h>
+#include <libtracker-db/tracker-db-index.h>
+#include <libtracker-db/tracker-db-manager.h>
+
+#include "tracker-dbus.h"
+#include "tracker-keywords.h"
+#include "tracker-db.h"
+#include "tracker-marshal.h"
+#include "tracker-indexer-client.h"
+
+#define TRACKER_KEYWORDS_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TRACKER_TYPE_KEYWORDS, TrackerKeywordsPrivate))
+
+typedef struct {
+	DBusGProxy *fd_proxy;
+} TrackerKeywordsPrivate;
+
+enum {
+	KEYWORD_ADDED,
+	KEYWORD_REMOVED,
+	LAST_SIGNAL
+};
+
+static void tracker_keywords_finalize (GObject	    *object);
+
+static guint signals[LAST_SIGNAL] = {0};
+
+G_DEFINE_TYPE(TrackerKeywords, tracker_keywords, G_TYPE_OBJECT)
+
+static void
+tracker_keywords_class_init (TrackerKeywordsClass *klass)
+{
+	GObjectClass *object_class;
+
+	object_class = G_OBJECT_CLASS (klass);
+
+	object_class->finalize = tracker_keywords_finalize;
+
+	signals[KEYWORD_ADDED] =
+		g_signal_new ("keyword-added",
+			      G_TYPE_FROM_CLASS (klass),
+			      G_SIGNAL_RUN_LAST,
+			      0,
+			      NULL, NULL,
+			      tracker_marshal_VOID__STRING_STRING_STRING,
+			      G_TYPE_NONE,
+			      3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
+	signals[KEYWORD_REMOVED] =
+		g_signal_new ("keyword-removed",
+			      G_TYPE_FROM_CLASS (klass),
+			      G_SIGNAL_RUN_LAST,
+			      0,
+			      NULL, NULL,
+			      tracker_marshal_VOID__STRING_STRING_STRING,
+			      G_TYPE_NONE,
+			      3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
+
+	g_type_class_add_private (object_class, sizeof (TrackerKeywordsPrivate));
+}
+
+static void
+tracker_keywords_init (TrackerKeywords *object)
+{
+}
+
+static void
+tracker_keywords_finalize (GObject *object)
+{
+	TrackerKeywordsPrivate *priv;
+
+	priv = TRACKER_KEYWORDS_GET_PRIVATE (object);
+
+	if (priv->fd_proxy) {
+		g_object_unref (priv->fd_proxy);
+	}
+
+	G_OBJECT_CLASS (tracker_keywords_parent_class)->finalize (object);
+}
+
+TrackerKeywords *
+tracker_keywords_new (void)
+{
+	return g_object_new (TRACKER_TYPE_KEYWORDS, NULL);
+}
+
+/*
+ * Functions
+ */
+void
+tracker_keywords_get_list (TrackerKeywords  *object,
+			   const gchar	    *service_type,
+			   DBusGMethodInvocation *context,
+			   GError	   **error)
+{
+	TrackerDBInterface *iface;
+	TrackerDBResultSet *result_set;
+	guint		    request_id;
+	GError		   *actual_error = NULL;
+	GPtrArray	   *values;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (service_type != NULL, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to get keywords list, "
+				  "service type:'%s'",
+				  service_type);
+
+	if (!tracker_ontology_service_is_valid (service_type)) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "Service type '%s' is invalid or has not been implemented yet",
+					     service_type);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	iface = tracker_db_manager_get_db_interface_by_service (service_type);
+	result_set = tracker_db_keywords_get_list (iface, service_type);
+	values = tracker_dbus_query_result_to_ptr_array (result_set);
+
+	if (result_set) {
+		g_object_unref (result_set);
+	}
+
+	dbus_g_method_return (context, values);
+	tracker_dbus_results_ptr_array_free (&values);
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_keywords_get (TrackerKeywords	     *object,
+		      const gchar	     *service_type,
+		      const gchar	     *uri,
+		      DBusGMethodInvocation  *context,
+		      GError		    **error)
+{
+	TrackerDBInterface *iface;
+	TrackerDBResultSet *result_set;
+	guint		    request_id;
+	gchar		   *id;
+	GError		   *actual_error = NULL;
+	gchar		  **values;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (service_type != NULL, context);
+	tracker_dbus_async_return_if_fail (uri != NULL, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to get keywords, "
+				  "service type:'%s', uri:'%s'",
+				  service_type,
+				  uri);
+
+	if (!tracker_ontology_service_is_valid (service_type)) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "Service type '%s' is invalid or has not been implemented yet",
+					     service_type);
+	}
+
+	if (!actual_error && tracker_is_empty_string (uri)) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "URI is empty");
+	}
+
+	if (actual_error) {
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	iface = tracker_db_manager_get_db_interface_by_service (service_type);
+	id = tracker_db_file_get_id_as_string (iface, service_type, uri);
+	if (!id) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "Entity '%s' was not found",
+					     uri);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	result_set = tracker_db_metadata_get (iface,
+					      id,
+					      "User:Keywords");
+	values = tracker_dbus_query_result_to_strv (result_set, 0, NULL);
+
+	if (result_set) {
+		g_object_unref (result_set);
+	}
+
+	g_free (id);
+
+	dbus_g_method_return (context, values);
+
+	if (values) {
+		g_strfreev (values);
+	}
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_keywords_add (TrackerKeywords	     *object,
+		      const gchar	     *service_type,
+		      const gchar	     *uri,
+		      gchar		    **keywords,
+		      DBusGMethodInvocation  *context,
+		      GError		     **error)
+{
+	TrackerDBInterface  *iface;
+	guint		     request_id;
+	gchar		    *id;
+	GError		    *actual_error = NULL;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (service_type != NULL, context);
+	tracker_dbus_async_return_if_fail (uri != NULL, context);
+	tracker_dbus_async_return_if_fail (keywords != NULL && *keywords != NULL, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to add keywords, "
+				  "service type:'%s', uri:'%s'",
+				  service_type,
+				  uri);
+
+	if (!tracker_ontology_service_is_valid (service_type)) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "Service type '%s' is invalid or has not been implemented yet",
+					     service_type);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	if (tracker_is_empty_string (uri)) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "URI is empty");
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	iface = tracker_db_manager_get_db_interface_by_service (service_type);
+	id = tracker_db_file_get_id_as_string (iface, service_type, uri);
+	if (!id) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "Entity '%s' was not found",
+					     uri);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	org_freedesktop_Tracker_Indexer_property_set (tracker_dbus_indexer_get_proxy (),
+						      service_type,
+						      uri,
+						      "User:Keywords",
+						      (const gchar **)keywords,
+						      &actual_error);
+
+	if (actual_error) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     NULL);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	g_free (id);
+
+	dbus_g_method_return (context);
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_keywords_remove (TrackerKeywords	*object,
+			 const gchar		*service_type,
+			 const gchar		*uri,
+			 gchar		       **keywords,
+			 DBusGMethodInvocation	*context,
+			 GError		       **error)
+{
+	TrackerDBInterface  *iface;
+	guint		     request_id;
+	gchar		    *service_id;
+	GError		    *actual_error = NULL;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (service_type != NULL, context);
+	tracker_dbus_async_return_if_fail (uri != NULL, context);
+	tracker_dbus_async_return_if_fail (keywords != NULL && g_strv_length (keywords) > 0, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to remove keywords, "
+				  "service type:'%s', uri:'%s'",
+				  service_type,
+				  uri);
+
+	if (!tracker_ontology_service_is_valid (service_type)) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "Service type '%s' is invalid or has not been implemented yet",
+					     service_type);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	if (tracker_is_empty_string (uri)) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "URI is empty");
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	/* Check the uri exists, so we dont start the indexer in vain */
+	iface = tracker_db_manager_get_db_interface_by_service (service_type);
+	service_id = tracker_db_file_get_id_as_string (iface, service_type, uri);
+	if (!service_id) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "Entity '%s' was not found",
+					     uri);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	org_freedesktop_Tracker_Indexer_property_remove (tracker_dbus_indexer_get_proxy (),
+							 service_type,
+							 uri,
+							 "User:Keywords",
+							 (const gchar **)keywords,
+							 &actual_error);
+
+	if (actual_error) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     NULL);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	g_free (service_id);
+
+	dbus_g_method_return (context);
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_keywords_remove_all (TrackerKeywords	    *object,
+			     const gchar	    *service_type,
+			     const gchar	    *uri,
+			     DBusGMethodInvocation  *context,
+			     GError		   **error)
+{
+	TrackerDBInterface *iface;
+	guint		    request_id;
+	gchar		  **values;
+	gchar		   *service_id;
+	GError		   *actual_error = NULL;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (service_type != NULL, context);
+	tracker_dbus_async_return_if_fail (uri != NULL, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to remove all keywords, "
+				  "service type:'%s', uri:'%s'",
+				  service_type,
+				  uri);
+
+	if (!tracker_ontology_service_is_valid (service_type)) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "Service type '%s' is invalid or has not been implemented yet",
+					     service_type);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	if (tracker_is_empty_string (uri)) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "URI is empty");
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	/* Check the uri exists, so we dont start the indexer in vain */
+	iface = tracker_db_manager_get_db_interface_by_service (service_type);
+	service_id = tracker_db_file_get_id_as_string (iface, service_type, uri);
+	if (!service_id) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "Entity '%s' was not found",
+					     uri);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	values = g_new0 (gchar *, 1);
+	values[0] = NULL;
+	org_freedesktop_Tracker_Indexer_property_remove (tracker_dbus_indexer_get_proxy (),
+							 service_type,
+							 uri,
+							 "User:Keywords",
+							 (const gchar **)values,
+							 &actual_error);
+
+	g_strfreev (values);
+	if (actual_error) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     NULL);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+
+	dbus_g_method_return (context);
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_keywords_search (TrackerKeywords	*object,
+			 gint			 live_query_id,
+			 const gchar		*service_type,
+			 const gchar	       **keywords,
+			 gint			 offset,
+			 gint			 max_hits,
+			 DBusGMethodInvocation	*context,
+			 GError		       **error)
+{
+	TrackerDBInterface  *iface;
+	TrackerDBResultSet  *result_set;
+	guint		     request_id;
+	const gchar	   **p;
+	GString		    *search;
+	GString		    *select;
+	GString		    *where;
+	gchar		    *related_metadata;
+	gchar		    *query;
+	gchar		   **values;
+	GError		    *actual_error = NULL;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (service_type != NULL, context);
+	tracker_dbus_async_return_if_fail (keywords != NULL, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to search keywords, "
+				  "query id:%d, service type:'%s', offset:%d, "
+				  "max hits:%d",
+				  live_query_id,
+				  service_type,
+				  offset,
+				  max_hits);
+
+	if (!tracker_ontology_service_is_valid (service_type)) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "Service_Type '%s' is invalid or has not been implemented yet",
+					     service_type);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	iface = tracker_db_manager_get_db_interface_by_service (service_type);
+
+	/* Sanity check values */
+	offset = MAX (offset, 0);
+
+	/* Create keyword search string */
+	search = g_string_new ("");
+	g_string_append_printf (search,
+				"'%s'",
+				keywords[0]);
+
+	for (p = keywords + 1; *p; p++) {
+		g_string_append_printf (search, ", '%s'", *p);
+	}
+
+	tracker_dbus_request_comment (request_id,
+				      "Executing keyword search on %s",
+				      search->str);
+
+	/* Create select string */
+	select = g_string_new (" Select distinct S.Path || '");
+	select = g_string_append (select, G_DIR_SEPARATOR_S);
+	select = g_string_append (select,
+				  "' || S.Name as EntityName from Services S, ServiceKeywordMetaData M ");
+
+	/* Create where string */
+	related_metadata = tracker_db_metadata_get_related_names (iface, "User:Keywords");
+
+	where = g_string_new ("");
+	g_string_append_printf (where,
+				" where S.ID = M.ServiceID and M.MetaDataID in (%s) and M.MetaDataValue in (%s) ",
+				related_metadata,
+				search->str);
+	g_free (related_metadata);
+	g_string_free (search, TRUE);
+
+	g_string_append_printf (where,
+				"  and	(S.ServiceTypeID in (select TypeId from ServiceTypes where TypeName = '%s' or Parent = '%s')) ",
+				service_type,
+				service_type);
+
+	/* Add offset and max_hits */
+	g_string_append_printf (where,
+				" Limit %d,%d",
+				offset,
+				max_hits);
+
+	/* Finalize query */
+	query = g_strconcat (select->str, where->str, NULL);
+	g_string_free (select, TRUE);
+	g_string_free (where, TRUE);
+
+	g_debug (query);
+
+	result_set = tracker_db_interface_execute_query (iface, NULL, query);
+	values = tracker_dbus_query_result_to_strv (result_set, 0, NULL);
+
+	if (result_set) {
+		g_object_unref (result_set);
+	}
+
+	g_free (query);
+
+	dbus_g_method_return (context, values);
+
+	if (values) {
+		g_strfreev (values);
+	}
+
+	tracker_dbus_request_success (request_id);
+}

Added: trunk/src/trackerd/tracker-keywords.h
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-keywords.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,91 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKERD_KEYWORDS_H__
+#define __TRACKERD_KEYWORDS_H__
+
+#include <glib-object.h>
+
+#define TRACKER_KEYWORDS_SERVICE	 "org.freedesktop.Tracker"
+#define TRACKER_KEYWORDS_PATH		 "/org/freedesktop/Tracker/Keywords"
+#define TRACKER_KEYWORDS_INTERFACE	 "org.freedesktop.Tracker.Keywords"
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_KEYWORDS		 (tracker_keywords_get_type ())
+#define TRACKER_KEYWORDS(object)	 (G_TYPE_CHECK_INSTANCE_CAST ((object), TRACKER_TYPE_KEYWORDS, TrackerKeywords))
+#define TRACKER_KEYWORDS_CLASS(klass)	 (G_TYPE_CHECK_CLASS_CAST ((klass), TRACKER_TYPE_KEYWORDS, TrackerKeywordsClass))
+#define TRACKER_IS_KEYWORDS(object)	 (G_TYPE_CHECK_INSTANCE_TYPE ((object), TRACKER_TYPE_KEYWORDS))
+#define TRACKER_IS_KEYWORDS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TRACKER_TYPE_KEYWORDS))
+#define TRACKER_KEYWORDS_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), TRACKER_TYPE_KEYWORDS, TrackerKeywordsClass))
+
+typedef struct TrackerKeywords	    TrackerKeywords;
+typedef struct TrackerKeywordsClass TrackerKeywordsClass;
+
+struct TrackerKeywords {
+	GObject parent;
+};
+
+struct TrackerKeywordsClass {
+	GObjectClass parent;
+};
+
+GType		 tracker_keywords_get_type   (void);
+TrackerKeywords *tracker_keywords_new	     (void);
+void		 tracker_keywords_get_list   (TrackerKeywords	     *object,
+					      const gchar	     *service_type,
+					      DBusGMethodInvocation  *context,
+					      GError		    **error);
+void		 tracker_keywords_get	     (TrackerKeywords	     *object,
+					      const gchar	     *service_type,
+					      const gchar	     *uri,
+					      DBusGMethodInvocation  *context,
+					      GError		    **error);
+void		 tracker_keywords_add	     (TrackerKeywords	     *object,
+					      const gchar	     *service_type,
+					      const gchar	     *uri,
+					      gchar		    **keywords,
+					      DBusGMethodInvocation  *context,
+					      GError		    **error);
+void		 tracker_keywords_remove     (TrackerKeywords	     *object,
+					      const gchar	     *service_type,
+					      const gchar	     *uri,
+					      gchar		    **keywords,
+					      DBusGMethodInvocation  *context,
+					      GError		    **error);
+void		 tracker_keywords_remove_all (TrackerKeywords	     *object,
+					      const gchar	     *service_type,
+					      const gchar	     *uri,
+					      DBusGMethodInvocation  *context,
+					      GError		    **error);
+void		 tracker_keywords_search     (TrackerKeywords	     *object,
+					      gint		      live_query_id,
+					      const gchar	     *service_type,
+					      const gchar	    **keywords,
+					      gint		      offset,
+					      gint		      max_hits,
+					      DBusGMethodInvocation  *context,
+					      GError		    **error);
+
+
+G_END_DECLS
+
+#endif /* __TRACKERD_KEYWORDS_H__ */

Added: trunk/src/trackerd/tracker-main.c
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-main.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,1010 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef DBUS_API_SUBJECT_TO_CHANGE
+#define DBUS_API_SUBJECT_TO_CHANGE
+#endif
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <locale.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <time.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+
+#include <libtracker-common/tracker-config.h>
+#include <libtracker-common/tracker-file-utils.h>
+#include <libtracker-common/tracker-hal.h>
+#include <libtracker-common/tracker-ioprio.h>
+#include <libtracker-common/tracker-language.h>
+#include <libtracker-common/tracker-log.h>
+#include <libtracker-common/tracker-module-config.h>
+#include <libtracker-common/tracker-nfs-lock.h>
+#include <libtracker-common/tracker-ontology.h>
+
+#include <libtracker-db/tracker-db-manager.h>
+#include <libtracker-db/tracker-db-index.h>
+#include <libtracker-db/tracker-db-index-manager.h>
+
+#include "tracker-crawler.h"
+#include "tracker-dbus.h"
+#include "tracker-indexer-client.h"
+#include "tracker-main.h"
+#include "tracker-monitor.h"
+#include "tracker-processor.h"
+#include "tracker-status.h"
+#include "tracker-xesam-manager.h"
+
+#ifdef G_OS_WIN32
+#include <windows.h>
+#include <pthread.h>
+#include "mingw-compat.h"
+#endif
+
+#define ABOUT								  \
+	"Tracker " VERSION "\n"						  \
+	"Copyright (c) 2005-2008 Jamie McCracken (jamiemcc gnome org)\n"
+
+#define LICENSE								  \
+	"This program is free software and comes without any warranty.\n" \
+	"It is licensed under version 2 or later of the General Public "  \
+	"License which can be viewed at:\n"				  \
+	"\n"								  \
+	"  http://www.gnu.org/licenses/gpl.txt\n";
+
+/* Throttle defaults */
+#define THROTTLE_DEFAULT	    0
+#define THROTTLE_DEFAULT_ON_BATTERY 5
+
+typedef enum {
+	TRACKER_RUNNING_NON_ALLOWED,
+	TRACKER_RUNNING_READ_ONLY,
+	TRACKER_RUNNING_MAIN_INSTANCE
+} TrackerRunningLevel;
+
+typedef struct {
+	GMainLoop	 *main_loop;
+	gchar		 *log_filename;
+
+	gchar		 *data_dir;
+	gchar		 *user_data_dir;
+	gchar		 *sys_tmp_dir;
+
+	gboolean	  reindex_on_shutdown;
+
+	TrackerProcessor *processor;
+} TrackerMainPrivate;
+
+/* Private */
+static GStaticPrivate	private_key = G_STATIC_PRIVATE_INIT;
+
+/* Private command line parameters */
+static gint		verbosity = -1;
+static gint		initial_sleep = -1;
+static gboolean		low_memory;
+static gchar	      **monitors_to_exclude;
+static gchar	      **monitors_to_include;
+static gchar	      **crawl_dirs;
+static gchar	      **disable_modules;
+
+static gboolean		force_reindex;
+static gboolean		disable_indexing;
+static gchar	       *language_code;
+
+static GOptionEntry	entries_daemon[] = {
+	{ "verbosity", 'v', 0,
+	  G_OPTION_ARG_INT, &verbosity,
+	  N_("Logging, 0 = errors only, "
+	     "1 = minimal, 2 = detailed and 3 = debug (default = 0)"),
+	  NULL },
+	{ "initial-sleep", 's', 0,
+	  G_OPTION_ARG_INT, &initial_sleep,
+	  N_("Seconds to wait before starting any crawling or indexing (default = 45)"),
+	  NULL },
+	{ "low-memory", 'm', 0,
+	  G_OPTION_ARG_NONE, &low_memory,
+	  N_("Minimizes the use of memory but may slow indexing down"),
+	  NULL },
+	{ "monitors-exclude-dirs", 'e', 0,
+	  G_OPTION_ARG_STRING_ARRAY, &monitors_to_exclude,
+	  N_("Directories to exclude for file change monitoring (you can do -e <path> -e <path>)"),
+	  NULL },
+	{ "monitors-include-dirs", 'i', 0,
+	  G_OPTION_ARG_STRING_ARRAY, &monitors_to_include,
+	  N_("Directories to include for file change monitoring (you can do -i <path> -i <path>)"),
+	  NULL },
+	{ "crawler-include-dirs", 'c', 0,
+	  G_OPTION_ARG_STRING_ARRAY, &crawl_dirs,
+	  N_("Directories to crawl to index files (you can do -c <path> -c <path>)"),
+	  NULL },
+	{ "disable-modules", 'd', 0,
+	  G_OPTION_ARG_STRING_ARRAY, &disable_modules,
+	  N_("Disable modules from being processed (you can do -d <module> -d <module)"),
+	  NULL },
+	{ NULL }
+};
+
+static GOptionEntry   entries_indexer[] = {
+	{ "force-reindex", 'r', 0,
+	  G_OPTION_ARG_NONE, &force_reindex,
+	  N_("Force a re-index of all content"),
+	  NULL },
+	{ "disable-indexing", 'n', 0,
+	  G_OPTION_ARG_NONE, &disable_indexing,
+	  N_("Disable any indexing and monitoring"), NULL },
+	{ "language", 'l', 0,
+	  G_OPTION_ARG_STRING, &language_code,
+	  N_("Language to use for stemmer and stop words "
+	     "(ISO 639-1 2 characters code)"),
+	  NULL },
+	{ NULL }
+};
+
+static void
+private_free (gpointer data)
+{
+	TrackerMainPrivate *private;
+
+	private = data;
+
+	if (private->processor) {
+		g_object_unref (private->processor);
+	}
+
+	g_free (private->sys_tmp_dir);
+	g_free (private->user_data_dir);
+	g_free (private->data_dir);
+
+	g_free (private->log_filename);
+
+	g_main_loop_unref (private->main_loop);
+
+	g_free (private);
+}
+
+static gchar *
+get_lock_file (void)
+{
+	TrackerMainPrivate *private;
+	gchar		   *lock_filename;
+	gchar		   *filename;
+
+	private = g_static_private_get (&private_key);
+
+	filename = g_strconcat (g_get_user_name (), "_tracker_lock", NULL);
+
+	lock_filename = g_build_filename (private->sys_tmp_dir,
+					  filename,
+					  NULL);
+	g_free (filename);
+
+	return lock_filename;
+}
+
+static TrackerRunningLevel
+check_runtime_level (TrackerConfig *config,
+		     TrackerHal    *hal)
+{
+	TrackerRunningLevel  runlevel;
+	gchar		    *lock_file;
+	gboolean	     use_nfs;
+	gint		     fd;
+
+	g_message ("Checking instances running...");
+
+	if (!tracker_config_get_enable_indexing (config)) {
+		g_message ("Indexing disabled, running in read-only mode");
+		return TRACKER_RUNNING_READ_ONLY;
+	}
+
+	use_nfs = tracker_config_get_nfs_locking (config);
+
+	lock_file = get_lock_file ();
+	fd = g_open (lock_file, O_RDWR | O_CREAT, 0640);
+
+	if (fd == -1) {
+		const gchar *error_string;
+
+		error_string = g_strerror (errno);
+		g_critical ("Can not open or create lock file:'%s', %s",
+			    lock_file,
+			    error_string);
+		g_free (lock_file);
+
+		return TRACKER_RUNNING_NON_ALLOWED;
+	}
+
+	g_free (lock_file);
+
+	if (lockf (fd, F_TLOCK, 0) < 0) {
+		if (use_nfs) {
+			g_message ("Already running, running in "
+				   "read-only mode (with NFS)");
+			runlevel = TRACKER_RUNNING_READ_ONLY;
+		} else {
+			g_message ("Already running, not allowed "
+				   "multiple instances (without NFS)");
+			runlevel = TRACKER_RUNNING_NON_ALLOWED;
+		}
+	} else {
+		g_message ("This is the first/main instance");
+
+		runlevel = TRACKER_RUNNING_MAIN_INSTANCE;
+
+#ifdef HAVE_HAL
+		if (!tracker_hal_get_battery_exists (hal) ||
+		    !tracker_hal_get_battery_in_use (hal)) {
+			return TRACKER_RUNNING_MAIN_INSTANCE;
+		}
+
+		if (!tracker_status_get_is_first_time_index () &&
+		    tracker_config_get_disable_indexing_on_battery (config)) {
+			g_message ("Battery in use");
+			g_message ("Config is set to not index on battery");
+			g_message ("Running in read only mode");
+			runlevel = TRACKER_RUNNING_READ_ONLY;
+		}
+
+		/* Special case first time situation which are
+		 * overwritten by the config option to disable or not
+		 * indexing on battery initially.
+		 */
+		if (tracker_status_get_is_first_time_index () &&
+		    tracker_config_get_disable_indexing_on_battery_init (config)) {
+			g_message ("Battery in use & reindex is needed");
+			g_message ("Config is set to not index on battery for initial index");
+			g_message ("Running in read only mode");
+			runlevel = TRACKER_RUNNING_READ_ONLY;
+		}
+#endif /* HAVE_HAL */
+	}
+
+	return runlevel;
+}
+
+static void
+log_option_list (GSList      *list,
+		 const gchar *str)
+{
+	GSList *l;
+
+	g_message ("%s:", str);
+
+	if (!list) {
+		g_message ("  DEFAULT");
+		return;
+	}
+
+	for (l = list; l; l = l->next) {
+		g_message ("  %s", (gchar*) l->data);
+	}
+}
+
+static void
+sanity_check_option_values (TrackerConfig *config)
+{
+	g_message ("General options:");
+	g_message ("  Initial sleep  ........................  %d (seconds)",
+		   tracker_config_get_initial_sleep (config));
+	g_message ("  Verbosity  ............................  %d",
+		   tracker_config_get_verbosity (config));
+	g_message ("  Low memory mode  ......................  %s",
+		   tracker_config_get_low_memory_mode (config) ? "yes" : "no");
+
+
+	g_message ("Daemon options:");
+	g_message ("  Throttle level  .......................  %d",
+		   tracker_config_get_throttle (config));
+	g_message ("  Indexing enabled	.....................  %s",
+		   tracker_config_get_enable_indexing (config) ? "yes" : "no");
+	g_message ("  Monitoring enabled  ...................  %s",
+		   tracker_config_get_enable_watches (config) ? "yes" : "no");
+
+	log_option_list (tracker_config_get_watch_directory_roots (config),
+			 "Monitor directories included");
+	log_option_list (tracker_config_get_no_watch_directory_roots (config),
+			 "Monitor directories excluded");
+	log_option_list (tracker_config_get_crawl_directory_roots (config),
+			 "Crawling directories");
+	log_option_list (tracker_config_get_no_index_file_types (config),
+			 "File types excluded from indexing");
+	log_option_list (tracker_config_get_disabled_modules (config),
+			 "Disabled modules");
+}
+
+static gboolean
+shutdown_timeout_cb (gpointer user_data)
+{
+	g_critical ("Could not exit in a timely fashion - terminating...");
+	exit (EXIT_FAILURE);
+
+	return FALSE;
+}
+
+static void
+signal_handler (gint signo)
+{
+	static gboolean in_loop = FALSE;
+
+	/* Die if we get re-entrant signals handler calls */
+	if (in_loop) {
+		exit (EXIT_FAILURE);
+	}
+
+	switch (signo) {
+	case SIGSEGV:
+		/* We are screwed if we get this so exit immediately! */
+		exit (EXIT_FAILURE);
+
+	case SIGBUS:
+	case SIGILL:
+	case SIGFPE:
+	case SIGPIPE:
+	case SIGABRT:
+	case SIGTERM:
+	case SIGINT:
+		in_loop = TRUE;
+		tracker_shutdown ();
+
+	default:
+		if (g_strsignal (signo)) {
+			g_message ("Received signal:%d->'%s'",
+				   signo,
+				   g_strsignal (signo));
+		}
+		break;
+	}
+}
+
+static void
+initialize_signal_handler (void)
+{
+#ifndef G_OS_WIN32
+	struct sigaction   act;
+	sigset_t	   empty_mask;
+
+	sigemptyset (&empty_mask);
+	act.sa_handler = signal_handler;
+	act.sa_mask    = empty_mask;
+	act.sa_flags   = 0;
+
+	sigaction (SIGTERM, &act, NULL);
+	sigaction (SIGILL,  &act, NULL);
+	sigaction (SIGBUS,  &act, NULL);
+	sigaction (SIGFPE,  &act, NULL);
+	sigaction (SIGHUP,  &act, NULL);
+	sigaction (SIGSEGV, &act, NULL);
+	sigaction (SIGABRT, &act, NULL);
+	sigaction (SIGUSR1, &act, NULL);
+	sigaction (SIGINT,  &act, NULL);
+#endif /* G_OS_WIN32 */
+}
+
+static void
+initialize_locations (void)
+{
+	TrackerMainPrivate *private;
+	gchar		   *filename;
+
+	private = g_static_private_get (&private_key);
+
+	/* Public locations */
+	private->user_data_dir =
+		g_build_filename (g_get_user_data_dir (),
+				  "tracker",
+				  "data",
+				  NULL);
+
+	private->data_dir =
+		g_build_filename (g_get_user_cache_dir (),
+				  "tracker",
+				  NULL);
+
+	filename = g_strdup_printf ("tracker-%s", g_get_user_name ());
+	private->sys_tmp_dir =
+		g_build_filename (g_get_tmp_dir (),
+				  filename,
+				  NULL);
+	g_free (filename);
+
+	/* Private locations */
+	private->log_filename =
+		g_build_filename (g_get_user_data_dir (),
+				  "tracker",
+				  "trackerd.log",
+				  NULL);
+}
+
+static void
+initialize_directories (void)
+{
+	TrackerMainPrivate *private;
+	gchar		   *filename;
+
+	private = g_static_private_get (&private_key);
+
+	/* NOTE: We don't create the database directories here, the
+	 * tracker-db-manager does that for us.
+	 */
+
+	g_message ("Checking directory exists:'%s'", private->user_data_dir);
+	g_mkdir_with_parents (private->user_data_dir, 00755);
+
+	g_message ("Checking directory exists:'%s'", private->data_dir);
+	g_mkdir_with_parents (private->data_dir, 00755);
+
+	/* Remove old tracker dirs */
+	filename = g_build_filename (g_get_home_dir (), ".Tracker", NULL);
+
+	if (g_file_test (filename, G_FILE_TEST_EXISTS)) {
+		tracker_path_remove (filename);
+	}
+
+	g_free (filename);
+
+	/* Remove database if we are reindexing */
+	filename = g_build_filename (private->sys_tmp_dir, "Attachments", NULL);
+	g_mkdir_with_parents (filename, 00700);
+	g_free (filename);
+
+	/* Remove existing log files */
+	tracker_file_unlink (private->log_filename);
+}
+
+static gboolean
+initialize_databases (void)
+{
+	/*
+	 * Create SQLite databases
+	 */
+	if (!tracker_status_get_is_readonly () && force_reindex) {
+		TrackerDBInterface *iface;
+
+		tracker_status_set_is_first_time_index (TRUE);
+
+		/* Reset stats for embedded services if they are being reindexed */
+
+		/* Here it doesn't matter which one we ask, as long as it has common.db
+		 * attached. The service ones are cached connections, so we can use
+		 * those instead of asking for an individual-file connection (like what
+		 * the original code had) */
+
+		/* iface = tracker_db_manager_get_db_interfaceX (TRACKER_DB_COMMON); */
+
+		iface = tracker_db_manager_get_db_interface_by_service (TRACKER_DB_FOR_FILE_SERVICE);
+
+		g_message ("*** DELETING STATS *** ");
+		tracker_db_exec_no_reply (iface,
+					  "update ServiceTypes set TypeCount = 0 where Embedded = 1");
+
+	}
+
+	/* Check db integrity if not previously shut down cleanly */
+	if (!tracker_status_get_is_readonly () &&
+	    !tracker_status_get_is_first_time_index () &&
+	    tracker_db_get_option_int ("IntegrityCheck") == 1) {
+		g_message ("Performing integrity check as the daemon was not shutdown cleanly");
+		/* FIXME: Finish */
+	}
+
+	if (!tracker_status_get_is_readonly ()) {
+		tracker_db_set_option_int ("IntegrityCheck", 1);
+	}
+
+	if (tracker_status_get_is_first_time_index ()) {
+		tracker_db_set_option_int ("InitialIndex", 1);
+	}
+
+	return TRUE;
+}
+
+
+static void
+shutdown_indexer (void)
+{
+}
+
+static void
+shutdown_databases (void)
+{
+	/* Reset integrity status as threads have closed cleanly */
+	tracker_db_set_option_int ("IntegrityCheck", 0);
+}
+
+static void
+shutdown_locations (void)
+{
+	/* Nothing to do here, this is done by the private free func */
+}
+
+static void
+shutdown_directories (void)
+{
+	TrackerMainPrivate *private;
+
+	private = g_static_private_get (&private_key);
+
+	/* If we are reindexing, just remove the databases */
+	if (private->reindex_on_shutdown) {
+		tracker_db_manager_remove_all ();
+	}
+}
+
+#ifdef HAVE_HAL
+
+static void
+set_up_throttle (TrackerHal    *hal,
+		 TrackerConfig *config)
+{
+	gint throttle;
+
+	/* If on a laptop battery and the throttling is default (i.e.
+	 * 0), then set the throttle to be higher so we don't kill
+	 * the laptop battery.
+	 */
+	throttle = tracker_config_get_throttle (config);
+
+	if (tracker_hal_get_battery_in_use (hal)) {
+		g_message ("We are running on battery");
+
+		if (throttle == THROTTLE_DEFAULT) {
+			tracker_config_set_throttle (config,
+						     THROTTLE_DEFAULT_ON_BATTERY);
+			g_message ("Setting throttle from %d to %d",
+				   throttle,
+				   THROTTLE_DEFAULT_ON_BATTERY);
+		} else {
+			g_message ("Not setting throttle, it is currently set to %d",
+				   throttle);
+		}
+	} else {
+		g_message ("We are not running on battery");
+
+		if (throttle == THROTTLE_DEFAULT_ON_BATTERY) {
+			tracker_config_set_throttle (config,
+						     THROTTLE_DEFAULT);
+			g_message ("Setting throttle from %d to %d",
+				   throttle,
+				   THROTTLE_DEFAULT);
+		} else {
+			g_message ("Not setting throttle, it is currently set to %d",
+				   throttle);
+		}
+	}
+}
+
+static void
+notify_battery_in_use_cb (GObject *gobject,
+			  GParamSpec *arg1,
+			  gpointer user_data)
+{
+	set_up_throttle (TRACKER_HAL (gobject),
+			 TRACKER_CONFIG (user_data));
+}
+
+#endif /* HAVE_HAL */
+
+static gboolean
+start_cb (gpointer user_data)
+{
+	TrackerMainPrivate *private;
+
+	if (!tracker_status_get_is_running ()) {
+		return FALSE;
+	}
+
+	private = g_static_private_get (&private_key);
+
+	if (private->processor) {
+		tracker_processor_start (private->processor);
+	}
+
+	return FALSE;
+}
+
+gint
+main (gint argc, gchar *argv[])
+{
+	GOptionContext		   *context = NULL;
+	GOptionGroup		   *group;
+	GError			   *error = NULL;
+	TrackerMainPrivate	   *private;
+	TrackerConfig		   *config;
+	TrackerLanguage		   *language;
+	TrackerHal		   *hal;
+	TrackerDBIndex		   *file_index;
+	TrackerDBIndex		   *file_update_index;
+	TrackerDBIndex		   *email_index;
+	TrackerRunningLevel	    runtime_level;
+	TrackerDBManagerFlags	    flags = 0;
+	TrackerDBIndexManagerFlags  index_flags = 0;
+	gboolean		    is_first_time_index;
+
+	g_type_init ();
+
+	private = g_new0 (TrackerMainPrivate, 1);
+	g_static_private_set (&private_key,
+			      private,
+			      private_free);
+
+	if (!g_thread_supported ())
+		g_thread_init (NULL);
+
+	dbus_g_thread_init ();
+
+	setlocale (LC_ALL, "");
+
+	bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+	textdomain (GETTEXT_PACKAGE);
+
+	/* Set timezone info */
+	tzset ();
+
+	/* Translators: this messagge will apper immediately after the
+	 * usage string - Usage: COMMAND <THIS_MESSAGE>
+	 */
+	context = g_option_context_new (_("- start the tracker daemon"));
+
+	/* Daemon group */
+	group = g_option_group_new ("daemon",
+				    _("Daemon Options"),
+				    _("Show daemon options"),
+				    NULL,
+				    NULL);
+	g_option_group_add_entries (group, entries_daemon);
+	g_option_context_add_group (context, group);
+
+	/* Indexer group */
+	group = g_option_group_new ("indexer",
+				    _("Indexer Options"),
+				    _("Show indexer options"),
+				    NULL,
+				    NULL);
+	g_option_group_add_entries (group, entries_indexer);
+	g_option_context_add_group (context, group);
+
+	g_option_context_parse (context, &argc, &argv, &error);
+	g_option_context_free (context);
+
+	if (error) {
+		g_printerr ("Invalid arguments, %s\n", error->message);
+		g_clear_error (&error);
+		return EXIT_FAILURE;
+	}
+
+	/* Print information */
+	g_print ("\n" ABOUT "\n" LICENSE "\n");
+	g_print ("Initializing trackerd...\n");
+
+	initialize_signal_handler ();
+
+	/* Check XDG spec locations XDG_DATA_HOME _MUST_ be writable. */
+	if (!tracker_env_check_xdg_dirs ()) {
+		return EXIT_FAILURE;
+	}
+
+	/* Set IO priority */
+	tracker_ioprio_init ();
+
+	/* nice() uses attribute "warn_unused_result" and so complains
+	 * if we do not check its returned value. But it seems that
+	 * since glibc 2.2.4, nice() can return -1 on a successful
+	 * call so we have to check value of errno too. Stupid...
+	 */
+	if (nice (19) == -1 && errno) {
+		const gchar *str;
+
+		str = g_strerror (errno);
+		g_message ("Couldn't set nice value to 19, %s",
+			   str ? str : "no error given");
+	}
+
+	/* This makes sure we have all the locations like the data
+	 * dir, user data dir, etc all configured.
+	 *
+	 * The initialize_directories() function makes sure everything
+	 * exists physically and/or is reset depending on various
+	 * options (like if we reindex, we remove the data dir).
+	 */
+	initialize_locations ();
+
+	/* Initialize major subsystems */
+	config = tracker_config_new ();
+	language = tracker_language_new (config);
+
+#ifdef HAVE_HAL
+	hal = tracker_hal_new ();
+
+	g_signal_connect (hal, "notify::battery-in-use",
+			  G_CALLBACK (notify_battery_in_use_cb),
+			  config);
+
+	set_up_throttle (hal, config);
+#endif /* HAVE_HAL */
+
+	/* Daemon command line arguments */
+	if (verbosity > -1) {
+		tracker_config_set_verbosity (config, verbosity);
+	}
+
+	if (initial_sleep > -1) {
+		tracker_config_set_initial_sleep (config, initial_sleep);
+	}
+
+	if (low_memory) {
+		tracker_config_set_low_memory_mode (config, TRUE);
+	}
+
+	if (monitors_to_exclude) {
+		tracker_config_add_no_watch_directory_roots (config, monitors_to_exclude);
+	}
+
+	if (monitors_to_include) {
+		tracker_config_add_watch_directory_roots (config, monitors_to_include);
+	}
+
+	if (crawl_dirs) {
+		tracker_config_add_crawl_directory_roots (config, crawl_dirs);
+	}
+
+	if (disable_modules) {
+		tracker_config_add_disabled_modules (config, disable_modules);
+	}
+
+	/* Indexer command line arguments */
+	if (disable_indexing) {
+		tracker_config_set_enable_indexing (config, FALSE);
+	}
+
+	if (language_code) {
+		tracker_config_set_language (config, language_code);
+	}
+
+	initialize_directories ();
+
+	/* Initialize other subsystems */
+	tracker_status_init (config);
+
+	tracker_log_init (private->log_filename, tracker_config_get_verbosity (config));
+	g_print ("Starting log:\n  File:'%s'\n", private->log_filename);
+
+	sanity_check_option_values (config);
+
+	tracker_nfs_lock_init (tracker_config_get_nfs_locking (config));
+
+	if (!tracker_dbus_init (config)) {
+		return EXIT_FAILURE;
+	}
+
+	tracker_module_config_init ();
+
+	flags |= TRACKER_DB_MANAGER_REMOVE_CACHE;
+	index_flags |= TRACKER_DB_INDEX_MANAGER_READONLY;
+
+	if (force_reindex) {
+		flags |= TRACKER_DB_MANAGER_FORCE_REINDEX;
+		index_flags |= TRACKER_DB_INDEX_MANAGER_FORCE_REINDEX;
+	}
+
+	if (tracker_config_get_low_memory_mode (config)) {
+		flags |= TRACKER_DB_MANAGER_LOW_MEMORY_MODE;
+	}
+
+	tracker_db_manager_init (flags, &is_first_time_index);
+	tracker_status_set_is_first_time_index (is_first_time_index);
+
+	if (!tracker_db_index_manager_init (index_flags,
+					    tracker_config_get_min_bucket_count (config),
+					    tracker_config_get_max_bucket_count (config))) {
+		return EXIT_FAILURE;
+	}
+
+	/*
+	 * Check instances running
+	 */
+	runtime_level = check_runtime_level (config, hal);
+
+	switch (runtime_level) {
+	case TRACKER_RUNNING_NON_ALLOWED:
+		return EXIT_FAILURE;
+
+	case TRACKER_RUNNING_READ_ONLY:
+		tracker_status_set_is_readonly (TRUE);
+		break;
+
+	case TRACKER_RUNNING_MAIN_INSTANCE:
+		tracker_status_set_is_readonly (FALSE);
+		break;
+	}
+
+	if (!initialize_databases ()) {
+		return EXIT_FAILURE;
+	}
+
+	file_index = tracker_db_index_manager_get_index (TRACKER_DB_INDEX_FILE);
+	file_update_index = tracker_db_index_manager_get_index (TRACKER_DB_INDEX_FILE_UPDATE);
+	email_index = tracker_db_index_manager_get_index (TRACKER_DB_INDEX_EMAIL);
+
+	if (!TRACKER_IS_DB_INDEX (file_index) ||
+	    !TRACKER_IS_DB_INDEX (file_update_index) ||
+	    !TRACKER_IS_DB_INDEX (email_index)) {
+		g_critical ("Could not create indexer for all indexes (file, file-update, email)");
+		return EXIT_FAILURE;
+	}
+
+	tracker_db_init (config, language, file_index, email_index);
+	tracker_xesam_manager_init ();
+
+	private->processor = tracker_processor_new (config, hal);
+
+	/* Make Tracker available for introspection */
+	if (!tracker_dbus_register_objects (config,
+					    language,
+					    file_index,
+					    email_index,
+					    private->processor)) {
+		return EXIT_FAILURE;
+	}
+
+	g_message ("Waiting for DBus requests...");
+
+	/* Set our status as running, if this is FALSE, threads stop
+	 * doing what they do and shutdown.
+	 */
+	tracker_status_set_is_running (TRUE);
+
+	if (!tracker_status_get_is_readonly ()) {
+		gint seconds;
+
+		seconds = tracker_config_get_initial_sleep (config);
+
+		if (seconds > 0) {
+			g_message ("Waiting %d seconds before starting",
+				   seconds);
+			g_timeout_add (seconds * 1000, start_cb, NULL);
+		} else {
+			g_idle_add (start_cb, NULL);
+		}
+	} else {
+		/* We set the state here because it is not set in the
+		 * processor otherwise.
+		 */
+		g_message ("Running in read-only mode, not starting crawler/indexing");
+		tracker_status_set_and_signal (TRACKER_STATUS_IDLE);
+	}
+
+	if (tracker_status_get_is_running ()) {
+		private->main_loop = g_main_loop_new (NULL, FALSE);
+		g_main_loop_run (private->main_loop);
+	}
+
+#if 0
+	/* We can block on this since we are likely to block on
+	 * shutting down otherwise anyway.
+	 */
+	org_freedesktop_Tracker_Indexer_pause_for_duration (tracker_dbus_indexer_get_proxy (),
+							    2,
+							    NULL);
+#endif
+
+	/*
+	 * Shutdown the daemon
+	 */
+	g_message ("Shutdown started");
+
+	tracker_status_set_and_signal (TRACKER_STATUS_SHUTDOWN);
+
+	g_timeout_add_full (G_PRIORITY_LOW, 5000, shutdown_timeout_cb, NULL, NULL);
+
+	g_message ("Waiting for indexer to finish");
+	org_freedesktop_Tracker_Indexer_shutdown (tracker_dbus_indexer_get_proxy (), &error);
+
+	if (error) {
+		g_message ("Could not shutdown the indexer, %s", error->message);
+		g_message ("Continuing anyway...");
+		g_error_free (error);
+	}
+
+	g_message ("Cleaning up");
+	if (private->processor) {
+		/* We do this instead of let the private data free
+		 * itself later so we can clean up references to this
+		 * elsewhere.
+		 */
+		g_object_unref (private->processor);
+		private->processor = NULL;
+	}
+
+	shutdown_indexer ();
+	shutdown_databases ();
+	shutdown_directories ();
+
+	/* Shutdown major subsystems */
+	tracker_xesam_manager_shutdown ();
+	tracker_dbus_shutdown ();
+	tracker_db_manager_shutdown ();
+	tracker_db_index_manager_shutdown ();
+	tracker_db_shutdown ();
+	tracker_module_config_shutdown ();
+	tracker_nfs_lock_shutdown ();
+	tracker_status_shutdown ();
+	tracker_log_shutdown ();
+
+#ifdef HAVE_HAL
+	g_signal_handlers_disconnect_by_func (hal,
+					      notify_battery_in_use_cb,
+					      config);
+
+	g_object_unref (hal);
+#endif /* HAVE_HAL */
+
+	g_object_unref (language);
+	g_object_unref (config);
+
+	shutdown_locations ();
+
+	g_print ("\nOK\n\n");
+
+	return EXIT_SUCCESS;
+}
+
+void
+tracker_shutdown (void)
+{
+	TrackerMainPrivate *private;
+
+	private = g_static_private_get (&private_key);
+
+	tracker_status_set_is_running (FALSE);
+
+	tracker_processor_stop (private->processor);
+
+	g_main_loop_quit (private->main_loop);
+}
+
+const gchar *
+tracker_get_sys_tmp_dir (void)
+{
+	TrackerMainPrivate *private;
+
+	private = g_static_private_get (&private_key);
+
+	return private->sys_tmp_dir;
+}
+
+void
+tracker_set_reindex_on_shutdown (gboolean value)
+{
+	TrackerMainPrivate *private;
+
+	private = g_static_private_get (&private_key);
+
+	private->reindex_on_shutdown = value;
+}

Added: trunk/src/trackerd/tracker-main.h
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-main.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,38 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2007, Michal Pryc (Michal Pryc Sun Com)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKERD_MAIN_H__
+#define __TRACKERD_MAIN_H__
+
+#include "config.h"
+
+G_BEGIN_DECLS
+
+void	     tracker_shutdown		     (void);
+
+const gchar *tracker_get_sys_tmp_dir	     (void);
+
+void	     tracker_set_reindex_on_shutdown (gboolean value);
+
+G_END_DECLS
+
+#endif /* __TRACKERD_MAIN_H__ */

Added: trunk/src/trackerd/tracker-marshal-main.c
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-marshal-main.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,2 @@
+#include "tracker-marshal.h"
+#include "tracker-marshal.c"

Added: trunk/src/trackerd/tracker-marshal.list
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-marshal.list	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,18 @@
+VOID:STRING,UINT,UINT,UINT,UINT
+VOID:STRING,STRING,INT,INT,INT,DOUBLE
+VOID:STRING,STRING,STRING
+VOID:STRING,BOOLEAN,BOOLEAN,BOOLEAN,BOOLEAN,BOOLEAN,BOOLEAN
+VOID:STRING,OBJECT,BOOLEAN
+VOID:STRING,OBJECT,OBJECT,BOOLEAN
+VOID:STRING,OBJECT
+VOID:BOXED
+
+# XESAM signals -- HitsRemoved, HitsModified
+VOID:STRING,BOXED
+
+# XESAM signals -- HitsAdded
+VOID:STRING,UINT
+
+# Indexer signals
+VOID:DOUBLE,UINT,BOOL
+VOID:DOUBLE,STRING,UINT,UINT

Added: trunk/src/trackerd/tracker-metadata.c
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-metadata.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,1081 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libtracker-common/tracker-dbus.h>
+#include <libtracker-common/tracker-log.h>
+#include <libtracker-common/tracker-field-data.h>
+#include <libtracker-common/tracker-utils.h>
+#include <libtracker-common/tracker-type-utils.h>
+
+#include <libtracker-db/tracker-db-dbus.h>
+#include <libtracker-db/tracker-db-index.h>
+#include <libtracker-db/tracker-db-manager.h>
+
+#include "tracker-indexer-client.h"
+#include "tracker-dbus.h"
+#include "tracker-metadata.h"
+#include "tracker-db.h"
+#include "tracker-marshal.h"
+
+#include "tracker-rdf-query.h"
+
+#define DEFAULT_METADATA_MAX_HITS 1024
+
+G_DEFINE_TYPE(TrackerMetadata, tracker_metadata, G_TYPE_OBJECT)
+
+static void
+tracker_metadata_class_init (TrackerMetadataClass *klass)
+{
+}
+
+static void
+tracker_metadata_init (TrackerMetadata *object)
+{
+}
+
+TrackerMetadata *
+tracker_metadata_new (void)
+{
+	return g_object_new (TRACKER_TYPE_METADATA, NULL);
+}
+
+/*
+ * Functions
+ */
+
+static gint
+metadata_sanity_check_max_hits (gint max_hits)
+{
+	if (max_hits < 1) {
+		return DEFAULT_METADATA_MAX_HITS;
+	}
+
+	return max_hits;
+}
+
+static TrackerFieldData *
+tracker_metadata_add_metadata_field (TrackerDBInterface *iface,
+		    const gchar        *service,
+		    GSList	      **fields,
+		    const gchar        *field_name,
+		    gboolean		is_select,
+		    gboolean		is_condition)
+{
+	TrackerFieldData *field_data;
+	gboolean	  field_exists;
+	GSList		 *l;
+
+	field_exists = FALSE;
+	field_data = NULL;
+
+	/* Check if field is already in list */
+	for (l = *fields; l; l = l->next) {
+		const gchar *this_field_name;
+
+		this_field_name = tracker_field_data_get_field_name (l->data);
+		if (!this_field_name) {
+			continue;
+		}
+
+		if (strcasecmp (this_field_name, field_name) == 0) {
+			field_data = l->data;
+			field_exists = TRUE;
+
+			if (is_condition) {
+				tracker_field_data_set_is_condition (field_data, TRUE);
+			}
+
+			break;
+		}
+	}
+
+	if (!field_exists) {
+		field_data = tracker_db_get_metadata_field (iface,
+							    service,
+							    field_name,
+							    g_slist_length (*fields),
+							    is_select,
+							    is_condition);
+		if (field_data) {
+			*fields = g_slist_prepend (*fields, field_data);
+		}
+	}
+
+	return field_data;
+}
+
+void
+tracker_metadata_get (TrackerMetadata	     *object,
+		      const gchar	     *service_type,
+		      const gchar	     *uri,
+		      gchar		    **keys,
+		      DBusGMethodInvocation  *context,
+		      GError		    **error)
+{
+	TrackerDBInterface  *iface;
+	TrackerDBResultSet  *result_set;
+	guint		     request_id;
+	gchar		    *service_id, *service_result;
+	guint		     i;
+	gchar		   **values;
+	GError		    *actual_error = NULL;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (service_type != NULL, context);
+	tracker_dbus_async_return_if_fail (uri != NULL, context);
+	tracker_dbus_async_return_if_fail (keys != NULL, context);
+	tracker_dbus_async_return_if_fail (g_strv_length (keys) > 0, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to get metadata values, "
+				  "service type:'%s'",
+				  service_type);
+
+	if (!tracker_ontology_service_is_valid (service_type)) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "Service '%s' is invalid or has not been implemented yet",
+					     service_type);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	iface = tracker_db_manager_get_db_interface_by_service (service_type);
+
+	service_id = tracker_db_file_get_id_as_string (iface, service_type, uri);
+	if (!service_id) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "Service URI '%s' not found",
+					     uri);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	/* Checking keys */
+	for (i = 0; i < g_strv_length (keys); i++) {
+
+		if (tracker_ontology_get_field_by_name (keys[i]) == NULL) {
+			tracker_dbus_request_failed (request_id,
+						     &actual_error,
+						     "Metadata field '%s' not registered in the system",
+						     uri);
+			dbus_g_method_return_error (context, actual_error);
+			g_error_free (actual_error);
+			return;
+		}
+	}
+
+	/* The parameter service_type can be "Files"
+	 * and the actual service type of the uri "Video"
+	 *
+	 * Note: Does this matter?
+	 */
+	service_result = tracker_db_service_get_by_entity (iface, service_id);
+	if (!service_result) {
+	       g_free (service_id);
+	       tracker_dbus_request_failed (request_id,
+					    &actual_error,
+					    "Service type can not be found for entity '%s'",
+					    uri);
+	       dbus_g_method_return_error (context, actual_error);
+	       g_error_free (actual_error);
+	       return;
+	}
+
+	result_set = tracker_db_metadata_get_array (iface, service_result, service_id, keys);
+	if (result_set) {
+		values = tracker_dbus_query_result_columns_to_strv (result_set, -1, -1, TRUE);
+		g_object_unref (result_set);
+	} else {
+		values = NULL;
+	}
+
+	if (!values) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "No metadata information was available");
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+	}
+
+	dbus_g_method_return (context, values);
+	g_strfreev (values);
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_metadata_set (TrackerMetadata	     *object,
+		      const gchar	     *service_type,
+		      const gchar	     *uri,
+		      gchar		    **keys,
+		      gchar		    **values,
+		      DBusGMethodInvocation  *context,
+		      GError		    **error)
+{
+	TrackerDBInterface *iface;
+	guint		    request_id;
+	gchar		   *service_id;
+	guint		    i;
+	GError		   *actual_error = NULL;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (service_type != NULL, context);
+	tracker_dbus_async_return_if_fail (keys != NULL, context);
+	tracker_dbus_async_return_if_fail (values != NULL, context);
+	tracker_dbus_async_return_if_fail (g_strv_length (keys) > 0, context);
+	tracker_dbus_async_return_if_fail (g_strv_length (values) > 0, context);
+	tracker_dbus_async_return_if_fail (g_strv_length (keys) == g_strv_length (values), context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to set metadata keys, "
+				  "service type:'%s' uri:'%s'",
+				  service_type, uri);
+
+	if (!tracker_ontology_service_is_valid (service_type)) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "Service_Type '%s' is invalid or has not been implemented yet",
+					     service_type);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	iface = tracker_db_manager_get_db_interface_by_service (service_type);
+
+	/* Check the uri exists, so we dont start the indexer in vain */
+	service_id = tracker_db_file_get_id_as_string (iface, service_type, uri);
+	if (!service_id) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "Service URI '%s' not found",
+					     uri);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	/* Checking keys */
+	for (i = 0; i < g_strv_length (keys); i++) {
+		TrackerField *field_def;
+		gchar	    **value;
+
+		field_def = tracker_ontology_get_field_by_name (keys[i]);
+
+		if (field_def == NULL) {
+			tracker_dbus_request_failed (request_id,
+						     &actual_error,
+						     "Metadata field '%s' not registered in the system",
+						     keys[i]);
+			dbus_g_method_return_error (context, actual_error);
+			g_error_free (actual_error);
+			return;
+		}
+
+		if (tracker_field_get_embedded (field_def)) {
+			tracker_dbus_request_failed (request_id,
+						     &actual_error,
+						     "Metadata field '%s' cannot be overwritten (is embedded)",
+						     keys[i]);
+			dbus_g_method_return_error (context, actual_error);
+			g_error_free (actual_error);
+			return;
+		}
+
+		value = tracker_string_to_string_list (values[i]);
+		org_freedesktop_Tracker_Indexer_property_set (tracker_dbus_indexer_get_proxy (),
+							      service_type,
+							      uri,
+							      keys[i],
+							      (const gchar **)value,
+							      &actual_error);
+		g_strfreev (value);
+		if (actual_error) {
+			tracker_dbus_request_failed (request_id, &actual_error, NULL);
+			dbus_g_method_return_error (context, actual_error);
+			g_error_free (actual_error);
+			return;
+		}
+
+
+	}
+
+	g_free (service_id);
+
+	/* FIXME: Check return value? */
+
+	dbus_g_method_return (context);
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_metadata_get_type_details (TrackerMetadata	  *object,
+				   const gchar		  *metadata,
+				   DBusGMethodInvocation  *context,
+				   GError		 **error)
+{
+	guint		  request_id;
+	TrackerField	 *def = NULL;
+	TrackerFieldType  field_type;
+	gchar		 *type;
+	gboolean	  is_embedded;
+	gboolean	  is_writable;
+	GError		 *actual_error = NULL;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (metadata != NULL, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to get metadata details, "
+				  "metadata type:'%s'",
+				  metadata);
+
+	def = tracker_ontology_get_field_by_name (metadata);
+	if (!def) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "Metadata name '%s' is invalid or unrecognized",
+					     metadata);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	field_type = tracker_field_get_data_type (def);
+
+	type = g_strdup (tracker_field_type_to_string (field_type));
+	is_embedded = tracker_field_get_embedded (def);
+	is_writable = !tracker_field_get_embedded (def);
+
+	dbus_g_method_return (context, type, is_embedded, is_writable);
+	g_free (type);
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_metadata_get_registered_types (TrackerMetadata	      *object,
+				       const gchar	      *service_type,
+				       DBusGMethodInvocation  *context,
+				       GError		     **error)
+{
+	guint		     request_id;
+	gchar		   **values = NULL;
+	const gchar	    *requested = NULL;
+	GSList		    *registered = NULL;
+	GError		    *actual_error = NULL;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (service_type != NULL, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to get registered metadata types, "
+				  "service_type:'%s'",
+				  service_type);
+
+	if (strcmp (service_type, "*") != 0 &&
+	    !tracker_ontology_service_is_valid (service_type)) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "Service_Type '%s' is invalid or has not been implemented yet",
+					     service_type);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	requested = (strcmp (service_type, "*") == 0 ? NULL : service_type);
+
+	registered = tracker_ontology_get_field_names_registered (requested);
+
+	values = tracker_gslist_to_string_list (registered);
+
+	g_slist_foreach (registered, (GFunc) g_free, NULL);
+	g_slist_free (registered);
+
+	dbus_g_method_return (context, values);
+
+	if (values) {
+		g_strfreev (values);
+	}
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_metadata_get_registered_classes (TrackerMetadata	*object,
+					 DBusGMethodInvocation	*context,
+					 GError		       **error)
+{
+	guint		     request_id;
+	gchar		   **values = NULL;
+	GSList		    *registered = NULL;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to get registered classes");
+
+	registered = tracker_ontology_get_service_names_registered ();
+
+	values = tracker_gslist_to_string_list (registered);
+
+	g_slist_foreach (registered, (GFunc) g_free, NULL);
+	g_slist_free (registered);
+
+	dbus_g_method_return (context, values);
+
+	if (values) {
+		g_strfreev (values);
+	}
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_metadata_get_unique_values (TrackerMetadata	   *object,
+				    const gchar		   *service_type,
+				    gchar		  **fields,
+				    const gchar		   *query_condition,
+				    gboolean		    order_desc,
+				    gint		    offset,
+				    gint		    max_hits,
+				    DBusGMethodInvocation  *context,
+				    GError		  **error)
+{
+	TrackerDBInterface *iface;
+	TrackerDBResultSet *result_set = NULL;
+	guint		    request_id;
+
+	GPtrArray	   *values = NULL;
+	GSList		   *field_list = NULL;
+	gchar		   *str_offset, *str_limit;
+
+	GString		   *sql_select;
+	GString		   *sql_from;
+	GString		   *sql_where;
+	GString		   *sql_order;
+	gchar		   *sql;
+
+	char		   *rdf_where;
+	char		   *rdf_from;
+	GError		   *actual_error = NULL;
+
+	guint		    i;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (service_type != NULL, context);
+	tracker_dbus_async_return_if_fail (fields != NULL, context);
+	tracker_dbus_async_return_if_fail (query_condition != NULL, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to get unique values, "
+				  "service type:'%s', query '%s'",
+				  service_type,
+				  query_condition);
+
+	if (!tracker_ontology_service_is_valid (service_type)) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "Service_Type '%s' is invalid or has not been implemented yet",
+					     service_type);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	iface = tracker_db_manager_get_db_interface_by_service (service_type);
+
+	sql_select = g_string_new ("SELECT DISTINCT ");
+	sql_from   = g_string_new ("\nFROM Services S ");
+	sql_where  = g_string_new ("\nWHERE ");
+	sql_order  = g_string_new ("\nORDER BY ");
+
+	for (i=0;i<g_strv_length(fields);i++) {
+		TrackerFieldData   *def = NULL;
+		def = tracker_metadata_add_metadata_field (iface, service_type, &field_list, fields[i], FALSE, TRUE);
+
+		if (!def) {
+			g_string_free (sql_select, TRUE);
+			g_string_free (sql_from, TRUE);
+			g_string_free (sql_where, TRUE);
+			g_string_free (sql_order, TRUE);
+
+			tracker_dbus_request_failed (request_id,
+						     &actual_error,
+						     "Invalid or non-existant metadata type '%s' specified",
+						     fields[i]);
+			dbus_g_method_return_error (context, actual_error);
+			g_error_free (actual_error);
+			return;
+		}
+
+		if (i) {
+			g_string_append_printf (sql_select, ",");
+			g_string_append_printf (sql_order, ",");
+		}
+
+		g_string_append_printf (sql_select, "%s", tracker_field_data_get_select_field (def));
+		g_string_append_printf (sql_order, " %s %s",
+					tracker_field_data_get_select_field (def),
+					order_desc ? "DESC" : "ASC" );
+
+	}
+
+	tracker_rdf_filter_to_sql (iface, query_condition, service_type,
+				   &field_list, &rdf_from, &rdf_where, &actual_error);
+
+	if (actual_error) {
+
+		g_string_free (sql_select, TRUE);
+		g_string_free (sql_from, TRUE);
+		g_string_free (sql_where, TRUE);
+		g_string_free (sql_order, TRUE);
+
+
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     NULL);
+
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	g_string_append_printf (sql_from, " %s ", rdf_from);
+	g_string_append_printf (sql_where, " %s ", rdf_where);
+
+	g_free (rdf_from);
+	g_free (rdf_where);
+
+	str_offset = tracker_gint_to_string (offset);
+	str_limit = tracker_gint_to_string (metadata_sanity_check_max_hits (max_hits));
+
+	g_string_append_printf (sql_order, " LIMIT %s,%s", str_offset, str_limit);
+
+	sql = g_strconcat (sql_select->str, " ", sql_from->str, " ", sql_where->str, " ", sql_order->str, NULL);
+
+	g_free (str_offset);
+	g_free (str_limit);
+
+	g_string_free (sql_select, TRUE);
+	g_string_free (sql_from, TRUE);
+	g_string_free (sql_where, TRUE);
+	g_string_free (sql_order, TRUE);
+
+	g_slist_foreach (field_list, (GFunc) g_object_unref, NULL);
+	g_slist_free (field_list);
+
+	g_message ("Unique values query executed:\n%s", sql);
+
+	result_set =  tracker_db_interface_execute_query (iface, NULL, sql);
+
+	g_free (sql);
+
+	values = tracker_dbus_query_result_to_ptr_array (result_set);
+
+	dbus_g_method_return (context, values);
+
+	tracker_dbus_results_ptr_array_free (&values);
+
+	if (result_set) {
+		g_object_unref (result_set);
+	}
+
+	tracker_dbus_request_success (request_id);
+
+	return;
+}
+
+void
+tracker_metadata_get_unique_values_with_count (TrackerMetadata	      *object,
+					       const gchar	      *service_type,
+					       gchar		     **fields,
+					       const gchar	      *query_condition,
+					       const gchar	      *count_field,
+					       gboolean		       order_desc,
+					       gint		       offset,
+					       gint		       max_hits,
+					       DBusGMethodInvocation  *context,
+					       GError		     **error)
+{
+	TrackerDBInterface *iface;
+	TrackerDBResultSet *result_set = NULL;
+	guint		    request_id;
+
+	GPtrArray	   *values = NULL;
+	GSList		   *field_list = NULL;
+	gchar		   *str_offset, *str_limit;
+
+	GString		   *sql_select;
+	GString		   *sql_from;
+	GString		   *sql_where;
+	GString		   *sql_order;
+	GString		   *sql_group;
+	gchar		   *sql;
+
+	char		   *rdf_where;
+	char		   *rdf_from;
+	GError		   *actual_error = NULL;
+
+	guint		    i;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (service_type != NULL, context);
+	tracker_dbus_async_return_if_fail (fields != NULL, context);
+	tracker_dbus_async_return_if_fail (query_condition != NULL, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to get unique values, "
+				  "service type:'%s', query '%s'"
+				  "count field :'%s'",
+				  service_type,
+				  query_condition,
+				  count_field);
+
+	if (!tracker_ontology_service_is_valid (service_type)) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "Service_Type '%s' is invalid or has not been implemented yet",
+					     service_type);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	iface = tracker_db_manager_get_db_interface_by_service (service_type);
+
+	sql_select = g_string_new ("SELECT DISTINCT ");
+	sql_from   = g_string_new ("\nFROM Services S ");
+	sql_where  = g_string_new ("\nWHERE ");
+	sql_order  = g_string_new ("\nORDER BY ");
+	sql_group  = g_string_new ("\nGROUP BY ");
+
+
+	for (i=0;i<g_strv_length(fields);i++) {
+		TrackerFieldData   *def = NULL;
+		def = tracker_metadata_add_metadata_field (iface, service_type, &field_list, fields[i], FALSE, TRUE);
+
+		if (!def) {
+			g_string_free (sql_select, TRUE);
+			g_string_free (sql_from, TRUE);
+			g_string_free (sql_where, TRUE);
+			g_string_free (sql_order, TRUE);
+			g_string_free (sql_group, TRUE);
+
+			tracker_dbus_request_failed (request_id,
+						     &actual_error,
+						     "Invalid or non-existant metadata type '%s' specified",
+						     fields[i]);
+			dbus_g_method_return_error (context, actual_error);
+			g_error_free (actual_error);
+			return;
+		}
+
+		if (i) {
+			g_string_append_printf (sql_select, ",");
+			g_string_append_printf (sql_order, ",");
+			g_string_append_printf (sql_group, ",");
+		}
+
+		g_string_append_printf (sql_select, "%s", tracker_field_data_get_select_field (def));
+		g_string_append_printf (sql_order, " %s %s",
+					tracker_field_data_get_select_field (def),
+					order_desc ? "DESC" : "ASC" );
+		g_string_append_printf (sql_group, "%s", tracker_field_data_get_select_field (def));
+
+	}
+
+	if (count_field && !(tracker_is_empty_string (count_field))) {
+		TrackerFieldData   *def = NULL;
+
+		def = tracker_metadata_add_metadata_field (iface, service_type, &field_list, count_field, FALSE, TRUE);
+
+		if (!def) {
+			g_string_free (sql_select, TRUE);
+			g_string_free (sql_from, TRUE);
+			g_string_free (sql_where, TRUE);
+			g_string_free (sql_order, TRUE);
+			g_string_free (sql_group, TRUE);
+
+			tracker_dbus_request_failed (request_id,
+						     &actual_error,
+						     "Invalid or non-existant metadata type '%s' specified",
+						     count_field);
+			dbus_g_method_return_error (context, actual_error);
+			g_error_free (actual_error);
+			return;
+		}
+
+		g_string_append_printf (sql_select, ", COUNT (DISTINCT %s)", tracker_field_data_get_select_field (def));
+	}
+
+	tracker_rdf_filter_to_sql (iface, query_condition, service_type,
+				   &field_list, &rdf_from, &rdf_where, &actual_error);
+
+	if (actual_error) {
+
+		g_string_free (sql_select, TRUE);
+		g_string_free (sql_from, TRUE);
+		g_string_free (sql_where, TRUE);
+		g_string_free (sql_order, TRUE);
+		g_string_free (sql_group, TRUE);
+
+
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     NULL);
+
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	g_string_append_printf (sql_from, " %s ", rdf_from);
+	g_string_append_printf (sql_where, " %s ", rdf_where);
+
+	g_free (rdf_from);
+	g_free (rdf_where);
+
+	str_offset = tracker_gint_to_string (offset);
+	str_limit = tracker_gint_to_string (metadata_sanity_check_max_hits (max_hits));
+
+	g_string_append_printf (sql_order, " LIMIT %s,%s", str_offset, str_limit);
+
+	sql = g_strconcat (sql_select->str, " ",
+			   sql_from->str, " ",
+			   sql_where->str, " ",
+			   sql_group->str, " ",
+			   sql_order->str, NULL);
+
+	g_free (str_offset);
+	g_free (str_limit);
+
+	g_string_free (sql_select, TRUE);
+	g_string_free (sql_from, TRUE);
+	g_string_free (sql_where, TRUE);
+	g_string_free (sql_order, TRUE);
+	g_string_free (sql_group, TRUE);
+
+	g_slist_foreach (field_list, (GFunc) g_object_unref, NULL);
+	g_slist_free (field_list);
+
+	g_message ("Unique values query executed:\n%s", sql);
+
+	result_set =  tracker_db_interface_execute_query (iface, NULL, sql);
+
+	g_free (sql);
+
+	values = tracker_dbus_query_result_to_ptr_array (result_set);
+
+	dbus_g_method_return (context, values);
+
+	tracker_dbus_results_ptr_array_free (&values);
+
+	if (result_set) {
+		g_object_unref (result_set);
+	}
+
+	tracker_dbus_request_success (request_id);
+
+	return;
+}
+
+static gboolean
+is_data_type_numeric (TrackerFieldType type) {
+	return (type == TRACKER_FIELD_TYPE_INTEGER
+		|| type == TRACKER_FIELD_TYPE_DOUBLE);
+}
+
+
+void
+tracker_metadata_get_sum (TrackerMetadata	 *object,
+			  const gchar		 *service_type,
+			  const gchar		 *field,
+			  const gchar		 *query_condition,
+			  DBusGMethodInvocation  *context,
+			  GError		**error)
+{
+	TrackerDBInterface *iface;
+	TrackerDBResultSet *result_set = NULL;
+	guint		    request_id;
+
+	gint		    sum;
+	GSList		   *fields = NULL;
+	TrackerFieldData   *def = NULL;
+	TrackerFieldType    data_type;
+	GString		   *sql_select;
+	GString		   *sql_from;
+	GString		   *sql_where;
+	gchar		   *sql;
+
+	char		   *rdf_where;
+	char		   *rdf_from;
+	GError		   *actual_error = NULL;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (service_type != NULL, context);
+	tracker_dbus_async_return_if_fail (field != NULL, context);
+	tracker_dbus_async_return_if_fail (query_condition != NULL, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to get sum, "
+				  "service type:'%s', field '%s', query '%s'",
+				  service_type,
+				  field,
+				  query_condition);
+
+	if (!tracker_ontology_service_is_valid (service_type)) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "Service '%s' is invalid or has not been implemented yet",
+					     service_type);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	iface = tracker_db_manager_get_db_interface_by_service (service_type);
+
+	sql_select = g_string_new ("SELECT ");
+	sql_from   = g_string_new ("\nFROM Services S ");
+	sql_where  = g_string_new ("\nWHERE ");
+
+	def = tracker_metadata_add_metadata_field (iface, service_type, &fields, field, FALSE, TRUE);
+
+	data_type = tracker_field_data_get_data_type (def);
+	if (!is_data_type_numeric (data_type)) {
+		g_string_free (sql_select, TRUE);
+		g_string_free (sql_from, TRUE);
+		g_string_free (sql_where, TRUE);
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "Cannot sum '%s': this metadata type is not numeric",
+					     field);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+
+	if (!def) {
+		g_string_free (sql_select, TRUE);
+		g_string_free (sql_from, TRUE);
+		g_string_free (sql_where, TRUE);
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "Invalid or non-existant metadata type '%s' specified",
+					     field);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	g_string_append_printf (sql_select, "SUM (%s)", tracker_field_data_get_select_field (def));
+
+	tracker_rdf_filter_to_sql (iface, query_condition, service_type,
+				   &fields, &rdf_from, &rdf_where, &actual_error);
+
+	if (actual_error) {
+
+		g_string_free (sql_select, TRUE);
+		g_string_free (sql_from, TRUE);
+		g_string_free (sql_where, TRUE);
+
+		tracker_dbus_request_failed (request_id, &actual_error, NULL);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	g_string_append_printf (sql_from, " %s ", rdf_from);
+	g_string_append_printf (sql_where, " %s ", rdf_where);
+
+	g_free (rdf_from);
+	g_free (rdf_where);
+
+	sql = g_strconcat (sql_select->str, " ", sql_from->str, " ", sql_where->str, NULL);
+
+	g_string_free (sql_select, TRUE);
+	g_string_free (sql_from, TRUE);
+	g_string_free (sql_where, TRUE);
+
+	g_slist_foreach (fields, (GFunc) g_object_unref, NULL);
+	g_slist_free (fields);
+
+	g_debug ("Sum query executed:\n%s", sql);
+
+	result_set =  tracker_db_interface_execute_query (iface, NULL, sql);
+
+	g_free (sql);
+
+	tracker_db_result_set_get (result_set, 0, &sum, -1);
+
+	if (result_set) {
+		g_object_unref (result_set);
+	}
+
+	dbus_g_method_return (context, sum);
+
+	tracker_dbus_request_success (request_id);
+
+	return;
+}
+
+
+void
+tracker_metadata_get_count (TrackerMetadata	   *object,
+			    const gchar		   *service_type,
+			    const gchar		   *field,
+			    const gchar		   *query_condition,
+			    DBusGMethodInvocation  *context,
+			    GError		  **error)
+{
+	TrackerDBInterface *iface;
+	TrackerDBResultSet *result_set = NULL;
+	guint		    request_id;
+	gint		    count;
+	GSList		   *fields = NULL;
+	TrackerFieldData   *def = NULL;
+
+	GString		   *sql_select;
+	GString		   *sql_from;
+	GString		   *sql_where;
+	gchar		   *sql;
+
+	char		   *rdf_where;
+	char		   *rdf_from;
+	GError		   *actual_error = NULL;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (service_type != NULL, context);
+	tracker_dbus_async_return_if_fail (field != NULL, context);
+	tracker_dbus_async_return_if_fail (query_condition != NULL, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to get count, "
+				  "service type:'%s', field '%s', query '%s'",
+				  service_type,
+				  field,
+				  query_condition);
+
+	if (!tracker_ontology_service_is_valid (service_type)) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "Service '%s' is invalid or has not been implemented yet",
+					     service_type);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	iface = tracker_db_manager_get_db_interface_by_service (service_type);
+
+	sql_select = g_string_new ("SELECT ");
+	sql_from   = g_string_new ("\nFROM Services S ");
+	sql_where  = g_string_new ("\nWHERE ");
+
+	def = tracker_metadata_add_metadata_field (iface, service_type, &fields, field, FALSE, TRUE);
+
+	if (!def) {
+		g_string_free (sql_select, TRUE);
+		g_string_free (sql_from, TRUE);
+		g_string_free (sql_where, TRUE);
+
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     "Invalid or non-existant metadata type '%s' specified",
+					     field);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	g_string_append_printf (sql_select, "COUNT (DISTINCT %s)", tracker_field_data_get_select_field (def));
+
+	tracker_rdf_filter_to_sql (iface, query_condition, service_type,
+				   &fields, &rdf_from, &rdf_where, &actual_error);
+
+	if (actual_error) {
+
+		g_string_free (sql_select, TRUE);
+		g_string_free (sql_from, TRUE);
+		g_string_free (sql_where, TRUE);
+
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     NULL);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	g_string_append_printf (sql_from, " %s ", rdf_from);
+	g_string_append_printf (sql_where, " %s ", rdf_where);
+
+	g_free (rdf_from);
+	g_free (rdf_where);
+
+	sql = g_strconcat (sql_select->str, " ", sql_from->str, " ", sql_where->str, NULL);
+
+	g_string_free (sql_select, TRUE);
+	g_string_free (sql_from, TRUE);
+	g_string_free (sql_where, TRUE);
+
+	g_slist_foreach (fields, (GFunc) g_object_unref, NULL);
+	g_slist_free (fields);
+
+	g_message ("Count query executed:\n%s", sql);
+
+	result_set =  tracker_db_interface_execute_query (iface, NULL, sql);
+
+	g_free (sql);
+
+	tracker_db_result_set_get (result_set, 0, &count, -1);
+
+	if (result_set) {
+		g_object_unref (result_set);
+	}
+
+	dbus_g_method_return (context, count);
+
+	tracker_dbus_request_success (request_id);
+
+	return;
+}
+

Added: trunk/src/trackerd/tracker-metadata.h
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-metadata.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,114 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKERD_METADATA_H__
+#define __TRACKERD_METADATA_H__
+
+#include <glib-object.h>
+
+#include <libtracker-db/tracker-db-index.h>
+
+#define TRACKER_METADATA_SERVICE	 "org.freedesktop.Tracker"
+#define TRACKER_METADATA_PATH		 "/org/freedesktop/Tracker/Metadata"
+#define TRACKER_METADATA_INTERFACE	 "org.freedesktop.Tracker.Metadata"
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_METADATA		 (tracker_metadata_get_type ())
+#define TRACKER_METADATA(object)	 (G_TYPE_CHECK_INSTANCE_CAST ((object), TRACKER_TYPE_METADATA, TrackerMetadata))
+#define TRACKER_METADATA_CLASS(klass)	 (G_TYPE_CHECK_CLASS_CAST ((klass), TRACKER_TYPE_METADATA, TrackerMetadataClass))
+#define TRACKER_IS_METADATA(object)	 (G_TYPE_CHECK_INSTANCE_TYPE ((object), TRACKER_TYPE_METADATA))
+#define TRACKER_IS_METADATA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TRACKER_TYPE_METADATA))
+#define TRACKER_METADATA_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), TRACKER_TYPE_METADATA, TrackerMetadataClass))
+
+typedef struct TrackerMetadata	    TrackerMetadata;
+typedef struct TrackerMetadataClass TrackerMetadataClass;
+
+struct TrackerMetadata {
+	GObject parent;
+};
+
+struct TrackerMetadataClass {
+	GObjectClass parent;
+};
+
+GType		 tracker_metadata_get_type		 (void);
+TrackerMetadata *tracker_metadata_new			 (void);
+void		 tracker_metadata_get			 (TrackerMetadata	 *object,
+							  const gchar		 *service_type,
+							  const gchar		 *uri,
+							  gchar			**keys,
+							  DBusGMethodInvocation  *context,
+							  GError		**error);
+void		 tracker_metadata_set			 (TrackerMetadata	 *object,
+							  const gchar		 *service_type,
+							  const gchar		 *uri,
+							  gchar			**keys,
+							  gchar			**metadata,
+							  DBusGMethodInvocation  *context,
+							  GError		**error);
+void		 tracker_metadata_get_type_details	 (TrackerMetadata	 *object,
+							  const gchar		 *metadata,
+							  DBusGMethodInvocation  *context,
+							  GError		**error);
+void		 tracker_metadata_get_registered_types	 (TrackerMetadata	 *object,
+							  const gchar		 *service_type,
+							  DBusGMethodInvocation  *context,
+							  GError		**error);
+void		 tracker_metadata_get_registered_classes (TrackerMetadata	 *object,
+							  DBusGMethodInvocation  *context,
+							  GError		**error);
+void		 tracker_metadata_get_unique_values	 (TrackerMetadata	 *object,
+							  const gchar		 *service_type,
+							  gchar			**fields,
+							  const gchar		 *query_condition,
+							  gboolean		  order_desc,
+							  gint			  offset,
+							  gint			  max_hits,
+							  DBusGMethodInvocation  *context,
+							  GError		**error);
+void		 tracker_metadata_get_sum		 (TrackerMetadata	 *object,
+							  const gchar		 *service_type,
+							  const gchar		 *field,
+							  const gchar		 *query_condition,
+							  DBusGMethodInvocation  *context,
+							  GError		**error);
+void		 tracker_metadata_get_count		 (TrackerMetadata	 *object,
+							  const gchar		 *service_type,
+							  const gchar		 *field,
+							  const gchar		 *query_condition,
+							  DBusGMethodInvocation  *context,
+							  GError		**error);
+
+void		 tracker_metadata_get_unique_values_with_count (TrackerMetadata        *object,
+								const gchar	       *service_type,
+								gchar		      **fields,
+								const gchar	       *query_condition,
+								const gchar	       *count,
+								gboolean		order_desc,
+								gint			offset,
+								gint			max_hits,
+								DBusGMethodInvocation  *context,
+								GError		      **error);
+
+G_END_DECLS
+
+#endif /* __TRACKERD_METADATA_H__ */

Added: trunk/src/trackerd/tracker-monitor.c
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-monitor.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,1667 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <libtracker-common/tracker-dbus.h>
+#include <libtracker-common/tracker-file-utils.h>
+#include <libtracker-common/tracker-module-config.h>
+
+#ifdef HAVE_INOTIFY
+#include <linux/inotify.h>
+#include <libinotify/libinotify.h>
+#define USE_LIBINOTIFY
+#endif /* HAVE_INOTIFY */
+
+#include "tracker-monitor.h"
+#include "tracker-dbus.h"
+#include "tracker-indexer-client.h"
+#include "tracker-marshal.h"
+#include "tracker-status.h"
+
+#define TRACKER_MONITOR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TRACKER_TYPE_MONITOR, TrackerMonitorPrivate))
+
+/* Each file that a monitor event is received for is added to the
+ * black list with a number. This number is the count for how many
+ * events we have received for that file. If the count is above this
+ * number below, then it is officially black listed and we don't
+ * propagate events up the stack.
+ */
+#define BLACK_LIST_MAX_HITS    5
+
+/* Each file is only on the black list for a certain time before it
+ * is removed. When it is removed, a signal is emitted to force the
+ * indexer to check the file out. This means, for example, if you
+ * download a file, the indexer only indexes it AFTER this timeout
+ * below AFTER it has stopped getting events. The timeout is in
+ * seconds.
+ */
+#define BLACK_LIST_MAX_SECONDS 30
+
+/* When we receive IO monitor events, we pause sending information to
+ * the indexer for a few seconds before continuing. We have to receive
+ * NO events for at least a few seconds before unpausing.
+ */
+#define PAUSE_FOR_IO_SECONDS   5
+
+struct _TrackerMonitorPrivate {
+	TrackerConfig *config;
+
+	GHashTable    *modules;
+
+	gboolean       enabled;
+
+	guint	       black_list_timeout_id;
+	GHashTable    *black_list_count;
+	GHashTable    *black_list_timestamps;
+
+	GType	       monitor_backend;
+
+	guint	       monitor_limit;
+	gboolean       monitor_limit_warned;
+	guint	       monitors_ignored;
+
+	/* For FAM, the _CHANGES_DONE event is not signalled, so we
+	 * have to just use the _CHANGED event instead.
+	 */
+	gboolean       use_changed_event;
+
+	/* Timeout id for pausing when we get IO */
+	guint	       unpause_timeout_id;
+
+#ifdef USE_LIBINOTIFY
+	GHashTable    *event_pairs;
+	GHashTable    *event_time_by_cookie;
+	GHashTable    *event_type_by_cookie;
+
+	guint	       event_check_timeout_id;
+#endif /* USE_LIBINOTIFY */
+};
+
+enum {
+	ITEM_CREATED,
+	ITEM_UPDATED,
+	ITEM_DELETED,
+	ITEM_MOVED,
+	LAST_SIGNAL
+};
+
+enum {
+	PROP_0,
+	PROP_ENABLED
+};
+
+static void	      tracker_monitor_finalize	   (GObject	   *object);
+static void	      tracker_monitor_set_property (GObject	   *object,
+						    guint	    prop_id,
+						    const GValue   *value,
+						    GParamSpec	   *pspec);
+static void	      tracker_monitor_get_property (GObject	   *object,
+						    guint	    prop_id,
+						    GValue	   *value,
+						    GParamSpec	   *pspec);
+static gboolean       black_list_check_items_cb    (gpointer	    data);
+static void	      black_list_print_all	   (TrackerMonitor *monitor);
+static guint	      get_inotify_limit		   (void);
+
+#ifdef USE_LIBINOTIFY
+static INotifyHandle *libinotify_monitor_directory (TrackerMonitor *monitor,
+						    GFile	   *file);
+static void	      libinotify_monitor_cancel    (gpointer	    data);
+#endif /* USE_LIBINOTIFY */
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE(TrackerMonitor, tracker_monitor, G_TYPE_OBJECT)
+
+static void
+tracker_monitor_class_init (TrackerMonitorClass *klass)
+{
+	GObjectClass *object_class;
+
+	object_class = G_OBJECT_CLASS (klass);
+
+	object_class->finalize = tracker_monitor_finalize;
+	object_class->set_property = tracker_monitor_set_property;
+	object_class->get_property = tracker_monitor_get_property;
+
+	signals[ITEM_CREATED] =
+		g_signal_new ("item-created",
+			      G_TYPE_FROM_CLASS (klass),
+			      G_SIGNAL_RUN_LAST,
+			      0,
+			      NULL, NULL,
+			      tracker_marshal_VOID__STRING_OBJECT_BOOLEAN,
+			      G_TYPE_NONE,
+			      3,
+			      G_TYPE_STRING,
+			      G_TYPE_OBJECT,
+			      G_TYPE_BOOLEAN);
+	signals[ITEM_UPDATED] =
+		g_signal_new ("item-updated",
+			      G_TYPE_FROM_CLASS (klass),
+			      G_SIGNAL_RUN_LAST,
+			      0,
+			      NULL, NULL,
+			      tracker_marshal_VOID__STRING_OBJECT_BOOLEAN,
+			      G_TYPE_NONE,
+			      3,
+			      G_TYPE_STRING,
+			      G_TYPE_OBJECT,
+			      G_TYPE_BOOLEAN);
+	signals[ITEM_DELETED] =
+		g_signal_new ("item-deleted",
+			      G_TYPE_FROM_CLASS (klass),
+			      G_SIGNAL_RUN_LAST,
+			      0,
+			      NULL, NULL,
+			      tracker_marshal_VOID__STRING_OBJECT_BOOLEAN,
+			      G_TYPE_NONE,
+			      3,
+			      G_TYPE_STRING,
+			      G_TYPE_OBJECT,
+			      G_TYPE_BOOLEAN);
+	signals[ITEM_MOVED] =
+		g_signal_new ("item-moved",
+			      G_TYPE_FROM_CLASS (klass),
+			      G_SIGNAL_RUN_LAST,
+			      0,
+			      NULL, NULL,
+			      tracker_marshal_VOID__STRING_OBJECT_OBJECT_BOOLEAN,
+			      G_TYPE_NONE,
+			      4,
+			      G_TYPE_STRING,
+			      G_TYPE_OBJECT,
+			      G_TYPE_OBJECT,
+			      G_TYPE_BOOLEAN);
+
+	g_object_class_install_property (object_class,
+					 PROP_ENABLED,
+					 g_param_spec_boolean ("enabled",
+							       "Enabled",
+							       "Enabled",
+							       TRUE,
+							       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+	g_type_class_add_private (object_class, sizeof (TrackerMonitorPrivate));
+}
+
+static void
+tracker_monitor_init (TrackerMonitor *object)
+{
+	TrackerMonitorPrivate *priv;
+	GFile		      *file;
+	GFileMonitor	      *monitor;
+	GList		      *all_modules, *l;
+	const gchar	      *name;
+
+	object->private = TRACKER_MONITOR_GET_PRIVATE (object);
+
+	priv = object->private;
+
+	/* By default we enable monitoring */
+	priv->enabled = TRUE;
+
+	/* For each module we create a hash table for monitors */
+	priv->modules =
+		g_hash_table_new_full (g_str_hash,
+				       g_str_equal,
+				       g_free,
+				       (GDestroyNotify) g_hash_table_unref);
+
+	/* The black list is for files which are CONSTANTLY being
+	 * updated causing a lot of traffic and processing. We add
+	 * these to the black list so we don't keep pushing events up
+	 * the stack. This is black list is not saved, it is per
+	 * session for the daemon only.
+	 */
+	priv->black_list_count =
+		g_hash_table_new_full (g_file_hash,
+				       (GEqualFunc) g_file_equal,
+				       g_object_unref,
+				       NULL);
+	priv->black_list_timestamps =
+		g_hash_table_new_full (g_file_hash,
+				       (GEqualFunc) g_file_equal,
+				       g_object_unref,
+				       NULL);
+
+#ifdef USE_LIBINOTIFY
+	/* We have a hash table with cookies so we can pair up move
+	 * events.
+	 */
+	priv->event_pairs =
+		g_hash_table_new_full (g_direct_hash,
+				       g_direct_equal,
+				       NULL,
+				       g_object_unref);
+	priv->event_time_by_cookie =
+		g_hash_table_new_full (g_direct_hash,
+				       g_direct_equal,
+				       NULL,
+				       NULL);
+	priv->event_type_by_cookie =
+		g_hash_table_new_full (g_direct_hash,
+				       g_direct_equal,
+				       NULL,
+				       NULL);
+#endif /* USE_LIBINOTIFY */
+
+	all_modules = tracker_module_config_get_modules ();
+
+	for (l = all_modules; l; l = l->next) {
+		GHashTable *monitors;
+
+		/* Create monitors table for this module */
+#ifdef USE_LIBINOTIFY
+		monitors =
+			g_hash_table_new_full (g_file_hash,
+					       (GEqualFunc) g_file_equal,
+					       (GDestroyNotify) g_object_unref,
+					       (GDestroyNotify) libinotify_monitor_cancel);
+#else  /* USE_LIBINOTIFY */
+		monitors =
+			g_hash_table_new_full (g_file_hash,
+					       (GEqualFunc) g_file_equal,
+					       (GDestroyNotify) g_object_unref,
+					       (GDestroyNotify) g_file_monitor_cancel);
+#endif /* USE_LIBINOTIFY */
+
+		g_hash_table_insert (priv->modules, g_strdup (l->data), monitors);
+	}
+
+	g_list_free (all_modules);
+
+	/* For the first monitor we get the type and find out if we
+	 * are using inotify, FAM, polling, etc.
+	 */
+	file = g_file_new_for_path (g_get_home_dir ());
+	monitor = g_file_monitor_directory (file,
+					    G_FILE_MONITOR_WATCH_MOUNTS,
+					    NULL,
+					    NULL);
+
+	priv->monitor_backend = G_OBJECT_TYPE (monitor);
+
+	/* We use the name because the type itself is actually
+	 * private and not available publically. Note this is
+	 * subject to change, but unlikely of course.
+	 */
+	name = g_type_name (priv->monitor_backend);
+	if (name) {
+		/* Set limits based on backend... */
+		if (strcmp (name, "GInotifyDirectoryMonitor") == 0) {
+			/* Using inotify */
+			g_message ("Monitor backend is INotify");
+
+			/* Setting limit based on kernel
+			 * settings in /proc...
+			 */
+			priv->monitor_limit = get_inotify_limit ();
+
+			/* We don't use 100% of the monitors, we allow other
+			 * applications to have at least 500 or so to use
+			 * between them selves. This only
+			 * applies to inotify because it is a
+			 * user shared resource.
+			 */
+			priv->monitor_limit -= 500;
+
+			/* Make sure we don't end up with a
+			 * negative maximum.
+			 */
+			priv->monitor_limit = MAX (priv->monitor_limit, 0);
+		}
+		else if (strcmp (name, "GFamDirectoryMonitor") == 0) {
+			/* Using Fam */
+			g_message ("Monitor backend is Fam");
+
+			/* Setting limit to an arbitary limit
+			 * based on testing
+			 */
+			priv->monitor_limit = 400;
+			priv->use_changed_event = TRUE;
+		}
+		else if (strcmp (name, "GFenDirectoryMonitor") == 0) {
+			/* Using Fen, what is this? */
+			g_message ("Monitor backend is Fen");
+
+			/* Guessing limit... */
+			priv->monitor_limit = 8192;
+		}
+		else if (strcmp (name, "GWin32DirectoryMonitor") == 0) {
+			/* Using Windows */
+			g_message ("Monitor backend is Windows");
+
+			/* Guessing limit... */
+			priv->monitor_limit = 8192;
+		}
+		else {
+			/* Unknown */
+			g_warning ("Monitor backend:'%s' is unknown, we have no limits "
+				   "in place because we don't know what we are dealing with!",
+				   name);
+
+			/* Guessing limit... */
+			priv->monitor_limit = 100;
+		}
+	}
+
+	g_message ("Monitor limit is %d", priv->monitor_limit);
+
+	g_file_monitor_cancel (monitor);
+	g_object_unref (monitor);
+	g_object_unref (file);
+}
+
+static void
+tracker_monitor_finalize (GObject *object)
+{
+	TrackerMonitorPrivate *priv;
+
+	priv = TRACKER_MONITOR_GET_PRIVATE (object);
+
+	if (priv->unpause_timeout_id) {
+		g_source_remove (priv->unpause_timeout_id);
+	}
+
+	black_list_print_all (TRACKER_MONITOR (object));
+
+	g_hash_table_unref (priv->black_list_timestamps);
+	g_hash_table_unref (priv->black_list_count);
+
+#ifdef USE_LIBINOTIFY
+	if (priv->event_check_timeout_id) {
+		g_source_remove (priv->event_check_timeout_id);
+	}
+
+	g_hash_table_unref (priv->event_type_by_cookie);
+	g_hash_table_unref (priv->event_time_by_cookie);
+	g_hash_table_unref (priv->event_pairs);
+#endif /* USE_LIBINOTIFY */
+
+	if (priv->black_list_timeout_id) {
+		g_source_remove (priv->black_list_timeout_id);
+	}
+
+	g_hash_table_unref (priv->modules);
+
+	g_object_unref (priv->config);
+
+	G_OBJECT_CLASS (tracker_monitor_parent_class)->finalize (object);
+}
+
+static void
+tracker_monitor_set_property (GObject	   *object,
+			      guint	    prop_id,
+			      const GValue *value,
+			      GParamSpec   *pspec)
+{
+	switch (prop_id) {
+	case PROP_ENABLED:
+		tracker_monitor_set_enabled (TRACKER_MONITOR (object),
+					     g_value_get_boolean (value));
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+	}
+}
+
+static void
+tracker_monitor_get_property (GObject	   *object,
+			      guint	    prop_id,
+			      GValue	   *value,
+			      GParamSpec   *pspec)
+{
+	TrackerMonitorPrivate *priv;
+
+	priv = TRACKER_MONITOR_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_ENABLED:
+		g_value_set_boolean (value, priv->enabled);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+	}
+}
+
+static guint
+get_inotify_limit (void)
+{
+	GError	    *error = NULL;
+	const gchar *filename;
+	gchar	    *contents = NULL;
+	guint	     limit;
+
+	filename = "/proc/sys/fs/inotify/max_user_watches";
+
+	if (!g_file_get_contents (filename,
+				  &contents,
+				  NULL,
+				  &error)) {
+		g_warning ("Couldn't get INotify monitor limit from:'%s', %s",
+			   filename,
+			   error ? error->message : "no error given");
+		g_clear_error (&error);
+
+		/* Setting limit to an arbitary limit */
+		limit = 8192;
+	} else {
+		limit = atoi (contents);
+		g_free (contents);
+	}
+
+	return limit;
+}
+
+static const gchar *
+get_queue_from_gfile (GHashTable *modules,
+		      GFile	 *file)
+{
+	GHashTable  *hash_table;
+	GList	    *all_modules, *l;
+	const gchar *module_name = NULL;
+
+	all_modules = g_hash_table_get_keys (modules);
+
+	for (l = all_modules; l && !module_name; l = l->next) {
+		hash_table = g_hash_table_lookup (modules, l->data);
+
+		if (g_hash_table_lookup (hash_table, file)) {
+			module_name = l->data;
+		}
+	}
+
+	g_list_free (all_modules);
+
+	return module_name;
+}
+
+static const gchar *
+get_module_name_from_gfile (TrackerMonitor *monitor,
+			    GFile	   *file,
+			    gboolean	   *is_directory)
+{
+	const gchar *module_name;
+
+	if (is_directory) {
+		*is_directory = TRUE;
+	}
+
+	/* First try to get the module name from the file, this will
+	 * only work if the event we received is for a directory.
+	 */
+	module_name = get_queue_from_gfile (monitor->private->modules, file);
+	if (!module_name) {
+		GFile *parent;
+
+		/* Second we try to get the module name from the base
+		 * name of the file.
+		 */
+		parent = g_file_get_parent (file);
+		module_name = get_queue_from_gfile (monitor->private->modules, parent);
+
+		if (!module_name) {
+			gchar *child_path;
+			gchar *parent_path;
+
+			child_path = g_file_get_path (file);
+			parent_path = g_file_get_path (parent);
+
+			g_warning ("Could not get module name from GFile (path:'%s' or parent:'%s')",
+				   child_path,
+				   parent_path);
+
+			g_free (parent_path);
+			g_free (child_path);
+
+			return NULL;
+		}
+
+		if (is_directory) {
+			*is_directory = FALSE;
+		}
+	}
+
+	return module_name;
+}
+
+static gboolean
+black_list_check_items_cb (gpointer data)
+{
+	TrackerMonitor *monitor;
+	GHashTableIter	iter;
+	GTimeVal	t;
+	gchar	       *path;
+	gpointer	key;
+	gpointer	value;
+	gsize		seconds;
+	gsize		seconds_now;
+	gsize		seconds_diff;
+	guint		count;
+
+	monitor = data;
+
+	g_get_current_time (&t);
+	seconds_now = t.tv_sec;
+
+	g_hash_table_iter_init (&iter, monitor->private->black_list_timestamps);
+	while (g_hash_table_iter_next (&iter, &key, &value)) {
+		seconds = GPOINTER_TO_SIZE (value);
+		seconds_diff = seconds_now - seconds;
+
+		/* Do absolutely nothing if this file hasn't been
+		 * black listed for at least BLACK_LIST_MAX_SECONDS.
+		 */
+		if (seconds_diff < BLACK_LIST_MAX_SECONDS) {
+			continue;
+		}
+
+		path = g_file_get_path (key);
+		g_debug ("Removing '%s' from black list count", path);
+		g_free (path);
+
+		/* Only signal if count >= BLACK_LIST_MAX_HITS, since
+		 * we have mitigated signals due to spamming, so we
+		 * make sure that we signal the indexer that this
+		 * file needs a check.
+		 */
+		value = g_hash_table_lookup (monitor->private->black_list_count, key);
+		count = GPOINTER_TO_UINT (value);
+
+		if (count >= BLACK_LIST_MAX_HITS) {
+			const gchar *module_name;
+			gboolean     is_directory;
+
+			module_name = get_module_name_from_gfile (data,
+								  key,
+								  &is_directory);
+			if (!module_name) {
+				continue;
+			}
+
+			g_signal_emit (data,
+				       signals[ITEM_CREATED], 0,
+				       module_name,
+				       key,
+				       is_directory);
+		}
+
+		/* Remove from hash tables (i.e. white list it) */
+		g_hash_table_remove (monitor->private->black_list_count, key);
+		g_hash_table_remove (monitor->private->black_list_timestamps, key);
+
+		/* Reset iterators */
+		g_hash_table_iter_init (&iter, monitor->private->black_list_timestamps);
+	}
+
+	/* If the hash tables are empty, don't keep calling this
+	 * callback, this interrupts and wastes battery. Set up the
+	 * timeout again when we need it instead.
+	 */
+	if (g_hash_table_size (monitor->private->black_list_count) < 1) {
+		g_debug ("No further items on the black list, removing check timeout");
+		monitor->private->black_list_timeout_id = 0;
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+black_list_file_check (TrackerMonitor *monitor,
+		       GFile	      *file)
+{
+	gpointer data;
+	guint	 count;
+
+	data = g_hash_table_lookup (monitor->private->black_list_count, file);
+	count = GPOINTER_TO_UINT (data);
+
+	return count >= BLACK_LIST_MAX_HITS;
+}
+
+static void
+black_list_file_increment (TrackerMonitor *monitor,
+			   GFile	  *file)
+{
+	GTimeVal  t;
+	gchar	 *path;
+	gpointer  data;
+	guint	  count;
+
+	data = g_hash_table_lookup (monitor->private->black_list_count, file);
+	count = GPOINTER_TO_UINT (data);
+
+	/* Replace the black listed item with the updated count for
+	 * how many times an event has occurred for this path
+	 */
+	count++;
+	g_get_current_time (&t);
+
+	g_hash_table_replace (monitor->private->black_list_count,
+			      g_object_ref (file),
+			      GUINT_TO_POINTER (count));
+	g_hash_table_replace (monitor->private->black_list_timestamps,
+			      g_object_ref (file),
+			      GSIZE_TO_POINTER (t.tv_sec));
+
+	if (monitor->private->black_list_timeout_id == 0) {
+		monitor->private->black_list_timeout_id =
+			g_timeout_add_seconds (1,
+					       black_list_check_items_cb,
+					       monitor);
+	}
+
+	path = g_file_get_path (file);
+	g_debug ("Adding '%s' to black list count:%d (MAX is %d)",
+		 path,
+		 count,
+		 BLACK_LIST_MAX_HITS);
+	g_free (path);
+}
+
+static void
+black_list_print_all (TrackerMonitor *monitor)
+{
+	GHashTableIter	iter;
+	gchar	       *path;
+	gpointer	key;
+	gpointer	value;
+	guint		count;
+	gboolean	none = TRUE;
+
+	g_message ("Summary of black list: (with >= %d events)",
+		   BLACK_LIST_MAX_HITS);
+
+	g_hash_table_iter_init (&iter, monitor->private->black_list_count);
+	while (g_hash_table_iter_next (&iter, &key, &value)) {
+		count = GPOINTER_TO_UINT (value);
+
+		if (count < BLACK_LIST_MAX_HITS) {
+			continue;
+		}
+
+		none = FALSE;
+
+		path = g_file_get_path (key);
+		g_message ("  %s (%d events)", path, count);
+		g_free (path);
+	}
+
+	if (none) {
+		g_message ("  NONE");
+	}
+}
+
+static void
+indexer_pause_cb (DBusGProxy *proxy,
+		  GError     *error,
+		  gpointer    user_data)
+{
+	if (error) {
+		g_message ("Could not pause the indexer, %s",
+			   error->message);
+	}
+}
+
+static void
+indexer_continue_cb (DBusGProxy *proxy,
+		     GError	*error,
+		     gpointer	 user_data)
+{
+	if (error) {
+		g_message ("Could not continue the indexer, %s",
+			   error->message);
+	}
+}
+
+static gboolean
+unpause_cb (gpointer data)
+{
+	TrackerMonitor *monitor;
+
+	monitor = data;
+
+	monitor->private->unpause_timeout_id = 0;
+	tracker_status_set_is_paused_for_io (FALSE);
+
+	g_message ("Resumming indexing now we have stopped "
+		   "receiving monitor events for %d seconds",
+		   PAUSE_FOR_IO_SECONDS);
+
+	if (!tracker_status_get_is_paused_manually ()) {
+		org_freedesktop_Tracker_Indexer_continue_async (tracker_dbus_indexer_get_proxy (),
+								indexer_continue_cb,
+								NULL);
+	}
+
+	return FALSE;
+}
+
+#ifdef USE_LIBINOTIFY
+
+static gchar *
+libinotify_monitor_event_to_string (guint32 event_type)
+{
+	GString *s;
+
+	s = g_string_new ("");
+
+	if (event_type & IN_ACCESS) {
+		s = g_string_append (s, "IN_ACCESS | ");
+	}
+	if (event_type & IN_MODIFY) {
+		s = g_string_append (s, "IN_MODIFY | ");
+	}
+	if (event_type & IN_ATTRIB) {
+		s = g_string_append (s, "IN_ATTRIB | ");
+	}
+	if (event_type & IN_CLOSE_WRITE) {
+		s = g_string_append (s, "IN_CLOSE_WRITE | ");
+	}
+	if (event_type & IN_CLOSE_NOWRITE) {
+		s = g_string_append (s, "IN_CLOSE_NOWRITE | ");
+	}
+	if (event_type & IN_OPEN) {
+		s = g_string_append (s, "IN_OPEN | ");
+	}
+	if (event_type & IN_MOVED_FROM) {
+		s = g_string_append (s, "IN_MOVED_FROM | ");
+	}
+	if (event_type & IN_MOVED_TO) {
+		s = g_string_append (s, "IN_MOVED_TO | ");
+	}
+	if (event_type & IN_CREATE) {
+		s = g_string_append (s, "IN_CREATE | ");
+	}
+	if (event_type & IN_DELETE) {
+		s = g_string_append (s, "IN_DELETE | ");
+	}
+	if (event_type & IN_DELETE_SELF) {
+		s = g_string_append (s, "IN_DELETE_SELF | ");
+	}
+	if (event_type & IN_MOVE_SELF) {
+		s = g_string_append (s, "IN_MOVE_SELF | ");
+	}
+	if (event_type & IN_UNMOUNT) {
+		s = g_string_append (s, "IN_UNMOUNT | ");
+	}
+	if (event_type & IN_Q_OVERFLOW) {
+		s = g_string_append (s, "IN_Q_OVERFLOW | ");
+	}
+	if (event_type & IN_IGNORED) {
+		s = g_string_append (s, "IN_IGNORED | ");
+	}
+
+	/* helper events */
+	if (event_type & IN_CLOSE) {
+		s = g_string_append (s, "IN_CLOSE* | ");
+	}
+	if (event_type & IN_MOVE) {
+		s = g_string_append (s, "IN_MOVE* | ");
+	}
+
+	/* special flags */
+	if (event_type & IN_MASK_ADD) {
+		s = g_string_append (s, "IN_MASK_ADD^ | ");
+	}
+	if (event_type & IN_ISDIR) {
+		s = g_string_append (s, "IN_ISDIR^ | ");
+	}
+	if (event_type & IN_ONESHOT) {
+		s = g_string_append (s, "IN_ONESHOT^ | ");
+	}
+
+	s->str[s->len - 3] = '\0';
+
+	return g_string_free (s, FALSE);
+}
+
+static gboolean
+libinotify_event_check_timeout_cb (gpointer data)
+{
+	TrackerMonitor *monitor;
+	GTimeVal	t;
+	GHashTableIter	iter;
+	gpointer	key, value;
+
+	monitor = data;
+
+	g_debug ("Checking for event pairs which have timed out...");
+
+	g_get_current_time (&t);
+
+	g_hash_table_iter_init (&iter, monitor->private->event_time_by_cookie);
+	while (g_hash_table_iter_next (&iter, &key, &value)) {
+		glong	     seconds;
+		glong	     seconds_then;
+		guint32      event_type;
+		GFile	    *file;
+		const gchar *module_name;
+		gboolean     is_directory;
+		gpointer     p;
+
+		seconds_then = GPOINTER_TO_SIZE (value);
+
+		seconds  = t.tv_sec;
+		seconds -= seconds_then;
+
+		g_debug ("Comparing now:%ld to then:%ld, diff:%ld",
+			 t.tv_sec,
+			 seconds_then,
+			 seconds);
+
+		if (seconds < 2) {
+			continue;
+		}
+
+		file = g_hash_table_lookup (monitor->private->event_pairs, key);
+		p = g_hash_table_lookup (monitor->private->event_type_by_cookie, key);
+		event_type = GPOINTER_TO_UINT (p);
+
+		/* We didn't receive an event pair for this
+		 * cookie, so we just generate the CREATE or
+		 * DELETE event for the file we know about.
+		 */
+		g_debug ("Event:%d with cookie:%d has timed out (%ld seconds have elapsed)",
+			 event_type,
+			 GPOINTER_TO_UINT (key),
+			 seconds);
+
+		module_name = get_module_name_from_gfile (monitor,
+							  file,
+							  &is_directory);
+
+		switch (event_type) {
+		case IN_MOVE_SELF:
+		case IN_MOVED_FROM:
+		case IN_DELETE:
+		case IN_DELETE_SELF:
+			/* So we new the source, but not the
+			 * target location for the event.
+			 */
+			g_signal_emit (monitor,
+				       signals[ITEM_DELETED], 0,
+				       module_name,
+				       file,
+				       is_directory);
+			break;
+
+		case IN_CREATE:
+		case IN_MOVED_TO:
+			/* So we new the target, but not the
+			 * source location for the event.
+			 */
+			g_signal_emit (monitor,
+				       signals[ITEM_CREATED], 0,
+				       module_name,
+				       file,
+				       is_directory);
+			break;
+		}
+
+		/* Clean up */
+		g_hash_table_remove (monitor->private->event_pairs, key);
+		g_hash_table_remove (monitor->private->event_time_by_cookie, key);
+		g_hash_table_remove (monitor->private->event_type_by_cookie, key);
+
+		/* Reset the iter, so we start from the top */
+		g_hash_table_iter_init (&iter, monitor->private->event_time_by_cookie);
+	}
+
+	if (g_hash_table_size (monitor->private->event_time_by_cookie) < 1) {
+		g_debug ("No more events to pair, removing timeout");
+		monitor->private->event_check_timeout_id = 0;
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+libinotify_monitor_event_cb (INotifyHandle *handle,
+			     const char    *monitor_name,
+			     const char    *filename,
+			     guint32	    event_type,
+			     guint32	    cookie,
+			     gpointer	    user_data)
+{
+	TrackerMonitor *monitor;
+	GFile	       *file;
+	GFile	       *other_file;
+	const gchar    *module_name;
+	gchar	       *str1;
+	gchar	       *str2;
+	gboolean	is_directory;
+	gchar	       *event_type_str;
+
+	monitor = user_data;
+
+	switch (event_type) {
+	case IN_Q_OVERFLOW:
+	case IN_OPEN:
+	case IN_CLOSE_NOWRITE:
+	case IN_ACCESS:
+	case IN_IGNORED:
+		/* Return, otherwise we spam with messages for every
+		 * file we open and look at.
+		 */
+		return;
+	default:
+		break;
+	}
+
+	if (G_UNLIKELY (!monitor->private->enabled)) {
+		g_debug ("Silently dropping monitor event, monitor disabled for now");
+		return;
+	}
+
+	if (monitor_name) {
+		str1 = g_build_filename (monitor_name, filename, NULL);
+		file = g_file_new_for_path (str1);
+	} else {
+		str1 = NULL;
+		file = g_file_new_for_path (filename);
+	}
+
+	other_file = NULL;
+
+	module_name = get_module_name_from_gfile (monitor,
+						  file,
+						  &is_directory);
+	if (!module_name) {
+		g_free (str1);
+		g_object_unref (file);
+		return;
+	}
+
+	if (!str1) {
+		str1 = g_file_get_path (file);
+	}
+
+	/* This doesn't outright black list a file, it purely adds
+	 * each file to the hash table so we have a count for every
+	 * path we get an event for. If the count is too high, we
+	 * then don't propagate the event up.
+	 */
+	switch (event_type) {
+	case IN_MOVE_SELF:
+	case IN_MOVED_FROM:
+	case IN_MOVED_TO:
+		/* If the event is a move type, we don't increment the
+		 * black list count to avoid missing the second event
+		 * to pair the two up.
+		 */
+		break;
+
+	default:
+		black_list_file_increment (monitor, file);
+		break;
+	}
+
+	if (other_file) {
+		str2 = g_file_get_path (other_file);
+	} else {
+		str2 = NULL;
+	}
+
+	event_type_str = libinotify_monitor_event_to_string (event_type);
+	g_message ("Received monitor event:%d->'%s' for file:'%s' (cookie:%d)",
+		   event_type,
+		   event_type_str,
+		   str1 ? str1 : "",
+		   cookie);
+	g_free (event_type_str);
+
+	if (!black_list_file_check (monitor, file)) {
+		if (monitor->private->unpause_timeout_id != 0) {
+			g_source_remove (monitor->private->unpause_timeout_id);
+		} else {
+			g_message ("Pausing indexing because we are "
+				   "receiving monitor events");
+
+			tracker_status_set_is_paused_for_io (TRUE);
+
+			org_freedesktop_Tracker_Indexer_pause_async (tracker_dbus_indexer_get_proxy (),
+								     indexer_pause_cb,
+								     NULL);
+		}
+
+		monitor->private->unpause_timeout_id =
+			g_timeout_add_seconds (PAUSE_FOR_IO_SECONDS,
+					       unpause_cb,
+					       monitor);
+
+		if (cookie > 0) {
+			/* First check if we already have a file in
+			 * the event pairs hash table.
+			 */
+			other_file = g_hash_table_lookup (monitor->private->event_pairs,
+							  GINT_TO_POINTER (cookie));
+			if (!other_file) {
+				GTimeVal t;
+
+				g_get_current_time (&t);
+				g_hash_table_insert (monitor->private->event_pairs,
+						     GUINT_TO_POINTER (cookie),
+						     g_object_ref (file));
+				g_hash_table_insert (monitor->private->event_time_by_cookie,
+						     GUINT_TO_POINTER (cookie),
+						     GSIZE_TO_POINTER (t.tv_sec));
+				g_hash_table_insert (monitor->private->event_type_by_cookie,
+						     GUINT_TO_POINTER (cookie),
+						     GUINT_TO_POINTER (event_type));
+			} else {
+				gchar *other_path;
+
+				other_path = g_file_get_path (other_file);
+				g_free (other_path);
+			}
+
+			/* Add a check for old cookies we didn't
+			 * receive the follow up pair event for.
+			 */
+			if (!monitor->private->event_check_timeout_id) {
+				g_debug ("Setting up event pair timeout check");
+
+				monitor->private->event_check_timeout_id =
+					g_timeout_add_seconds (2,
+							       libinotify_event_check_timeout_cb,
+							       monitor);
+			}
+		}
+
+		switch (event_type) {
+		case IN_MODIFY:
+			if (!monitor->private->use_changed_event) {
+				/* Do nothing */
+				break;
+			}
+
+		case IN_CLOSE_WRITE:
+		case IN_ATTRIB:
+			g_signal_emit (monitor,
+				       signals[ITEM_UPDATED], 0,
+				       module_name,
+				       file,
+				       is_directory);
+			break;
+
+		case IN_MOVE_SELF:
+		case IN_MOVED_FROM:
+		case IN_DELETE:
+		case IN_DELETE_SELF:
+			if (cookie == 0) {
+				g_signal_emit (monitor,
+					       signals[ITEM_DELETED], 0,
+					       module_name,
+					       file,
+					       is_directory);
+			} else if (other_file) {
+				g_signal_emit (monitor,
+					       signals[ITEM_MOVED], 0,
+					       module_name,
+					       file,
+					       other_file,
+					       is_directory);
+				g_hash_table_remove (monitor->private->event_pairs,
+						     GUINT_TO_POINTER (cookie));
+				g_hash_table_remove (monitor->private->event_time_by_cookie,
+						     GUINT_TO_POINTER (cookie));
+				g_hash_table_remove (monitor->private->event_type_by_cookie,
+						     GUINT_TO_POINTER (cookie));
+			}
+
+			break;
+
+		case IN_CREATE:
+		case IN_MOVED_TO:
+			/* FIXME: What if we don't monitor the other
+			 * location?
+			 */
+			if (cookie == 0) {
+				g_signal_emit (monitor,
+					       signals[ITEM_CREATED], 0,
+					       module_name,
+					       file,
+					       is_directory);
+			} else if (other_file) {
+				g_signal_emit (monitor,
+					       signals[ITEM_MOVED], 0,
+					       module_name,
+					       other_file,
+					       file,
+					       is_directory);
+				g_hash_table_remove (monitor->private->event_pairs,
+						     GUINT_TO_POINTER (cookie));
+				g_hash_table_remove (monitor->private->event_time_by_cookie,
+						     GUINT_TO_POINTER (cookie));
+				g_hash_table_remove (monitor->private->event_type_by_cookie,
+						     GUINT_TO_POINTER (cookie));
+			}
+
+			break;
+
+		case IN_UNMOUNT:
+			g_signal_emit (monitor,
+				       signals[ITEM_DELETED], 0,
+				       module_name,
+				       file,
+				       is_directory);
+			break;
+
+		case IN_Q_OVERFLOW:
+		case IN_OPEN:
+		case IN_CLOSE_NOWRITE:
+		case IN_ACCESS:
+		case IN_IGNORED:
+			/* Do nothing */
+			break;
+		}
+	} else {
+		g_message ("Not propagating event, file is black listed");
+	}
+
+	g_free (str1);
+	g_free (str2);
+	g_object_unref (file);
+}
+
+static INotifyHandle *
+libinotify_monitor_directory (TrackerMonitor *monitor,
+			      GFile	     *file)
+{
+	INotifyHandle *handle;
+	gchar	      *path;
+	guint32	       mask;
+	unsigned long  flags;
+	gboolean       is_directory;
+
+	is_directory = TRUE;
+	flags = 0;
+	mask = IN_ALL_EVENTS;
+
+	/* For files */
+	if (is_directory) {
+		flags &= ~IN_FLAG_FILE_BASED;
+	} else {
+		flags |= IN_FLAG_FILE_BASED;
+	}
+
+	path = g_file_get_path (file);
+	handle = inotify_monitor_add (path,
+				      mask,
+				      flags,
+				      libinotify_monitor_event_cb,
+				      monitor);
+	g_free (path);
+
+	if (!handle) {
+		return NULL;
+	}
+
+	return handle;
+}
+
+static void
+libinotify_monitor_cancel (gpointer data)
+{
+	inotify_monitor_remove (data);
+}
+
+#else  /* USE_LIBINOTIFY */
+
+static const gchar *
+monitor_event_to_string (GFileMonitorEvent event_type)
+{
+	switch (event_type) {
+	case G_FILE_MONITOR_EVENT_CHANGED:
+		return "G_FILE_MONITOR_EVENT_CHANGED";
+	case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
+		return "G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT";
+	case G_FILE_MONITOR_EVENT_DELETED:
+		return "G_FILE_MONITOR_EVENT_DELETED";
+	case G_FILE_MONITOR_EVENT_CREATED:
+		return "G_FILE_MONITOR_EVENT_CREATED";
+	case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
+		return "G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED";
+	case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
+		return "G_FILE_MONITOR_EVENT_PRE_UNMOUNT";
+	case G_FILE_MONITOR_EVENT_UNMOUNTED:
+		return "G_FILE_MONITOR_EVENT_UNMOUNTED";
+	}
+
+	return "unknown";
+}
+
+static void
+monitor_event_cb (GFileMonitor	    *file_monitor,
+		  GFile		    *file,
+		  GFile		    *other_file,
+		  GFileMonitorEvent  event_type,
+		  gpointer	     user_data)
+{
+	TrackerMonitor *monitor;
+	const gchar    *module_name;
+	gchar	       *str1;
+	gchar	       *str2;
+	gboolean	is_directory;
+
+	monitor = user_data;
+
+	if (G_UNLIKELY (!monitor->private->enabled)) {
+		g_debug ("Silently dropping monitor event, monitor disabled for now");
+		return;
+	}
+
+	module_name = get_module_name_from_gfile (monitor,
+						  file,
+						  &is_directory);
+	if (!module_name) {
+		return;
+	}
+
+	str1 = g_file_get_path (file);
+
+	/* This doesn't outright black list a file, it purely adds
+	 * each file to the hash table so we have a count for every
+	 * path we get an event for. If the count is too high, we
+	 * then don't propagate the event up.
+	 */
+	black_list_file_increment (monitor, file);
+
+	if (other_file) {
+		str2 = g_file_get_path (other_file);
+	} else {
+		str2 = NULL;
+	}
+
+	g_message ("Received monitor event:%d->'%s' for file:'%s' and other file:'%s'",
+		   event_type,
+		   monitor_event_to_string (event_type),
+		   str1,
+		   str2 ? str2 : "");
+
+	if (!black_list_file_check (monitor, file)) {
+		if (monitor->private->unpause_timeout_id != 0) {
+			g_source_remove (monitor->private->unpause_timeout_id);
+		} else {
+			g_message ("Pausing indexing because we are "
+				   "receiving monitor events");
+
+			tracker_status_set_is_paused_for_io (TRUE);
+
+			org_freedesktop_Tracker_Indexer_pause_async (tracker_dbus_indexer_get_proxy (),
+								     indexer_pause_cb,
+								     NULL);
+		}
+
+		monitor->private->unpause_timeout_id =
+			g_timeout_add_seconds (PAUSE_FOR_IO_SECONDS,
+					       unpause_cb,
+					       monitor);
+
+		switch (event_type) {
+		case G_FILE_MONITOR_EVENT_CHANGED:
+			if (!monitor->private->use_changed_event) {
+				/* Do nothing */
+				break;
+			}
+
+		case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
+		case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
+			g_signal_emit (monitor,
+				       signals[ITEM_UPDATED], 0,
+				       module_name,
+				       file,
+				       is_directory);
+			break;
+
+		case G_FILE_MONITOR_EVENT_DELETED:
+			g_signal_emit (monitor,
+				       signals[ITEM_DELETED], 0,
+				       module_name,
+				       file,
+				       is_directory);
+			break;
+
+		case G_FILE_MONITOR_EVENT_CREATED:
+			g_signal_emit (monitor,
+				       signals[ITEM_CREATED], 0,
+				       module_name,
+				       file,
+				       is_directory);
+			break;
+
+		case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
+			g_signal_emit (monitor,
+				       signals[ITEM_DELETED], 0,
+				       module_name,
+				       file,
+				       is_directory);
+			break;
+
+		case G_FILE_MONITOR_EVENT_UNMOUNTED:
+			/* Do nothing */
+			break;
+		}
+	} else {
+		g_message ("Not propagating event, file is black listed");
+	}
+
+	g_free (str1);
+	g_free (str2);
+}
+
+#endif /* USE_LIBINOTIFY */
+
+TrackerMonitor *
+tracker_monitor_new (TrackerConfig *config)
+{
+	TrackerMonitor	      *monitor;
+	TrackerMonitorPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), NULL);
+
+	monitor = g_object_new (TRACKER_TYPE_MONITOR, NULL);
+
+	priv = monitor->private;
+
+	priv->config = g_object_ref (config);
+
+	return monitor;
+}
+
+gboolean
+tracker_monitor_get_enabled (TrackerMonitor *monitor)
+{
+	g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), FALSE);
+
+	return monitor->private->enabled;
+}
+
+void
+tracker_monitor_set_enabled (TrackerMonitor *monitor,
+			     gboolean	     enabled)
+{
+	g_return_if_fail (TRACKER_IS_MONITOR (monitor));
+
+	monitor->private->enabled = enabled;
+
+	g_object_notify (G_OBJECT (monitor), "enabled");
+}
+
+gboolean
+tracker_monitor_add (TrackerMonitor *monitor,
+		     const gchar    *module_name,
+		     GFile	    *file)
+{
+#ifdef USE_LIBINOTIFY
+	INotifyHandle *file_monitor;
+#else  /* USE_LIBINOTIFY */
+	GFileMonitor *file_monitor;
+	GError	     *error = NULL;
+#endif /* USE_LIBINOTIFY */
+	GHashTable   *monitors;
+	GSList	     *ignored_roots;
+	GSList	     *l;
+	gchar	     *path;
+
+	g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), FALSE);
+	g_return_val_if_fail (module_name != NULL, FALSE);
+	g_return_val_if_fail (G_IS_FILE (file), FALSE);
+
+	if (!tracker_config_get_enable_watches (monitor->private->config)) {
+		return TRUE;
+	}
+
+	monitors = g_hash_table_lookup (monitor->private->modules, module_name);
+	if (!monitors) {
+		g_warning ("No monitor hash table for module:'%s'",
+			   module_name);
+		return FALSE;
+	}
+
+	if (g_hash_table_lookup (monitors, file)) {
+		return TRUE;
+	}
+
+	/* Cap the number of monitors */
+	if (g_hash_table_size (monitors) >= monitor->private->monitor_limit) {
+		monitor->private->monitors_ignored++;
+
+		if (!monitor->private->monitor_limit_warned) {
+			g_warning ("The maximum number of monitors to set (%d) "
+				   "has been reached, not adding any new ones",
+				   monitor->private->monitor_limit);
+			monitor->private->monitor_limit_warned = TRUE;
+		}
+
+		return FALSE;
+	}
+
+	path = g_file_get_path (file);
+
+	ignored_roots = tracker_config_get_no_watch_directory_roots (monitor->private->config);
+
+	/* Check this location isn't excluded in the config */
+	for (l = ignored_roots; l; l = l->next) {
+		if (strcmp (path, l->data) == 0) {
+			g_message ("Not adding montior for:'%s', path is in config ignore list",
+				   path);
+			g_free (path);
+			return FALSE;
+		}
+	}
+
+	/* We don't check if a file exists or not since we might want
+	 * to monitor locations which don't exist yet.
+	 *
+	 * Also, we assume ALL paths passed are directories.
+	 */
+#ifdef USE_LIBINOTIFY
+	file_monitor = libinotify_monitor_directory (monitor, file);
+
+	if (!file_monitor) {
+		g_warning ("Could not add monitor for path:'%s'",
+			   path);
+		g_free (path);
+		return FALSE;
+	}
+
+	g_hash_table_insert (monitors,
+			     g_object_ref (file),
+			     file_monitor);
+#else  /* USE_LIBINOTIFY */
+	file_monitor = g_file_monitor_directory (file,
+						 G_FILE_MONITOR_WATCH_MOUNTS,
+						 NULL,
+						 &error);
+
+	if (error) {
+		g_warning ("Could not add monitor for path:'%s', %s",
+			   path,
+			   error->message);
+		g_free (path);
+		g_error_free (error);
+		return FALSE;
+	}
+
+	g_signal_connect (file_monitor, "changed",
+			  G_CALLBACK (monitor_event_cb),
+			  monitor);
+
+	g_hash_table_insert (monitors,
+			     g_object_ref (file),
+			     file_monitor);
+#endif /* USE_LIBINOTIFY */
+
+	g_debug ("Added monitor for module:'%s', path:'%s', total monitors:%d",
+		 module_name,
+		 path,
+		 g_hash_table_size (monitors));
+
+	g_free (path);
+
+	return TRUE;
+}
+
+gboolean
+tracker_monitor_remove (TrackerMonitor *monitor,
+			const gchar    *module_name,
+			GFile	       *file)
+{
+	GFileMonitor *file_monitor;
+	GHashTable   *monitors;
+	gchar	     *path;
+
+	g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), FALSE);
+	g_return_val_if_fail (module_name != NULL, FALSE);
+	g_return_val_if_fail (G_IS_FILE (file), FALSE);
+
+	if (!tracker_config_get_enable_watches (monitor->private->config)) {
+		return TRUE;
+	}
+
+	monitors = g_hash_table_lookup (monitor->private->modules, module_name);
+	if (!monitors) {
+		g_warning ("No monitor hash table for module:'%s'",
+			   module_name);
+		return FALSE;
+	}
+
+	file_monitor = g_hash_table_lookup (monitors, file);
+	if (!file_monitor) {
+		return TRUE;
+	}
+
+	/* We reset this because now it is possible we have limit - 1 */
+	monitor->private->monitor_limit_warned = FALSE;
+
+	g_hash_table_remove (monitors, file);
+
+	path = g_file_get_path (file);
+
+	g_debug ("Removed monitor for module:'%s', path:'%s', total monitors:%d",
+		 module_name,
+		 path,
+		 g_hash_table_size (monitors));
+
+	g_free (path);
+
+	return TRUE;
+}
+
+gboolean
+tracker_monitor_is_watched (TrackerMonitor *monitor,
+			    const gchar    *module_name,
+			    GFile	   *file)
+{
+	GHashTable *monitors;
+
+	g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), FALSE);
+	g_return_val_if_fail (module_name != NULL, FALSE);
+	g_return_val_if_fail (G_IS_FILE (file), FALSE);
+
+	monitors = g_hash_table_lookup (monitor->private->modules, module_name);
+	if (!monitors) {
+		g_warning ("No monitor hash table for module:'%s'",
+			   module_name);
+		return FALSE;
+	}
+
+	return g_hash_table_lookup (monitors, file) != NULL;
+}
+
+gboolean
+tracker_monitor_is_watched_by_string (TrackerMonitor *monitor,
+				      const gchar    *module_name,
+				      const gchar    *path)
+{
+	GFile	   *file;
+	GHashTable *monitors;
+	gboolean    watched;
+
+	g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), FALSE);
+	g_return_val_if_fail (module_name != NULL, FALSE);
+	g_return_val_if_fail (path != NULL, FALSE);
+
+	monitors = g_hash_table_lookup (monitor->private->modules, module_name);
+	if (!monitors) {
+		g_warning ("No monitor hash table for module:'%s'",
+			   module_name);
+		return FALSE;
+	}
+
+	file = g_file_new_for_path (path);
+	watched = g_hash_table_lookup (monitors, file) != NULL;
+	g_object_unref (file);
+
+	return watched;
+}
+
+guint
+tracker_monitor_get_count (TrackerMonitor *monitor,
+			   const gchar	  *module_name)
+{
+	guint count;
+
+	g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), 0);
+
+	if (module_name) {
+		GHashTable *monitors;
+
+		monitors = g_hash_table_lookup (monitor->private->modules, module_name);
+		if (!monitors) {
+			g_warning ("No monitor hash table for module:'%s'",
+				   module_name);
+			return 0;
+		}
+
+		count = g_hash_table_size (monitors);
+	} else {
+		GList *all_modules, *l;
+
+		all_modules = g_hash_table_get_values (monitor->private->modules);
+
+		for (l = all_modules, count = 0; l; l = l->next) {
+			count += g_hash_table_size (l->data);
+		}
+
+		g_list_free (all_modules);
+	}
+
+	return count;
+}
+
+guint
+tracker_monitor_get_ignored (TrackerMonitor *monitor)
+{
+	g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), 0);
+
+	return monitor->private->monitors_ignored;
+}

Added: trunk/src/trackerd/tracker-monitor.h
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-monitor.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,75 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKERD_MONITOR_H__
+#define __TRACKERD_MONITOR_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include <libtracker-common/tracker-config.h>
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_MONITOR		(tracker_monitor_get_type ())
+#define TRACKER_MONITOR(object)		(G_TYPE_CHECK_INSTANCE_CAST ((object), TRACKER_TYPE_MONITOR, TrackerMonitor))
+#define TRACKER_MONITOR_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), TRACKER_TYPE_MONITOR, TrackerMonitorClass))
+#define TRACKER_IS_MONITOR(object)	(G_TYPE_CHECK_INSTANCE_TYPE ((object), TRACKER_TYPE_MONITOR))
+#define TRACKER_IS_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TRACKER_TYPE_MONITOR))
+#define TRACKER_MONITOR_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj), TRACKER_TYPE_MONITOR, TrackerMonitorClass))
+
+typedef struct _TrackerMonitor	       TrackerMonitor;
+typedef struct _TrackerMonitorClass    TrackerMonitorClass;
+typedef struct _TrackerMonitorPrivate  TrackerMonitorPrivate;
+
+struct _TrackerMonitor {
+	GObject		       parent;
+	TrackerMonitorPrivate *private;
+};
+
+struct _TrackerMonitorClass {
+	GObjectClass	       parent;
+};
+
+GType		tracker_monitor_get_type	     (void);
+TrackerMonitor *tracker_monitor_new		     (TrackerConfig  *config);
+gboolean	tracker_monitor_get_enabled	     (TrackerMonitor *monitor);
+void		tracker_monitor_set_enabled	     (TrackerMonitor *monitor,
+						      gboolean	      enabled);
+gboolean	tracker_monitor_add		     (TrackerMonitor *monitor,
+						      const gchar    *module_name,
+						      GFile	     *file);
+gboolean	tracker_monitor_remove		     (TrackerMonitor *monitor,
+						      const gchar    *module_name,
+						      GFile	     *file);
+gboolean	tracker_monitor_is_watched	     (TrackerMonitor *monitor,
+						      const gchar    *module_name,
+						      GFile	     *file);
+gboolean	tracker_monitor_is_watched_by_string (TrackerMonitor *monitor,
+						      const gchar    *module_name,
+						      const gchar    *path);
+guint		tracker_monitor_get_count	     (TrackerMonitor *monitor,
+						      const gchar    *module_name);
+guint		tracker_monitor_get_ignored	     (TrackerMonitor *monitor);
+
+G_END_DECLS
+
+#endif /* __TRACKERD_MONITOR_H__ */

Added: trunk/src/trackerd/tracker-processor.c
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-processor.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,1707 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libtracker-common/tracker-dbus.h>
+#include <libtracker-common/tracker-file-utils.h>
+#include <libtracker-common/tracker-hal.h>
+#include <libtracker-common/tracker-module-config.h>
+#include <libtracker-common/tracker-utils.h>
+
+#include <libtracker-db/tracker-db-index.h>
+#include <libtracker-db/tracker-db-index-manager.h>
+
+#include "tracker-processor.h"
+#include "tracker-crawler.h"
+#include "tracker-daemon.h"
+#include "tracker-dbus.h"
+#include "tracker-indexer-client.h"
+#include "tracker-main.h"
+#include "tracker-monitor.h"
+#include "tracker-status.h"
+
+#define TRACKER_PROCESSOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRACKER_TYPE_PROCESSOR, TrackerProcessorPrivate))
+
+#define ITEMS_QUEUE_PROCESS_INTERVAL 2000
+#define ITEMS_QUEUE_PROCESS_MAX      1000
+
+#define ITEMS_SIGNAL_TO_DAEMON_RATIO 500
+
+typedef enum {
+	SENT_TYPE_NONE,
+	SENT_TYPE_CREATED,
+	SENT_TYPE_UPDATED,
+	SENT_TYPE_DELETED,
+	SENT_TYPE_MOVED
+} SentType;
+
+struct TrackerProcessorPrivate {
+	TrackerConfig  *config;
+	TrackerHal     *hal;
+	TrackerMonitor *monitor;
+
+	DBusGProxy     *indexer_proxy;
+
+	/* Crawlers */
+	GHashTable     *crawlers;
+
+	/* File queues for indexer */
+	guint		item_queues_handler_id;
+
+	GHashTable     *items_created_queues;
+	GHashTable     *items_updated_queues;
+	GHashTable     *items_deleted_queues;
+	GHashTable     *items_moved_queues;
+
+	SentType	sent_type;
+	GStrv		sent_items;
+	const gchar    *sent_module_name;
+
+	/* Status */
+	GList	       *modules;
+	GList	       *current_module;
+	gboolean	iterated_modules;
+	gboolean	iterated_removable_media;
+
+	GTimer	       *timer;
+
+	gboolean	interrupted;
+	gboolean	finished;
+
+	/* Statistics */
+	guint		directories_found;
+	guint		directories_ignored;
+	guint		files_found;
+	guint		files_ignored;
+
+	guint		items_done;
+	guint		items_remaining;
+
+	gdouble		seconds_elapsed;
+};
+
+enum {
+	FINISHED,
+	LAST_SIGNAL
+};
+
+static void tracker_processor_finalize	    (GObject	      *object);
+static void crawler_destroy_notify	    (gpointer	       data);
+static void item_queue_destroy_notify	    (gpointer	       data);
+static void process_module_next		    (TrackerProcessor *processor);
+static void indexer_status_cb		    (DBusGProxy       *proxy,
+					     gdouble	       seconds_elapsed,
+					     const gchar      *current_module_name,
+					     guint	       items_done,
+					     guint	       items_remaining,
+					     gpointer	       user_data);
+static void indexer_finished_cb		    (DBusGProxy       *proxy,
+					     gdouble	       seconds_elapsed,
+					     guint	       items_done,
+					     gboolean	       interrupted,
+					     gpointer	       user_data);
+static void monitor_item_created_cb	    (TrackerMonitor   *monitor,
+					     const gchar      *module_name,
+					     GFile	      *file,
+					     gboolean	       is_directory,
+					     gpointer	       user_data);
+static void monitor_item_updated_cb	    (TrackerMonitor   *monitor,
+					     const gchar      *module_name,
+					     GFile	      *file,
+					     gboolean	       is_directory,
+					     gpointer	       user_data);
+static void monitor_item_deleted_cb	    (TrackerMonitor   *monitor,
+					     const gchar      *module_name,
+					     GFile	      *file,
+					     gboolean	       is_directory,
+					     gpointer	       user_data);
+static void monitor_item_moved_cb	    (TrackerMonitor   *monitor,
+					     const gchar      *module_name,
+					     GFile	      *file,
+					     GFile	      *other_file,
+					     gboolean	       is_directory,
+					     gpointer	       user_data);
+static void crawler_processing_file_cb	    (TrackerCrawler   *crawler,
+					     const gchar      *module_name,
+					     GFile	      *file,
+					     gpointer	       user_data);
+static void crawler_processing_directory_cb (TrackerCrawler   *crawler,
+					     const gchar      *module_name,
+					     GFile	      *file,
+					     gpointer	       user_data);
+static void crawler_finished_cb		    (TrackerCrawler   *crawler,
+					     const gchar      *module_name,
+					     guint	       directories_found,
+					     guint	       directories_ignored,
+					     guint	       files_found,
+					     guint	       files_ignored,
+					     gpointer	       user_data);
+
+#ifdef HAVE_HAL
+static void mount_point_added_cb	    (TrackerHal       *hal,
+					     const gchar      *mount_point,
+					     gpointer	       user_data);
+static void mount_point_removed_cb	    (TrackerHal       *hal,
+					     const gchar      *mount_point,
+					     gpointer	       user_data);
+#endif /* HAVE_HAL */
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE (TrackerProcessor, tracker_processor, G_TYPE_OBJECT)
+
+static void
+tracker_processor_class_init (TrackerProcessorClass *class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+	object_class->finalize = tracker_processor_finalize;
+
+	signals[FINISHED] =
+		g_signal_new ("finished",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (TrackerProcessorClass, finished),
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__VOID,
+			      G_TYPE_NONE, 0);
+
+	g_type_class_add_private (object_class, sizeof (TrackerProcessorPrivate));
+}
+
+static void
+tracker_processor_init (TrackerProcessor *object)
+{
+	TrackerProcessorPrivate *priv;
+	GList			*l;
+
+	object->private = TRACKER_PROCESSOR_GET_PRIVATE (object);
+
+	priv = object->private;
+
+	priv->modules = tracker_module_config_get_modules ();
+
+	/* For each module we create a TrackerCrawler and keep them in
+	 * a hash table to look up.
+	 */
+	priv->crawlers =
+		g_hash_table_new_full (g_str_hash,
+				       g_str_equal,
+				       g_free,
+				       crawler_destroy_notify);
+
+
+	/* For each module we create a hash table for queues for items
+	 * to update/create/delete in the indexer. This is sent on
+	 * when the queue is processed.
+	 */
+	priv->items_created_queues =
+		g_hash_table_new_full (g_str_hash,
+				       g_str_equal,
+				       g_free,
+				       item_queue_destroy_notify);
+	priv->items_updated_queues =
+		g_hash_table_new_full (g_str_hash,
+				       g_str_equal,
+				       g_free,
+				       item_queue_destroy_notify);
+	priv->items_deleted_queues =
+		g_hash_table_new_full (g_str_hash,
+				       g_str_equal,
+				       g_free,
+				       item_queue_destroy_notify);
+	priv->items_moved_queues =
+		g_hash_table_new_full (g_str_hash,
+				       g_str_equal,
+				       g_free,
+				       item_queue_destroy_notify);
+
+	for (l = priv->modules; l; l = l->next) {
+		/* Create queues for this module */
+		g_hash_table_insert (priv->items_created_queues,
+				     g_strdup (l->data),
+				     g_queue_new ());
+		g_hash_table_insert (priv->items_updated_queues,
+				     g_strdup (l->data),
+				     g_queue_new ());
+		g_hash_table_insert (priv->items_deleted_queues,
+				     g_strdup (l->data),
+				     g_queue_new ());
+		g_hash_table_insert (priv->items_moved_queues,
+				     g_strdup (l->data),
+				     g_queue_new ());
+	}
+}
+
+static void
+tracker_processor_finalize (GObject *object)
+{
+	TrackerProcessorPrivate *priv;
+
+	priv = TRACKER_PROCESSOR_GET_PRIVATE (object);
+
+	if (priv->timer) {
+		g_timer_destroy (priv->timer);
+	}
+
+	if (priv->item_queues_handler_id) {
+		g_source_remove (priv->item_queues_handler_id);
+		priv->item_queues_handler_id = 0;
+	}
+
+	if (priv->items_moved_queues) {
+		g_hash_table_unref (priv->items_moved_queues);
+	}
+
+	if (priv->items_deleted_queues) {
+		g_hash_table_unref (priv->items_deleted_queues);
+	}
+
+	if (priv->items_updated_queues) {
+		g_hash_table_unref (priv->items_updated_queues);
+	}
+
+	if (priv->items_created_queues) {
+		g_hash_table_unref (priv->items_created_queues);
+	}
+
+	if (priv->crawlers) {
+		g_hash_table_unref (priv->crawlers);
+	}
+
+	g_list_free (priv->modules);
+
+	dbus_g_proxy_disconnect_signal (priv->indexer_proxy, "Finished",
+					G_CALLBACK (indexer_finished_cb),
+					NULL);
+	dbus_g_proxy_disconnect_signal (priv->indexer_proxy, "Status",
+					G_CALLBACK (indexer_status_cb),
+					NULL);
+	g_object_unref (priv->indexer_proxy);
+
+	g_signal_handlers_disconnect_by_func (priv->monitor,
+					      G_CALLBACK (monitor_item_deleted_cb),
+					      object);
+	g_signal_handlers_disconnect_by_func (priv->monitor,
+					      G_CALLBACK (monitor_item_updated_cb),
+					      object);
+	g_signal_handlers_disconnect_by_func (priv->monitor,
+					      G_CALLBACK (monitor_item_created_cb),
+					      object);
+	g_signal_handlers_disconnect_by_func (priv->monitor,
+					      G_CALLBACK (monitor_item_moved_cb),
+					      object);
+	g_object_unref (priv->monitor);
+
+#ifdef HAVE_HAL
+	if (priv->hal) {
+		g_signal_handlers_disconnect_by_func (priv->hal,
+						      mount_point_added_cb,
+						      object);
+		g_signal_handlers_disconnect_by_func (priv->hal,
+						      mount_point_removed_cb,
+						      object);
+
+		g_object_unref (priv->hal);
+	}
+#endif /* HAVE_HAL */
+
+	g_object_unref (priv->config);
+
+	G_OBJECT_CLASS (tracker_processor_parent_class)->finalize (object);
+}
+
+
+static void
+get_remote_roots (TrackerProcessor  *processor,
+		  GSList	   **mounted_directory_roots,
+		  GSList	   **removable_device_roots)
+{
+	GSList *l1;
+	GSList *l2;
+
+#ifdef HAVE_HAL
+	l1 = tracker_hal_get_mounted_directory_roots (processor->private->hal);
+	l2 = tracker_hal_get_removable_device_roots (processor->private->hal);
+#else  /* HAVE_HAL */
+	l1 = NULL;
+	l2 = NULL;
+#endif /* HAVE_HAL */
+
+	/* The options to index removable media and the index mounted
+	 * directories are both mutually exclusive even though
+	 * removable media is mounted on a directory.
+	 *
+	 * Since we get ALL mounted directories from HAL, we need to
+	 * remove those which are removable device roots.
+	 */
+	if (l2) {
+		GSList *l;
+		GSList *list = NULL;
+
+		for (l = l1; l; l = l->next) {
+			if (g_slist_find_custom (l2, l->data, (GCompareFunc) strcmp)) {
+				continue;
+			}
+
+			list = g_slist_prepend (list, l->data);
+		}
+
+		*mounted_directory_roots = g_slist_reverse (list);
+	} else {
+		*mounted_directory_roots = NULL;
+	}
+
+	*removable_device_roots = g_slist_copy (l2);
+}
+
+static gboolean
+path_should_be_ignored_for_media (TrackerProcessor *processor,
+				  const gchar	   *path)
+{
+	GSList	 *roots = NULL;
+	GSList	 *mounted_directory_roots = NULL;
+	GSList	 *removable_device_roots = NULL;
+	GSList	 *l;
+	gboolean  ignore_mounted_directories;
+	gboolean  ignore_removable_devices;
+	gboolean  ignore = FALSE;
+
+	ignore_mounted_directories =
+		!tracker_config_get_index_mounted_directories (processor->private->config);
+	ignore_removable_devices =
+		!tracker_config_get_index_removable_devices (processor->private->config);
+
+	if (ignore_mounted_directories || ignore_removable_devices) {
+		get_remote_roots (processor,
+				  &mounted_directory_roots,
+				  &removable_device_roots);
+	}
+
+	if (ignore_mounted_directories) {
+		roots = g_slist_concat (roots, mounted_directory_roots);
+	}
+
+	if (ignore_removable_devices) {
+		roots = g_slist_concat (roots, removable_device_roots);
+	}
+
+	for (l = roots; l && !ignore; l = l->next) {
+		/* If path matches a mounted or removable device by
+		 * prefix then we should ignore it since we don't
+		 * crawl those by choice in the config.
+		 */
+		if (strcmp (path, l->data) == 0) {
+			ignore = TRUE;
+		}
+
+		/* FIXME: Should we add a DIR_SEPARATOR on the end of
+		 * these before comparing them?
+		 */
+		if (g_str_has_prefix (path, l->data)) {
+			ignore = TRUE;
+		}
+	}
+
+	g_slist_free (roots);
+
+	return ignore;
+}
+
+static GQueue *
+get_next_queue_with_data (GList       *modules,
+			  GHashTable  *hash_table,
+			  gchar      **module_name)
+{
+	GQueue *found_queue;
+	GQueue *queue;
+	GList  *l;
+
+	if (module_name) {
+		*module_name = NULL;
+	}
+
+	for (l = modules, found_queue = NULL; l && !found_queue; l = l->next) {
+		queue = g_hash_table_lookup (hash_table, l->data);
+
+		if (g_queue_get_length (queue) > 0) {
+			if (module_name) {
+				*module_name = l->data;
+				found_queue = queue;
+			}
+		}
+	}
+
+	return found_queue;
+}
+
+static void
+crawler_destroy_notify (gpointer data)
+{
+	TrackerCrawler *crawler;
+
+	crawler = TRACKER_CRAWLER (data);
+
+	if (crawler) {
+		guint signals;
+
+		signals = g_signal_handlers_disconnect_matched (crawler,
+								G_SIGNAL_MATCH_FUNC,
+								0,
+								0,
+								NULL,
+								G_CALLBACK (crawler_processing_file_cb),
+								NULL);
+		signals = g_signal_handlers_disconnect_matched (crawler,
+								G_SIGNAL_MATCH_FUNC,
+								0,
+								0,
+								NULL,
+								G_CALLBACK (crawler_processing_directory_cb),
+								NULL);
+		signals = g_signal_handlers_disconnect_matched (crawler,
+								G_SIGNAL_MATCH_FUNC,
+								0,
+								0,
+								NULL,
+								G_CALLBACK (crawler_finished_cb),
+								NULL);
+
+		g_object_unref (crawler);
+	}
+}
+
+static void
+item_queue_destroy_notify (gpointer data)
+{
+	GQueue *queue;
+
+	queue = (GQueue *) data;
+
+	g_queue_foreach (queue, (GFunc) g_object_unref, NULL);
+	g_queue_free (queue);
+}
+
+static void
+item_queue_readd_items (GQueue *queue,
+			GStrv	strv)
+{
+	if (queue) {
+		GStrv p;
+		gint  i;
+
+		for (p = strv, i = 0; *p; p++, i++) {
+			g_queue_push_nth (queue, g_file_new_for_path (*p), i);
+		}
+	}
+}
+
+static void
+item_queue_processed_cb (DBusGProxy *proxy,
+			 GError     *error,
+			 gpointer    user_data)
+{
+	TrackerProcessor *processor;
+
+	processor = user_data;
+
+	if (error) {
+		GQueue *queue;
+
+		g_message ("Items could not be processed by the indexer, %s",
+			   error->message);
+		g_error_free (error);
+
+		/* Put files back into queue */
+		switch (processor->private->sent_type) {
+		case SENT_TYPE_CREATED:
+			queue = g_hash_table_lookup (processor->private->items_created_queues,
+						     processor->private->sent_module_name);
+			break;
+		case SENT_TYPE_UPDATED:
+			queue = g_hash_table_lookup (processor->private->items_updated_queues,
+						     processor->private->sent_module_name);
+			break;
+		case SENT_TYPE_DELETED:
+			queue = g_hash_table_lookup (processor->private->items_deleted_queues,
+						     processor->private->sent_module_name);
+			break;
+		case SENT_TYPE_MOVED:
+			queue = g_hash_table_lookup (processor->private->items_moved_queues,
+						     processor->private->sent_module_name);
+			break;
+		case SENT_TYPE_NONE:
+		default:
+			queue = NULL;
+			break;
+		}
+
+		item_queue_readd_items (queue, processor->private->sent_items);
+	} else {
+		g_debug ("Sent!");
+	}
+
+	g_strfreev (processor->private->sent_items);
+
+	/* Reset for next batch to be sent */
+	processor->private->sent_items = NULL;
+	processor->private->sent_module_name = NULL;
+	processor->private->sent_type = SENT_TYPE_NONE;
+}
+
+static gboolean
+item_queue_handlers_cb (gpointer user_data)
+{
+	TrackerProcessor *processor;
+	GQueue		 *queue;
+	GStrv		  files;
+	gchar		 *module_name;
+
+	processor = user_data;
+
+	/* This way we don't send anything to the indexer from monitor
+	 * events but we still queue them ready to send when we are
+	 * unpaused.
+	 */
+	if (tracker_status_get_is_paused_manually () ||
+	    tracker_status_get_is_paused_for_io ()) {
+		g_message ("We are paused, sending nothing to the index until we are unpaused");
+		return TRUE;
+	}
+
+	/* Now we try to send items to the indexer */
+	tracker_status_set_and_signal (TRACKER_STATUS_INDEXING);
+
+	/* This is here so we don't try to send something if we are
+	 * still waiting for a response from the last send.
+	 */
+	if (processor->private->sent_type != SENT_TYPE_NONE) {
+		g_message ("Still waiting for response from indexer, "
+			   "not sending more files yet");
+		return TRUE;
+	}
+
+	/* Process the deleted items first */
+	queue = get_next_queue_with_data (processor->private->modules,
+					  processor->private->items_deleted_queues,
+					  &module_name);
+
+	if (queue) {
+		files = tracker_dbus_queue_gfile_to_strv (queue, ITEMS_QUEUE_PROCESS_MAX);
+
+		g_message ("Queue for module:'%s' deleted items processed, sending first %d to the indexer",
+			   module_name,
+			   g_strv_length (files));
+
+		processor->private->sent_type = SENT_TYPE_DELETED;
+		processor->private->sent_module_name = module_name;
+		processor->private->sent_items = files;
+
+		org_freedesktop_Tracker_Indexer_files_delete_async (processor->private->indexer_proxy,
+								    module_name,
+								    (const gchar **) files,
+								    item_queue_processed_cb,
+								    processor);
+
+		return TRUE;
+	}
+
+	/* Process the created items next */
+	queue = get_next_queue_with_data (processor->private->modules,
+					  processor->private->items_created_queues,
+					  &module_name);
+
+	if (queue) {
+		files = tracker_dbus_queue_gfile_to_strv (queue, ITEMS_QUEUE_PROCESS_MAX);
+
+		g_message ("Queue for module:'%s' created items processed, sending first %d to the indexer",
+			   module_name,
+			   g_strv_length (files));
+
+		processor->private->sent_type = SENT_TYPE_CREATED;
+		processor->private->sent_module_name = module_name;
+		processor->private->sent_items = files;
+
+		org_freedesktop_Tracker_Indexer_files_check_async (processor->private->indexer_proxy,
+								   module_name,
+								   (const gchar **) files,
+								   item_queue_processed_cb,
+								   processor);
+
+		return TRUE;
+	}
+
+	/* Process the updated items next */
+	queue = get_next_queue_with_data (processor->private->modules,
+					  processor->private->items_updated_queues,
+					  &module_name);
+
+	if (queue) {
+		files = tracker_dbus_queue_gfile_to_strv (queue, ITEMS_QUEUE_PROCESS_MAX);
+
+		g_message ("Queue for module:'%s' updated items processed, sending first %d to the indexer",
+			   module_name,
+			   g_strv_length (files));
+
+		processor->private->sent_type = SENT_TYPE_UPDATED;
+		processor->private->sent_module_name = module_name;
+		processor->private->sent_items = files;
+
+		org_freedesktop_Tracker_Indexer_files_update_async (processor->private->indexer_proxy,
+								    module_name,
+								    (const gchar **) files,
+								    item_queue_processed_cb,
+								    processor);
+
+		return TRUE;
+	}
+
+	/* Process the moved items last */
+	queue = get_next_queue_with_data (processor->private->modules,
+					  processor->private->items_moved_queues,
+					  &module_name);
+
+	if (queue) {
+		const gchar *source;
+		const gchar *target;
+
+		files = tracker_dbus_queue_gfile_to_strv (queue, 2);
+
+		if (files) {
+			source = files[0];
+			target = files[1];
+		} else {
+			source = NULL;
+			target = NULL;
+		}
+
+		g_message ("Queue for module:'%s' moved items processed, sending first %d to the indexer",
+			   module_name,
+			   g_strv_length (files));
+
+		processor->private->sent_type = SENT_TYPE_MOVED;
+		processor->private->sent_module_name = module_name;
+		processor->private->sent_items = files;
+
+		org_freedesktop_Tracker_Indexer_file_move_async (processor->private->indexer_proxy,
+								 module_name,
+								 source,
+								 target,
+								 item_queue_processed_cb,
+								 processor);
+
+		return TRUE;
+	}
+
+	g_message ("No items in any queues to process, doing nothing");
+	processor->private->item_queues_handler_id = 0;
+
+	return FALSE;
+}
+
+static void
+item_queue_handlers_set_up (TrackerProcessor *processor)
+{
+	if (processor->private->item_queues_handler_id != 0) {
+		return;
+	}
+
+	processor->private->item_queues_handler_id =
+		g_timeout_add (ITEMS_QUEUE_PROCESS_INTERVAL,
+			       item_queue_handlers_cb,
+			       processor);
+}
+
+static gboolean
+is_path_on_ignore_list (GSList	    *ignore_list,
+			const gchar *path)
+{
+	GSList *l;
+
+	if (!ignore_list || !path) {
+		return FALSE;
+	}
+
+	for (l = ignore_list; l; l = l->next) {
+		if (strcmp (path, l->data) == 0) {
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+static void
+process_module_files_add_removable_media (TrackerProcessor *processor)
+{
+	TrackerCrawler *crawler;
+	GSList	       *roots;
+	GSList	       *l;
+	const gchar    *module_name = "files";
+
+	crawler = g_hash_table_lookup (processor->private->crawlers, module_name);
+
+#ifdef HAVE_HAL
+	roots = tracker_hal_get_removable_device_roots (processor->private->hal);
+#else  /* HAVE_HAL */
+	roots = NULL;
+#endif /* HAVE_HAL */
+
+	g_message ("  Removable media monitors being added:");
+
+	for (l = roots; l; l = l->next) {
+		GFile *file;
+
+		/* This is dreadfully inefficient */
+		if (path_should_be_ignored_for_media (processor, l->data)) {
+			g_message ("	%s (ignored due to config)", (gchar*) l->data);
+			continue;
+		}
+
+		g_message ("	%s", (gchar*) l->data);
+
+		file = g_file_new_for_path (l->data);
+		tracker_monitor_add (processor->private->monitor, module_name, file);
+		g_object_unref (file);
+	}
+
+	if (g_slist_length (roots) == 0) {
+		g_message ("	NONE");
+	}
+
+	g_message ("  Removable media crawls being added:");
+
+	for (l = roots; l; l = l->next) {
+		/* This is dreadfully inefficient */
+		if (path_should_be_ignored_for_media (processor, l->data)) {
+			g_message ("	%s (ignored due to config)", (gchar*) l->data);
+			continue;
+		}
+
+		g_message ("	%s", (gchar*) l->data);
+		tracker_crawler_add_path (crawler, l->data);
+	}
+
+	if (g_slist_length (roots) == 0) {
+		g_message ("	NONE");
+	}
+
+	tracker_crawler_set_use_module_paths (crawler, FALSE);
+}
+
+static void
+process_module_files_add_legacy_options (TrackerProcessor *processor)
+{
+	TrackerCrawler *crawler;
+	GSList	       *no_watch_roots;
+	GSList	       *watch_roots;
+	GSList	       *crawl_roots;
+	GSList	       *l;
+	guint		watch_root_count;
+	guint		crawl_root_count;
+	const gchar    *module_name = "files";
+
+	crawler = g_hash_table_lookup (processor->private->crawlers, module_name);
+
+	no_watch_roots = tracker_config_get_no_watch_directory_roots (processor->private->config);
+	watch_roots = tracker_config_get_watch_directory_roots (processor->private->config);
+	crawl_roots = tracker_config_get_crawl_directory_roots (processor->private->config);
+
+	watch_root_count = 0;
+	crawl_root_count = 0;
+
+	/* This module get special treatment to make sure legacy
+	 * options are supported.
+	 */
+
+	/* Print ignored locations */
+	g_message ("  User ignored crawls & monitors:");
+	for (l = no_watch_roots; l; l = l->next) {
+		g_message ("	%s", (gchar*) l->data);
+	}
+
+	if (g_slist_length (no_watch_roots) == 0) {
+		g_message ("	NONE");
+	}
+
+	/* Add monitors, from WatchDirectoryRoots config key */
+	g_message ("  User monitors being added:");
+	for (l = watch_roots; l; l = l->next) {
+		GFile *file;
+
+		if (is_path_on_ignore_list (no_watch_roots, l->data)) {
+			continue;
+		}
+
+		g_message ("	%s", (gchar*) l->data);
+
+		file = g_file_new_for_path (l->data);
+		tracker_monitor_add (processor->private->monitor, module_name, file);
+		g_object_unref (file);
+
+		watch_root_count++;
+	}
+
+	if (g_slist_length (watch_roots) == 0) {
+		g_message ("	NONE");
+	}
+
+	/* Add crawls, from WatchDirectoryRoots and
+	 * CrawlDirectoryRoots config keys.
+	 */
+	g_message ("  User crawls being added:");
+
+	for (l = watch_roots; l; l = l->next) {
+		if (is_path_on_ignore_list (no_watch_roots, l->data)) {
+			continue;
+		}
+
+		g_message ("	%s", (gchar*) l->data);
+		tracker_crawler_add_path (crawler, l->data);
+	}
+
+	for (l = crawl_roots; l; l = l->next) {
+		if (is_path_on_ignore_list (no_watch_roots, l->data)) {
+			continue;
+		}
+
+		g_message ("	%s", (gchar*) l->data);
+		tracker_crawler_add_path (crawler, l->data);
+
+		crawl_root_count++;
+	}
+
+	if (g_slist_length (watch_roots) == 0 &&
+	    g_slist_length (crawl_roots) == 0) {
+		g_message ("	NONE");
+	}
+}
+
+static void
+process_module (TrackerProcessor *processor,
+		const gchar	 *module_name,
+		gboolean	  is_removable_media)
+{
+	TrackerCrawler *crawler;
+	GSList	       *disabled_modules;
+
+	g_message ("Processing module:'%s' %s",
+		   module_name,
+		   is_removable_media ? "(for removable media)" : "");
+
+	/* Check it is enabled */
+	if (!tracker_module_config_get_enabled (module_name)) {
+		g_message ("  Module disabled");
+		process_module_next (processor);
+		return;
+	}
+
+	/* Check it is is not disabled by the user locally */
+	disabled_modules = tracker_config_get_disabled_modules (processor->private->config);
+	if (g_slist_find_custom (disabled_modules, module_name, (GCompareFunc) strcmp)) {
+		g_message ("  Module disabled by user");
+		process_module_next (processor);
+		return;
+	}
+
+	/* Here we set up legacy .cfg options like watch roots */
+	tracker_status_set_and_signal (TRACKER_STATUS_WATCHING);
+
+	if (strcmp (module_name, "files") == 0) {
+		if (is_removable_media) {
+			process_module_files_add_removable_media (processor);
+		} else {
+			process_module_files_add_legacy_options (processor);
+		}
+	}
+
+	/* Gets all files and directories */
+	tracker_status_set_and_signal (TRACKER_STATUS_PENDING);
+
+	crawler = g_hash_table_lookup (processor->private->crawlers, module_name);
+
+	if (!tracker_crawler_start (crawler)) {
+		/* If there is nothing to crawl, we are done, process
+		 * the next module.
+		 */
+		process_module_next (processor);
+	}
+}
+
+static void
+process_module_next (TrackerProcessor *processor)
+{
+	const gchar *module_name;
+
+	/* Don't recursively iterate the modules if this function is
+	 * called, check first.
+	 */
+	if (!processor->private->current_module) {
+		if (!processor->private->iterated_modules) {
+			processor->private->current_module = processor->private->modules;
+		}
+	} else {
+		processor->private->current_module = processor->private->current_module->next;
+	}
+
+	/* If we have no further modules to iterate */
+	if (!processor->private->current_module) {
+		processor->private->iterated_modules = TRUE;
+
+		/* If we have no more modules but some removable media
+		 * was added during the initial crawl, we then make
+		 * sure we crawl the new removable media too.
+		 */
+		if (processor->private->iterated_removable_media) {
+			processor->private->interrupted = FALSE;
+			tracker_processor_stop (processor);
+			return;
+		}
+
+		/* We use this for removable media */
+		module_name = "files";
+		processor->private->iterated_removable_media = TRUE;
+	} else {
+		module_name = processor->private->current_module->data;
+	}
+
+	/* Set up new crawler for new module */
+	process_module (processor, module_name, processor->private->iterated_removable_media);
+}
+
+static void
+indexer_status_cb (DBusGProxy  *proxy,
+		   gdouble	seconds_elapsed,
+		   const gchar *current_module_name,
+		   guint	items_done,
+		   guint	items_remaining,
+		   gpointer	user_data)
+{
+	TrackerProcessor *processor;
+	TrackerDBIndex	 *index;
+	GQueue		 *queue;
+	GFile		 *file;
+	gchar		 *path = NULL;
+	gchar		 *str1;
+	gchar		 *str2;
+
+	processor = user_data;
+
+	/* Update our local copy */
+	processor->private->items_done = items_done;
+	processor->private->items_remaining = items_remaining;
+	processor->private->seconds_elapsed = seconds_elapsed;
+
+	if (items_remaining < 1 ||
+	    current_module_name == NULL ||
+	    current_module_name[0] == '\0') {
+		return;
+	}
+
+	/* Signal to any applications */
+	queue = g_hash_table_lookup (processor->private->items_created_queues, current_module_name);
+	file = g_queue_peek_tail (queue);
+	if (file) {
+		path = g_file_get_path (file);
+	}
+
+	g_signal_emit_by_name (tracker_dbus_get_object (TRACKER_TYPE_DAEMON),
+			       "index-progress",
+			       tracker_module_config_get_index_service (current_module_name),
+			       path ? path : "",
+			       items_done,
+			       items_remaining,
+			       items_done + items_remaining,
+			       seconds_elapsed);
+	g_free (path);
+
+	/* Tell the index that it can reload, really we should do
+	 * module_name->index type so we don't do this for both
+	 * every time:
+	 */
+	index = tracker_db_index_manager_get_index (TRACKER_DB_INDEX_FILE);
+	tracker_db_index_set_reload (index, TRUE);
+
+	index = tracker_db_index_manager_get_index (TRACKER_DB_INDEX_EMAIL);
+	tracker_db_index_set_reload (index, TRUE);
+
+	/* Message to the console about state */
+	str1 = tracker_seconds_estimate_to_string (seconds_elapsed,
+						   TRUE,
+						   items_done,
+						   items_remaining);
+	str2 = tracker_seconds_to_string (seconds_elapsed, TRUE);
+
+	g_message ("Indexed %d/%d, module:'%s', %s left, %s elapsed",
+		   items_done,
+		   items_done + items_remaining,
+		   current_module_name,
+		   str1,
+		   str2);
+
+	g_free (str2);
+	g_free (str1);
+}
+
+static void
+indexer_finished_cb (DBusGProxy  *proxy,
+		     gdouble	  seconds_elapsed,
+		     guint	  items_done,
+		     gboolean	  interrupted,
+		     gpointer	  user_data)
+{
+	TrackerProcessor *processor;
+	TrackerDBIndex	 *index;
+	GObject		 *object;
+	gchar		 *str;
+
+	processor = user_data;
+	object = tracker_dbus_get_object (TRACKER_TYPE_DAEMON);
+
+	processor->private->items_done = items_done;
+	processor->private->items_remaining = 0;
+	processor->private->seconds_elapsed = seconds_elapsed;
+
+	/* Signal to any applications */
+	g_signal_emit_by_name (object,
+			       "index-progress",
+			       "", /* Service */
+			       "", /* Path */
+			       items_done,
+			       0,
+			       items_done,
+			       seconds_elapsed);
+
+	/* Tell the index that it can reload, really we should do
+	 * module_name->index type so we don't do this for both
+	 * every time:
+	 */
+	index = tracker_db_index_manager_get_index (TRACKER_DB_INDEX_FILE);
+	tracker_db_index_set_reload (index, TRUE);
+
+	index = tracker_db_index_manager_get_index (TRACKER_DB_INDEX_EMAIL);
+	tracker_db_index_set_reload (index, TRUE);
+
+	/* Message to the console about state */
+	str = tracker_seconds_to_string (seconds_elapsed, FALSE);
+
+	g_message ("Indexer finished in %s, %d items indexed in total",
+		   str,
+		   items_done);
+	g_free (str);
+
+	/* Do we even need this step Optimizing ? */
+	tracker_status_set_and_signal (TRACKER_STATUS_OPTIMIZING);
+
+	/* Now the indexer is done, we can signal our status as IDLE */
+	tracker_status_set_and_signal (TRACKER_STATUS_IDLE);
+
+	/* Signal to the applet we are finished */
+	g_signal_emit_by_name (object,
+			       "index-finished",
+			       seconds_elapsed);
+
+	/* Signal the processor is now finished */
+	processor->private->finished = TRUE;
+	g_signal_emit (processor, signals[FINISHED], 0);
+}
+
+static void
+processor_files_check (TrackerProcessor *processor,
+		       const gchar	*module_name,
+		       GFile		*file,
+		       gboolean		 is_directory)
+{
+	TrackerCrawler *crawler;
+	GQueue	       *queue;
+	gboolean	ignored;
+	gchar	       *path;
+
+	path = g_file_get_path (file);
+	crawler = g_hash_table_lookup (processor->private->crawlers, module_name);
+	ignored = tracker_crawler_is_path_ignored (crawler, path, is_directory);
+
+	g_debug ("%s:'%s' (create monitor event or user request)",
+		 ignored ? "Ignored" : "Found ",
+		 path);
+
+	g_free (path);
+
+	if (ignored) {
+		return;
+	}
+
+	queue = g_hash_table_lookup (processor->private->items_created_queues, module_name);
+	g_queue_push_tail (queue, g_object_ref (file));
+
+	item_queue_handlers_set_up (processor);
+}
+
+static void
+processor_files_update (TrackerProcessor *processor,
+			const gchar	 *module_name,
+			GFile		 *file,
+			gboolean	  is_directory)
+{
+	TrackerCrawler *crawler;
+	GQueue	       *queue;
+	gchar	       *path;
+	gboolean	ignored;
+
+	path = g_file_get_path (file);
+	crawler = g_hash_table_lookup (processor->private->crawlers, module_name);
+	ignored = tracker_crawler_is_path_ignored (crawler, path, is_directory);
+
+	g_debug ("%s:'%s' (update monitor event or user request)",
+		 ignored ? "Ignored" : "Found ",
+		 path);
+
+	g_free (path);
+
+	if (ignored) {
+		return;
+	}
+
+	queue = g_hash_table_lookup (processor->private->items_updated_queues, module_name);
+	g_queue_push_tail (queue, g_object_ref (file));
+
+	item_queue_handlers_set_up (processor);
+}
+
+static void
+processor_files_delete (TrackerProcessor *processor,
+			const gchar	 *module_name,
+			GFile		 *file,
+			gboolean	  is_directory)
+{
+	TrackerCrawler *crawler;
+	GQueue	       *queue;
+	gchar	       *path;
+	gboolean	ignored;
+
+	path = g_file_get_path (file);
+	crawler = g_hash_table_lookup (processor->private->crawlers, module_name);
+	ignored = tracker_crawler_is_path_ignored (crawler, path, is_directory);
+
+	g_debug ("%s:'%s' (delete monitor event or user request)",
+		 ignored ? "Ignored" : "Found ",
+		 path);
+
+	g_free (path);
+
+	if (ignored) {
+		return;
+	}
+
+	queue = g_hash_table_lookup (processor->private->items_deleted_queues, module_name);
+	g_queue_push_tail (queue, g_object_ref (file));
+
+	item_queue_handlers_set_up (processor);
+}
+
+static void
+processor_files_move (TrackerProcessor *processor,
+		      const gchar      *module_name,
+		      GFile	       *file,
+		      GFile	       *other_file,
+		      gboolean		is_directory)
+{
+	TrackerCrawler *crawler;
+	GQueue	       *queue;
+	gchar	       *path;
+	gchar	       *other_path;
+	gboolean	path_ignored;
+	gboolean	other_path_ignored;
+
+	path = g_file_get_path (file);
+	other_path = g_file_get_path (other_file);
+	crawler = g_hash_table_lookup (processor->private->crawlers, module_name);
+
+	path_ignored = tracker_crawler_is_path_ignored (crawler, path, is_directory);
+	other_path_ignored = tracker_crawler_is_path_ignored (crawler, other_path, is_directory);
+
+	g_debug ("%s:'%s'->'%s':%s (move monitor event or user request)",
+		 path_ignored ? "Ignored" : "Found ",
+		 path,
+		 other_path,
+		 other_path_ignored ? "Ignored" : " Found");
+
+	g_free (other_path);
+	g_free (path);
+
+	if (path_ignored && other_path_ignored) {
+		/* Do nothing */
+		return;
+	} else if (path_ignored) {
+		/* Check new file */
+		queue = g_hash_table_lookup (processor->private->items_created_queues, module_name);
+		g_queue_push_tail (queue, g_object_ref (other_file));
+	} else if (other_path_ignored) {
+		/* Delete old file */
+		queue = g_hash_table_lookup (processor->private->items_deleted_queues, module_name);
+		g_queue_push_tail (queue, g_object_ref (file));
+	} else {
+		/* Move old file to new file */
+		queue = g_hash_table_lookup (processor->private->items_moved_queues, module_name);
+		g_queue_push_tail (queue, g_object_ref (file));
+		g_queue_push_tail (queue, g_object_ref (other_file));
+	}
+
+	item_queue_handlers_set_up (processor);
+}
+
+static void
+monitor_item_created_cb (TrackerMonitor *monitor,
+			 const gchar	*module_name,
+			 GFile		*file,
+			 gboolean	 is_directory,
+			 gpointer	 user_data)
+{
+	tracker_processor_files_check (user_data, module_name, file, is_directory);
+}
+
+static void
+monitor_item_updated_cb (TrackerMonitor *monitor,
+			 const gchar	*module_name,
+			 GFile		*file,
+			 gboolean	 is_directory,
+			 gpointer	 user_data)
+{
+	processor_files_update (user_data, module_name, file, is_directory);
+}
+
+static void
+monitor_item_deleted_cb (TrackerMonitor *monitor,
+			 const gchar	*module_name,
+			 GFile		*file,
+			 gboolean	 is_directory,
+			 gpointer	 user_data)
+{
+	processor_files_delete (user_data, module_name, file, is_directory);
+}
+
+static void
+monitor_item_moved_cb (TrackerMonitor *monitor,
+		       const gchar    *module_name,
+		       GFile	      *file,
+		       GFile	      *other_file,
+		       gboolean        is_directory,
+		       gpointer        user_data)
+{
+	processor_files_move (user_data, module_name, file, other_file, is_directory);
+}
+
+static void
+crawler_processing_file_cb (TrackerCrawler *crawler,
+			    const gchar    *module_name,
+			    GFile	   *file,
+			    gpointer	    user_data)
+{
+	TrackerProcessor *processor;
+	GQueue		 *queue;
+
+	processor = user_data;
+
+	/* Add files in queue to our queues to send to the indexer */
+	queue = g_hash_table_lookup (processor->private->items_created_queues, module_name);
+	g_queue_push_tail (queue, g_object_ref (file));
+}
+
+static void
+crawler_processing_directory_cb (TrackerCrawler *crawler,
+				 const gchar	*module_name,
+				 GFile		*file,
+				 gpointer	 user_data)
+{
+	TrackerProcessor *processor;
+	GQueue		 *queue;
+	gboolean	  add_monitor;
+
+	processor = user_data;
+
+	/* FIXME: Get ignored directories from .cfg? We know that
+	 * normally these would have monitors because these
+	 * directories are those crawled based on the module config.
+	 */
+	add_monitor = TRUE;
+
+	/* Should we add? */
+	if (add_monitor) {
+		tracker_monitor_add (processor->private->monitor, module_name, file);
+	}
+
+	/* Add files in queue to our queues to send to the indexer */
+	queue = g_hash_table_lookup (processor->private->items_created_queues, module_name);
+	g_queue_push_tail (queue, g_object_ref (file));
+}
+
+static void
+crawler_finished_cb (TrackerCrawler *crawler,
+		     const gchar    *module_name,
+		     guint	     directories_found,
+		     guint	     directories_ignored,
+		     guint	     files_found,
+		     guint	     files_ignored,
+		     gpointer	     user_data)
+{
+	TrackerProcessor *processor;
+
+	processor = user_data;
+
+	processor->private->directories_found += directories_found;
+	processor->private->directories_ignored += directories_ignored;
+	processor->private->files_found += files_found;
+	processor->private->files_ignored += files_ignored;
+
+	/* Proceed to next module */
+	process_module_next (processor);
+}
+
+#ifdef HAVE_HAL
+
+static void
+mount_point_added_cb (TrackerHal  *hal,
+		      const gchar *mount_point,
+		      gpointer	   user_data)
+{
+	TrackerProcessor *processor;
+	TrackerStatus	  status;
+
+	processor = user_data;
+
+	g_message ("** TRAWLING THROUGH NEW MOUNT POINT:'%s'", mount_point);
+
+	status = tracker_status_get ();
+
+	processor->private->iterated_removable_media = FALSE;
+
+	if (status == TRACKER_STATUS_INDEXING ||
+	    status == TRACKER_STATUS_OPTIMIZING ||
+	    status == TRACKER_STATUS_IDLE) {
+		/* If we are indexing then we must have already
+		 * crawled all locations so we need to start up the
+		 * processor again for the removable media once more.
+		 */
+		process_module_next (processor);
+	}
+}
+
+static void
+mount_point_removed_cb (TrackerHal  *hal,
+			const gchar *mount_point,
+			gpointer     user_data)
+{
+	TrackerProcessor *processor;
+	GFile		 *file;
+
+	processor = user_data;
+
+	g_message ("** CLEANING UP OLD MOUNT POINT:'%s'", mount_point);
+
+	/* Remove the monitor? */
+	file = g_file_new_for_path (mount_point);
+	tracker_monitor_remove (processor->private->monitor, "files", file);
+	g_object_unref (file);
+}
+
+#endif /* HAVE_HAL */
+
+TrackerProcessor *
+tracker_processor_new (TrackerConfig *config,
+		       TrackerHal    *hal)
+{
+	TrackerProcessor	*processor;
+	TrackerProcessorPrivate *priv;
+	TrackerCrawler		*crawler;
+	DBusGProxy		*proxy;
+	GList			*l;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), NULL);
+
+#ifdef HAVE_HAL
+	g_return_val_if_fail (TRACKER_IS_HAL (hal), NULL);
+#endif /* HAVE_HAL */
+
+	tracker_status_set_and_signal (TRACKER_STATUS_INITIALIZING);
+
+	processor = g_object_new (TRACKER_TYPE_PROCESSOR, NULL);
+	priv = processor->private;
+
+	/* Set up config */
+	priv->config = g_object_ref (config);
+
+#ifdef HAVE_HAL
+	/* Set up hal */
+	priv->hal = g_object_ref (hal);
+
+	g_signal_connect (priv->hal, "mount-point-added",
+			  G_CALLBACK (mount_point_added_cb),
+			  processor);
+	g_signal_connect (priv->hal, "mount-point-removed",
+			  G_CALLBACK (mount_point_removed_cb),
+			  processor);
+#endif /* HAVE_HAL */
+
+	/* Set up the crawlers now we have config and hal */
+	for (l = priv->modules; l; l = l->next) {
+		crawler = tracker_crawler_new (priv->config, l->data);
+
+		g_signal_connect (crawler, "processing-file",
+				  G_CALLBACK (crawler_processing_file_cb),
+				  processor);
+		g_signal_connect (crawler, "processing-directory",
+				  G_CALLBACK (crawler_processing_directory_cb),
+				  processor);
+		g_signal_connect (crawler, "finished",
+				  G_CALLBACK (crawler_finished_cb),
+				  processor);
+
+		g_hash_table_insert (priv->crawlers,
+				     g_strdup (l->data),
+				     crawler);
+	}
+
+	/* Set up the monitor */
+	priv->monitor = tracker_monitor_new (config);
+
+	g_message ("Disabling monitor events until we have crawled the file system");
+	tracker_monitor_set_enabled (priv->monitor, FALSE);
+
+	g_signal_connect (priv->monitor, "item-created",
+			  G_CALLBACK (monitor_item_created_cb),
+			  processor);
+	g_signal_connect (priv->monitor, "item-updated",
+			  G_CALLBACK (monitor_item_updated_cb),
+			  processor);
+	g_signal_connect (priv->monitor, "item-deleted",
+			  G_CALLBACK (monitor_item_deleted_cb),
+			  processor);
+	g_signal_connect (priv->monitor, "item-moved",
+			  G_CALLBACK (monitor_item_moved_cb),
+			  processor);
+
+	/* Set up the indexer proxy and signalling to know when we are
+	 * finished.
+	 */
+	proxy = tracker_dbus_indexer_get_proxy ();
+	priv->indexer_proxy = g_object_ref (proxy);
+
+	dbus_g_proxy_connect_signal (proxy, "Status",
+				     G_CALLBACK (indexer_status_cb),
+				     processor,
+				     NULL);
+	dbus_g_proxy_connect_signal (proxy, "Finished",
+				     G_CALLBACK (indexer_finished_cb),
+				     processor,
+				     NULL);
+
+	return processor;
+}
+
+void
+tracker_processor_start (TrackerProcessor *processor)
+{
+	g_return_if_fail (TRACKER_IS_PROCESSOR (processor));
+
+	g_message ("Starting to process %d modules...",
+		   g_list_length (processor->private->modules));
+
+	if (processor->private->timer) {
+		g_timer_destroy (processor->private->timer);
+	}
+
+	processor->private->timer = g_timer_new ();
+
+	processor->private->interrupted = TRUE;
+
+	process_module_next (processor);
+}
+
+void
+tracker_processor_stop (TrackerProcessor *processor)
+{
+	gdouble elapsed;
+
+	g_return_if_fail (TRACKER_IS_PROCESSOR (processor));
+
+	if (processor->private->interrupted) {
+		TrackerCrawler *crawler;
+
+		crawler = g_hash_table_lookup (processor->private->crawlers,
+					       processor->private->current_module->data);
+		tracker_crawler_stop (crawler);
+
+	}
+
+	/* Now we have finished crawling, we enable monitor events */
+	g_message ("Enabling monitor events");
+	tracker_monitor_set_enabled (processor->private->monitor, TRUE);
+
+	g_message ("Process %s\n",
+		   processor->private->finished ? "has finished" : "been stopped");
+
+	if (processor->private->timer) {
+		g_timer_stop (processor->private->timer);
+		elapsed = g_timer_elapsed (processor->private->timer, NULL);
+	} else {
+		elapsed = 0;
+	}
+
+	g_message ("Total time taken : %4.4f seconds",
+		   elapsed);
+	g_message ("Total directories: %d (%d ignored)",
+		   processor->private->directories_found,
+		   processor->private->directories_ignored);
+	g_message ("Total files      : %d (%d ignored)",
+		   processor->private->files_found,
+		   processor->private->files_ignored);
+	g_message ("Total monitors   : %d\n",
+		   tracker_monitor_get_count (processor->private->monitor, NULL));
+
+	/* Here we set to IDLE when we were stopped, otherwise, we
+	 * we are currently in the process of sending files to the
+	 * indexer and we set the state to INDEXING
+	 */
+	if (processor->private->interrupted) {
+		/* Do we even need this step optimizing ? */
+		tracker_status_set_and_signal (TRACKER_STATUS_OPTIMIZING);
+
+		/* All done */
+		tracker_status_set_and_signal (TRACKER_STATUS_IDLE);
+
+		processor->private->finished = TRUE;
+		g_signal_emit (processor, signals[FINISHED], 0);
+	} else {
+		item_queue_handlers_set_up (processor);
+	}
+}
+
+void
+tracker_processor_files_check (TrackerProcessor *processor,
+			       const gchar	*module_name,
+			       GFile		*file,
+			       gboolean		 is_directory)
+{
+	g_return_if_fail (TRACKER_IS_PROCESSOR (processor));
+	g_return_if_fail (module_name != NULL);
+	g_return_if_fail (G_IS_FILE (file));
+
+	processor_files_check (processor, module_name, file, is_directory);
+}
+
+void
+tracker_processor_files_update (TrackerProcessor *processor,
+				const gchar	 *module_name,
+				GFile		 *file,
+				gboolean	  is_directory)
+{
+	g_return_if_fail (TRACKER_IS_PROCESSOR (processor));
+	g_return_if_fail (module_name != NULL);
+	g_return_if_fail (G_IS_FILE (file));
+
+	processor_files_update (processor, module_name, file, is_directory);
+}
+
+void
+tracker_processor_files_delete (TrackerProcessor *processor,
+				const gchar	 *module_name,
+				GFile		 *file,
+				gboolean	  is_directory)
+{
+	g_return_if_fail (TRACKER_IS_PROCESSOR (processor));
+	g_return_if_fail (module_name != NULL);
+	g_return_if_fail (G_IS_FILE (file));
+
+	processor_files_delete (processor, module_name, file, is_directory);
+}
+
+void
+tracker_processor_files_move (TrackerProcessor *processor,
+			      const gchar      *module_name,
+			      GFile	       *file,
+			      GFile	       *other_file,
+			      gboolean		is_directory)
+{
+	g_return_if_fail (TRACKER_IS_PROCESSOR (processor));
+	g_return_if_fail (module_name != NULL);
+	g_return_if_fail (G_IS_FILE (file));
+	g_return_if_fail (G_IS_FILE (other_file));
+
+	processor_files_move (processor, module_name, file, other_file, is_directory);
+}
+
+guint
+tracker_processor_get_directories_found (TrackerProcessor *processor)
+{
+	g_return_val_if_fail (TRACKER_IS_PROCESSOR (processor), 0);
+
+	return processor->private->directories_found;
+}
+
+guint
+tracker_processor_get_directories_ignored (TrackerProcessor *processor)
+{
+	g_return_val_if_fail (TRACKER_IS_PROCESSOR (processor), 0);
+
+	return processor->private->directories_ignored;
+}
+
+guint
+tracker_processor_get_directories_total (TrackerProcessor *processor)
+{
+	g_return_val_if_fail (TRACKER_IS_PROCESSOR (processor), 0);
+
+	return processor->private->directories_found + processor->private->directories_ignored;
+}
+
+guint
+tracker_processor_get_files_found (TrackerProcessor *processor)
+{
+	g_return_val_if_fail (TRACKER_IS_PROCESSOR (processor), 0);
+
+	return processor->private->files_found;
+}
+
+guint
+tracker_processor_get_files_ignored (TrackerProcessor *processor)
+{
+	g_return_val_if_fail (TRACKER_IS_PROCESSOR (processor), 0);
+
+	return processor->private->files_ignored;
+}
+
+guint
+tracker_processor_get_files_total (TrackerProcessor *processor)
+{
+	g_return_val_if_fail (TRACKER_IS_PROCESSOR (processor), 0);
+
+	return processor->private->files_found + processor->private->files_ignored;
+}
+
+gdouble
+tracker_processor_get_seconds_elapsed (TrackerProcessor *processor)
+{
+	g_return_val_if_fail (TRACKER_IS_PROCESSOR (processor), 0);
+
+	return processor->private->seconds_elapsed;
+}

Added: trunk/src/trackerd/tracker-processor.h
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-processor.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,91 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKERD_PROCESSOR_H__
+#define __TRACKERD_PROCESSOR_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include <libtracker-common/tracker-config.h>
+#include <libtracker-common/tracker-hal.h>
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_PROCESSOR	       (tracker_processor_get_type())
+#define TRACKER_PROCESSOR(o)	       (G_TYPE_CHECK_INSTANCE_CAST ((o), TRACKER_TYPE_PROCESSOR, TrackerProcessor))
+#define TRACKER_PROCESSOR_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c),	 TRACKER_TYPE_PROCESSOR, TrackerProcessorClass))
+#define TRACKER_IS_PROCESSOR(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), TRACKER_TYPE_PROCESSOR))
+#define TRACKER_IS_PROCESSOR_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c),	 TRACKER_TYPE_PROCESSOR))
+#define TRACKER_PROCESSOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o),  TRACKER_TYPE_PROCESSOR, TrackerProcessorClass))
+
+typedef struct TrackerProcessor        TrackerProcessor;
+typedef struct TrackerProcessorClass   TrackerProcessorClass;
+typedef struct TrackerProcessorPrivate TrackerProcessorPrivate;
+
+struct TrackerProcessor {
+	GObject			 parent;
+	TrackerProcessorPrivate *private;
+};
+
+struct TrackerProcessorClass {
+	GObjectClass		 parent;
+
+	void (*finished) (TrackerProcessor *processor);
+};
+
+GType		  tracker_processor_get_type		    (void) G_GNUC_CONST;
+
+TrackerProcessor *tracker_processor_new			    (TrackerConfig    *config,
+							     TrackerHal       *hal);
+void		  tracker_processor_start		    (TrackerProcessor *processor);
+void		  tracker_processor_stop		    (TrackerProcessor *processor);
+
+/* Required API for org.freedesktop.Tracker.Files */
+void		  tracker_processor_files_check		    (TrackerProcessor *processor,
+							     const gchar      *module_name,
+							     GFile	      *file,
+							     gboolean	       is_directory);
+void		  tracker_processor_files_update	    (TrackerProcessor *processor,
+							     const gchar      *module_name,
+							     GFile	      *file,
+							     gboolean	       is_directory);
+void		  tracker_processor_files_delete	    (TrackerProcessor *processor,
+							     const gchar      *module_name,
+							     GFile	      *file,
+							     gboolean	       is_directory);
+void		  tracker_processor_files_move		    (TrackerProcessor *processor,
+							     const gchar      *module_name,
+							     GFile	      *file,
+							     GFile	      *other_file,
+							     gboolean	       is_directory);
+
+/* Statistics */
+guint		  tracker_processor_get_directories_found   (TrackerProcessor *processor);
+guint		  tracker_processor_get_directories_ignored (TrackerProcessor *processor);
+guint		  tracker_processor_get_directories_total   (TrackerProcessor *processor);
+guint		  tracker_processor_get_files_found	    (TrackerProcessor *processor);
+guint		  tracker_processor_get_files_ignored	    (TrackerProcessor *processor);
+guint		  tracker_processor_get_files_total	    (TrackerProcessor *processor);
+gdouble		  tracker_processor_get_seconds_elapsed     (TrackerProcessor *processor);
+
+G_END_DECLS
+
+#endif /* __TRACKERD_PROCESSOR_H__ */

Added: trunk/src/trackerd/tracker-query-tree.c
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-query-tree.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,882 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+/* These defines are required to allow lrintf() without warnings when
+ * building.
+ */
+#define _ISOC9X_SOURCE	1
+#define _ISOC99_SOURCE	1
+
+#define __USE_ISOC9X	1
+#define __USE_ISOC99	1
+
+#include <string.h>
+#include <math.h>
+#include <depot.h>
+
+#include <glib-object.h>
+
+#include <libtracker-common/tracker-config.h>
+#include <libtracker-common/tracker-parser.h>
+#include <libtracker-common/tracker-ontology.h>
+
+#include <libtracker-db/tracker-db-index-item.h>
+#include <libtracker-db/tracker-db-index-manager.h>
+
+#include "tracker-query-tree.h"
+#include "tracker-utils.h"
+
+#define TRACKER_QUERY_TREE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRACKER_TYPE_QUERY_TREE, TrackerQueryTreePrivate))
+
+#define MAX_HIT_BUFFER 480000
+#define SCORE_MULTIPLIER 100000
+
+typedef enum   OperationType OperationType;
+typedef enum   TreeNodeType TreeNodeType;
+typedef struct TreeNode TreeNode;
+typedef struct TrackerQueryTreePrivate TrackerQueryTreePrivate;
+typedef struct ComposeHitsData ComposeHitsData;
+typedef struct SearchHitData SearchHitData;
+
+enum OperationType {
+	OP_NONE,
+	OP_AND,
+	OP_OR
+};
+
+struct TreeNode {
+	TreeNode      *left;
+	TreeNode      *right;
+	OperationType  op;
+	gchar	      *term;
+};
+
+struct TrackerQueryTreePrivate {
+	gchar		*query_str;
+	TreeNode	*tree;
+	TrackerConfig	*config;
+	TrackerLanguage *language;
+	GArray		*services;
+};
+
+struct ComposeHitsData {
+	OperationType  op;
+	GHashTable    *other_table;
+	GHashTable    *dest_table;
+};
+
+struct SearchHitData {
+	guint32 service_type_id;
+	guint32 score;
+};
+
+enum {
+	PROP_0,
+	PROP_QUERY,
+	PROP_SERVICES
+};
+
+static void tracker_query_tree_class_init   (TrackerQueryTreeClass *class);
+static void tracker_query_tree_init	    (TrackerQueryTree	   *tree);
+static void tracker_query_tree_finalize     (GObject		   *object);
+static void tracker_query_tree_set_property (GObject		   *object,
+					     guint		    prop_id,
+					     const GValue	   *value,
+					     GParamSpec		   *pspec);
+static void tracker_query_tree_get_property (GObject		   *object,
+					     guint		    prop_id,
+					     GValue		   *value,
+					     GParamSpec		   *pspec);
+
+G_DEFINE_TYPE (TrackerQueryTree, tracker_query_tree, G_TYPE_OBJECT)
+
+static void
+tracker_query_tree_class_init (TrackerQueryTreeClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	object_class->finalize = tracker_query_tree_finalize;
+	object_class->set_property = tracker_query_tree_set_property;
+	object_class->get_property = tracker_query_tree_get_property;
+
+	g_object_class_install_property (object_class,
+					 PROP_QUERY,
+					 g_param_spec_string ("query",
+							      "Query",
+							      "Query",
+							      NULL,
+							      G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_SERVICES,
+					 g_param_spec_pointer ("services",
+							       "Services",
+							       "GArray of services",
+							       G_PARAM_READWRITE));
+	g_type_class_add_private (object_class,
+				  sizeof (TrackerQueryTreePrivate));
+}
+
+static void
+tracker_query_tree_init (TrackerQueryTree *tree)
+{
+}
+
+static TreeNode *
+tree_node_leaf_new (const gchar *term)
+{
+	TreeNode *node;
+
+	node = g_slice_new0 (TreeNode);
+	node->term = g_strdup (term);
+	node->op = OP_NONE;
+
+	return node;
+}
+
+static TreeNode *
+tree_node_operator_new (OperationType op)
+{
+	TreeNode *node;
+
+	node = g_slice_new0 (TreeNode);
+	node->op = op;
+
+	return node;
+}
+
+static void
+tree_node_free (TreeNode *node)
+{
+	if (!node)
+		return;
+
+	/* Free string if any */
+	g_free (node->term);
+
+	/* free subnodes */
+	tree_node_free (node->left);
+	tree_node_free (node->right);
+
+	g_slice_free (TreeNode, node);
+}
+
+static void
+tracker_query_tree_finalize (GObject *object)
+{
+	TrackerQueryTreePrivate *priv;
+
+	priv = TRACKER_QUERY_TREE_GET_PRIVATE (object);
+
+	tree_node_free (priv->tree);
+	g_free (priv->query_str);
+
+	G_OBJECT_CLASS (tracker_query_tree_parent_class)->finalize (object);
+}
+
+static void
+tracker_query_tree_set_property (GObject      *object,
+				 guint	       prop_id,
+				 const GValue *value,
+				 GParamSpec   *pspec)
+{
+	switch (prop_id) {
+	case PROP_QUERY:
+		tracker_query_tree_set_query (TRACKER_QUERY_TREE (object),
+					      g_value_get_string (value));
+		break;
+	case PROP_SERVICES:
+		tracker_query_tree_set_services (TRACKER_QUERY_TREE (object),
+						 g_value_get_pointer (value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+	}
+}
+
+static void
+tracker_query_tree_get_property (GObject      *object,
+				 guint	       prop_id,
+				 GValue       *value,
+				 GParamSpec   *pspec)
+{
+	TrackerQueryTreePrivate *priv;
+
+	priv = TRACKER_QUERY_TREE_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_QUERY:
+		g_value_set_string (value, priv->query_str);
+		break;
+	case PROP_SERVICES:
+		g_value_set_pointer (value, priv->services);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+	}
+}
+
+TrackerQueryTree *
+tracker_query_tree_new (const gchar	*query_str,
+			TrackerConfig	*config,
+			TrackerLanguage *language,
+			GArray		*services)
+{
+	TrackerQueryTree	*object;
+	TrackerQueryTreePrivate *priv;
+
+	/* We accept one or both index and services, but not NULL for
+	 * both since we require at least one of them.
+	 */
+	g_return_val_if_fail (query_str != NULL, NULL);
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), NULL);
+	g_return_val_if_fail (TRACKER_IS_LANGUAGE (language), NULL);
+	g_return_val_if_fail (services != NULL, NULL);
+
+	/* NOTE: The "query" has to come AFTER the "config" and
+	 * "language" properties since setting the query actually
+	 * uses the priv->config and priv->language settings.
+	 * Changing this order results in warnings.
+	 */
+
+	object = g_object_new (TRACKER_TYPE_QUERY_TREE, NULL);
+
+	priv = TRACKER_QUERY_TREE_GET_PRIVATE (object);
+
+	priv->config = g_object_ref (config);
+	priv->language = g_object_ref (language);
+
+	if (services) {
+		tracker_query_tree_set_services (object, services);
+	}
+
+	tracker_query_tree_set_query (object, query_str);
+
+	return object;
+}
+
+#if 0
+static void
+print_tree (TreeNode *node)
+{
+	if (!node) {
+		g_print ("NULL ");
+		return;
+	}
+
+	switch (node->op) {
+	case OP_AND:
+		g_print ("AND ");
+		print_tree (node->left);
+		print_tree (node->right);
+		break;
+	case OP_OR:
+		g_print ("OR ");
+		print_tree (node->left);
+		print_tree (node->right);
+		break;
+	default:
+		g_print ("%s ", node->term);
+	}
+}
+#endif
+
+static void
+create_query_tree (TrackerQueryTree *tree)
+{
+	TrackerQueryTreePrivate *priv;
+	TreeNode *node, *stack_node;
+	GQueue *queue, *stack;
+	gboolean last_element_is_term = FALSE;
+	gchar *parsed_str, **strings;
+	gint i;
+
+	priv = TRACKER_QUERY_TREE_GET_PRIVATE (tree);
+
+	strings = g_strsplit (priv->query_str, " ", -1);
+	queue = g_queue_new ();
+	stack = g_queue_new ();
+
+	/* Create a parse tree that recognizes queries such as:
+	 * "foo"
+	 * "foo and bar"
+	 * "foo or bar"
+	 * "foo and bar or baz"
+	 * "foo or bar and baz"
+	 *
+	 * The operator "and" will have higher priority than the operator "or",
+	 * and will also be assumed to exist if there is no operator between two
+	 * search terms.
+	 */
+
+	/* Step 1. Create polish notation for the search term, the
+	 * stack will be used to store operators temporarily
+	 */
+	for (i = 0; strings[i]; i++) {
+		OperationType op;
+
+		if (!strings[i] || !*strings[i])
+			continue;
+
+		/* get operator type */
+		if (strcmp (strings[i], "and") == 0) {
+			op = OP_AND;
+		} else if (strcmp (strings [i], "or") == 0) {
+			op = OP_OR;
+		} else {
+			if (last_element_is_term) {
+				/* last element was a search term, assume the "and"
+				 * operator between these two elements, and wait
+				 * for actually parsing the second term */
+				op = OP_AND;
+				i--;
+			} else {
+				op = OP_NONE;
+			}
+		}
+
+		/* create node for the operation type */
+		switch (op) {
+		case OP_AND:
+			node = tree_node_operator_new (OP_AND);
+			stack_node = g_queue_peek_head (stack);
+
+			/* push in the queue operators with fewer priority, "or" in this case */
+			while (stack_node && stack_node->op == OP_OR) {
+				stack_node = g_queue_pop_head (stack);
+				g_queue_push_head (queue, stack_node);
+
+				stack_node = g_queue_peek_head (stack);
+			}
+
+			g_queue_push_head (stack, node);
+			last_element_is_term = FALSE;
+			break;
+		case OP_OR:
+			node = tree_node_operator_new (OP_OR);
+			g_queue_push_head (stack, node);
+			last_element_is_term = FALSE;
+			break;
+		default:
+			/* search term */
+			parsed_str = tracker_parser_text_to_string (strings[i],
+								    priv->language,
+								    tracker_config_get_max_word_length (priv->config),
+								    tracker_config_get_min_word_length (priv->config),
+								    TRUE,
+								    FALSE,
+								    FALSE);
+			node = tree_node_leaf_new (g_strstrip (parsed_str));
+			g_queue_push_head (queue, node);
+			last_element_is_term = TRUE;
+
+			g_free (parsed_str);
+		}
+	}
+
+	/* we've finished parsing, queue all remaining operators in the stack */
+	while ((stack_node = g_queue_pop_head (stack)) != NULL) {
+		g_queue_push_head (queue, stack_node);
+	}
+
+	/* step 2: run through the reverse polish notation and connect nodes to
+	 * create a tree, the stack will be used to store temporarily nodes
+	 * until they're connected to a parent node */
+	while ((node = g_queue_pop_tail (queue)) != NULL) {
+		switch (node->op) {
+		case OP_AND:
+		case OP_OR:
+			node->left = g_queue_pop_head (stack);
+			node->right = g_queue_pop_head (stack);
+			g_queue_push_head (stack, node);
+			break;
+		default:
+			g_queue_push_head (stack, node);
+			break;
+		}
+
+		priv->tree = node;
+	}
+
+	g_strfreev (strings);
+	g_queue_free (stack);
+	g_queue_free (queue);
+}
+
+void
+tracker_query_tree_set_query (TrackerQueryTree *tree,
+			      const gchar      *query_str)
+{
+	TrackerQueryTreePrivate *priv;
+	gchar *str;
+
+	g_return_if_fail (TRACKER_IS_QUERY_TREE (tree));
+	g_return_if_fail (query_str != NULL);
+
+	priv = TRACKER_QUERY_TREE_GET_PRIVATE (tree);
+
+	str = g_strdup (query_str);
+	g_free (priv->query_str);
+	priv->query_str = str;
+
+	/* construct the parse tree */
+	create_query_tree (tree);
+
+	g_object_notify (G_OBJECT (tree), "query");
+}
+
+G_CONST_RETURN gchar *
+tracker_query_tree_get_query (TrackerQueryTree *tree)
+{
+	TrackerQueryTreePrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_QUERY_TREE (tree), NULL);
+
+	priv = TRACKER_QUERY_TREE_GET_PRIVATE (tree);
+	return priv->query_str;
+}
+
+void
+tracker_query_tree_set_services (TrackerQueryTree *tree,
+				 GArray		  *services)
+{
+	TrackerQueryTreePrivate *priv;
+	GArray *copy = NULL;
+
+	g_return_if_fail (TRACKER_IS_QUERY_TREE (tree));
+
+	priv = TRACKER_QUERY_TREE_GET_PRIVATE (tree);
+
+	if (priv->services != services) {
+		if (services) {
+			copy = g_array_new (TRUE, TRUE, sizeof (gint));
+			g_array_append_vals (copy, services->data, services->len);
+		}
+
+		if (priv->services)
+			g_array_free (priv->services, TRUE);
+
+		priv->services = copy;
+		g_object_notify (G_OBJECT (tree), "services");
+	}
+}
+
+GArray *
+tracker_query_tree_get_services (TrackerQueryTree *tree)
+{
+	TrackerQueryTreePrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_QUERY_TREE (tree), NULL);
+
+	priv = TRACKER_QUERY_TREE_GET_PRIVATE (tree);
+
+	return priv->services;
+}
+
+static void
+get_tree_words (TreeNode *node, GSList **list)
+{
+	if (!node)
+		return;
+
+	if (node->op == OP_NONE)
+		*list = g_slist_prepend (*list, node->term);
+	else {
+		get_tree_words (node->left, list);
+		get_tree_words (node->right, list);
+	}
+}
+
+static gint
+get_idf_score (TrackerDBIndexItem *details,
+	       gfloat		   idf)
+{
+	guint32 score;
+	gfloat	f;
+
+	score = tracker_db_index_item_get_score (details);
+	f = idf * score * SCORE_MULTIPLIER;
+
+	return (f > 1.0) ? lrintf (f) : 1;
+}
+
+static gboolean
+in_array (GArray *array,
+	  gint	  element)
+{
+	guint i;
+
+	if (!array)
+		return TRUE;
+
+	for (i = 0; i < array->len; i++) {
+		if (g_array_index (array, gint, i) == element)
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+static void
+search_hit_data_free (gpointer data)
+{
+	g_slice_free (SearchHitData, data);
+}
+
+static void
+add_search_term_hits_to_hash_table (TrackerDBIndexItem *items,
+				    guint		item_count,
+				    GArray	       *services,
+				    GHashTable	       *result)
+{
+	guint i;
+
+	for (i = 0; i < item_count; i++) {
+		SearchHitData *data;
+		gint	       service;
+
+		service = tracker_db_index_item_get_service_type (&items[i]);
+
+		if (in_array (services, service)) {
+			data = g_slice_new (SearchHitData);
+			data->service_type_id = service;
+			data->score = get_idf_score (&items[i], (float) 1 / item_count);
+
+			g_hash_table_insert (result, GINT_TO_POINTER (items[i].id), data);
+		}
+	}
+}
+
+static GHashTable *
+get_search_term_hits (TrackerQueryTree *tree,
+		      const gchar      *term)
+{
+	TrackerQueryTreePrivate *priv;
+	TrackerDBIndexItem	*items;
+	guint			 item_count;
+	GHashTable		*result;
+	GHashTable		*indexes_checked;
+	guint			 i;
+
+	priv = TRACKER_QUERY_TREE_GET_PRIVATE (tree);
+
+	result = g_hash_table_new_full (NULL,
+					NULL,
+					NULL,
+					search_hit_data_free);
+
+	if (!priv->services) {
+		return result;
+	}
+
+	/* Make sure we don't get information from the same
+	 * index more than once.
+	 */
+	indexes_checked = g_hash_table_new (g_direct_hash,
+					    g_direct_equal);
+
+	for (i = 0; i < priv->services->len; i++) {
+		TrackerDBIndex *index;
+		guint		id;
+
+		id = g_array_index (priv->services, gint, i);
+		index = tracker_db_index_manager_get_index_by_service_id (id);
+
+		if (g_hash_table_lookup (indexes_checked, index)) {
+			continue;
+		}
+
+		g_hash_table_insert (indexes_checked,
+				     index,
+				     GINT_TO_POINTER (1));
+
+		items = tracker_db_index_get_word_hits (index, term, &item_count);
+
+		if (!items) {
+			continue;
+		}
+
+		add_search_term_hits_to_hash_table (items,
+						    item_count,
+						    priv->services,
+						    result);
+		g_free (items);
+	}
+
+	g_hash_table_unref (indexes_checked);
+
+	return result;
+}
+
+static void
+compose_hits_foreach (gpointer key,
+		      gpointer value,
+		      gpointer user_data)
+{
+	SearchHitData	*hit1, *hit2, *hit;
+	ComposeHitsData *data;
+
+	data = (ComposeHitsData *) user_data;
+	hit1 = (SearchHitData *) value;
+	hit2 = g_hash_table_lookup (data->other_table, key);
+
+	if (data->op == OP_OR) {
+		/* compose both scores in the same entry */
+		hit = g_slice_dup (SearchHitData, hit1);
+
+		if (hit2)
+			hit->score += hit2->score;
+
+		g_hash_table_insert (data->dest_table, key, hit);
+	} else if (data->op == OP_AND) {
+		/* only compose if the key is in both tables */
+		if (hit2) {
+			hit = g_slice_dup (SearchHitData, hit1);
+			hit->score += hit2->score;
+			g_hash_table_insert (data->dest_table, key, hit);
+		}
+	} else {
+		g_assert_not_reached ();
+	}
+}
+
+static GHashTable *
+compose_hits (OperationType  op,
+	      GHashTable    *left_table,
+	      GHashTable    *right_table)
+{
+	ComposeHitsData data;
+	GHashTable *foreach_table;
+
+	data.op = op;
+
+	/* try to run the foreach in the table with less hits */
+	if (g_hash_table_size (left_table) < g_hash_table_size (right_table)) {
+		foreach_table = left_table;
+		data.other_table = right_table;
+	} else {
+		foreach_table = right_table;
+		data.other_table = left_table;
+	}
+
+	if (op == OP_OR) {
+		data.dest_table = g_hash_table_ref (data.other_table);
+	} else {
+		data.dest_table = g_hash_table_new_full (NULL,
+							 NULL,
+							 NULL,
+							 search_hit_data_free);
+	}
+
+	g_hash_table_foreach (foreach_table, (GHFunc) compose_hits_foreach, &data);
+
+	return data.dest_table;
+}
+
+static GHashTable *
+get_node_hits (TrackerQueryTree *tree,
+	       TreeNode		*node)
+{
+	GHashTable *left_table, *right_table, *result;
+
+	if (!node)
+		return NULL;
+
+	switch (node->op) {
+	case OP_NONE:
+		result = get_search_term_hits (tree, node->term);
+		break;
+	case OP_AND:
+	case OP_OR:
+		left_table = get_node_hits (tree, node->left);
+		right_table = get_node_hits (tree, node->right);
+		result = compose_hits (node->op, left_table, right_table);
+
+		g_hash_table_unref (left_table);
+		g_hash_table_unref (right_table);
+		break;
+	default:
+		g_assert_not_reached ();
+	}
+
+	return result;
+}
+
+static void
+get_hits_foreach (gpointer key,
+		  gpointer value,
+		  gpointer user_data)
+{
+	GArray *array;
+	TrackerDBIndexItemRank rank;
+	SearchHitData *hit_data;
+
+	array = (GArray *) user_data;
+	hit_data = (SearchHitData *) value;
+
+	rank.service_id = GPOINTER_TO_UINT (key);
+	rank.service_type_id = hit_data->service_type_id;
+	rank.score = hit_data->score;
+
+	g_array_append_val (array, rank);
+}
+
+static gint
+compare_search_hits (gconstpointer a,
+		     gconstpointer b)
+{
+	TrackerDBIndexItemRank *ap, *bp;
+
+	ap = (TrackerDBIndexItemRank *) a;
+	bp = (TrackerDBIndexItemRank *) b;
+
+	return (bp->score - ap->score);
+}
+
+GSList *
+tracker_query_tree_get_words (TrackerQueryTree *tree)
+{
+	TrackerQueryTreePrivate *priv;
+	GSList *list = NULL;
+
+	g_return_val_if_fail (TRACKER_IS_QUERY_TREE (tree), NULL);
+
+	priv = TRACKER_QUERY_TREE_GET_PRIVATE (tree);
+	get_tree_words (priv->tree, &list);
+
+	return list;
+}
+
+GArray *
+tracker_query_tree_get_hits (TrackerQueryTree *tree,
+			     guint	       offset,
+			     guint	       limit)
+{
+	TrackerQueryTreePrivate *priv;
+	GHashTable *table;
+	GArray *array;
+
+	g_return_val_if_fail (TRACKER_IS_QUERY_TREE (tree), NULL);
+
+	priv = TRACKER_QUERY_TREE_GET_PRIVATE (tree);
+
+	g_return_val_if_fail (priv->tree != NULL, NULL);
+
+	table = get_node_hits (tree, priv->tree);
+	array = g_array_sized_new (TRUE, TRUE, sizeof (TrackerDBIndexItemRank),
+				   g_hash_table_size (table));
+
+	g_hash_table_foreach (table, (GHFunc) get_hits_foreach, array);
+	g_array_sort (array, compare_search_hits);
+
+	if (offset > 0) {
+		g_array_remove_range (array, 0, CLAMP (offset, 0, array->len));
+	}
+
+	if (limit > 0 && limit < array->len) {
+		g_array_remove_range (array, limit, array->len - limit);
+	}
+
+	return array;
+}
+
+gint
+tracker_query_tree_get_hit_count (TrackerQueryTree *tree)
+{
+	TrackerQueryTreePrivate *priv;
+	GHashTable *table;
+	gint count;
+
+	g_return_val_if_fail (TRACKER_IS_QUERY_TREE (tree), 0);
+
+	priv = TRACKER_QUERY_TREE_GET_PRIVATE (tree);
+	table = get_node_hits (tree, priv->tree);
+
+	if (!table)
+		return 0;
+
+	count = g_hash_table_size (table);
+	g_hash_table_destroy (table);
+
+	return count;
+}
+
+static void
+get_hit_count_foreach (gpointer key,
+		       gpointer value,
+		       gpointer user_data)
+{
+	GArray *array = (GArray *) user_data;
+	TrackerHitCount count;
+
+	count.service_type_id = GPOINTER_TO_INT (key);
+	count.count = GPOINTER_TO_INT (value);
+
+	g_array_append_val (array, count);
+}
+
+GArray *
+tracker_query_tree_get_hit_counts (TrackerQueryTree *tree)
+{
+	GHashTable *table;
+	GArray *hits, *counts;
+	guint i;
+
+	g_return_val_if_fail (TRACKER_IS_QUERY_TREE (tree), NULL);
+
+	table = g_hash_table_new (NULL, NULL);
+	hits = tracker_query_tree_get_hits (tree, 0, 0);
+	counts = g_array_sized_new (TRUE, TRUE, sizeof (TrackerHitCount), 10);
+
+	for (i = 0; i < hits->len; i++) {
+		TrackerDBIndexItemRank rank;
+		gpointer p;
+		gint count, parent_id;
+
+		rank = g_array_index (hits, TrackerDBIndexItemRank, i);
+		p = g_hash_table_lookup (table,
+					 GINT_TO_POINTER (rank.service_type_id));
+		count = GPOINTER_TO_INT (p);
+		count++;
+
+		g_hash_table_insert (table,
+				     GINT_TO_POINTER (rank.service_type_id),
+				     GINT_TO_POINTER (count));
+
+		/* Update service's parent count too (if it has a
+		 * parent).
+		 */
+		parent_id = tracker_ontology_get_service_parent_id_by_id (rank.service_type_id);
+
+		if (parent_id != -1) {
+			p = g_hash_table_lookup (table, GINT_TO_POINTER (parent_id));
+			count = GPOINTER_TO_INT (p);
+			count++;
+
+			g_hash_table_insert (table,
+					     GINT_TO_POINTER (parent_id),
+					     GINT_TO_POINTER (count));
+		}
+	}
+
+	g_hash_table_foreach (table, (GHFunc) get_hit_count_foreach, counts);
+	g_array_free (hits, TRUE);
+
+	return counts;
+}

Added: trunk/src/trackerd/tracker-query-tree.h
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-query-tree.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,78 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKERD_QUERY_TREE_H__
+#define __TRACKERD_QUERY_TREE_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#include <glib-object.h>
+
+#include <libtracker-common/tracker-config.h>
+#include <libtracker-common/tracker-language.h>
+
+#define TRACKER_TYPE_QUERY_TREE		(tracker_query_tree_get_type())
+#define TRACKER_QUERY_TREE(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), TRACKER_TYPE_QUERY_TREE, TrackerQueryTree))
+#define TRACKER_QUERY_TREE_CLASS(c)	(G_TYPE_CHECK_CLASS_CAST ((c),	  TRACKER_TYPE_QUERY_TREE, TrackerQueryTreeClass))
+#define TRACKER_IS_QUERY_TREE(o)	(G_TYPE_CHECK_INSTANCE_TYPE ((o), TRACKER_TYPE_QUERY_TREE))
+#define TRACKER_IS_QUERY_TREE_CLASS(c)	(G_TYPE_CHECK_CLASS_TYPE ((c),	  TRACKER_TYPE_QUERY_TREE))
+#define TRACKER_QUERY_TREE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o),  TRACKER_TYPE_QUERY_TREE, TrackerQueryTreeClass))
+
+typedef struct TrackerQueryTree TrackerQueryTree;
+typedef struct TrackerQueryTreeClass TrackerQueryTreeClass;
+typedef struct TrackerHitCount TrackerHitCount;
+
+struct TrackerQueryTree {
+	GObject parent;
+};
+
+struct TrackerQueryTreeClass {
+	GObjectClass parent_class;
+};
+
+struct TrackerHitCount {
+	guint service_type_id;
+	guint count;
+};
+
+GType		      tracker_query_tree_get_type	(void);
+TrackerQueryTree *    tracker_query_tree_new		(const gchar	  *query_str,
+							 TrackerConfig	  *config,
+							 TrackerLanguage  *language,
+							 GArray		  *services);
+G_CONST_RETURN gchar *tracker_query_tree_get_query	(TrackerQueryTree *tree);
+void		      tracker_query_tree_set_query	(TrackerQueryTree *tree,
+							 const gchar	  *query_str);
+
+GArray *	      tracker_query_tree_get_services	(TrackerQueryTree *tree);
+void		      tracker_query_tree_set_services	(TrackerQueryTree *tree,
+							 GArray		  *services);
+GSList *	      tracker_query_tree_get_words	(TrackerQueryTree *tree);
+GArray *	      tracker_query_tree_get_hits	(TrackerQueryTree *tree,
+							 guint		   offset,
+							 guint		   limit);
+gint		      tracker_query_tree_get_hit_count	(TrackerQueryTree *tree);
+GArray *	      tracker_query_tree_get_hit_counts (TrackerQueryTree *tree);
+
+G_END_DECLS
+
+#endif /* __TRACKERD_QUERY_TREE_H__ */

Added: trunk/src/trackerd/tracker-rdf-query.c
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-rdf-query.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,1508 @@
+/* Tracker - indexer and metadata database engine
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <string.h>
+
+#include <libtracker-common/tracker-field-data.h>
+#include <libtracker-common/tracker-log.h>
+#include <libtracker-common/tracker-ontology.h>
+#include <libtracker-common/tracker-type-utils.h>
+#include <libtracker-common/tracker-utils.h>
+
+#include "tracker-rdf-query.h"
+#include "tracker-db.h"
+
+/* RDF Query Condition
+ * <rdfq:Condition>
+ *	<rdfq:and>
+ *		<rdfq:greaterThan>
+ *			<rdfq:Property name="File:Size" />
+ *			<rdf:Integer>1000000</rdf:Integer>
+ *		</rdfq:greaterThan>
+ *		<rdfq:equals>
+ *			<rdfq:Property name="File:Path" />
+ *			<rdf:String>/home/jamie</rdf:String>
+ *		</rdfq:equals>
+ *	</rdfq:and>
+ * </rdfq:Condition>
+ */
+
+/* Main elements */
+#define ELEMENT_RDF_CONDITION		"rdfq:Condition"
+#define ELEMENT_RDF_PROPERTY		"rdfq:Property"
+
+/* Operators */
+#define ELEMENT_RDF_AND			"rdfq:and"
+#define ELEMENT_RDF_OR			"rdfq:or"
+#define ELEMENT_RDF_NOT			"rdfq:not"
+#define ELEMENT_RDF_EQUALS		"rdfq:equals"
+#define ELEMENT_RDF_GREATER_THAN	"rdfq:greaterThan"
+#define ELEMENT_RDF_GREATER_OR_EQUAL	"rdfq:greaterOrEqual"
+#define ELEMENT_RDF_LESS_THAN		"rdfq:lessThan"
+#define ELEMENT_RDF_LESS_OR_EQUAL	"rdfq:lessOrEqual"
+
+/* Extension operators - "contains" does a substring or full text
+ * match, "in_Set" does string in list match
+ */
+#define ELEMENT_RDF_CONTAINS		"rdfq:contains"
+#define ELEMENT_RDF_REGEX		"rdfq:regex"
+#define ELEMENT_RDF_STARTS_WITH		"rdfq:startsWith"
+#define ELEMENT_RDF_IN_SET		"rdfq:inSet"
+
+/* Types */
+#define ELEMENT_RDF_INTEGER		"rdf:Integer"
+#define ELEMENT_RDF_DATE		"rdf:Date"	/* Format can
+							 * be iso 8601
+							 * with
+							 * optional
+							 * timezone
+							 * "yyyy-mm-ddThh:mm:ss"
+							 * or
+							 * "yyyy-mm-ddThh:mm:ss+hh:mm"
+							 * - most
+							 * other
+							 * formats are
+							 * supported
+							 * too
+							 */
+#define ELEMENT_RDF_STRING		"rdf:String"
+#define ELEMENT_RDF_FLOAT		"rdf:Float"
+
+#define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0)
+
+enum {
+	NO_ERROR,
+	PARSE_ERROR,
+};
+
+typedef enum {
+	STATE_START,
+	STATE_CONDITION,
+	STATE_END_CONDITION,
+	STATE_PROPERTY,
+	STATE_AND,
+	STATE_END_AND,
+	STATE_OR,
+	STATE_END_OR,
+	STATE_NOT,
+	STATE_END_NOT,
+	STATE_EQUALS,
+	STATE_END_EQUALS,
+	STATE_GREATER_THAN,
+	STATE_END_GREATER_THAN,
+	STATE_GREATER_OR_EQUAL,
+	STATE_END_GREATER_OR_EQUAL,
+	STATE_LESS_THAN,
+	STATE_END_LESS_THAN,
+	STATE_LESS_OR_EQUAL,
+	STATE_END_LESS_OR_EQUAL,
+	STATE_CONTAINS,
+	STATE_END_CONTAINS,
+	STATE_REGEX,
+	STATE_END_REGEX,
+	STATE_STARTS_WITH,
+	STATE_END_STARTS_WITH,
+	STATE_IN_SET,
+	STATE_END_IN_SET,
+	STATE_INTEGER,
+	STATE_END_INTEGER,
+	STATE_STRING,
+	STATE_END_STRING,
+	STATE_FLOAT,
+	STATE_END_FLOAT,
+	STATE_DATE,
+	STATE_END_DATE
+} ParseState;
+
+typedef enum {
+	OP_NONE,
+	OP_EQUALS,
+	OP_GREATER,
+	OP_GREATER_EQUAL,
+	OP_LESS,
+	OP_LESS_EQUAL,
+	OP_CONTAINS,
+	OP_REGEX,
+	OP_SET,
+	OP_STARTS
+} Operators;
+
+typedef enum {
+	LOP_NONE,
+	LOP_AND,
+	LOP_OR
+} LogicOperators;
+
+typedef struct {
+	GMarkupParseContext *context;
+	GMarkupParser	    *parser;
+	GSList		    *stack;
+	GSList		    *fields;
+	gboolean	     query_okay;
+	gint		     statement_count;
+	LogicOperators	     current_logic_operator;
+	Operators	     current_operator;
+	gchar		    *current_field;
+	gchar		    *current_value;
+	TrackerDBInterface  *iface;
+	GString		    *sql_select;
+	GString		    *sql_from;
+	GString		    *sql_where;
+	GString		    *sql_order;
+	gchar		    *service;
+} ParserData;
+
+static GQuark error_quark;
+
+static void start_element_handler (GMarkupParseContext	*context,
+				   const gchar		*element_name,
+				   const gchar	       **attribute_names,
+				   const gchar	       **attribute_values,
+				   gpointer		 user_data,
+				   GError	       **error);
+static void end_element_handler   (GMarkupParseContext	*context,
+				   const gchar		*element_name,
+				   gpointer		 user_data,
+				   GError	       **error);
+static void text_handler	  (GMarkupParseContext	*context,
+				   const gchar		*text,
+				   gsize		 text_len,
+				   gpointer		 user_data,
+				   GError	       **error);
+static void error_handler	  (GMarkupParseContext	*context,
+				   GError		*error,
+				   gpointer		 user_data);
+
+static gboolean
+is_operator (ParseState state)
+{
+	return
+		state == STATE_EQUALS ||
+		state == STATE_GREATER_THAN ||
+		state == STATE_LESS_THAN ||
+		state == STATE_CONTAINS ||
+		state == STATE_IN_SET ||
+		state == STATE_LESS_OR_EQUAL ||
+		state == STATE_GREATER_OR_EQUAL ||
+		state == STATE_STARTS_WITH ||
+		state == STATE_REGEX;
+}
+
+static gboolean
+is_end_operator (ParseState state)
+{
+	return
+		state == STATE_END_EQUALS ||
+		state == STATE_END_GREATER_THAN ||
+		state == STATE_END_LESS_THAN ||
+		state == STATE_END_CONTAINS ||
+		state == STATE_END_IN_SET ||
+		state == STATE_END_LESS_OR_EQUAL ||
+		state == STATE_END_GREATER_OR_EQUAL ||
+		state == STATE_END_STARTS_WITH ||
+		state == STATE_REGEX;
+}
+
+static gboolean
+is_logic (ParseState state)
+{
+	return
+		state == STATE_AND ||
+		state == STATE_OR ||
+		state == STATE_NOT;
+}
+
+static gboolean
+is_end_logic (ParseState state)
+{
+	return
+		state == STATE_END_AND ||
+		state == STATE_END_NOT ||
+		state == STATE_END_OR;
+}
+
+static void
+set_error (GError	       **err,
+	   GMarkupParseContext	*context,
+	   gint			 error_code,
+	   const gchar		*format,
+	   ...)
+{
+	gint	 line, ch;
+	va_list  args;
+	gchar	*str;
+
+	g_markup_parse_context_get_position (context, &line, &ch);
+
+	va_start (args, format);
+	str = g_strdup_vprintf (format, args);
+	va_end (args);
+
+	g_set_error (err,
+		     error_quark,
+		     error_code,
+		     "Line %d character %d: %s",
+		     line, ch, str);
+
+	g_free (str);
+}
+
+static gboolean
+set_error_on_fail (gboolean		 condition,
+		   GMarkupParseContext	*context,
+		   const gchar		*msg,
+		   GError	       **err)
+{
+	if (!condition) {
+		set_error (err, context, 1, msg);
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+static const gchar *
+get_attribute_value (const gchar *name,
+		     const gchar **names,
+		     const gchar **values)
+{
+	gint i;
+
+	i = 0;
+
+	while (names[i]) {
+		if (strcmp (name, names[i]) == 0) {
+			return values[i];
+		}
+		i++;
+	}
+
+	return NULL;
+}
+
+static const gchar *
+get_attribute_value_required (GMarkupParseContext  *context,
+			      const gchar	   *tag,
+			      const gchar	   *name,
+			      const gchar	  **names,
+			      const gchar	  **values,
+			      GError		  **error)
+{
+	const gchar *value;
+
+	value = get_attribute_value (name, names, values);
+
+	if (!value) {
+		set_error (error,
+			   context,
+			   PARSE_ERROR,
+			   "%s must have \"%s\" attribute",
+			   tag, name);
+	}
+
+	return value;
+}
+
+static void
+push_stack (ParserData *data, ParseState state)
+{
+	data->stack = g_slist_prepend (data->stack, GINT_TO_POINTER (state));
+}
+
+static void
+pop_stack (ParserData *data)
+{
+	g_return_if_fail (data->stack != NULL);
+
+	data->stack = g_slist_remove (data->stack, data->stack->data);
+}
+
+static ParseState
+peek_state (ParserData *data)
+{
+	g_return_val_if_fail (data->stack != NULL, STATE_START);
+
+	return GPOINTER_TO_INT (data->stack->data);
+}
+
+static void
+pop_stack_until (ParserData *data, ParseState state)
+{
+	while (data->stack != NULL) {
+
+		if (state == peek_state (data)) {
+			pop_stack (data);
+			break;
+		}
+
+		pop_stack (data);
+	}
+}
+
+static TrackerFieldData *
+add_metadata_field (ParserData	*data,
+		    const gchar *field_name,
+		    gboolean	 is_select,
+		    gboolean	 is_condition)
+{
+	TrackerFieldData *field_data;
+	gboolean	  field_exists;
+	GSList		 *l;
+
+	field_exists = FALSE;
+	field_data = NULL;
+
+	/* Check if field is already in list */
+	for (l = data->fields; l; l = l->next) {
+		const gchar *this_field_name;
+
+		this_field_name = tracker_field_data_get_field_name (l->data);
+
+		if (!this_field_name) {
+			continue;
+		}
+
+		if (strcasecmp (this_field_name, field_name) == 0) {
+			field_data = l->data;
+			field_exists = TRUE;
+
+			if (is_condition) {
+				tracker_field_data_set_is_condition (field_data, TRUE);
+			}
+
+			if (is_select) {
+				if (!tracker_field_data_get_is_select (field_data)) {
+					tracker_field_data_set_is_select (field_data, TRUE);
+					g_string_append_printf (data->sql_select, ", %s",
+								tracker_field_data_get_select_field (field_data));
+				}
+			}
+
+			break;
+		}
+	}
+
+	if (!field_exists) {
+		field_data = tracker_db_get_metadata_field (data->iface,
+							    data->service,
+							    field_name,
+							    g_slist_length (data->fields),
+							    is_select,
+							    is_condition);
+		if (field_data) {
+			data->fields = g_slist_prepend (data->fields, field_data);
+			if (is_select) {
+				g_string_append_printf (data->sql_select, ", %s",
+							tracker_field_data_get_select_field (field_data));
+			}
+		}
+	}
+
+	return field_data;
+}
+
+static void
+start_element_handler (GMarkupParseContext  *context,
+		       const gchar	    *element_name,
+		       const gchar	   **attribute_names,
+		       const gchar	   **attribute_values,
+		       gpointer		     user_data,
+		       GError		   **error)
+{
+	ParserData *data;
+	ParseState state;
+
+	data = user_data;
+	state = peek_state (data);
+
+	if (ELEMENT_IS (ELEMENT_RDF_CONDITION)) {
+		if (set_error_on_fail (state == STATE_START,
+				       context,
+				       "Condition element not expected here",
+				       error)) {
+			return;
+		}
+
+		push_stack (data, STATE_CONDITION);
+
+	} else if (ELEMENT_IS (ELEMENT_RDF_PROPERTY)) {
+		const char *name;
+
+		if (set_error_on_fail (is_operator (state),
+				       context,
+				       "Property element not expected here",
+				       error)) {
+			return;
+		}
+
+		name = get_attribute_value_required (context,
+						     "<rdfq:Property>", "name",
+						     attribute_names, attribute_values,
+						     error);
+
+		if (!name) {
+			return;
+		} else {
+			if (data->current_operator == OP_NONE) {
+				set_error (error,
+					   context,
+					   PARSE_ERROR,
+					   "no operator found for Property \"%s\"",
+					   name);
+				return;
+			}
+
+			data->current_field = g_strdup (name);
+
+			push_stack (data, STATE_PROPERTY);
+		}
+	} else if (ELEMENT_IS (ELEMENT_RDF_AND)) {
+		if (set_error_on_fail (state == STATE_CONDITION ||
+				       is_logic (state) ||
+				       is_end_logic (state) ||
+				       is_end_operator (state),
+				       context,
+				       "AND element not expected here",
+				       error)) {
+			return;
+		}
+
+		if (data->statement_count >= 1) {
+			if (data->current_logic_operator == LOP_AND) {
+				data->sql_where = g_string_append (data->sql_where,
+								   " AND ");
+			} else {
+				if (data->current_logic_operator == LOP_OR) {
+					data->sql_where = g_string_append (data->sql_where,
+									   " OR ");
+				}
+			}
+		}
+
+		data->statement_count = 0;
+		data->sql_where = g_string_append (data->sql_where, " ( ");
+		data->current_logic_operator = LOP_AND;
+		push_stack (data, STATE_AND);
+	} else if (ELEMENT_IS (ELEMENT_RDF_OR)) {
+		if (set_error_on_fail (state == STATE_CONDITION ||
+				       is_logic (state) ||
+				       is_end_logic (state) ||
+				       is_end_operator (state),
+				       context,
+				       "OR element not expected here",
+				       error)) {
+			return;
+		}
+
+		if (data->statement_count >= 1) {
+			if (data->current_logic_operator == LOP_AND) {
+				data->sql_where = g_string_append (data->sql_where,
+								   " AND ");
+			} else {
+				if (data->current_logic_operator == LOP_OR) {
+					data->sql_where = g_string_append (data->sql_where,
+									   " OR ");
+				}
+			}
+		}
+
+		data->statement_count = 0;
+		data->sql_where = g_string_append (data->sql_where, " ( ");
+		data->current_logic_operator = LOP_OR;
+		push_stack (data, STATE_OR);
+
+	} else if (ELEMENT_IS (ELEMENT_RDF_NOT)) {
+		if (set_error_on_fail (state == STATE_CONDITION ||
+				       is_logic (state) ||
+				       is_end_logic (state) ||
+				       is_end_operator (state),
+				       context,
+				       "NOT element not expected here",
+				       error)) {
+			return;
+		}
+
+		if (data->statement_count >= 1) {
+			if (data->current_logic_operator == LOP_AND) {
+				data->sql_where = g_string_append (data->sql_where,
+								   " AND ");
+			} else {
+				if (data->current_logic_operator == LOP_OR) {
+					data->sql_where = g_string_append (data->sql_where,
+									   " OR ");
+				}
+			}
+		}
+
+		data->statement_count = 0;
+		data->sql_where = g_string_append (data->sql_where, " NOT ( ");
+		push_stack (data, STATE_NOT);
+
+	} else if (ELEMENT_IS (ELEMENT_RDF_EQUALS)) {
+		if (set_error_on_fail (state == STATE_CONDITION ||
+				       is_logic (state) ||
+				       ((data->current_logic_operator == LOP_AND ||
+					 data->current_logic_operator == LOP_OR) &&
+					is_end_operator (state)),
+				       context,
+				       "EQUALS element not expected here",
+				       error)) {
+			return;
+		}
+
+		data->current_operator = OP_EQUALS;
+		push_stack (data, STATE_EQUALS);
+
+	} else if (ELEMENT_IS (ELEMENT_RDF_GREATER_THAN)) {
+
+		if (set_error_on_fail (state == STATE_CONDITION ||
+				       is_logic (state) ||
+				       ((data->current_logic_operator == LOP_AND ||
+					 data->current_logic_operator == LOP_OR) &&
+					is_end_operator (state)),
+				       context,
+				       "GREATERTHAN element not expected here",
+				       error)) {
+			return;
+		}
+
+		data->current_operator = OP_GREATER;
+		push_stack (data, STATE_GREATER_THAN);
+	} else if (ELEMENT_IS (ELEMENT_RDF_GREATER_OR_EQUAL)) {
+		if (set_error_on_fail (state == STATE_CONDITION ||
+				       is_logic (state) ||
+				       ((data->current_logic_operator == LOP_AND ||
+					 data->current_logic_operator == LOP_OR) &&
+					is_end_operator (state)),
+				       context,
+				       "GREATEROREQUAL element not expected here",
+				       error)) {
+			return;
+		}
+
+		data->current_operator = OP_GREATER_EQUAL;
+		push_stack (data, STATE_GREATER_OR_EQUAL);
+	} else if (ELEMENT_IS (ELEMENT_RDF_LESS_THAN )) {
+		if (set_error_on_fail (state == STATE_CONDITION ||
+				       is_logic (state) ||
+				       ((data->current_logic_operator == LOP_AND ||
+					 data->current_logic_operator == LOP_OR) &&
+					is_end_operator (state)),
+				       context,
+				       "LESSTHAN element not expected here",
+				       error)) {
+			return;
+		}
+
+		data->current_operator = OP_LESS;
+		push_stack (data, STATE_LESS_THAN);
+	} else if (ELEMENT_IS (ELEMENT_RDF_LESS_OR_EQUAL)) {
+		if (set_error_on_fail (state == STATE_CONDITION ||
+				       is_logic (state) ||
+				       ((data->current_logic_operator == LOP_AND ||
+					 data->current_logic_operator == LOP_OR) &&
+					is_end_operator (state)),
+				       context,
+				       "LESSOREQUAL element not expected here",
+				       error)) {
+			return;
+		}
+
+		data->current_operator = OP_LESS_EQUAL;
+		push_stack (data, STATE_LESS_OR_EQUAL);
+	} else if (ELEMENT_IS (ELEMENT_RDF_CONTAINS)) {
+		if (set_error_on_fail (state == STATE_CONDITION ||
+				       is_logic (state) ||
+				       ((data->current_logic_operator == LOP_AND ||
+					 data->current_logic_operator == LOP_OR) &&
+					is_end_operator (state)),
+				       context,
+				       "CONTAINS element not expected here",
+				       error)) {
+			return;
+		}
+
+		data->current_operator = OP_CONTAINS;
+		push_stack (data, STATE_CONTAINS);
+	} else if (ELEMENT_IS (ELEMENT_RDF_REGEX)) {
+		if (set_error_on_fail (state == STATE_CONDITION ||
+				       is_logic (state) ||
+				       ((data->current_logic_operator == LOP_AND ||
+					 data->current_logic_operator == LOP_OR) &&
+					is_end_operator (state)),
+				       context,
+				       "REGEX element not expected here",
+				       error)) {
+			return;
+		}
+
+		data->current_operator = OP_REGEX;
+		push_stack (data, STATE_REGEX);
+	} else if (ELEMENT_IS (ELEMENT_RDF_STARTS_WITH)) {
+		if (set_error_on_fail (state == STATE_CONDITION ||
+				       is_logic (state) ||
+				       ((data->current_logic_operator == LOP_AND ||
+					 data->current_logic_operator == LOP_OR) &&
+					is_end_operator (state)),
+				       context,
+				       "STARTSWITH element not expected here",
+				       error)) {
+			return;
+		}
+
+		data->current_operator = OP_STARTS;
+		push_stack (data, STATE_STARTS_WITH);
+	} else if (ELEMENT_IS (ELEMENT_RDF_IN_SET)) {
+		if (set_error_on_fail (state == STATE_CONDITION ||
+				       is_logic (state) ||
+				       ((data->current_logic_operator == LOP_AND ||
+					 data->current_logic_operator == LOP_OR) &&
+					is_end_operator (state)),
+				       context,
+				       "IN SET element not expected here",
+				       error)) {
+			return;
+		}
+
+		data->current_operator = OP_SET;
+		push_stack (data, STATE_IN_SET);
+	} else if (ELEMENT_IS (ELEMENT_RDF_INTEGER)) {
+		if (set_error_on_fail (state == STATE_PROPERTY,
+				       context,
+				       "INTEGER element not expected here",
+				       error)) {
+			return;
+		}
+
+		push_stack (data, STATE_INTEGER);
+	} else if (ELEMENT_IS (ELEMENT_RDF_DATE)) {
+		if (set_error_on_fail (state == STATE_PROPERTY,
+				       context,
+				       "DATE element not expected here",
+				       error)) {
+			return;
+		}
+
+		push_stack (data, STATE_DATE);
+	} else if (ELEMENT_IS (ELEMENT_RDF_STRING)) {
+		if (set_error_on_fail (state == STATE_PROPERTY,
+				       context,
+				       "STRING element not expected here",
+				       error)) {
+			return;
+		}
+
+		push_stack (data, STATE_STRING);
+	} else if (ELEMENT_IS (ELEMENT_RDF_FLOAT)) {
+		if (set_error_on_fail (state == STATE_PROPERTY,
+				       context,
+				       "FLOAT element not expected here",
+				       error)) {
+			return;
+		}
+
+		push_stack (data, STATE_FLOAT);
+	}
+}
+
+static gchar *
+get_value (const gchar *value, gboolean quote)
+{
+	if (quote) {
+		return g_strconcat (" '", value, "' ", NULL);
+	} else {
+		return g_strdup (value);
+	}
+}
+
+static gboolean
+build_sql (ParserData *data)
+{
+	TrackerFieldData  *field_data;
+	ParseState	   state;
+	gchar		  *avalue, *value, *sub;
+	const gchar	  *where_field;
+	GString		  *str;
+	gchar		 **s;
+
+	g_return_val_if_fail (data->current_field &&
+			      data->current_operator != OP_NONE &&
+			      data->current_value,
+			      FALSE);
+
+	str = g_string_new ("");
+
+	data->statement_count++;
+
+	state = peek_state (data);
+
+	avalue = get_value (data->current_value, (state != STATE_END_DATE &&
+						  state != STATE_END_INTEGER &&
+						  state != STATE_END_FLOAT));
+
+	field_data = add_metadata_field (data, data->current_field, FALSE, TRUE);
+
+	if (!field_data) {
+		g_free (avalue);
+		g_free (data->current_field);
+		g_free (data->current_value);
+		data->current_field = NULL;
+		data->current_value = NULL;
+		return FALSE;
+	}
+
+	if (tracker_field_data_get_data_type (field_data) == TRACKER_FIELD_TYPE_DATE) {
+		gchar *bvalue;
+		gint   cvalue;
+
+		bvalue = tracker_date_format (avalue);
+		cvalue = tracker_string_to_date (bvalue);
+		value = tracker_gint_to_string (cvalue);
+		g_free (bvalue);
+	} else {
+		value = g_strdup (avalue);
+	}
+
+	g_free (avalue);
+
+	if (data->statement_count > 1) {
+		if (data->current_logic_operator == LOP_AND) {
+			data->sql_where = g_string_append (data->sql_where, "\n AND ");
+		} else {
+			if (data->current_logic_operator == LOP_OR) {
+				data->sql_where = g_string_append (data->sql_where, "\n OR ");
+			}
+		}
+	}
+
+	where_field = tracker_field_data_get_where_field (field_data);
+
+	switch (data->current_operator) {
+	case OP_EQUALS:
+		sub = strchr (data->current_value, '*');
+		if (sub) {
+			g_string_append_printf (str, " (%s glob '%s') ",
+						where_field,
+						data->current_value);
+		} else {
+			TrackerFieldType data_type;
+
+			data_type = tracker_field_data_get_data_type (field_data);
+
+			g_string_append_printf (str, " (%s = %s) ",
+						where_field,
+						value);
+		}
+		break;
+
+	case OP_GREATER:
+		g_string_append_printf (str, " (%s > %s) ",
+					where_field,
+					value);
+		break;
+
+	case OP_GREATER_EQUAL:
+		g_string_append_printf (str, " (%s >= %s) ",
+					where_field,
+					value);
+		break;
+
+	case OP_LESS:
+		g_string_append_printf (str, " (%s < %s) ",
+					where_field,
+					value);
+		break;
+
+	case OP_LESS_EQUAL:
+		g_string_append_printf (str, " (%s <= %s) ",
+					where_field,
+					value);
+		break;
+
+	case OP_CONTAINS:
+		sub = strchr (data->current_value, '*');
+
+		if (sub) {
+			g_string_append_printf (str, " (%s like '%%%%%s%%%%') ",
+						where_field,
+						data->current_value);
+		} else {
+			g_string_append_printf (str, " (%s like '%%%%%s%%%%') ",
+						where_field,
+						data->current_value);
+		}
+		break;
+
+	case OP_STARTS:
+		sub = strchr (data->current_value, '*');
+
+		if (sub) {
+			g_string_append_printf (str, " (%s like '%s') ",
+						where_field,
+						data->current_value);
+		} else {
+			g_string_append_printf (str, " (%s like '%s%%%%') ",
+						where_field,
+						data->current_value);
+		}
+		break;
+
+	case OP_REGEX:
+		g_string_append_printf (str, " (%s REGEXP '%s') ",
+					where_field,
+					data->current_value);
+		break;
+
+	case OP_SET:
+		s = g_strsplit (data->current_value, ",", 0);
+
+		if (s && s[0]) {
+			gchar **p;
+
+			g_string_append_printf (str, " (%s in ('%s'",
+						where_field,
+						s[0]);
+
+			for (p = s + 1; *p; p++) {
+				g_string_append_printf (str, ",'%s'", *p);
+			}
+
+			g_string_append_printf (str, ") ) " );
+		}
+		break;
+
+	default:
+		break;
+	}
+
+	data->sql_where = g_string_append (data->sql_where, str->str);
+
+	g_string_free (str, TRUE);
+
+	g_free (data->current_field);
+	data->current_field = NULL;
+
+	g_free (data->current_value);
+	data->current_value = NULL;
+
+	g_free (value);
+
+	return TRUE;
+}
+
+static void
+end_element_handler (GMarkupParseContext *context,
+		     const gchar	 *element_name,
+		     gpointer		 user_data,
+		     GError		 **error)
+{
+	ParserData *data;
+
+	data = user_data;
+
+	if (ELEMENT_IS (ELEMENT_RDF_CONDITION)) {
+
+		push_stack (data, STATE_END_CONDITION);
+		data->query_okay = TRUE;
+
+	} else if (ELEMENT_IS (ELEMENT_RDF_AND)) {
+
+		data->sql_where = g_string_append (data->sql_where, " ) ");
+
+		pop_stack_until (data, STATE_AND);
+
+		if (peek_state (data) != STATE_AND) {
+			if (peek_state (data) == STATE_OR) {
+				data->current_logic_operator = LOP_OR;
+			} else {
+				data->current_logic_operator = LOP_NONE;
+			}
+		}
+
+	} else if (ELEMENT_IS (ELEMENT_RDF_OR)) {
+
+		data->sql_where = g_string_append (data->sql_where, " ) ");
+
+		pop_stack_until (data, STATE_OR);
+
+		if (peek_state (data) != STATE_OR) {
+			if (peek_state (data) == STATE_AND) {
+				data->current_logic_operator = LOP_AND;
+			} else {
+				data->current_logic_operator = LOP_NONE;
+			}
+		}
+
+	} else if (ELEMENT_IS (ELEMENT_RDF_NOT)) {
+
+		data->sql_where = g_string_append (data->sql_where, " ) ");
+		pop_stack_until (data, STATE_NOT);
+
+
+	} else if (ELEMENT_IS (ELEMENT_RDF_EQUALS)) {
+
+		if (!build_sql (data)) {
+			set_error (error, context, 1, "parse error");
+			return;
+		}
+		push_stack (data, STATE_END_EQUALS);
+
+	} else if (ELEMENT_IS (ELEMENT_RDF_GREATER_THAN)) {
+
+		if (!build_sql (data)) {
+			set_error (error, context, 1, "parse error");
+			return;
+		}
+
+		push_stack (data, STATE_END_GREATER_THAN);
+
+	} else if (ELEMENT_IS (ELEMENT_RDF_GREATER_OR_EQUAL)) {
+
+		if (!build_sql (data)) {
+			set_error (error, context, 1, "parse error");
+			return;
+		}
+
+		push_stack (data, STATE_END_GREATER_OR_EQUAL);
+
+	} else if (ELEMENT_IS (ELEMENT_RDF_LESS_THAN )) {
+
+		if (!build_sql (data)) {
+			set_error (error, context, 1, "parse error");
+			return;
+		}
+
+		push_stack (data, STATE_END_LESS_THAN );
+
+	} else if (ELEMENT_IS (ELEMENT_RDF_LESS_OR_EQUAL )) {
+
+		if (!build_sql (data)) {
+			set_error (error, context, 1, "parse error");
+			return;
+		}
+
+		push_stack (data, STATE_END_LESS_OR_EQUAL );
+
+
+	} else if (ELEMENT_IS (ELEMENT_RDF_CONTAINS)) {
+
+		if (!build_sql (data)) {
+			set_error (error, context, 1, "parse error");
+			return;
+		}
+
+		push_stack (data, STATE_END_CONTAINS);
+
+	} else if (ELEMENT_IS (ELEMENT_RDF_REGEX)) {
+
+		if (!build_sql (data)) {
+			set_error (error, context, 1, "parse error");
+			return;
+		}
+
+		push_stack (data, STATE_END_REGEX);
+
+	} else if (ELEMENT_IS (ELEMENT_RDF_STARTS_WITH)) {
+
+		if (!build_sql (data)) {
+			set_error (error, context, 1, "parse error");
+			return;
+		}
+
+		push_stack (data, STATE_END_STARTS_WITH);
+
+	} else if (ELEMENT_IS (ELEMENT_RDF_IN_SET)) {
+
+		if (!build_sql (data)) {
+			set_error (error, context, 1, "parse error");
+			return;
+		}
+
+		push_stack (data, STATE_END_IN_SET);
+
+
+	} else if (ELEMENT_IS (ELEMENT_RDF_INTEGER)) {
+
+		push_stack (data, STATE_END_INTEGER);
+
+
+	} else if (ELEMENT_IS (ELEMENT_RDF_DATE)) {
+
+		push_stack (data, STATE_END_DATE);
+
+
+	} else if (ELEMENT_IS (ELEMENT_RDF_STRING)) {
+
+		push_stack (data, STATE_END_STRING);
+
+	}  else if (ELEMENT_IS (ELEMENT_RDF_FLOAT)) {
+
+		push_stack (data, STATE_END_FLOAT);
+	}
+}
+
+
+static void
+text_handler (GMarkupParseContext *context,
+	      const gchar	  *text,
+	      gsize		  text_len,
+	      gpointer		  user_data,
+	      GError		  **error)
+{
+	ParserData *data;
+	ParseState state;
+
+	data = user_data;
+	state = peek_state (data);
+
+	switch (state) {
+
+		case STATE_INTEGER:
+		case STATE_STRING:
+		case STATE_DATE:
+		case STATE_FLOAT:
+
+			data->current_value = g_strstrip (g_strndup (text, text_len));
+			break;
+
+		default :
+			break;
+	}
+}
+
+
+static void
+error_handler (GMarkupParseContext *context,
+	       GError		   *error,
+	       gpointer		   user_data)
+{
+	g_message ("in rdf query parse: %s", error->message);
+}
+
+
+static GString *
+get_select_header (const char *service)
+{
+	GString *result;
+	int type;
+
+	result = g_string_new ("");
+	type = tracker_ontology_get_service_id_by_name (service);
+
+	switch (type) {
+
+		case 0:
+		case 1:
+		case 2:
+		case 3:
+		case 4:
+		case 5:
+		case 6:
+		case 7:
+		case 8:
+			g_string_append_printf (result, " Select DISTINCT (S.Path || '%s' || S.Name) as uri, GetServiceName(S.ServiceTypeID) as stype ", G_DIR_SEPARATOR_S);
+			break;
+
+		default :
+			g_string_append_printf (result, " Select DISTINCT (S.Path || '%s' || S.Name) as uri, GetServiceName(S.ServiceTypeID) as stype ", G_DIR_SEPARATOR_S);
+			break;
+	}
+
+	return result;
+
+}
+
+
+gchar *
+tracker_rdf_query_to_sql (TrackerDBInterface  *iface,
+			  const gchar	      *query,
+			  const gchar	      *service,
+			  gchar		     **fields,
+			  gint		       field_count,
+			  const gchar	      *search_text,
+			  const gchar	      *keyword,
+			  gboolean	       sort_by_service,
+			  gchar		     **sort_fields,
+			  gint		       sort_field_count,
+			  gboolean	       sort_desc,
+			  gint		       offset,
+			  gint		       limit,
+			  GError	     **error)
+{
+	static gboolean  inited = FALSE;
+	ParserData	 data;
+	gchar		*result;
+	gchar		*table_name;
+	gboolean	 do_search = FALSE;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (iface), NULL);
+	g_return_val_if_fail (query != NULL, NULL);
+	g_return_val_if_fail (service != NULL, NULL);
+	g_return_val_if_fail (fields != NULL, NULL);
+	g_return_val_if_fail (search_text != NULL, NULL);
+	g_return_val_if_fail (keyword != NULL, NULL);
+
+	if (!inited) {
+		error_quark = g_quark_from_static_string ("RDF-parser-error-quark");
+		inited = TRUE;
+	}
+
+	memset (&data, 0, sizeof (data));
+	data.iface = iface;
+	data.statement_count = 0;
+	data.service = (gchar*) service;
+	data.sql_select = get_select_header (service);
+
+	if (field_count > 0) {
+		gint i;
+
+		for (i = 0; i < field_count; i++) {
+			TrackerFieldData *field_data;
+
+			field_data = add_metadata_field (&data, fields[i], TRUE, FALSE);
+
+			if (!field_data) {
+				g_set_error (error,
+					     error_quark,
+					     PARSE_ERROR,
+					     "RDF Query failed, field:'%s' not found",
+					     sort_fields[i]);
+
+				g_slist_foreach (data.fields,
+						 (GFunc) g_object_unref,
+						 NULL);
+				g_slist_free (data.fields);
+				g_string_free (data.sql_select, TRUE);
+
+				return NULL;
+			}
+		}
+	}
+
+	table_name = "Services";
+
+	data.sql_from = g_string_new ("");
+
+	if (!tracker_is_empty_string (search_text)) {
+		do_search = TRUE;
+		g_string_append_printf (data.sql_from,
+					"\n FROM %s S INNER JOIN SearchResults1 M ON S.ID = M.SID ",
+					table_name);
+	} else {
+		g_string_append_printf (data.sql_from,
+					"\n FROM %s S ",
+					table_name);
+	}
+
+	if (!tracker_is_empty_string (keyword)) {
+		gchar *keyword_metadata;
+
+		keyword_metadata = tracker_db_metadata_get_related_names (iface,
+									  "DC:Keywords");
+		g_string_append_printf (data.sql_from,
+					"\n INNER JOIN ServiceKeywordMetaData K ON S.ID = K.ServiceID and K.MetaDataID in (%s) and K.MetaDataValue = '%s' ",
+					keyword_metadata,
+					keyword);
+		g_free (keyword_metadata);
+	}
+
+	data.sql_where = g_string_new ("");
+
+	if (strlen (query) < 10) {
+		g_string_append_printf (data.sql_where,
+					"\n WHERE (S.ServiceTypeID in (select TypeId from ServiceTypes where TypeName = '%s' or Parent = '%s')) ",
+					service,
+					service);
+	} else {
+		g_string_append_printf (data.sql_where,
+					"\n WHERE (S.ServiceTypeID in (select TypeId from ServiceTypes where TypeName = '%s' or Parent = '%s')) AND ",
+					service,
+					service);
+	}
+
+	if (limit < 1) {
+		limit = 1024;
+	}
+
+	data.sql_order = g_string_new ("");
+
+	if (sort_by_service) {
+		if (do_search) {
+			g_string_append_printf (data.sql_order,"M.Score desc, S.ServiceTypeID, uri ");
+		} else {
+			g_string_append_printf (data.sql_order,"S.ServiceTypeID, uri ");
+		}
+	} else {
+		if (do_search) {
+			g_string_append_printf (data.sql_order,"M.Score desc ");
+		} else {
+			/* Nothing */
+		}
+
+	}
+
+	if (sort_field_count>0) {
+		gint i;
+
+		for (i = 0; i < sort_field_count; i++) {
+			TrackerFieldData *field_data;
+
+			field_data = add_metadata_field (&data, sort_fields[i], FALSE, FALSE);
+
+			if (!field_data) {
+				g_set_error (error,
+					     error_quark,
+					     PARSE_ERROR,
+					     "RDF Query failed, sort field:'%s' not found",
+					     sort_fields[i]);
+				g_slist_foreach (data.fields,
+						 (GFunc) g_object_unref,
+						 NULL);
+				g_slist_free (data.fields);
+				g_string_free (data.sql_select, TRUE);
+				g_string_free (data.sql_where, TRUE);
+				g_string_free (data.sql_order, TRUE);
+
+				return NULL;
+			}
+
+			if (i) {
+				g_string_append_printf (data.sql_order, ", ");
+			}
+
+			g_string_append_printf (data.sql_order, "%s %s",
+						tracker_field_data_get_select_field (field_data),
+						sort_desc ? "DESC" : "ASC");
+		}
+	}
+
+	if (!tracker_is_empty_string (data.sql_order->str)) {
+		g_string_prepend (data.sql_order, "\n ORDER BY ");
+	}
+
+	g_string_append_printf (data.sql_order, " LIMIT ");
+
+	g_string_append_printf (data.sql_order, "%d,%d ", offset, limit);
+
+	data.parser = g_new0 (GMarkupParser, 1);
+	data.parser->start_element = start_element_handler;
+	data.parser->text = text_handler;
+	data.parser->end_element = end_element_handler;
+	data.parser->error = error_handler;
+
+	data.current_operator = OP_NONE;
+	data.current_logic_operator = LOP_NONE;
+	data.query_okay = FALSE;
+
+	data.context = g_markup_parse_context_new (data.parser, 0, &data, NULL);
+
+	push_stack (&data, STATE_START);
+
+	result = NULL;
+
+	if (!g_markup_parse_context_parse (data.context, query, -1, error)) {
+		g_string_free (data.sql_select, TRUE);
+		g_string_free (data.sql_from, TRUE);
+		g_string_free (data.sql_where, TRUE);
+		g_string_free (data.sql_order, TRUE);
+	} else {
+		GSList *l;
+
+		for (l = data.fields; l; l = l->next) {
+			if (!tracker_field_data_get_is_condition (l->data)) {
+				if (tracker_field_data_get_needs_join (l->data)) {
+					g_string_append_printf (data.sql_from,
+								"\n LEFT OUTER JOIN %s %s ON (S.ID = %s.ServiceID and %s.MetaDataID = %s) ",
+								tracker_field_data_get_table_name (l->data),
+								tracker_field_data_get_alias (l->data),
+								tracker_field_data_get_alias (l->data),
+								tracker_field_data_get_alias (l->data),
+								tracker_field_data_get_id_field (l->data));
+				}
+			} else {
+				gchar *related_metadata;
+
+				related_metadata = tracker_db_metadata_get_related_names (iface,
+											  tracker_field_data_get_field_name (l->data));
+				g_string_append_printf (data.sql_from,
+							"\n INNER JOIN %s %s ON (S.ID = %s.ServiceID and %s.MetaDataID in (%s)) ",
+							tracker_field_data_get_table_name (l->data),
+							tracker_field_data_get_alias (l->data),
+							tracker_field_data_get_alias (l->data),
+							tracker_field_data_get_alias (l->data),
+							related_metadata);
+				g_free (related_metadata);
+			}
+		}
+
+		result = g_strconcat (data.sql_select->str,
+				      " ",
+				      data.sql_from->str,
+				      " ",
+				      data.sql_where->str,
+				      " ",
+				      data.sql_order->str,
+				      NULL);
+
+		g_string_free (data.sql_select, TRUE);
+		g_string_free (data.sql_from, TRUE);
+		g_string_free (data.sql_where, TRUE);
+		g_string_free (data.sql_order, TRUE);
+	}
+
+	g_slist_foreach (data.fields, (GFunc) g_object_unref, NULL);
+	g_slist_free (data.fields);
+
+	g_slist_free (data.stack);
+	g_markup_parse_context_free (data.context);
+
+	if (data.current_field) {
+		g_free (data.current_field);
+	}
+
+	if (data.current_value) {
+		g_free (data.current_value);
+	}
+
+	g_free (data.parser);
+
+	return result;
+}
+
+
+/*
+ * The following function turns an rdf query into a filter that can be used for example
+ * for getting unique values of a field with a certain query.
+ * FIXME It is not pretty. The calling function needs to define Services S that is joined in this method.
+ */
+
+void
+tracker_rdf_filter_to_sql (TrackerDBInterface *iface,
+			   const char	      *query,
+			   const char	      *service,
+			   GSList	     **fields,
+			   char		     **from,
+			   char		     **where,
+			   GError	     **error)
+{
+	static gboolean inited = FALSE;
+	ParserData	data;
+
+	g_return_if_fail (TRACKER_IS_DB_INTERFACE (iface));
+	g_return_if_fail (service != NULL);
+	g_return_if_fail (from != NULL);
+	g_return_if_fail (where != NULL);
+
+	if (!inited) {
+		error_quark = g_quark_from_static_string ("RDF-parser-error-quark");
+		inited = TRUE;
+	}
+
+	memset (&data, 0, sizeof (data));
+	data.iface = iface;
+	data.statement_count = 0;
+	data.service = (char *) service;
+
+	data.sql_from = g_string_new ("");
+	data.sql_where = g_string_new ("");
+
+	data.fields = *fields;
+
+	if (strlen (query) < 10) {
+		g_string_append_printf (data.sql_where, " (S.ServiceTypeID in (select TypeId from ServiceTypes where TypeName = '%s' or Parent = '%s')) ", service, service);
+	} else {
+		g_string_append_printf (data.sql_where, " (S.ServiceTypeID in (select TypeId from ServiceTypes where TypeName = '%s' or Parent = '%s')) AND ", service, service);
+	}
+
+	data.parser = g_new0 (GMarkupParser, 1);
+	data.parser->start_element = start_element_handler;
+	data.parser->text = text_handler;
+	data.parser->end_element = end_element_handler;
+	data.parser->error = error_handler;
+
+	data.current_operator = OP_NONE;
+	data.current_logic_operator = LOP_NONE;
+	data.query_okay = FALSE;
+
+	data.context = g_markup_parse_context_new (data.parser, 0, &data, NULL);
+
+	push_stack (&data, STATE_START);
+
+	if ( (query != NULL) && (!g_markup_parse_context_parse (data.context, query, -1, error))) {
+
+		*from = NULL;
+		*where = NULL;
+
+		g_string_free (data.sql_from, TRUE);
+		g_string_free (data.sql_where, TRUE);
+
+	} else {
+		GSList *l;
+
+		for (l = data.fields; l; l = l->next) {
+			if (!tracker_field_data_get_is_condition (l->data)) {
+				if (tracker_field_data_get_needs_join (l->data)) {
+					g_string_append_printf (data.sql_from,
+								"\n LEFT OUTER JOIN %s %s ON (S.ID = %s.ServiceID and %s.MetaDataID = %s) ",
+								tracker_field_data_get_table_name (l->data),
+								tracker_field_data_get_alias (l->data),
+								tracker_field_data_get_alias (l->data),
+								tracker_field_data_get_alias (l->data),
+								tracker_field_data_get_id_field (l->data));
+				}
+			} else {
+				gchar *related_metadata;
+
+				related_metadata = tracker_db_metadata_get_related_names (iface,
+											  tracker_field_data_get_field_name (l->data));
+				g_string_append_printf (data.sql_from,
+							"\n INNER JOIN %s %s ON (S.ID = %s.ServiceID and %s.MetaDataID in (%s)) ",
+							tracker_field_data_get_table_name (l->data),
+							tracker_field_data_get_alias (l->data),
+							tracker_field_data_get_alias (l->data),
+							tracker_field_data_get_alias (l->data),
+							related_metadata);
+				g_free (related_metadata);
+			}
+		}
+
+
+		*from  = g_strdup (data.sql_from->str);
+		*where = g_strdup (data.sql_where->str);
+		g_string_free (data.sql_from, TRUE);
+		g_string_free (data.sql_where, TRUE);
+
+
+	}
+
+	*fields = data.fields;
+
+	g_slist_free (data.stack);
+	g_markup_parse_context_free (data.context);
+
+	if (data.current_field) {
+		g_free (data.current_field);
+	}
+
+	if (data.current_value) {
+		g_free (data.current_value);
+	}
+
+	g_free (data.parser);
+}

Added: trunk/src/trackerd/tracker-rdf-query.h
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-rdf-query.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKERD_RDF_QUERY_H__
+#define __TRACKERD_RDF_QUERY_H__
+
+#include <glib.h>
+
+#include <libtracker-db/tracker-db-manager.h>
+
+G_BEGIN_DECLS
+
+gchar *tracker_rdf_query_to_sql (TrackerDBInterface  *iface,
+				 const gchar	     *query,
+				 const gchar	     *service,
+				 gchar		    **fields,
+				 gint		      field_count,
+				 const gchar	     *search_text,
+				 const gchar	     *keyword,
+				 gboolean	      sort_by_service,
+				 gchar		    **sort_fields,
+				 gint		      sort_field_count,
+				 gboolean	      sort_desc,
+				 gint		      offset,
+				 gint		      limit,
+				 GError		    **error);
+
+void tracker_rdf_filter_to_sql	(TrackerDBInterface  *iface,
+				 const gchar	     *query,
+				 const gchar	     *service,
+				 GSList		    **fields,
+				 gchar		    **from,
+				 gchar		    **where,
+				 GError		    **error);
+
+G_END_DECLS
+
+#endif /* __TRACKERD_RDF_QUERY_H__ */

Added: trunk/src/trackerd/tracker-search.c
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-search.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,1251 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libtracker-common/tracker-dbus.h>
+#include <libtracker-common/tracker-config.h>
+#include <libtracker-common/tracker-language.h>
+#include <libtracker-common/tracker-log.h>
+#include <libtracker-common/tracker-ontology.h>
+#include <libtracker-common/tracker-parser.h>
+#include <libtracker-common/tracker-utils.h>
+
+#include <libtracker-db/tracker-db-dbus.h>
+#include <libtracker-db/tracker-db-index.h>
+#include <libtracker-db/tracker-db-manager.h>
+
+#include "tracker-db.h"
+#include "tracker-dbus.h"
+#include "tracker-search.h"
+#include "tracker-rdf-query.h"
+#include "tracker-query-tree.h"
+#include "tracker-marshal.h"
+
+#define TRACKER_SEARCH_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TRACKER_TYPE_SEARCH, TrackerSearchPrivate))
+
+#define DEFAULT_SEARCH_MAX_HITS 1024
+
+typedef struct {
+	TrackerConfig	*config;
+	TrackerLanguage *language;
+	TrackerDBIndex	*file_index;
+	TrackerDBIndex	*email_index;
+} TrackerSearchPrivate;
+
+static void tracker_search_finalize (GObject *object);
+
+G_DEFINE_TYPE(TrackerSearch, tracker_search, G_TYPE_OBJECT)
+
+static void
+tracker_search_class_init (TrackerSearchClass *klass)
+{
+	GObjectClass *object_class;
+
+	object_class = G_OBJECT_CLASS (klass);
+
+	object_class->finalize = tracker_search_finalize;
+
+	g_type_class_add_private (object_class, sizeof (TrackerSearchPrivate));
+}
+
+static void
+tracker_search_init (TrackerSearch *object)
+{
+}
+
+static void
+tracker_search_finalize (GObject *object)
+{
+	TrackerSearchPrivate *priv;
+
+	priv = TRACKER_SEARCH_GET_PRIVATE (object);
+
+	g_object_unref (priv->email_index);
+	g_object_unref (priv->file_index);
+	g_object_unref (priv->language);
+	g_object_unref (priv->config);
+
+	G_OBJECT_CLASS (tracker_search_parent_class)->finalize (object);
+}
+
+TrackerSearch *
+tracker_search_new (TrackerConfig   *config,
+		    TrackerLanguage *language,
+		    TrackerDBIndex  *file_index,
+		    TrackerDBIndex  *email_index)
+{
+	TrackerSearch	     *object;
+	TrackerSearchPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), NULL);
+	g_return_val_if_fail (TRACKER_IS_LANGUAGE (language), NULL);
+	g_return_val_if_fail (TRACKER_IS_DB_INDEX (file_index), NULL);
+	g_return_val_if_fail (TRACKER_IS_DB_INDEX (email_index), NULL);
+
+	object = g_object_new (TRACKER_TYPE_SEARCH, NULL);
+
+	priv = TRACKER_SEARCH_GET_PRIVATE (object);
+
+	priv->config = g_object_ref (config);
+	priv->language = g_object_ref (language);
+	priv->file_index = g_object_ref (file_index);
+	priv->email_index = g_object_ref (email_index);
+
+	return object;
+}
+
+/*
+ * Functions
+ */
+static gint
+search_sanity_check_max_hits (gint max_hits)
+{
+	if (max_hits < 1) {
+		return DEFAULT_SEARCH_MAX_HITS;
+	}
+
+	return max_hits;
+}
+
+static const gchar *
+search_utf8_p_from_offset_skipping_decomp (const gchar *str,
+					   gint		offset)
+{
+	const gchar *p, *q;
+	gchar	    *casefold, *normal;
+
+	g_return_val_if_fail (str != NULL, NULL);
+
+	p = str;
+
+	while (offset > 0) {
+		q = g_utf8_next_char (p);
+		casefold = g_utf8_casefold (p, q - p);
+		normal = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFC);
+		offset -= g_utf8_strlen (normal, -1);
+		g_free (casefold);
+		g_free (normal);
+		p = q;
+	}
+
+	return p;
+}
+
+static const char *
+search_utf8_strcasestr_array (const gchar  *haystack,
+			      gchar	  **needles)
+{
+	gsize	      needle_len;
+	gsize	      haystack_len;
+	const gchar  *ret = NULL;
+	const gchar  *needle;
+	gchar	    **array;
+	gchar	     *p;
+	gchar	     *casefold;
+	gchar	     *caseless_haystack;
+	gint	      i;
+
+	g_return_val_if_fail (haystack != NULL, NULL);
+
+	casefold = g_utf8_casefold (haystack, -1);
+	caseless_haystack = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFC);
+	g_free (casefold);
+
+	if (!caseless_haystack) {
+		return NULL;
+	}
+
+	haystack_len = g_utf8_strlen (caseless_haystack, -1);
+
+	for (array = needles; *array; array++) {
+		needle = *array;
+		needle_len = g_utf8_strlen (needle, -1);
+
+		if (needle_len == 0) {
+			continue;
+		}
+
+		if (haystack_len < needle_len) {
+			continue;
+		}
+
+		p = (gchar *) caseless_haystack;
+		needle_len = strlen (needle);
+		i = 0;
+
+		while (*p) {
+			if ((strncmp (p, needle, needle_len) == 0)) {
+				ret = search_utf8_p_from_offset_skipping_decomp (haystack, i);
+				goto done;
+			}
+
+			p = g_utf8_next_char (p);
+			i++;
+		}
+	}
+
+done:
+	g_free (caseless_haystack);
+
+	return ret;
+}
+
+static gint
+search_get_word_break (const char *a)
+{
+	gchar **words;
+	gint	value;
+
+	words = g_strsplit_set (a, "\t\n\v\f\r !\"#$%&'()*/<=>?[\\]^`{|}~+,.:;@\"[]" , -1);
+
+	if (!words) {
+		return 0;
+	}
+
+	value = strlen (words[0]);
+	g_strfreev (words);
+
+	return value;
+}
+
+
+static gboolean
+search_is_word_break (const char a)
+{
+	const gchar *breaks = "\t\n\v\f\r !\"#$%&'()*/<=>?[\\]^`{|}~+,.:;@\"[]";
+	gint	     i;
+
+	for (i = 0; breaks[i]; i++) {
+		if (a == breaks[i]) {
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+static char *
+search_highlight_terms (const gchar  *text,
+			gchar	    **terms)
+{
+	GStrv	      p;
+	GString      *s;
+	const gchar  *str;
+	gchar	     *text_copy;
+	gint	      term_len;
+
+	if (!text || !terms) {
+		return NULL;
+	}
+
+	s = NULL;
+	text_copy = g_strdup (text);
+
+	for (p = terms; *p; p++) {
+		const gchar  *text_p;
+		gchar	    **single_term;
+
+		single_term = g_new (gchar*, 2);
+		single_term[0] = g_strdup (*p);
+		single_term[1] = NULL;
+
+		s = g_string_new ("");
+		text_p = text_copy;
+
+		while ((str = search_utf8_strcasestr_array (text_p, single_term))) {
+			gchar *pre_snip;
+			gchar *term;
+
+			pre_snip = g_strndup (text_p, (str - text_p));
+			term_len = search_get_word_break (str);
+			term = g_strndup (str, term_len);
+
+			text_p = str + term_len;
+			g_string_append_printf (s, "%s<b>%s</b>", pre_snip, term);
+
+			g_free (pre_snip);
+			g_free (term);
+		}
+
+		if (text_p) {
+			g_string_append (s, text_p);
+		}
+
+		g_strfreev (single_term);
+	}
+
+	g_free (text_copy);
+	text_copy = g_string_free (s, FALSE);
+
+	return text_copy;
+}
+
+static gchar *
+search_get_snippet (const gchar  *text,
+		    gchar	**terms,
+		    gint	  length)
+{
+	const gchar *ptr = NULL;
+	const gchar *end_ptr;
+	const gchar *tmp;
+	gint	     i;
+	gint	     text_len;
+
+	if (!text || !terms) {
+		return NULL;
+	}
+
+	text_len = strlen (text);
+	ptr = search_utf8_strcasestr_array (text, terms);
+
+	if (ptr) {
+		gchar *snippet;
+		gchar *snippet_escaped;
+		gchar *snippet_highlighted;
+
+		tmp = ptr;
+		i = 0;
+
+		/* Get snippet before  the matching term */
+		while ((ptr = g_utf8_prev_char (ptr)) && ptr >= text && i < length) {
+			if (*ptr == '\n') {
+				break;
+			}
+
+			i++;
+		}
+
+		/* Try to start beginning of snippet on a word break */
+		if (*ptr != '\n' && ptr > text) {
+			i = 0;
+
+			while (!search_is_word_break (*ptr) && i < (length / 2)) {
+				ptr = g_utf8_next_char (ptr);
+				i++;
+			}
+		}
+
+		ptr = g_utf8_next_char (ptr);
+
+		if (!ptr || ptr < text) {
+			return NULL;
+		}
+
+		end_ptr = tmp;
+		i = 0;
+
+		/* Get snippet after match */
+		while ((end_ptr = g_utf8_next_char (end_ptr)) &&
+		       end_ptr <= text_len + text &&
+		       i < length) {
+			i++;
+
+			if (*end_ptr == '\n') {
+				break;
+			}
+		}
+
+		while (end_ptr > text_len + text) {
+			end_ptr = g_utf8_prev_char (end_ptr);
+		}
+
+		/* Try to end snippet on a word break */
+		if (*end_ptr != '\n' && end_ptr < text_len + text) {
+			i=0;
+			while (!search_is_word_break (*end_ptr) && i < (length / 2)) {
+				end_ptr = g_utf8_prev_char (end_ptr);
+				i++;
+			}
+		}
+
+		if (!end_ptr || !ptr) {
+			return NULL;
+		}
+
+		snippet = g_strndup (ptr, end_ptr - ptr);
+		i = strlen (snippet);
+		snippet_escaped = g_markup_escape_text (snippet, i);
+		g_free (snippet);
+
+		snippet_highlighted = search_highlight_terms (snippet_escaped, terms);
+		g_free (snippet_escaped);
+
+		return snippet_highlighted;
+	}
+
+	ptr = text;
+	i = 0;
+
+	while ((ptr = g_utf8_next_char (ptr)) && ptr <= text_len + text && i < length) {
+		i++;
+
+		if (*ptr == '\n') {
+			break;
+		}
+	}
+
+	if (ptr > text_len + text) {
+		ptr = g_utf8_prev_char (ptr);
+	}
+
+	if (ptr) {
+		gchar *snippet;
+		gchar *snippet_escaped;
+		gchar *snippet_highlighted;
+
+		snippet = g_strndup (text, ptr - text);
+		snippet_escaped = g_markup_escape_text (snippet, ptr - text);
+		snippet_highlighted = search_highlight_terms (snippet_escaped, terms);
+
+		g_free (snippet);
+		g_free (snippet_escaped);
+
+		return snippet_highlighted;
+	} else {
+		return NULL;
+	}
+}
+
+void
+tracker_search_get_hit_count (TrackerSearch	     *object,
+			      const gchar	     *service,
+			      const gchar	     *search_text,
+			      DBusGMethodInvocation  *context,
+			      GError		    **error)
+{
+	TrackerSearchPrivate *priv;
+	TrackerQueryTree     *tree;
+	GError		     *actual_error = NULL;
+	GArray		     *array;
+	guint		      request_id;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (service != NULL, context);
+	tracker_dbus_async_return_if_fail (search_text != NULL, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to get hit count, "
+				  "service:'%s', search text:'%s'",
+				  service,
+				  search_text);
+
+	if (!tracker_ontology_service_is_valid (service)) {
+		g_set_error (&actual_error,
+			     TRACKER_DBUS_ERROR,
+			     0,
+			     "Service '%s' is invalid or has not been implemented yet",
+			     service);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	if (tracker_is_empty_string (search_text)) {
+		g_set_error (&actual_error,
+			     TRACKER_DBUS_ERROR,
+			     0,
+			     "No search term was specified");
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	priv = TRACKER_SEARCH_GET_PRIVATE (object);
+
+	array = tracker_db_create_array_of_services (service, FALSE);
+	tree = tracker_query_tree_new (search_text,
+				       priv->config,
+				       priv->language,
+				       array);
+
+	dbus_g_method_return (context, tracker_query_tree_get_hit_count (tree));
+
+	g_object_unref (tree);
+	g_array_free (array, TRUE);
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_search_get_hit_count_all (TrackerSearch		 *object,
+				  const gchar		 *search_text,
+				  DBusGMethodInvocation  *context,
+				  GError		**error)
+{
+	TrackerSearchPrivate  *priv;
+	TrackerDBResultSet    *result_set = NULL;
+	TrackerQueryTree      *tree;
+	GError		      *actual_error = NULL;
+	GArray		      *hit_counts;
+	guint		       request_id;
+	guint		       i;
+	GArray		      *array;
+	GPtrArray	      *values = NULL;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (search_text != NULL, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to get search hit count for all, "
+				  "search text:'%s'",
+				  search_text);
+
+	if (tracker_is_empty_string (search_text)) {
+		g_set_error (&actual_error,
+			     TRACKER_DBUS_ERROR,
+			     0,
+			     "No search term was specified");
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	priv = TRACKER_SEARCH_GET_PRIVATE (object);
+
+	array = tracker_db_create_array_of_services (NULL, FALSE);
+	tree = tracker_query_tree_new (search_text,
+				       priv->config,
+				       priv->language,
+				       array);
+	g_array_free (array, TRUE);
+
+	hit_counts = tracker_query_tree_get_hit_counts (tree);
+
+	for (i = 0; i < hit_counts->len; i++) {
+		TrackerHitCount count;
+		GValue		value = { 0, };
+
+		if (G_UNLIKELY (!result_set)) {
+			result_set = _tracker_db_result_set_new (2);
+		}
+
+		count = g_array_index (hit_counts, TrackerHitCount, i);
+		_tracker_db_result_set_append (result_set);
+
+		g_value_init (&value, G_TYPE_STRING);
+		g_value_take_string (&value,
+				     tracker_ontology_get_service_by_id (count.service_type_id));
+		_tracker_db_result_set_set_value (result_set, 0, &value);
+		g_value_unset (&value);
+
+		g_value_init (&value, G_TYPE_INT);
+		g_value_set_int (&value, count.count);
+		_tracker_db_result_set_set_value (result_set, 1, &value);
+		g_value_unset (&value);
+	}
+
+	values = tracker_dbus_query_result_to_ptr_array (result_set);
+
+	dbus_g_method_return (context, values);
+
+	tracker_dbus_results_ptr_array_free (&values);
+
+	if (result_set) {
+		tracker_db_result_set_rewind (result_set);
+		g_object_unref (result_set);
+	}
+
+	g_array_free (hit_counts, TRUE);
+	g_object_unref (tree);
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_search_text (TrackerSearch	    *object,
+		     gint		     live_query_id,
+		     const gchar	    *service,
+		     const gchar	    *search_text,
+		     gint		     offset,
+		     gint		     max_hits,
+		     DBusGMethodInvocation  *context,
+		     GError		   **error)
+{
+	GError		    *actual_error = NULL;
+	TrackerDBInterface  *iface;
+	TrackerDBResultSet  *result_set;
+	guint		     request_id;
+	gchar		   **strv = NULL;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (service != NULL, context);
+	tracker_dbus_async_return_if_fail (search_text != NULL, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to search text, "
+				  "query id:%d, service:'%s', search text:'%s', "
+				  "offset:%d, max hits:%d",
+				  live_query_id,
+				  service,
+				  search_text,
+				  offset,
+				  max_hits);
+
+	if (!tracker_ontology_service_is_valid (service)) {
+		g_set_error (&actual_error,
+			     TRACKER_DBUS_ERROR,
+			     0,
+			    "Service '%s' is invalid or has not been implemented yet",
+			    service);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	if (tracker_is_empty_string (search_text)) {
+		g_set_error (&actual_error,
+			     TRACKER_DBUS_ERROR,
+			     0,
+			     "No search term was specified");
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	iface = tracker_db_manager_get_db_interface_by_service (service);
+
+	result_set = tracker_db_search_text (iface,
+					     service,
+					     search_text,
+					     offset,
+					     search_sanity_check_max_hits (max_hits),
+					     FALSE,
+					     FALSE);
+
+	if (result_set) {
+		gchar	 *prefix, *name;
+		gboolean  valid = TRUE;
+		gint	  row_count;
+		gint	  i;
+
+		row_count = tracker_db_result_set_get_n_rows (result_set) + 1;
+		strv = g_new (gchar*, row_count);
+		i = 0;
+
+		while (valid) {
+			tracker_db_result_set_get (result_set,
+						   0, &prefix,
+						   1, &name,
+						   -1);
+
+			strv[i++] = g_build_filename (prefix, name, NULL);
+			valid = tracker_db_result_set_iter_next (result_set);
+
+			g_free (prefix);
+			g_free (name);
+		}
+
+		strv[i] = NULL;
+
+		g_object_unref (result_set);
+	}
+
+	if (!strv) {
+		strv = g_new (gchar*, 1);
+		strv[0] = NULL;
+		tracker_dbus_request_comment (request_id,
+					      "Search found no results");
+	}
+
+	dbus_g_method_return (context, strv);
+
+	g_strfreev (strv);
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_search_text_detailed (TrackerSearch	     *object,
+			      gint		      live_query_id,
+			      const gchar	     *service,
+			      const gchar	     *search_text,
+			      gint		      offset,
+			      gint		      max_hits,
+			      DBusGMethodInvocation  *context,
+			      GError		    **error)
+{
+	GError		   *actual_error = NULL;
+	TrackerDBInterface *iface;
+	TrackerDBResultSet *result_set;
+	guint		    request_id;
+	GPtrArray	   *values = NULL;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (service != NULL, context);
+	tracker_dbus_async_return_if_fail (search_text != NULL, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to search text detailed, "
+				  "query id:%d, service:'%s', search text:'%s', "
+				  "offset:%d, max hits:%d",
+				  live_query_id,
+				  service,
+				  search_text,
+				  offset,
+				  max_hits);
+
+	if (!tracker_ontology_service_is_valid (service)) {
+		g_set_error (&actual_error,
+			     TRACKER_DBUS_ERROR,
+			     0,
+			     "Service '%s' is invalid or has not been implemented yet",
+			     service);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	if (tracker_is_empty_string (search_text)) {
+		g_set_error (&actual_error,
+			     TRACKER_DBUS_ERROR,
+			     0,
+			     "No search term was specified");
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	iface = tracker_db_manager_get_db_interface_by_service (service);
+
+	result_set = tracker_db_search_text (iface,
+					     service,
+					     search_text,
+					     offset,
+					     search_sanity_check_max_hits (max_hits),
+					     FALSE,
+					     TRUE);
+
+	values = tracker_dbus_query_result_to_ptr_array (result_set);
+
+	dbus_g_method_return (context, values);
+
+	tracker_dbus_results_ptr_array_free (&values);
+
+	if (result_set) {
+		g_object_unref (result_set);
+	}
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_search_get_snippet (TrackerSearch	   *object,
+			    const gchar		   *service,
+			    const gchar		   *id,
+			    const gchar		   *search_text,
+			    DBusGMethodInvocation  *context,
+			    GError		  **error)
+{
+	GError		   *actual_error = NULL;
+	TrackerDBInterface *iface;
+	TrackerDBResultSet *result_set;
+	guint		    request_id;
+	gchar		   *snippet = NULL;
+	gchar		   *service_id;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (service != NULL, context);
+	tracker_dbus_async_return_if_fail (id != NULL, context);
+	tracker_dbus_async_return_if_fail (search_text != NULL, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to get snippet, "
+				  "service:'%s', search text:'%s', id:'%s'",
+				  service,
+				  search_text,
+				  id);
+
+	if (!tracker_ontology_service_is_valid (service)) {
+		g_set_error (&actual_error,
+			     TRACKER_DBUS_ERROR,
+			     0,
+			     "Service '%s' is invalid or has not been implemented yet",
+			     service);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	if (tracker_is_empty_string (search_text)) {
+		g_set_error (&actual_error,
+			     TRACKER_DBUS_ERROR,
+			     0,
+			     "No search term was specified");
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	iface = tracker_db_manager_get_db_interface_by_service (service);
+
+	service_id = tracker_db_file_get_id_as_string (iface, service, id);
+	if (!service_id) {
+		g_set_error (&actual_error,
+			     TRACKER_DBUS_ERROR,
+			     0,
+			     "Service URI '%s' not found",
+			     id);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	result_set = tracker_db_exec_proc (iface,
+					   "GetAllContents",
+					   service_id,
+					   NULL);
+	g_free (service_id);
+
+	if (result_set) {
+		TrackerSearchPrivate  *priv;
+		gchar		     **strv;
+		gchar		      *text;
+
+		priv = TRACKER_SEARCH_GET_PRIVATE (object);
+
+		tracker_db_result_set_get (result_set, 0, &text, -1);
+		strv = tracker_parser_text_into_array (text,
+						       priv->language,
+						       tracker_config_get_max_word_length (priv->config),
+						       tracker_config_get_min_word_length (priv->config));
+
+		if (strv && strv[0]) {
+			snippet = search_get_snippet (text, strv, 120);
+		}
+
+		g_strfreev (strv);
+		g_free (text);
+		g_object_unref (result_set);
+	}
+
+	/* Sanity check snippet, using NULL will crash */
+	if (!snippet || !g_utf8_validate (snippet, -1, NULL) ) {
+		snippet = g_strdup (" ");
+	}
+
+	dbus_g_method_return (context, snippet);
+
+	g_free (snippet);
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_search_files_by_text (TrackerSearch	    *object,
+			      gint		     live_query_id,
+			      const gchar	    *search_text,
+			      gint		     offset,
+			      gint		     max_hits,
+			      gboolean		     group_results,
+			      DBusGMethodInvocation  *context,
+			      GError		    **error)
+{
+	/* TrackerDBInterface *iface; */
+	TrackerDBResultSet *result_set;
+	guint		    request_id;
+	GHashTable	   *values = NULL;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (search_text != NULL, context);
+
+	tracker_dbus_request_new (request_id,
+				   "DBus request to search files by text, "
+				  "query id:%d, search text:'%s', offset:%d"
+				   "max hits:%d, group results:'%s'",
+				  live_query_id,
+				  search_text,
+				  offset,
+				  max_hits,
+				  group_results ? "yes" : "no");
+
+	/* iface = tracker_db_manager_get_db_interface_by_service (TRACKER_DB_FOR_FILE_SERVICE); */
+
+	/* FIXME: This function no longer exists, it was returning
+	 * NULL in every case, this DBus function needs rewriting or
+	 * to be removed.
+	 */
+	result_set = NULL;
+
+	/* result_set = tracker_db_search_files_by_text (iface,  */
+	/*					      search_text,  */
+	/*					      offset,  */
+	/*					      search_sanity_check_max_hits (max_hits), */
+	/*					      group_results); */
+
+	values = tracker_dbus_query_result_to_hash_table (result_set);
+
+	dbus_g_method_return (context, values);
+
+	g_hash_table_destroy (values);
+
+	if (result_set) {
+		g_object_unref (result_set);
+	}
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_search_metadata (TrackerSearch		*object,
+			 const gchar		*service,
+			 const gchar		*field,
+			 const gchar		*search_text,
+			 gint			 offset,
+			 gint			 max_hits,
+			 DBusGMethodInvocation	*context,
+			 GError		       **error)
+{
+	GError		   *actual_error = NULL;
+	TrackerDBInterface *iface;
+	TrackerDBResultSet *result_set;
+	guint		    request_id;
+	gchar		  **values;
+
+	/* FIXME: This function is completely redundant */
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (service != NULL, context);
+	tracker_dbus_async_return_if_fail (field != NULL, context);
+	tracker_dbus_async_return_if_fail (search_text != NULL, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to search metadata, "
+				  "service:'%s', search text:'%s', field:'%s', "
+				  "offset:%d, max hits:%d",
+				  service,
+				  search_text,
+				  field,
+				  offset,
+				  max_hits);
+
+	if (!tracker_ontology_service_is_valid (service)) {
+		g_set_error (&actual_error,
+			     TRACKER_DBUS_ERROR,
+			     0,
+			     "Service '%s' is invalid or has not been implemented yet",
+			     service);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	iface = tracker_db_manager_get_db_interface_by_service (service);
+
+	/* FIXME: This function no longer exists, it was returning
+	 * NULL in every case, this DBus function needs rewriting or
+	 * to be removed.
+	 */
+	result_set = NULL;
+
+	/* result_set = tracker_db_search_metadata (iface,  */
+	/*					 service,  */
+	/*					 field,  */
+	/*					 text,	*/
+	/*					 offset,  */
+	/*					 search_sanity_check_max_hits (max_hits)); */
+
+	values = tracker_dbus_query_result_to_strv (result_set, 0, NULL);
+
+	dbus_g_method_return (context, values);
+
+	g_strfreev (values);
+
+	if (result_set) {
+		g_object_unref (result_set);
+	}
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_search_matching_fields (TrackerSearch	      *object,
+				const gchar	      *service,
+				const gchar	      *id,
+				const gchar	      *search_text,
+				DBusGMethodInvocation  *context,
+				GError		      **error)
+{
+	GError		   *actual_error = NULL;
+	TrackerDBInterface *iface;
+	TrackerDBResultSet *result_set;
+	guint		    request_id;
+	GHashTable	   *values = NULL;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (service != NULL, context);
+	tracker_dbus_async_return_if_fail (id != NULL, context);
+	tracker_dbus_async_return_if_fail (search_text != NULL, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to search matching fields, "
+				  "service:'%s', search text:'%s', id:'%s'",
+				  service,
+				  search_text,
+				  id);
+
+	if (!tracker_ontology_service_is_valid (service)) {
+		g_set_error (&actual_error,
+			     TRACKER_DBUS_ERROR,
+			     0,
+			     "Service '%s' is invalid or has not been implemented yet",
+			     service);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	if (tracker_is_empty_string (id)) {
+		g_set_error (&actual_error,
+			     TRACKER_DBUS_ERROR,
+			     0,
+			     "ID field must have a value");
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	iface = tracker_db_manager_get_db_interface_by_service (service);
+
+	/* FIXME: This function no longer exists, it was returning
+	 * NULL in every case, this DBus function needs rewriting or
+	 * to be removed.
+	 */
+	result_set = NULL;
+
+	/* result_set = tracker_db_search_matching_metadata (iface,  */
+	/*						  service,  */
+	/*						  id,  */
+	/*						  search_text); */
+
+	values = tracker_dbus_query_result_to_hash_table (result_set);
+
+	dbus_g_method_return (context, values);
+
+	g_hash_table_destroy (values);
+
+	if (result_set) {
+		g_object_unref (result_set);
+	}
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_search_query (TrackerSearch	     *object,
+		      gint		      live_query_id,
+		      const gchar	     *service,
+		      gchar		    **fields,
+		      const gchar	     *search_text,
+		      const gchar	     *keyword,
+		      const gchar	     *query_condition,
+		      gboolean		      sort_by_service,
+		      gchar		    **sort_fields,
+		      gboolean		      sort_desc,
+		      gint		      offset,
+		      gint		      max_hits,
+		      DBusGMethodInvocation  *context,
+		      GError		    **error)
+{
+	GError		   *actual_error = NULL;
+	TrackerDBInterface *iface;
+	TrackerDBResultSet *result_set;
+	guint		    request_id;
+	GPtrArray	   *values = NULL;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (service != NULL, context);
+	tracker_dbus_async_return_if_fail (fields != NULL, context);
+	tracker_dbus_async_return_if_fail (search_text != NULL, context);
+	tracker_dbus_async_return_if_fail (keyword != NULL, context);
+	tracker_dbus_async_return_if_fail (query_condition != NULL, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to search query, "
+				  "query id:%d, service:'%s', search text '%s', "
+				  "keyword:'%s', query condition:'%s', offset:%d, "
+				  "max hits:%d, sort by service:'%s', sort descending'%s'",
+				  live_query_id,
+				  service,
+				  search_text,
+				  keyword,
+				  query_condition,
+				  offset,
+				  max_hits,
+				  sort_by_service ? "yes" : "no",
+				  sort_desc ? "yes" : "no");
+
+	if (!tracker_ontology_service_is_valid (service)) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     0,
+					     "Service '%s' is invalid or has not been implemented yet",
+					     service);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	result_set = NULL;
+
+	iface = tracker_db_manager_get_db_interface_by_service (service);
+
+	if (query_condition) {
+		GError *query_error = NULL;
+		gchar  *query_translated;
+
+		tracker_dbus_request_comment (request_id,
+					      "Executing RDF query:'%s' with search "
+					      "term:'%s' and keyword:'%s'",
+					      query_condition,
+					      search_text,
+					      keyword);
+
+		query_translated = tracker_rdf_query_to_sql (iface,
+							     query_condition,
+							     service,
+							     fields,
+							     g_strv_length (fields),
+							     search_text,
+							     keyword,
+							     sort_by_service,
+							     sort_fields,
+							     g_strv_length (sort_fields),
+							     sort_desc,
+							     offset,
+							     search_sanity_check_max_hits (max_hits),
+							     &query_error);
+
+		if (query_error) {
+			tracker_dbus_request_failed (request_id,
+						     &query_error,
+						     NULL);
+			dbus_g_method_return_error (context, query_error);
+			g_error_free (query_error);
+			return;
+		} else if (!query_translated) {
+			tracker_dbus_request_failed (request_id,
+						     &actual_error,
+						     0,
+						     "Invalid rdf query, no error given");
+			dbus_g_method_return_error (context, actual_error);
+			g_error_free (actual_error);
+			return;
+		}
+
+		tracker_dbus_request_comment (request_id,
+					      "Translated RDF query:'%s'",
+					      query_translated);
+
+		if (!tracker_is_empty_string (search_text)) {
+			tracker_db_search_text (iface,
+						service,
+						search_text,
+						0,
+						999999,
+						TRUE,
+						FALSE);
+		}
+
+		result_set = tracker_db_interface_execute_query (iface,
+								 NULL,
+								 query_translated);
+		g_free (query_translated);
+	}
+
+	values = tracker_dbus_query_result_to_ptr_array (result_set);
+
+	dbus_g_method_return (context, values);
+
+	tracker_dbus_results_ptr_array_free (&values);
+
+	if (result_set) {
+		g_object_unref (result_set);
+	}
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_search_suggest (TrackerSearch	       *object,
+			const gchar	       *search_text,
+			gint			max_dist,
+			DBusGMethodInvocation  *context,
+			GError		      **error)
+{
+	GError		     *actual_error = NULL;
+	TrackerSearchPrivate *priv;
+	guint		      request_id;
+	gchar		     *value;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (search_text != NULL, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request to for suggested words, "
+				  "term:'%s', max dist:%d",
+				  search_text,
+				  max_dist);
+
+	priv = TRACKER_SEARCH_GET_PRIVATE (object);
+
+	/* First we try the file index */
+	value = tracker_db_index_get_suggestion (priv->file_index,
+						 search_text,
+						 max_dist);
+	if (!value) {
+		/* Second we try the email index */
+		value = tracker_db_index_get_suggestion (priv->email_index,
+							 search_text,
+							 max_dist);
+	}
+
+	if (!value) {
+		g_set_error (&actual_error,
+			     TRACKER_DBUS_ERROR,
+			     0,
+			     "Possible data error in index, no suggestions given for '%s'",
+			     search_text);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	} else {
+		dbus_g_method_return (context, value);
+		tracker_dbus_request_comment (request_id,
+				      "Suggested spelling for '%s' is '%s'",
+				      search_text, value);
+		g_free (value);
+	}
+
+	tracker_dbus_request_success (request_id);
+}

Added: trunk/src/trackerd/tracker-search.h
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-search.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,135 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKERD_SEARCH_H__
+#define __TRACKERD_SEARCH_H__
+
+#include <glib-object.h>
+
+#include <libtracker-common/tracker-language.h>
+
+#include <libtracker-db/tracker-db-index.h>
+
+#define TRACKER_SEARCH_SERVICE	       "org.freedesktop.Tracker"
+#define TRACKER_SEARCH_PATH	       "/org/freedesktop/Tracker/Search"
+#define TRACKER_SEARCH_INTERFACE       "org.freedesktop.Tracker.Search"
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_SEARCH	       (tracker_search_get_type ())
+#define TRACKER_SEARCH(object)	       (G_TYPE_CHECK_INSTANCE_CAST ((object), TRACKER_TYPE_SEARCH, TrackerSearch))
+#define TRACKER_SEARCH_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), TRACKER_TYPE_SEARCH, TrackerSearchClass))
+#define TRACKER_IS_SEARCH(object)      (G_TYPE_CHECK_INSTANCE_TYPE ((object), TRACKER_TYPE_SEARCH))
+#define TRACKER_IS_SEARCH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TRACKER_TYPE_SEARCH))
+#define TRACKER_SEARCH_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), TRACKER_TYPE_SEARCH, TrackerSearchClass))
+
+typedef struct TrackerSearch	  TrackerSearch;
+typedef struct TrackerSearchClass TrackerSearchClass;
+
+struct TrackerSearch {
+	GObject parent;
+};
+
+struct TrackerSearchClass {
+	GObjectClass parent;
+};
+
+GType	       tracker_search_get_type		(void);
+TrackerSearch *tracker_search_new		(TrackerConfig		*config,
+						 TrackerLanguage	*language,
+						 TrackerDBIndex		*file_index,
+						 TrackerDBIndex		*email_index);
+void	       tracker_search_get_hit_count	(TrackerSearch		*object,
+						 const gchar		*service,
+						 const gchar		*search_text,
+						 DBusGMethodInvocation	*context,
+						 GError		       **error);
+void	       tracker_search_get_hit_count_all (TrackerSearch		*object,
+						 const gchar		*search_text,
+						 DBusGMethodInvocation	*context,
+						 GError		       **error_in);
+void	       tracker_search_text		(TrackerSearch		*object,
+						 gint			 live_query_id,
+						 const gchar		*service,
+						 const gchar		*search_text,
+						 gint			 offset,
+						 gint			 max_hits,
+						 DBusGMethodInvocation	*context,
+						 GError		       **error_in);
+void	       tracker_search_text_detailed	(TrackerSearch		*object,
+						 gint			 live_query_id,
+						 const gchar		*service,
+						 const gchar		*search_text,
+						 gint			 offset,
+						 gint			 max_hits,
+						 DBusGMethodInvocation	*context,
+						 GError		       **error);
+void	       tracker_search_get_snippet	(TrackerSearch		*object,
+						 const gchar		*service,
+						 const gchar		*id,
+						 const gchar		*search_text,
+						 DBusGMethodInvocation	*context,
+						 GError		       **error);
+void	       tracker_search_files_by_text	(TrackerSearch		*object,
+						 gint			 live_query_id,
+						 const gchar		*search_text,
+						 gint			 offset,
+						 gint			 max_hits,
+						 gboolean		 group_results,
+						 DBusGMethodInvocation	*context,
+						 GError		       **error);
+void	       tracker_search_metadata		(TrackerSearch		*object,
+						 const gchar		*service,
+						 const gchar		*field,
+						 const gchar		*search_text,
+						 gint			 offset,
+						 gint			 max_hits,
+						 DBusGMethodInvocation	*context,
+						 GError		       **error);
+void	       tracker_search_matching_fields	(TrackerSearch		*object,
+						 const gchar		*service,
+						 const gchar		*id,
+						 const gchar		*search_text,
+						 DBusGMethodInvocation	*context,
+						 GError		       **error);
+void	       tracker_search_query		(TrackerSearch		*object,
+						 gint			 live_query_id,
+						 const gchar		*service,
+						 gchar		       **fields,
+						 const gchar		*search_text,
+						 const gchar		*keyword,
+						 const gchar		*query_condition,
+						 gboolean		 sort_by_service,
+						 gchar		       **sort_fields,
+						 gint			 sort_field_count,
+						 gint			 offset,
+						 gint			 max_hits,
+						 DBusGMethodInvocation	*context,
+						 GError		       **error);
+void	       tracker_search_suggest		(TrackerSearch		*object,
+						 const gchar		*search_text,
+						 gint			 max_dist,
+						 DBusGMethodInvocation	*context,
+						 GError		       **error);
+
+G_END_DECLS
+
+#endif /* __TRACKERD_SEARCH_H__ */

Added: trunk/src/trackerd/tracker-status.c
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-status.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,470 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "tracker-status.h"
+#include "tracker-dbus.h"
+#include "tracker-daemon.h"
+#include "tracker-main.h"
+
+typedef struct {
+	TrackerStatus  status;
+	gpointer       type_class;
+
+	TrackerConfig *config;
+
+	gboolean       is_running;
+	gboolean       is_readonly;
+	gboolean       is_first_time_index;
+	gboolean       is_paused_manually;
+	gboolean       is_paused_for_io;
+	gboolean       in_merge;
+} TrackerStatusPrivate;
+
+static GStaticPrivate private_key = G_STATIC_PRIVATE_INIT;
+
+static void
+private_free (gpointer data)
+{
+	TrackerStatusPrivate *private;
+
+	private = data;
+
+	if (private->config) {
+		g_object_unref (private->config);
+	}
+
+	if (private->type_class) {
+		g_type_class_unref (private->type_class);
+	}
+
+	g_free (private);
+}
+
+gboolean
+tracker_status_init (TrackerConfig *config)
+{
+	GType		      type;
+	TrackerStatusPrivate *private;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), FALSE);
+
+	private = g_static_private_get (&private_key);
+	if (private) {
+		g_warning ("Already initialized (%s)",
+			   __FUNCTION__);
+		return FALSE;
+	}
+
+	private = g_new0 (TrackerStatusPrivate, 1);
+
+	private->status = TRACKER_STATUS_INITIALIZING;
+
+	/* Since we don't reference this enum anywhere, we do
+	 * it here to make sure it exists when we call
+	 * g_type_class_peek(). This wouldn't be necessary if
+	 * it was a param in a GObject for example.
+	 *
+	 * This does mean that we are leaking by 1 reference
+	 * here and should clean it up, but it doesn't grow so
+	 * this is acceptable.
+	 */
+	type = tracker_status_get_type ();
+	private->type_class = g_type_class_ref (type);
+
+	private->config = g_object_ref (config);
+
+	private->is_running = FALSE;
+	private->is_readonly = FALSE;
+	private->is_first_time_index = FALSE;
+	private->is_paused_manually = FALSE;
+	private->is_paused_for_io = FALSE;
+	private->in_merge = FALSE;
+
+	g_static_private_set (&private_key,
+			      private,
+			      private_free);
+
+	return TRUE;
+}
+
+void
+tracker_status_shutdown (void)
+{
+	TrackerStatusPrivate *private;
+
+	private = g_static_private_get (&private_key);
+	if (!private) {
+		g_warning ("Not initialized (%s)",
+			   __FUNCTION__);
+		return;
+	}
+
+	g_static_private_free (&private_key);
+}
+
+GType
+tracker_status_get_type (void)
+{
+	static GType type = 0;
+
+	if (type == 0) {
+		static const GEnumValue values[] = {
+			{ TRACKER_STATUS_INITIALIZING,
+			  "TRACKER_STATUS_INITIALIZING",
+			  "Initializing" },
+			{ TRACKER_STATUS_WATCHING,
+			  "TRACKER_STATUS_WATCHING",
+			  "Watching" },
+			{ TRACKER_STATUS_INDEXING,
+			  "TRACKER_STATUS_INDEXING",
+			  "Indexing" },
+			{ TRACKER_STATUS_PENDING,
+			  "TRACKER_STATUS_PENDING",
+			  "Pending" },
+			{ TRACKER_STATUS_OPTIMIZING,
+			  "TRACKER_STATUS_OPTIMIZING",
+			  "Optimizing" },
+			{ TRACKER_STATUS_IDLE,
+			  "TRACKER_STATUS_IDLE",
+			  "Idle" },
+			{ TRACKER_STATUS_SHUTDOWN,
+			  "TRACKER_STATUS_SHUTDOWN",
+			  "Shutdown" },
+			{ 0, NULL, NULL }
+		};
+
+		type = g_enum_register_static ("TrackerStatus", values);
+	}
+
+	return type;
+}
+
+const gchar *
+tracker_status_to_string (TrackerStatus status)
+{
+	GType	    type;
+	GEnumClass *enum_class;
+	GEnumValue *enum_value;
+
+	type = tracker_status_get_type ();
+	enum_class = G_ENUM_CLASS (g_type_class_peek (type));
+	enum_value = g_enum_get_value (enum_class, status);
+
+	if (!enum_value) {
+		enum_value = g_enum_get_value (enum_class, TRACKER_STATUS_IDLE);
+	}
+
+	return enum_value->value_nick;
+}
+
+TrackerStatus
+tracker_status_get (void)
+{
+	TrackerStatusPrivate *private;
+
+	private = g_static_private_get (&private_key);
+	g_return_val_if_fail (private != NULL, TRACKER_STATUS_INITIALIZING);
+
+	return private->status;
+}
+
+const gchar *
+tracker_status_get_as_string (void)
+{
+	TrackerStatusPrivate *private;
+
+	private = g_static_private_get (&private_key);
+	g_return_val_if_fail (private != NULL, tracker_status_to_string (TRACKER_STATUS_INITIALIZING));
+
+	return tracker_status_to_string (private->status);
+}
+
+void
+tracker_status_set (TrackerStatus new_status)
+{
+	TrackerStatusPrivate *private;
+
+	private = g_static_private_get (&private_key);
+	g_return_if_fail (private != NULL);
+
+	private->status = new_status;
+}
+
+void
+tracker_status_signal (void)
+{
+	TrackerStatusPrivate *private;
+	GObject		     *object;
+	gboolean	      pause_on_battery;
+
+	private = g_static_private_get (&private_key);
+	g_return_if_fail (private != NULL);
+
+	object = tracker_dbus_get_object (TRACKER_TYPE_DAEMON);
+
+	/* There are times on startup whe we haven't initialized the
+	 * DBus objects yet so signalling status is not practical.
+	 */
+	if (!object) {
+		return;
+	}
+
+	if (private->is_first_time_index) {
+		pause_on_battery =
+			tracker_config_get_disable_indexing_on_battery_init (private->config);
+	} else {
+		pause_on_battery =
+			tracker_config_get_disable_indexing_on_battery (private->config);
+	}
+
+	g_signal_emit_by_name (object,
+			       "index-state-change",
+			       tracker_status_to_string (private->status),
+			       private->is_first_time_index,
+			       private->in_merge,
+			       private->is_paused_manually,
+			       pause_on_battery,
+			       private->is_paused_for_io,
+			       !private->is_readonly);
+}
+
+void
+tracker_status_set_and_signal (TrackerStatus new_status)
+{
+	TrackerStatusPrivate *private;
+	gboolean	      emit;
+
+	private = g_static_private_get (&private_key);
+	g_return_if_fail (private != NULL);
+
+	emit = private->status != new_status;
+
+	if (!emit) {
+		return;
+	}
+
+	g_message ("State change from '%s' --> '%s'",
+		   tracker_status_to_string (private->status),
+		   tracker_status_to_string (new_status));
+
+	tracker_status_set (new_status);
+	tracker_status_signal ();
+}
+
+gboolean
+tracker_status_get_is_readonly (void)
+{
+	TrackerStatusPrivate *private;
+
+	private = g_static_private_get (&private_key);
+	g_return_val_if_fail (private != NULL, FALSE);
+
+	return private->is_readonly;
+}
+
+void
+tracker_status_set_is_readonly (gboolean value)
+{
+	TrackerStatusPrivate *private;
+	gboolean	      emit;
+
+	private = g_static_private_get (&private_key);
+	g_return_if_fail (private != NULL);
+
+	emit = private->is_readonly != value;
+
+	if (!emit) {
+		return;
+	}
+
+	/* Set value */
+	private->is_readonly = value;
+
+	/* Signal the status change */
+	tracker_status_signal ();
+}
+
+gboolean
+tracker_status_get_is_running (void)
+{
+	TrackerStatusPrivate *private;
+
+	private = g_static_private_get (&private_key);
+	g_return_val_if_fail (private != NULL, FALSE);
+
+	return private->is_running;
+}
+
+void
+tracker_status_set_is_running (gboolean value)
+{
+	TrackerStatusPrivate *private;
+	gboolean	      emit;
+
+	private = g_static_private_get (&private_key);
+	g_return_if_fail (private != NULL);
+
+	emit = private->is_running != value;
+
+	if (!emit) {
+		return;
+	}
+
+	/* Set value */
+	private->is_running = value;
+
+	/* Signal the status change */
+	tracker_status_signal ();
+}
+
+gboolean
+tracker_status_get_is_first_time_index (void)
+{
+	TrackerStatusPrivate *private;
+
+	private = g_static_private_get (&private_key);
+	g_return_val_if_fail (private != NULL, FALSE);
+
+	return private->is_first_time_index;
+}
+
+void
+tracker_status_set_is_first_time_index (gboolean value)
+{
+	TrackerStatusPrivate *private;
+	gboolean	      emit;
+
+	private = g_static_private_get (&private_key);
+	g_return_if_fail (private != NULL);
+
+	emit = private->is_first_time_index != value;
+
+	if (!emit) {
+		return;
+	}
+
+	/* Set value */
+	private->is_first_time_index = value;
+
+	/* Signal the status change */
+	tracker_status_signal ();
+}
+
+gboolean
+tracker_status_get_in_merge (void)
+{
+	TrackerStatusPrivate *private;
+
+	private = g_static_private_get (&private_key);
+	g_return_val_if_fail (private != NULL, FALSE);
+
+	return private->in_merge;
+}
+
+void
+tracker_status_set_in_merge (gboolean value)
+{
+	TrackerStatusPrivate *private;
+	gboolean	      emit;
+
+	private = g_static_private_get (&private_key);
+	g_return_if_fail (private != NULL);
+
+	emit = private->in_merge != value;
+
+	if (!emit) {
+		return;
+	}
+
+	/* Set value */
+	private->in_merge = value;
+
+	/* Signal the status change */
+	tracker_status_signal ();
+}
+
+gboolean
+tracker_status_get_is_paused_manually (void)
+{
+	TrackerStatusPrivate *private;
+
+	private = g_static_private_get (&private_key);
+	g_return_val_if_fail (private != NULL, FALSE);
+
+	return private->is_paused_manually;
+}
+
+void
+tracker_status_set_is_paused_manually (gboolean value)
+{
+	TrackerStatusPrivate *private;
+	gboolean	      emit;
+
+	private = g_static_private_get (&private_key);
+	g_return_if_fail (private != NULL);
+
+	emit = private->is_paused_manually != value;
+
+	if (!emit) {
+		return;
+	}
+
+	/* Set value */
+	private->is_paused_manually = value;
+
+	/* Signal the status change */
+	tracker_status_signal ();
+}
+
+gboolean
+tracker_status_get_is_paused_for_io (void)
+{
+	TrackerStatusPrivate *private;
+
+	private = g_static_private_get (&private_key);
+	g_return_val_if_fail (private != NULL, FALSE);
+
+	return private->is_paused_for_io;
+}
+
+void
+tracker_status_set_is_paused_for_io (gboolean value)
+{
+	TrackerStatusPrivate *private;
+	gboolean	      emit;
+
+	private = g_static_private_get (&private_key);
+	g_return_if_fail (private != NULL);
+
+	emit = private->is_paused_for_io != value;
+
+	if (!emit) {
+		return;
+	}
+
+	/* Set value */
+	private->is_paused_for_io = value;
+
+	/* Signal the status change */
+	tracker_status_signal ();
+}

Added: trunk/src/trackerd/tracker-status.h
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-status.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,75 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKERD_STATUS_H__
+#define __TRACKERD_STATUS_H__
+
+#include <glib-object.h>
+
+#include <libtracker-common/tracker-config.h>
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_STATUS (tracker_status_get_type ())
+
+typedef enum {
+	TRACKER_STATUS_INITIALIZING,
+	TRACKER_STATUS_WATCHING,
+	TRACKER_STATUS_INDEXING,
+	TRACKER_STATUS_PENDING,
+	TRACKER_STATUS_OPTIMIZING,
+	TRACKER_STATUS_IDLE,
+	TRACKER_STATUS_SHUTDOWN
+} TrackerStatus;
+
+
+gboolean      tracker_status_init		     (TrackerConfig *config);
+void	      tracker_status_shutdown		     (void);
+
+GType	      tracker_status_get_type		     (void) G_GNUC_CONST;
+const gchar * tracker_status_to_string		     (TrackerStatus  status);
+TrackerStatus tracker_status_get		     (void);
+const gchar * tracker_status_get_as_string	     (void);
+void	      tracker_status_set		     (TrackerStatus  new_status);
+void	      tracker_status_set_and_signal	     (TrackerStatus  new_status);
+void	      tracker_status_signal		     (void);
+
+gboolean      tracker_status_get_is_readonly	     (void);
+void	      tracker_status_set_is_readonly	     (gboolean	     value);
+
+gboolean      tracker_status_get_is_running	     (void);
+void	      tracker_status_set_is_running	     (gboolean	     value);
+
+void	      tracker_status_set_is_first_time_index (gboolean	     value);
+gboolean      tracker_status_get_is_first_time_index (void);
+
+gboolean      tracker_status_get_in_merge	     (void);
+void	      tracker_status_set_in_merge	     (gboolean	     value);
+
+gboolean      tracker_status_get_is_paused_manually  (void);
+void	      tracker_status_set_is_paused_manually  (gboolean	     value);
+
+gboolean      tracker_status_get_is_paused_for_io    (void);
+void	      tracker_status_set_is_paused_for_io    (gboolean	     value);
+
+G_END_DECLS
+
+#endif /* __TRACKERD_STATUS_H__ */

Added: trunk/src/trackerd/tracker-utils.c
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-utils.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2007, Michal Pryc (Michal Pryc Sun Com)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+#include "config.h"
+
+#include <sys/statvfs.h>
+
+#include <libtracker-common/tracker-log.h>
+#include <libtracker-common/tracker-config.h>
+
+#include "tracker-utils.h"
+#include "tracker-main.h"
+#include "tracker-xesam-manager.h"
+
+gchar *
+tracker_get_radix_by_suffix (const gchar *str,
+			     const gchar *suffix)
+{
+	g_return_val_if_fail (str, NULL);
+	g_return_val_if_fail (suffix, NULL);
+
+	if (g_str_has_suffix (str, suffix)) {
+		return g_strndup (str, g_strrstr (str, suffix) - str);
+	} else {
+		return NULL;
+	}
+}
+
+void
+tracker_add_metadata_to_table (GHashTable  *meta_table,
+			       const gchar *key,
+			       const gchar *value)
+{
+	GSList *list;
+
+	list = g_hash_table_lookup (meta_table, (gchar*) key);
+	list = g_slist_prepend (list, (gchar*) value);
+	g_hash_table_steal (meta_table, key);
+	g_hash_table_insert (meta_table, (gchar*) key, list);
+}
+

Added: trunk/src/trackerd/tracker-utils.h
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-utils.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,40 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2007, Michal Pryc (Michal Pryc Sun Com)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKERD_UTILS_H__
+#define __TRACKERD_UTILS_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+gchar *  tracker_get_radix_by_suffix	    (const gchar  *str,
+					     const gchar  *suffix);
+void	 tracker_notify_file_data_available (void);
+void	 tracker_add_metadata_to_table	    (GHashTable   *meta_table,
+					     const char   *key,
+					     const char   *value);
+void	 tracker_add_io_grace		    (const char   *uri);
+
+G_END_DECLS
+
+#endif /* __TRACKERD_UTILS_H__ */

Added: trunk/src/trackerd/tracker-xesam-live-search.c
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-xesam-live-search.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,1363 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia
+ * Authors: Philip Van Hoof (pvanhoof gnome org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <string.h>
+
+#include <dbus/dbus-glib-bindings.h>
+
+#include "tracker-xesam-live-search.h"
+#include "tracker-xesam.h"
+#include "tracker-xesam-manager.h"
+#include "tracker-xesam-query.h"
+#include "tracker-dbus.h"
+#include "tracker-db.h"
+
+struct _TrackerXesamLiveSearchPriv {
+	TrackerXesamSession *session;
+	gchar		    *search_id;
+	gboolean	     active;
+	gboolean	     closed;
+	gchar		    *query;
+	gchar		    *from_sql;
+	gchar		    *where_sql;
+	gchar		    *join_sql;
+};
+
+enum {
+	PROP_0,
+	PROP_XMLQUERY
+};
+
+G_DEFINE_TYPE (TrackerXesamLiveSearch, tracker_xesam_live_search, G_TYPE_OBJECT)
+
+#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TRACKER_TYPE_XESAM_LIVE_SEARCH, struct _TrackerXesamLiveSearchPriv))
+
+static void
+tracker_xesam_live_search_finalize (GObject *object)
+{
+	TrackerXesamLiveSearch *self = (TrackerXesamLiveSearch *) object;
+	TrackerXesamLiveSearchPriv *priv = self->priv;
+
+	if (priv->session)
+		g_object_unref (priv->session);
+
+	g_free (priv->search_id);
+	g_free (priv->query);
+
+	g_free (priv->from_sql);
+	g_free (priv->join_sql);
+	g_free (priv->where_sql);
+}
+
+void
+tracker_xesam_live_search_set_session (TrackerXesamLiveSearch *self,
+				       gpointer		       session)
+{
+	TrackerXesamLiveSearchPriv *priv = self->priv;
+
+	g_return_if_fail (TRACKER_IS_XESAM_LIVE_SEARCH (self));
+	g_return_if_fail (session != NULL);
+
+	if (priv->session) {
+		g_object_unref (priv->session);
+	}
+
+	if (session) {
+		priv->session = g_object_ref (session);
+	} else {
+		priv->session = NULL;
+	}
+}
+
+void
+tracker_xesam_live_search_set_xml_query (TrackerXesamLiveSearch *self,
+					 const gchar		*query)
+{
+	TrackerXesamLiveSearchPriv *priv;
+
+	g_return_if_fail (TRACKER_IS_XESAM_LIVE_SEARCH (self));
+
+	priv = self->priv;
+
+	g_free (priv->query);
+
+	if (query) {
+		priv->query = g_strdup (query);
+	} else {
+		priv->query = NULL;
+	}
+}
+
+static void
+xesam_search_set_property (GObject	*object,
+			   guint	param_id,
+			   const GValue *value,
+			   GParamSpec	*pspec)
+{
+	switch (param_id) {
+	case PROP_XMLQUERY:
+		tracker_xesam_live_search_set_xml_query (TRACKER_XESAM_LIVE_SEARCH (object),
+							 g_value_get_pointer (value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	};
+}
+
+
+static void
+tracker_xesam_live_search_class_init (TrackerXesamLiveSearchClass *klass)
+{
+	GObjectClass *object_class;
+
+	object_class = G_OBJECT_CLASS (klass);
+
+	object_class->finalize = tracker_xesam_live_search_finalize;
+	object_class->set_property = xesam_search_set_property;
+
+	g_object_class_install_property (object_class,
+					 PROP_XMLQUERY,
+					 g_param_spec_pointer ("xml-query",
+							       "XML Query",
+							       "XML Query",
+							       G_PARAM_WRITABLE));
+
+	g_type_class_add_private (klass, sizeof (struct _TrackerXesamLiveSearchPriv));
+
+}
+
+static void
+tracker_xesam_live_search_init (TrackerXesamLiveSearch *self)
+{
+	TrackerXesamLiveSearchPriv *priv;
+
+	priv = self->priv = GET_PRIV (self);
+
+	priv->session = NULL;
+	priv->search_id = NULL;
+	priv->session = NULL;
+
+	priv->active = FALSE;
+	priv->closed = FALSE;
+	priv->query = NULL;
+	priv->from_sql = g_strdup ("");
+	priv->join_sql = g_strdup ("");
+	priv->where_sql = g_strdup ("");
+
+}
+
+/**
+ * tracker_xesam_live_search_emit_hits_added:
+ * @self: A #TrackerXesamLiveSearch
+ * @count: The number of hits added
+ *
+ * Emits the @hits-added signal on the DBus proxy for Xesam
+ **/
+void
+tracker_xesam_live_search_emit_hits_added (TrackerXesamLiveSearch *self,
+					   guint		   count)
+{
+	GObject *xesam;
+
+	g_return_if_fail (TRACKER_IS_XESAM_LIVE_SEARCH (self));
+
+	xesam = tracker_dbus_get_object (TRACKER_TYPE_XESAM);
+
+	g_signal_emit_by_name (xesam, "hits-added",
+			       tracker_xesam_live_search_get_id (self),
+			       count);
+}
+
+/**
+ * tracker_xesam_live_search_emit_hits_removed:
+ * @self: A #TrackerXesamLiveSearch
+ * @hit_ids: modified hit ids
+ * @hit_ids_length: length of the @hit_ids array
+ *
+ * Emits the @hits-removed signal on the DBus proxy for Xesam
+ *
+ * The hit ids in the array no longer match the query. Any calls to GetHitData
+ * on any of the given hit ids should return unset fields.
+ **/
+void
+tracker_xesam_live_search_emit_hits_removed (TrackerXesamLiveSearch *self,
+					     GArray		    *hit_ids)
+{
+	GObject *xesam;
+
+	g_return_if_fail (TRACKER_IS_XESAM_LIVE_SEARCH (self));
+	g_return_if_fail (hit_ids != NULL);
+
+	xesam = tracker_dbus_get_object (TRACKER_TYPE_XESAM);
+
+	g_signal_emit_by_name (xesam, "hits-removed",
+			       tracker_xesam_live_search_get_id (self),
+			       hit_ids);
+}
+
+/**
+ * tracker_xesam_live_search_emit_hits_modified:
+ * @selfs: A #TrackerXesamLiveSearch
+ * @hit_ids: modified hit ids
+ * @hit_ids_length: length of the @hit_ids array
+ *
+ * Emits the @hits-modified signal on the DBus proxy for Xesam
+ *
+ * The documents corresponding to the hit ids in the array have been modified.
+ * They can have been moved in which case their uri will have changed.
+ **/
+void
+tracker_xesam_live_search_emit_hits_modified (TrackerXesamLiveSearch *self,
+					      GArray		     *hit_ids)
+{
+	GObject *xesam;
+
+	g_return_if_fail (TRACKER_IS_XESAM_LIVE_SEARCH (self));
+	g_return_if_fail (hit_ids != NULL);
+
+	xesam = tracker_dbus_get_object (TRACKER_TYPE_XESAM);
+
+	g_signal_emit_by_name (xesam, "hits-modified",
+			       tracker_xesam_live_search_get_id (self),
+			       hit_ids);
+}
+
+/**
+ * tracker_xesam_live_search_emit_done:
+ * @self: A #TrackerXesamLiveSearch
+ *
+ * Emits the @search-done signal on the DBus proxy for Xesam.
+ *
+ * The given search has scanned the entire index. For non-live searches this
+ * means that no more hits will be available. For a live search this means that
+ * all future signals (@hits-Added, @hits-removed, @hits-modified) will be
+ * related to objects that changed in the index.
+ **/
+void
+tracker_xesam_live_search_emit_done (TrackerXesamLiveSearch *self)
+{
+	GObject *xesam;
+
+	g_return_if_fail (TRACKER_IS_XESAM_LIVE_SEARCH (self));
+
+	xesam = tracker_dbus_get_object (TRACKER_TYPE_XESAM);
+
+	g_signal_emit_by_name (xesam, "search-done",
+			       tracker_xesam_live_search_get_id (self));
+}
+
+
+
+/* Created and Modified items */
+static void
+get_hits_added_modified (TrackerXesamLiveSearch  *self,
+			 MatchWithEventsFlags	  flags,
+			 TrackerDBInterface	 *iface,
+			 GArray			**added,
+			 GArray			**modified)
+{
+	gboolean	    ls_valid = TRUE;
+	GArray		   *m_added = NULL;
+	GArray		   *m_modified = NULL;
+	TrackerDBResultSet *result_set;
+
+	/* Right now we are ignoring flags (both creates and updates are
+	 * searched) */
+
+	result_set = tracker_db_live_search_get_new_ids (iface,
+							 tracker_xesam_live_search_get_id (self),
+							 tracker_xesam_live_search_get_from_query (self),
+							 tracker_xesam_live_search_get_join_query (self),
+							 tracker_xesam_live_search_get_where_query (self)); /* Query */
+
+	if (!result_set) {
+		return;
+	}
+
+	while (ls_valid) {
+		GValue	     ls_value = { 0, };
+		GValue	     ev_type = { 0, };
+		gint	     ls_i_value;
+		const gchar *str;
+
+		_tracker_db_result_set_get_value (result_set, 0, &ls_value);
+		_tracker_db_result_set_get_value (result_set, 1, &ev_type);
+
+		str = g_value_get_string (&ev_type);
+
+		ls_i_value = g_value_get_int (&ls_value);
+
+		if (!strcmp (str, "Update")) {
+			gboolean noadd = FALSE;
+			guint	 i;
+
+			if (m_modified == NULL) {
+				m_modified = g_array_new (FALSE, TRUE, sizeof (guint32));
+			} else {
+				for (i = 0 ; i < m_modified->len; i++)
+					if (g_array_index (m_modified, guint32, i) == (guint32) ls_i_value) {
+						noadd = TRUE;
+						break;
+					}
+			}
+			if (!noadd)
+				g_array_append_val (m_modified, ls_i_value);
+		} else {
+			if (m_added == NULL)
+				m_added = g_array_new (FALSE, TRUE, sizeof (guint32));
+			g_array_append_val (m_added, ls_i_value);
+		}
+
+		g_value_unset (&ev_type);
+		g_value_unset (&ls_value);
+
+		ls_valid = tracker_db_result_set_iter_next (result_set);
+	}
+
+	g_object_unref (result_set);
+
+	*added = m_added;
+	*modified = m_modified;
+}
+
+/* Created and Modified items */
+static void
+get_all_hits (TrackerXesamLiveSearch  *self,
+	      TrackerDBInterface      *iface,
+	      GArray		     **hits)
+{
+	TrackerDBResultSet *result_set;
+	gboolean	    valid;
+
+	g_return_if_fail (hits != NULL);
+
+	*hits = NULL;
+
+	result_set = tracker_db_live_search_get_all_ids (iface,
+							 tracker_xesam_live_search_get_id (self));
+
+	if (!result_set) {
+		return;
+	}
+
+	valid = TRUE;
+
+	while (valid) {
+		GValue ls_value = { 0, };
+		gint   ls_i_value;
+
+		_tracker_db_result_set_get_value (result_set, 0, &ls_value);
+		ls_i_value = g_value_get_int (&ls_value);
+
+		if (*hits == NULL) {
+			*hits = g_array_new (FALSE, TRUE, sizeof (guint32));
+		}
+
+		g_array_append_val (*hits, ls_i_value);
+		g_value_unset (&ls_value);
+
+		valid = tracker_db_result_set_iter_next (result_set);
+	}
+
+	g_object_unref (result_set);
+}
+
+
+/**
+ * tracker_xesam_live_search_match_with_events:
+ * @self: A #TrackerXesamLiveSearch
+ * @added: (caller-owns) (out): added items
+ * @removed: (caller-owns) (out): removed items
+ * @modified: (caller-owns) (out): modified items
+ *
+ * Find all items that match with the current events for @self.
+ **/
+void
+tracker_xesam_live_search_match_with_events (TrackerXesamLiveSearch  *self,
+					     MatchWithEventsFlags     flags,
+					     GArray		    **added,
+					     GArray		    **removed,
+					     GArray		    **modified)
+{
+	TrackerDBInterface *iface;
+	TrackerDBResultSet *result_set;
+
+	g_return_if_fail (TRACKER_IS_XESAM_LIVE_SEARCH (self));
+	g_return_if_fail (added != NULL);
+	g_return_if_fail (removed != NULL);
+	g_return_if_fail (modified != NULL);
+
+	iface = tracker_db_manager_get_db_interface_by_service (TRACKER_DB_FOR_XESAM_SERVICE);
+
+	*added = NULL;
+	*removed = NULL;
+	*modified = NULL;
+
+	if (flags & MATCH_WITH_EVENTS_DELETES) {
+		/* Deleted items */
+		result_set = tracker_db_live_search_get_deleted_ids (iface,
+								     tracker_xesam_live_search_get_id (self));
+
+		if (result_set) {
+			gboolean valid;
+
+			valid = TRUE;
+
+			while (valid) {
+				GValue ls_value = { 0, };
+				gint   ls_i_value;
+
+				_tracker_db_result_set_get_value (result_set,
+								  0,
+								  &ls_value);
+				ls_i_value = g_value_get_int (&ls_value);
+
+				if (*removed == NULL) {
+					*removed = g_array_new (FALSE,
+								TRUE,
+								sizeof (guint32));
+				}
+
+				g_array_append_val (*removed, ls_i_value);
+				g_value_unset (&ls_value);
+
+				valid = tracker_db_result_set_iter_next (result_set);
+			}
+
+			g_object_unref (result_set);
+		}
+	}
+
+	if (flags & MATCH_WITH_EVENTS_CREATES || flags & MATCH_WITH_EVENTS_MODIFIES) {
+		/* Created and Modified items */
+		get_hits_added_modified (self, flags, iface, added, modified);
+	}
+}
+
+
+/**
+ * tracker_xesam_live_search_close:
+ * @self: a #TrackerXesamLiveSearch
+ * @error: (null-ok) (out): a #GError
+ *
+ * Close @self. An error will be thrown if @self was already closed.
+ **/
+void
+tracker_xesam_live_search_close (TrackerXesamLiveSearch  *self,
+				 GError			**error)
+{
+	TrackerXesamLiveSearchPriv *priv;
+
+	g_return_if_fail (TRACKER_IS_XESAM_LIVE_SEARCH (self));
+
+	priv = self->priv;
+
+	if (priv->closed) {
+		g_set_error (error,
+			     TRACKER_XESAM_ERROR_DOMAIN,
+			     TRACKER_XESAM_ERROR_SEARCH_CLOSED,
+			     "Search was already closed");
+	} else {
+		TrackerDBInterface *iface;
+
+		iface = tracker_db_manager_get_db_interface_by_service (TRACKER_DB_FOR_XESAM_SERVICE);
+
+		g_message ("Closing search '%s'",
+			   tracker_xesam_live_search_get_id (self));
+
+		tracker_db_live_search_stop (iface,
+					     tracker_xesam_live_search_get_id (self));
+	}
+
+	priv->closed = TRUE;
+	priv->active = FALSE;
+}
+
+/**
+ * tracker_xesam_live_search_get_hit_count:
+ * @self: a #TrackerXesamLiveSearch
+ * @count: (out): the current number of found hits
+ * @error: (null-ok) (out): a #GError
+ *
+ * Get the current number of found hits.
+ *
+ * An error will be thrown if the search has not been started with
+ * @tracker_xesam_live_search_activate yet.
+ **/
+void
+tracker_xesam_live_search_get_hit_count (TrackerXesamLiveSearch  *self,
+					 guint			 *count,
+					 GError			**error)
+{
+	TrackerXesamLiveSearchPriv *priv;
+
+	g_return_if_fail (TRACKER_IS_XESAM_LIVE_SEARCH (self));
+	g_return_if_fail (count != NULL);
+
+	priv = self->priv;
+
+	if (!priv->active) {
+		g_set_error (error,
+			     TRACKER_XESAM_ERROR_DOMAIN,
+			     TRACKER_XESAM_ERROR_SEARCH_NOT_ACTIVE,
+			     "Search is not active");
+	} else {
+		TrackerDBInterface *iface;
+		TrackerDBResultSet *result_set;
+		GValue		    value = {0, };
+
+		iface = tracker_db_manager_get_db_interface_by_service (TRACKER_DB_FOR_XESAM_SERVICE);
+
+		result_set = tracker_db_live_search_get_hit_count (iface,
+								   tracker_xesam_live_search_get_id (self));
+		_tracker_db_result_set_get_value (result_set, 0, &value);
+		*count = g_value_get_int (&value);
+		g_value_unset (&value);
+		g_object_unref (result_set);
+	}
+}
+
+typedef struct {
+	gint key;
+	gpointer value;
+} OneRow;
+
+static inline gpointer
+rows_lookup (GPtrArray *rows, gint key)
+{
+	guint	 i;
+	gpointer value = NULL;
+
+	for (i = 0; i < rows->len; i++) {
+		OneRow *row = g_ptr_array_index (rows, i);
+		if (row->key == key) {
+			value = row->value;
+			break;
+		}
+	}
+
+	return value;
+}
+
+static inline void
+rows_destroy (GPtrArray *rows)
+{
+	guint i;
+
+	for (i = 0; i < rows->len; i++) {
+		g_slice_free (OneRow, g_ptr_array_index (rows, i));
+	}
+
+	g_ptr_array_free (rows, TRUE);
+}
+
+static inline void
+rows_insert (GPtrArray *rows, gint key, gpointer value)
+{
+	OneRow *row = g_slice_new (OneRow);
+
+	row->key = key;
+	row->value = value;
+
+	g_ptr_array_add (rows, row);
+}
+
+static inline void
+rows_migrate (GPtrArray *rows, GPtrArray *result)
+{
+	guint i;
+
+	for (i = 0; i < rows->len; i++) {
+		OneRow *one = g_ptr_array_index (rows, i);
+		GPtrArray *row = one->value;
+		g_ptr_array_add (result, row);
+	}
+}
+
+/**
+ * Retrieving Hits
+ * The return value of GetHits and GetHitData is a sorted array of hits. A hit
+ * consists of an array of fields as requested through the session property
+ * hit.fields, or as method parameter in the case of GetHitData. All available
+ * fields can be found in the Xesam Ontology. Since the signature of the return
+ * value is aav a single hit is on the form av. This allows hit properties to be
+ * integers, strings or arrays of any type. An array of strings is fx. needed
+ * for email CC fields and keywords/tags for example.
+ *
+ * The returned fields are ordered according to hit.fields. Fx.
+ * if hit.fields = ["xesam:title", "xesam:userKeywords", "xesam:size"], a
+ * return value would look like:
+ *
+ * [
+ *  ["Desktop Search Survey", ["xesam", "search", "hot stuff"], 54367]
+ *  ["Gnome Tips and Tricks", ["gnome", "hacking"], 437294]
+ * ]
+ *
+ * It's a root GPtrArray with 'GPtrArray' typed elements. Those child GPtrArray
+ * elements contain GValue instances.
+ **/
+static void
+get_hit_data (TrackerXesamLiveSearch  *self,
+	      TrackerDBResultSet      *result_set,
+	      GPtrArray		     **hit_data,
+	      GStrv		       fields)
+{
+	GPtrArray  *result = g_ptr_array_new ();
+	GPtrArray  *rows = g_ptr_array_new ();
+	gboolean    valid = TRUE;
+	guint	    field_count;
+
+	field_count = g_strv_length (fields);
+
+	while (valid) {
+
+		guint		       column;
+		GPtrArray	      *row;
+		GValue		       value_in = {0, };
+		gboolean	       insert = FALSE;
+		gint		       key;
+		TrackerFieldType  data_type;
+		TrackerField	 *field_def;
+
+		_tracker_db_result_set_get_value (result_set, 0, &value_in);
+
+		/* key must be the first column, as an int, unique per row that
+		 * must actually be returned. Example:
+		 *
+		 * 1, a, b, c, 1
+		 * 1, a, b, c, 2
+		 * 1, a, b, c, 3
+		 * 1, a, b, c, 4
+		 * 2, a, b, c, 1
+		 * 3, a, b, c, 1
+		 * 4, a, b, c, 2
+		 * 5, a, b, c, 2
+		 *
+		 * for:
+		 *
+		 * [
+		 *    [a, b, c, [1, 2, 3, 4]]
+		 *    [a, b, c, [1]]
+		 *    [a, b, c, [1]]
+		 *    [a, b, c, [2]]
+		 *    [a, b, c, [2]]
+		 * ]
+		 **/
+
+		key = g_value_get_int (&value_in);
+
+		/* Think before rewriting this using a GHashTable: A GHashTable
+		 * doesn't preserve the sort order. The sort order is indeed
+		 * significant for the Xesam spec. */
+
+		row = rows_lookup (rows, key);
+
+		if (!row) {
+			row = g_ptr_array_new ();
+			insert = TRUE;
+		}
+
+		for (column = 1; column < field_count + 1; column++) {
+			GValue cur_value = {0, };
+
+			_tracker_db_result_set_get_value (result_set,
+							  column,
+							  &cur_value);
+
+			field_def = tracker_ontology_get_field_by_name (fields[column-1]);
+			data_type = tracker_field_get_data_type (field_def);
+
+			if (tracker_field_get_multiple_values (field_def)) {
+
+				switch (data_type) {
+				case TRACKER_FIELD_TYPE_DATE:
+				case TRACKER_FIELD_TYPE_STRING: {
+					GValue	  *variant;
+					GPtrArray *my_array;
+
+					if (row->len <= (unsigned int) column) {
+						variant = g_new0 (GValue, 1);
+						g_value_init (variant,
+							      dbus_g_type_get_collection ("GPtrArray",
+											  G_TYPE_STRING));
+
+						my_array = g_ptr_array_new ();
+						g_value_set_boxed_take_ownership (variant,
+										  my_array);
+
+						g_ptr_array_add (row, variant);
+
+					} else {
+						variant = g_ptr_array_index (row, column-1);
+						my_array = g_value_get_boxed (variant);
+					}
+
+					g_ptr_array_add  (my_array,
+							  g_value_dup_string (&cur_value));
+
+					break;
+				}
+
+				case TRACKER_FIELD_TYPE_INTEGER: {
+					GValue *variant;
+					GArray *my_array;
+					gint	int_val;
+
+					if (row->len <= (unsigned int) column) {
+						variant = g_new0 (GValue, 1);
+						g_value_init (variant,
+							      dbus_g_type_get_collection ("GArray",
+											  G_TYPE_INT));
+
+						my_array = g_array_new (FALSE,
+									 TRUE,
+									 sizeof (gfloat));
+						g_value_set_boxed_take_ownership (variant, my_array);
+
+						g_ptr_array_add (row, variant);
+					} else {
+						variant = g_ptr_array_index (row, column);
+						my_array = g_value_get_boxed (variant);
+					}
+
+					int_val = g_value_get_int (&cur_value);
+					g_array_append_val (my_array, int_val);
+
+					break;
+				}
+
+				case TRACKER_FIELD_TYPE_DOUBLE: {
+					GValue	 *variant;
+					GArray	 *my_array;
+					gfloat	  float_val;
+
+					if (row->len <= (unsigned int) column) {
+						variant = g_new0 (GValue, 1);
+						g_value_init (variant,
+							      dbus_g_type_get_collection ("GArray",
+										     G_TYPE_FLOAT));
+
+						my_array = g_array_new (FALSE,
+									 TRUE,
+									 sizeof (gboolean));
+						g_value_set_boxed_take_ownership (variant, my_array);
+
+						g_ptr_array_add (row, variant);
+					} else {
+						variant = g_ptr_array_index (row, column);
+						my_array = g_value_get_boxed (variant);
+					}
+
+					float_val = g_value_get_float (&cur_value);
+					g_array_append_val (my_array, float_val);
+				}
+				break;
+				default:
+					g_warning ("Unknown type in get_hits: %d", data_type);
+
+				}
+			} else {
+				if (insert) {
+					GValue *value = g_new0 (GValue, 1);
+
+					g_value_init (value,
+						      G_VALUE_TYPE (&cur_value));
+
+					g_value_copy (&cur_value, value);
+					g_ptr_array_add (row, value);
+				}
+
+				/* Else it's a redundant cell (a previous
+				 * loop-cycle has added this item to the
+				 * final to-return result already, using
+				 * the top-row). */
+
+			}
+			g_value_unset (&cur_value);
+		}
+
+
+		if (insert) {
+			rows_insert (rows, key, row);
+		}
+
+		valid = tracker_db_result_set_iter_next (result_set);
+	}
+
+	rows_migrate (rows, result);
+	rows_destroy (rows);
+
+	*hit_data = result;
+}
+
+
+/**
+ * tracker_xesam_live_search_get_hits:
+ * @self: a #TrackerXesamLiveSearch
+ * @num: Number of hits to retrieve
+ * @hits: (out) (caller-owns): An array of field data for each hit as requested
+ * via the hit fields property
+ * @error: (null-ok) (out): a #GError
+ *
+ * Get the field data for the next num hits. This call blocks until there is num
+ * hits available or the index has been fully searched (and SearchDone emitted).
+ *
+ * An error will be thrown if the search has not been started with
+ * @tracker_xesam_live_search_activate yet.
+ **/
+void
+tracker_xesam_live_search_get_hits (TrackerXesamLiveSearch  *self,
+				    guint		     count,
+				    GPtrArray		   **hits,
+				    GError		   **error)
+{
+	TrackerXesamLiveSearchPriv *priv;
+
+	g_return_if_fail (TRACKER_IS_XESAM_LIVE_SEARCH (self));
+	g_return_if_fail (hits != NULL);
+
+	priv = self->priv;
+
+	if (!priv->active)
+		g_set_error (error, TRACKER_XESAM_ERROR_DOMAIN,
+				TRACKER_XESAM_ERROR_SEARCH_NOT_ACTIVE,
+				"Search is not active");
+	else {
+		TrackerXesamSession *session;
+		GValue		    *value;
+		GError		    *tmp_error = NULL;
+
+		session = priv->session;
+
+		tracker_xesam_session_get_property (session,
+						    "hit.fields",
+						    &value,
+						    &tmp_error);
+
+		if (tmp_error) {
+			g_propagate_error(error, tmp_error);
+			return;
+		}
+
+		if (value) {
+			TrackerDBInterface  *iface;
+			TrackerDBResultSet  *result_set;
+			GStrv		     fields;
+
+			fields = g_value_get_boxed (value);
+
+			iface = tracker_db_manager_get_db_interface_by_service (TRACKER_DB_FOR_XESAM_SERVICE);
+
+			/* For ottela: fetch results for get_hits */
+
+			result_set = tracker_db_live_search_get_hit_data (iface,
+									  tracker_xesam_live_search_get_id (self),
+									  fields);
+
+			if (result_set) {
+
+				get_hit_data (self,
+					      result_set,
+					      hits,
+					      fields);
+
+				g_object_unref (result_set);
+			} else {
+				*hits =  g_ptr_array_new ();
+			}
+
+			g_value_unset (value);
+			g_free (value);
+		}
+	}
+}
+
+void
+tracker_xesam_live_search_get_range_hits (TrackerXesamLiveSearch  *self,
+					  guint			   a,
+					  guint			   b,
+					  GPtrArray		 **hits,
+					  GError		 **error)
+{
+	TrackerXesamLiveSearchPriv *priv;
+
+	g_return_if_fail (TRACKER_IS_XESAM_LIVE_SEARCH (self));
+	g_return_if_fail (hits != NULL);
+
+	priv = self->priv;
+
+	if (!priv->active) {
+		g_set_error (error,
+			     TRACKER_XESAM_ERROR_DOMAIN,
+			     TRACKER_XESAM_ERROR_SEARCH_NOT_ACTIVE,
+			     "Search is not active");
+	} else {
+		TrackerXesamSession *session = priv->session;
+		TrackerDBInterface  *iface;
+		TrackerDBResultSet  *result_set;
+		GValue		    *value;
+		GError		    *tmp_error = NULL;
+
+		iface = tracker_db_manager_get_db_interface_by_service (TRACKER_DB_FOR_XESAM_SERVICE);
+
+		tracker_xesam_session_get_property (session,
+						    "hit.fields",
+						    &value,
+						    &tmp_error);
+
+		if (tmp_error) {
+			g_propagate_error(error, tmp_error);
+			return;
+		}
+
+		if (value) {
+			GStrv fields;
+
+			fields = g_value_get_boxed (value);
+
+			result_set = tracker_db_live_search_get_hit_data (iface,
+									  tracker_xesam_live_search_get_id (self),
+									  fields);
+
+			if (result_set) {
+
+				get_hit_data (self,
+					      result_set,
+					      hits,
+					      fields);
+
+				g_object_unref (result_set);
+			} else {
+				*hits = g_ptr_array_new ();
+			}
+
+			g_value_unset (value);
+			g_free (value);
+		}
+	}
+}
+
+/**
+ * tracker_xesam_live_search_get_hit_data:
+ * @self: a #TrackerXesamLiveSearch
+ * @hit_ids: Array of hit serial numbers for which to retrieve data
+ * @fields: The names of the fields to retrieve for the listed hits. It is
+ * recommended that this is a subset of the fields listed in hit.fields and
+ * hit.fields.extended
+ * @hit_data: Array of hits in the same order as the hit ids specified. See
+ * the section about hit retrieval below. If @hits-removed has been emitted on
+ * a hit, the returned hit data will consist of unset fields, ie this is not an
+ * error condition.
+ * @error: (null-ok) (out): a #GError
+ *
+ * Get renewed or additional hit metadata. Primarily intended for snippets or
+ * modified hits. The hit_ids argument is an array of serial numbers as per hit
+ * entries returned by GetHits. The returned hits will be in the same order as
+ * the provided @hit_ids. The requested properties does not have to be the ones
+ * listed in in the hit.fields or hit.fields.extended session properties,
+ * although this is the recommended behavior.
+ *
+ * An error will be raised if the search handle has been closed or is unknown.
+ * An error will also be thrown if the search has not been started with
+ * @tracker_xesam_live_search_activate yet
+ *
+ * Calling on a hit that has been marked removed by the @hits-removed signal
+ * will not result in an error, but return only unset fields.
+ **/
+void
+tracker_xesam_live_search_get_hit_data (TrackerXesamLiveSearch	*self,
+					GArray			*hit_ids,
+					GStrv			 fields,
+					GPtrArray	       **hit_data,
+					GError		       **error)
+{
+	TrackerXesamLiveSearchPriv *priv;
+
+	g_return_if_fail (TRACKER_IS_XESAM_LIVE_SEARCH (self));
+	g_return_if_fail (hit_ids != NULL);
+	g_return_if_fail (hit_data != NULL);
+
+	priv = self->priv;
+
+	if (!priv->active) {
+		g_set_error (error,
+			     TRACKER_XESAM_ERROR_DOMAIN,
+			     TRACKER_XESAM_ERROR_SEARCH_NOT_ACTIVE,
+			     "Search is not active yet");
+	} else {
+		TrackerDBInterface *iface;
+		TrackerDBResultSet *result_set;
+
+		iface = tracker_db_manager_get_db_interface_by_service (TRACKER_DB_FOR_XESAM_SERVICE);
+
+		result_set = tracker_db_live_search_get_hit_data (iface,
+								  tracker_xesam_live_search_get_id (self),
+								  fields);
+
+		if (result_set) {
+
+			get_hit_data (self,
+				      result_set,
+				      hit_data,
+				      fields);
+
+			g_object_unref (result_set);
+		} else {
+			*hit_data = g_ptr_array_new ();
+		}
+	}
+}
+
+void
+tracker_xesam_live_search_get_range_hit_data (TrackerXesamLiveSearch  *self,
+					      guint		       a,
+					      guint		       b,
+					      GStrv		       fields,
+					      GPtrArray		     **hit_data,
+					      GError		     **error)
+{
+	TrackerXesamLiveSearchPriv *priv;
+
+	g_return_if_fail (TRACKER_IS_XESAM_LIVE_SEARCH (self));
+	g_return_if_fail (fields != NULL);
+	g_return_if_fail (hit_data != NULL);
+
+	priv = self->priv;
+
+	if (!priv->active) {
+		g_set_error (error,
+			     TRACKER_XESAM_ERROR_DOMAIN,
+			     TRACKER_XESAM_ERROR_SEARCH_NOT_ACTIVE,
+			     "Search is not active yet");
+	} else {
+		TrackerDBInterface *iface;
+		TrackerDBResultSet *result_set;
+
+		iface = tracker_db_manager_get_db_interface_by_service (TRACKER_DB_FOR_XESAM_SERVICE);
+
+		result_set = tracker_db_live_search_get_hit_data (iface,
+								  tracker_xesam_live_search_get_id (self),
+								  fields);
+
+		if (result_set) {
+
+			get_hit_data (self,
+				      result_set,
+				      hit_data,
+				      fields);
+
+			g_object_unref (result_set);
+		} else {
+			*hit_data = g_ptr_array_new ();
+		}
+	}
+}
+
+/**
+ * tracker_xesam_live_search_is_active:
+ * @self: a #TrackerXesamLiveSearch
+ *
+ * Get whether or not @self is active.
+ *
+ * @returns: whether or not @self is active
+ **/
+gboolean
+tracker_xesam_live_search_is_active (TrackerXesamLiveSearch *self)
+{
+	TrackerXesamLiveSearchPriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_XESAM_LIVE_SEARCH (self), FALSE);
+
+	priv = self->priv;
+
+	return priv->active;
+}
+
+/**
+ * tracker_xesam_live_search_activate:
+ * @self: a #TrackerXesamLiveSearch
+ *
+ * Activates @self
+ *
+ * An error will be thrown if @self is closed.
+ **/
+void
+tracker_xesam_live_search_activate (TrackerXesamLiveSearch  *self,
+				    GError		   **error)
+{
+	TrackerXesamLiveSearchPriv *priv = self->priv;
+
+	if (priv->closed)
+		g_set_error (error, TRACKER_XESAM_ERROR_DOMAIN,
+				TRACKER_XESAM_ERROR_SEARCH_CLOSED,
+				"Search is closed");
+	else {
+		TrackerDBInterface *iface;
+		GArray		   *hits;
+
+		iface = tracker_db_manager_get_db_interface_by_service (TRACKER_DB_FOR_XESAM_SERVICE);
+
+		tracker_db_live_search_start (iface,
+					      tracker_xesam_live_search_get_from_query (self),
+					      tracker_xesam_live_search_get_join_query (self),
+					      tracker_xesam_live_search_get_where_query (self),
+					      tracker_xesam_live_search_get_id (self));
+
+		get_all_hits (self, iface, &hits);
+
+		if (hits && hits->len > 0) {
+			g_debug ("Emitting HitsAdded");
+			tracker_xesam_live_search_emit_hits_added (self, hits->len);
+		}
+
+		if (hits) {
+			g_array_free (hits, TRUE);
+		}
+
+		g_timeout_add_full (G_PRIORITY_DEFAULT,
+				    100,
+				    (GSourceFunc) tracker_xesam_live_search_emit_done,
+				    g_object_ref (self),
+				    (GDestroyNotify) g_object_unref);
+	}
+
+	priv->active = TRUE;
+}
+
+/**
+ * tracker_xesam_live_search_get_query:
+ * @self: a #TrackerXesamLiveSearch
+ *
+ * * API will change *
+ *
+ * Gets the query
+ *
+ * @returns: a read-only string with the query
+ **/
+const gchar *
+tracker_xesam_live_search_get_xml_query (TrackerXesamLiveSearch *self)
+{
+	TrackerXesamLiveSearchPriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_XESAM_LIVE_SEARCH (self), NULL);
+
+	priv = self->priv;
+
+	return priv->query;
+}
+
+/**
+ * tracker_xesam_live_search_set_id:
+ * @self: A #TrackerXesamLiveSearch
+ * @search_id: a unique ID string for @self
+ *
+ * Set a read-only unique ID string for @self.
+ **/
+void
+tracker_xesam_live_search_set_id (TrackerXesamLiveSearch *self,
+				  const gchar		 *search_id)
+{
+	TrackerXesamLiveSearchPriv *priv;
+
+	g_return_if_fail (TRACKER_IS_XESAM_LIVE_SEARCH (self));
+
+	priv = self->priv;
+
+	g_free (priv->search_id);
+
+	if (search_id) {
+		priv->search_id = g_strdup (search_id);
+	} else {
+		priv->search_id = NULL;
+	}
+}
+
+/**
+ * tracker_xesam_live_search_get_id:
+ * @self: A #TrackerXesamLiveSearch
+ *
+ * Get the read-only unique ID string for @self.
+ *
+ * returns: a unique id
+ **/
+const gchar*
+tracker_xesam_live_search_get_id (TrackerXesamLiveSearch *self)
+{
+	TrackerXesamLiveSearchPriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_XESAM_LIVE_SEARCH (self), NULL);
+
+	priv = self->priv;
+
+	return priv->search_id;
+}
+
+
+/**
+ * tracker_xesam_live_search_parse_query:
+ * @self: a #TrackerXesamLiveSearch
+ *
+ * Parses the current xml query and sets the sql
+ *
+ * Return value: whether parsing succeeded, if not @error will also be set
+ **/
+gboolean
+tracker_xesam_live_search_parse_query (TrackerXesamLiveSearch  *self,
+				       GError		      **error)
+{
+	TrackerXesamLiveSearchPriv *priv;
+	TrackerDBInterface	   *iface;
+	GObject			   *xesam;
+	GError			   *parse_error = NULL;
+	gchar			   *orig_from, *orig_join, *orig_where;
+
+	g_return_val_if_fail (TRACKER_IS_XESAM_LIVE_SEARCH (self), FALSE);
+
+	priv = self->priv;
+
+	iface = tracker_db_manager_get_db_interface_by_service (TRACKER_DB_FOR_XESAM_SERVICE);
+
+	xesam = tracker_dbus_get_object (TRACKER_TYPE_XESAM);
+
+	orig_from = priv->from_sql;
+	orig_join = priv->join_sql;
+	orig_where = priv->where_sql;
+
+	priv->from_sql = NULL;
+	priv->join_sql = NULL;
+	priv->where_sql = NULL;
+
+	tracker_xesam_query_to_sql (iface,
+				    priv->query,
+				    &priv->from_sql,
+				    &priv->join_sql,
+				    &priv->where_sql,
+				    &parse_error);
+
+	if (parse_error) {
+		gchar *str;
+
+		str = g_strdup_printf ("Parse error: %s",
+				       parse_error->message);
+		g_set_error (error,
+			     TRACKER_XESAM_ERROR_DOMAIN,
+			     TRACKER_XESAM_ERROR_PARSING_FAILED,
+			     str);
+		g_free (str);
+		g_error_free (parse_error);
+
+		g_free (priv->from_sql);
+		g_free (priv->join_sql);
+		g_free (priv->where_sql);
+
+		priv->from_sql = orig_from;
+		priv->join_sql = orig_join;
+		priv->where_sql = orig_where;
+
+		return FALSE;
+	} else {
+		g_free (orig_from);
+		g_free (orig_join);
+		g_free (orig_where);
+	}
+
+	g_message ("Parsed to:\n\t%s\n\t%s\n\t%s",
+		   priv->from_sql,
+		   priv->join_sql,
+		   priv->where_sql);
+
+	return TRUE;
+}
+
+/**
+ * tracker_xesam_live_search_get_from_query:
+ * @self: a #TrackerXesamLiveSearch
+ *
+ * Gets the parsed FROM SQL string for the query
+ *
+ * @returns: a read-only string with the FROM query
+ **/
+const gchar*
+tracker_xesam_live_search_get_from_query (TrackerXesamLiveSearch *self)
+{
+	TrackerXesamLiveSearchPriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_XESAM_LIVE_SEARCH (self), NULL);
+
+	priv = self->priv;
+
+	return priv->from_sql;
+}
+
+/**
+ * tracker_xesam_live_search_get_join_query:
+ * @self: a #TrackerXesamLiveSearch
+ *
+ * Gets the parsed JOIN SQL string for the query
+ *
+ * @returns: a read-only string with the JOIN query
+ **/
+const gchar*
+tracker_xesam_live_search_get_join_query (TrackerXesamLiveSearch *self)
+{
+	TrackerXesamLiveSearchPriv *priv = self->priv;
+	return (const gchar *) priv->join_sql;
+}
+
+/**
+ * tracker_xesam_live_search_get_where_query:
+ * @self: a #TrackerXesamLiveSearch
+ *
+ * Gets the parsed WHERE SQL for the query
+ *
+ * @returns: a read-only string with the WHERE query
+ **/
+const gchar*
+tracker_xesam_live_search_get_where_query (TrackerXesamLiveSearch *self)
+{
+	TrackerXesamLiveSearchPriv *priv;
+
+	g_return_val_if_fail (TRACKER_IS_XESAM_LIVE_SEARCH (self), NULL);
+
+	priv = self->priv;
+
+	return priv->where_sql;
+}
+
+/**
+ * tracker_xesam_live_search_new:
+ *
+ * Create a new #TrackerXesamLiveSearch
+ *
+ * @returns: (caller-owns): a new #TrackerXesamLiveSearch
+ **/
+TrackerXesamLiveSearch*
+tracker_xesam_live_search_new (const gchar *query_xml)
+{
+	return g_object_new (TRACKER_TYPE_XESAM_LIVE_SEARCH,
+			     "xml-query", query_xml,
+			     NULL);
+}
+
+
+
+

Added: trunk/src/trackerd/tracker-xesam-live-search.h
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-xesam-live-search.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,120 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia
+ * Authors: Philip Van Hoof (pvanhoof gnome org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKERD_XESAM_LIVE_SEARCH_H__
+#define __TRACKERD_XESAM_LIVE_SEARCH_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <libtracker-db/tracker-db-interface-sqlite.h>
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_XESAM_LIVE_SEARCH (tracker_xesam_live_search_get_type ())
+#define TRACKER_XESAM_LIVE_SEARCH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRACKER_TYPE_XESAM_LIVE_SEARCH, TrackerXesamLiveSearch))
+#define TRACKER_XESAM_LIVE_SEARCH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TRACKER_TYPE_XESAM_LIVE_SEARCH, TrackerXesamLiveSearchClass))
+#define TRACKER_IS_XESAM_LIVE_SEARCH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRACKER_TYPE_XESAM_LIVE_SEARCH))
+#define TRACKER_IS_XESAM_LIVE_SEARCH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TRACKER_TYPE_XESAM_LIVE_SEARCH))
+#define TRACKER_XESAM_LIVE_SEARCH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TRACKER_TYPE_XESAM_LIVE_SEARCH, TrackerXesamLiveSearchClass))
+
+typedef struct _TrackerXesamLiveSearch TrackerXesamLiveSearch;
+typedef struct _TrackerXesamLiveSearchClass TrackerXesamLiveSearchClass;
+typedef struct _TrackerXesamLiveSearchPriv TrackerXesamLiveSearchPriv;
+
+typedef enum {
+	MATCH_WITH_EVENTS_CREATES = 1<<0,
+	MATCH_WITH_EVENTS_DELETES = 1<<1,
+	MATCH_WITH_EVENTS_MODIFIES = 1<<2
+} MatchWithEventsFlags;
+
+#define MATCH_WITH_EVENTS_ALL_FLAGS (MATCH_WITH_EVENTS_CREATES|MATCH_WITH_EVENTS_DELETES|MATCH_WITH_EVENTS_MODIFIES)
+
+struct _TrackerXesamLiveSearch {
+	GObject parent_instance;
+	TrackerXesamLiveSearchPriv * priv;
+};
+struct _TrackerXesamLiveSearchClass {
+	GObjectClass parent_class;
+};
+
+TrackerXesamLiveSearch *
+	     tracker_xesam_live_search_new		  (const gchar		   *query_xml);
+GType	     tracker_xesam_live_search_get_type		  (void);
+void	     tracker_xesam_live_search_set_id		  (TrackerXesamLiveSearch  *self,
+							   const gchar		   *search_id);
+const gchar* tracker_xesam_live_search_get_id		  (TrackerXesamLiveSearch  *self);
+const gchar* tracker_xesam_live_search_get_where_query	  (TrackerXesamLiveSearch  *self);
+const gchar* tracker_xesam_live_search_get_from_query	  (TrackerXesamLiveSearch  *self);
+const gchar* tracker_xesam_live_search_get_join_query	  (TrackerXesamLiveSearch  *self);
+const gchar* tracker_xesam_live_search_get_xml_query	  (TrackerXesamLiveSearch  *self);
+void	     tracker_xesam_live_search_set_xml_query	  (TrackerXesamLiveSearch  *self,
+							   const gchar		   *xml_query);
+void	     tracker_xesam_live_search_set_session	  (TrackerXesamLiveSearch  *self,
+							   gpointer		    session);
+void	     tracker_xesam_live_search_set_session	  (TrackerXesamLiveSearch  *self,
+							   gpointer		    session);
+void	     tracker_xesam_live_search_activate		  (TrackerXesamLiveSearch  *self,
+							   GError		  **error);
+gboolean     tracker_xesam_live_search_is_active	  (TrackerXesamLiveSearch  *self);
+gboolean     tracker_xesam_live_search_parse_query	  (TrackerXesamLiveSearch  *self,
+							   GError		  **error);
+void	     tracker_xesam_live_search_get_hit_data	  (TrackerXesamLiveSearch  *self,
+							   GArray		   *hit_ids,
+							   GStrv		    fields,
+							   GPtrArray		  **hit_data,
+							   GError		  **error);
+void	     tracker_xesam_live_search_get_hits		  (TrackerXesamLiveSearch  *self,
+							   guint		    count,
+							   GPtrArray		  **hits,
+							   GError		  **error);
+void	     tracker_xesam_live_search_get_range_hit_data (TrackerXesamLiveSearch  *self,
+							   guint		    a,
+							   guint		    b,
+							   GStrv		    fields,
+							   GPtrArray		  **hit_data,
+							   GError		  **error);
+void	     tracker_xesam_live_search_get_range_hits	  (TrackerXesamLiveSearch  *self,
+							   guint		    a,
+							   guint		    b,
+							   GPtrArray		  **hits,
+							   GError		  **error);
+void	     tracker_xesam_live_search_get_hit_count	  (TrackerXesamLiveSearch  *self,
+							   guint		   *count,
+							   GError		  **error);
+void	     tracker_xesam_live_search_close		  (TrackerXesamLiveSearch  *self,
+							   GError		  **error);
+void	     tracker_xesam_live_search_emit_hits_added	  (TrackerXesamLiveSearch  *self,
+							   guint		    count);
+void	     tracker_xesam_live_search_emit_hits_removed  (TrackerXesamLiveSearch  *self,
+							   GArray		   *hit_ids);
+void	     tracker_xesam_live_search_emit_hits_modified (TrackerXesamLiveSearch  *self,
+							   GArray		   *hit_ids);
+void	     tracker_xesam_live_search_emit_done	  (TrackerXesamLiveSearch  *self);
+void	     tracker_xesam_live_search_match_with_events  (TrackerXesamLiveSearch  *self,
+							   MatchWithEventsFlags     flags,
+							   GArray		  **added,
+							   GArray		  **removed,
+							   GArray		  **modified);
+
+G_END_DECLS
+
+#endif /* __TRACKER_XESAM_LIVE_SEARCH_H__ */

Added: trunk/src/trackerd/tracker-xesam-manager.c
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-xesam-manager.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,472 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia
+ * Authors: Philip Van Hoof (pvanhoof gnome org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <libtracker-common/tracker-config.h>
+#include <libtracker-common/tracker-utils.h>
+
+#include <libtracker-db/tracker-db-manager.h>
+
+#include "tracker-xesam-manager.h"
+#include "tracker-dbus.h"
+#include "tracker-main.h"
+
+static gboolean		   initialized;
+static TrackerDBInterface *xesam_db_iface;
+static GHashTable	  *xesam_sessions;
+static gchar		  *xesam_dir;
+static gboolean		   indexing_finished;
+static guint		   live_search_handler_id;
+
+static void
+indexer_status_cb (DBusGProxy  *proxy,
+		   gdouble	seconds_elapsed,
+		   const gchar *current_module_name,
+		   guint	items_done,
+		   guint	items_remaining,
+		   gpointer	user_data)
+{
+	tracker_xesam_manager_wakeup ();
+}
+
+static void
+indexer_started_cb (DBusGProxy *proxy,
+		    gpointer	user_data)
+{
+	/* So now when we get status updates we DO NOT process live
+	 * events and update live searches. The indexer is using the cache.
+	 */
+	g_message ("Disabling live search event updates (indexer started)");
+	indexing_finished = FALSE;
+}
+
+static void
+indexer_finished_cb (DBusGProxy *proxy,
+		     gdouble	 seconds_elapsed,
+		     guint	 items_done,
+		     gboolean	 interrupted,
+		     gpointer	 user_data)
+{
+	/* So now when we get status updates we can process live
+	 * events and update live searches.
+	 */
+	g_message ("Enabling live search event updates (indexer finished)");
+	indexing_finished = TRUE;
+
+	/* Shouldn't we release ref (A) here? (see below) */
+	/* g_object_unref (proxy) */
+}
+
+GQuark
+tracker_xesam_manager_error_quark (void)
+{
+	static GQuark quark = 0;
+
+	if (quark == 0) {
+		quark = g_quark_from_static_string ("TrackerXesam");
+	}
+
+	return quark;
+}
+
+void
+tracker_xesam_manager_init (void)
+{
+	DBusGProxy *proxy;
+
+	if (initialized) {
+		return;
+	}
+
+	/* Set up sessions hash table */
+	xesam_sessions = g_hash_table_new_full (g_str_hash,
+						g_str_equal,
+						(GDestroyNotify) g_free,
+						(GDestroyNotify) g_object_unref);
+
+	/* Set up locations */
+	xesam_dir = g_build_filename (g_get_home_dir (), ".xesam", NULL);
+
+	/* Set up DBus proxy to the indexer process */
+	proxy = tracker_dbus_indexer_get_proxy ();
+
+	/* When is this ref released? (A) */
+	g_object_ref (proxy);
+
+	dbus_g_proxy_connect_signal (proxy, "Status",
+				     G_CALLBACK (indexer_status_cb),
+				     NULL,
+				     NULL);
+	dbus_g_proxy_connect_signal (proxy, "Started",
+				     G_CALLBACK (indexer_started_cb),
+				     NULL,
+				     NULL);
+	dbus_g_proxy_connect_signal (proxy, "Finished",
+				     G_CALLBACK (indexer_finished_cb),
+				     NULL,
+				     NULL);
+
+	/* Set the indexing finished state back to unfinished */
+	indexing_finished = FALSE;
+
+	/* Get the DB interface now instead of later when the database
+	 * is potentially being hammered with new information by the
+	 * indexer. Before, if we just got it in the live update from
+	 * the indexer, we couldn't create the interface quickly
+	 * because the database is being used heavily by the indexer
+	 * already. It is best to do this initially to avoid that.
+	 */
+	xesam_db_iface = tracker_db_manager_get_db_interface_by_service (TRACKER_DB_FOR_XESAM_SERVICE);
+}
+
+void
+tracker_xesam_manager_shutdown (void)
+{
+	DBusGProxy *proxy;
+
+	if (!initialized) {
+		return;
+	}
+
+	g_object_unref (xesam_db_iface);
+	xesam_db_iface = NULL;
+
+	proxy = tracker_dbus_indexer_get_proxy ();
+	dbus_g_proxy_disconnect_signal (proxy, "Status",
+					G_CALLBACK (indexer_status_cb),
+					NULL);
+	dbus_g_proxy_disconnect_signal (proxy, "Started",
+					G_CALLBACK (indexer_started_cb),
+					NULL);
+	dbus_g_proxy_disconnect_signal (proxy, "Finished",
+					G_CALLBACK (indexer_finished_cb),
+					NULL);
+	g_object_unref (proxy);
+
+	indexing_finished = FALSE;
+
+	if (live_search_handler_id != 0) {
+		g_source_remove (live_search_handler_id);
+		live_search_handler_id = 0;
+	}
+
+	g_free (xesam_dir);
+	xesam_dir = NULL;
+
+	g_hash_table_unref (xesam_sessions);
+	xesam_sessions = NULL;
+}
+
+TrackerXesamSession *
+tracker_xesam_manager_create_session (TrackerXesam  *xesam,
+				      gchar	   **session_id,
+				      GError	   **error)
+{
+	TrackerXesamSession *session;
+
+	session = tracker_xesam_session_new ();
+	tracker_xesam_session_set_id (session, tracker_xesam_manager_generate_unique_key ());
+
+	g_hash_table_insert (xesam_sessions,
+			     g_strdup (tracker_xesam_session_get_id (session)),
+			     g_object_ref (session));
+
+	if (session_id) {
+		*session_id = g_strdup (tracker_xesam_session_get_id (session));
+	}
+
+	return session;
+}
+
+void
+tracker_xesam_manager_close_session (const gchar  *session_id,
+				     GError	 **error)
+{
+	gpointer inst = g_hash_table_lookup (xesam_sessions, session_id);
+
+	if (!inst) {
+		g_set_error (error,
+			     TRACKER_XESAM_ERROR_DOMAIN,
+			     TRACKER_XESAM_ERROR_SESSION_ID_NOT_REGISTERED,
+			     "Session ID is not registered");
+	} else {
+		g_hash_table_remove (xesam_sessions, session_id);
+	}
+}
+
+TrackerXesamSession *
+tracker_xesam_manager_get_session (const gchar	*session_id,
+				   GError      **error)
+{
+	TrackerXesamSession *session = g_hash_table_lookup (xesam_sessions, session_id);
+
+	if (session) {
+		g_object_ref (session);
+	} else {
+		g_set_error (error,
+			     TRACKER_XESAM_ERROR_DOMAIN,
+			     TRACKER_XESAM_ERROR_SESSION_ID_NOT_REGISTERED,
+			     "Session ID is not registered");
+	}
+
+	return session;
+}
+
+TrackerXesamSession *
+tracker_xesam_manager_get_session_for_search (const gchar	      *search_id,
+					      TrackerXesamLiveSearch **search_in,
+					      GError		     **error)
+{
+	TrackerXesamSession *session = NULL;
+	GList		    *sessions;
+
+	sessions = g_hash_table_get_values (xesam_sessions);
+
+	while (sessions) {
+		TrackerXesamLiveSearch *search;
+
+		search = tracker_xesam_session_get_search (sessions->data, search_id, NULL);
+
+		if (search) {
+			/* Search got a reference added already */
+			if (search_in) {
+				*search_in = search;
+			} else {
+				g_object_unref (search);
+			}
+
+			session = g_object_ref (sessions->data);
+			break;
+		}
+
+		sessions = g_list_next (sessions);
+	}
+
+	g_list_free (sessions);
+
+	if (!session) {
+		g_set_error (error,
+			     TRACKER_XESAM_ERROR_DOMAIN,
+			     TRACKER_XESAM_ERROR_SEARCH_ID_NOT_REGISTERED,
+			     "Search ID is not registered");
+	}
+
+	return session;
+}
+
+TrackerXesamLiveSearch *
+tracker_xesam_manager_get_live_search (const gchar  *search_id,
+				       GError	   **error)
+{
+	TrackerXesamLiveSearch *search = NULL;
+	GList		       *sessions;
+
+	sessions = g_hash_table_get_values (xesam_sessions);
+
+	while (sessions) {
+		TrackerXesamLiveSearch *p;
+
+		p = tracker_xesam_session_get_search (sessions->data, search_id, NULL);
+
+		if (p) {
+			/* Search got a reference added already */
+			search = p;
+			break;
+		}
+
+		sessions = g_list_next (sessions);
+	}
+
+	g_list_free (sessions);
+
+	if (!search) {
+		g_set_error (error,
+			     TRACKER_XESAM_ERROR_DOMAIN,
+			     TRACKER_XESAM_ERROR_SEARCH_ID_NOT_REGISTERED,
+			     "Search ID is not registered");
+	}
+
+	return search;
+}
+
+static gboolean
+live_search_handler (gpointer data)
+{
+	GList	 *sessions;
+	gboolean  reason_to_live = FALSE;
+
+	sessions = g_hash_table_get_values (xesam_sessions);
+
+	while (sessions) {
+		GList *searches;
+
+		g_debug ("Session being handled, ID :%s",
+			 tracker_xesam_session_get_id (sessions->data));
+
+		searches = tracker_xesam_session_get_searches (sessions->data);
+
+		while (searches) {
+			TrackerXesamLiveSearch *search;
+			GArray		       *added = NULL;
+			GArray		       *removed = NULL;
+			GArray		       *modified = NULL;
+
+			g_debug ("Search being handled, ID :%s",
+				 tracker_xesam_live_search_get_id (searches->data));
+
+			search = searches->data;
+
+			/* TODO: optimize by specifying what exactly got changed
+			 * during this event ping in the MatchWithEventsFlags
+			 being passed (second parameter) */
+
+			tracker_xesam_live_search_match_with_events (search,
+								     MATCH_WITH_EVENTS_ALL_FLAGS,
+								     &added,
+								     &removed,
+								     &modified);
+
+			if (added && added->len > 0) {
+				reason_to_live = TRUE;
+				tracker_xesam_live_search_emit_hits_added (search, added->len);
+			}
+
+			if (added) {
+				g_array_free (added, TRUE);
+			}
+
+			if (removed && removed->len > 0) {
+				reason_to_live = TRUE;
+				tracker_xesam_live_search_emit_hits_removed (search, removed);
+			}
+
+			if (removed) {
+				g_array_free (removed, TRUE);
+			}
+
+			if (modified && modified->len > 0) {
+				reason_to_live = TRUE;
+				tracker_xesam_live_search_emit_hits_modified (search, modified);
+			}
+
+			if (modified) {
+				g_array_free (modified, TRUE);
+			}
+
+			searches = g_list_next (searches);
+		}
+
+		g_list_free (searches);
+
+		sessions = g_list_next (sessions);
+	}
+
+	g_list_free (sessions);
+
+	tracker_db_xesam_delete_handled_events (xesam_db_iface);
+
+	return reason_to_live;
+}
+
+static void
+live_search_handler_destroy (gpointer data)
+{
+	live_search_handler_id = 0;
+}
+
+void
+tracker_xesam_manager_wakeup (void)
+{
+	/* This happens each time a new event is created:
+	 *
+	 * We could do this in a thread too, in case blocking the
+	 * GMainLoop is not ideal (it's not, because during these
+	 * blocks of code, no DBus request handler can run).
+	 *
+	 * In case of a thread we could use usleep() and stop the
+	 * thread if we didn't get a wakeup-call nor we had items to
+	 * process this loop
+	 *
+	 * There are problems with this. Right now we WAIT until
+	 * after indexing has completed otherwise we are in a
+	 * situation where a "status" signal from the indexer makes us
+	 * delete events from the Events table. This requires the
+	 * cache db and means we end up waiting for the indexer to
+	 * finish doing what it is doing first. The daemon then stops
+	 * pretty much and blocks. This is bad. So we wait for the
+	 * indexing to be finished before doing this.
+	 */
+	if (!indexing_finished) {
+		return;
+	}
+
+	if (live_search_handler_id == 0) {
+		live_search_handler_id =
+			g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE,
+					    2000, /* 2 seconds */
+					    live_search_handler,
+					    NULL,
+					    live_search_handler_destroy);
+	}
+}
+
+gchar *
+tracker_xesam_manager_generate_unique_key (void)
+{
+	static guint  serial = 0;
+	gchar	     *key;
+	guint	      t, ut, p, u, r;
+	GTimeVal      tv;
+
+	g_get_current_time (&tv);
+
+	t = tv.tv_sec;
+	ut = tv.tv_usec;
+
+	p = getpid ();
+
+#ifdef HAVE_GETUID
+	u = getuid ();
+#else
+	u = 0;
+#endif
+
+	r = g_random_int ();
+	key = g_strdup_printf ("%ut%uut%uu%up%ur%uk%u",
+			       serial, t, ut, u, p, r,
+			       GPOINTER_TO_UINT (&key));
+
+	++serial;
+
+	return key;
+}
+
+gboolean
+tracker_xesam_manager_is_uri_in_xesam_dir (const gchar *uri)
+{
+	g_return_val_if_fail (uri != NULL, FALSE);
+
+	return g_str_has_prefix (uri, xesam_dir);
+}

Added: trunk/src/trackerd/tracker-xesam-manager.h
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-xesam-manager.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,70 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia
+ * Authors: Philip Van Hoof (pvanhoof gnome org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKERD_XESAM_MANAGER_H__
+#define __TRACKERD_XESAM_MANAGER_H__
+
+#include <glib.h>
+
+#include "tracker-xesam-session.h"
+#include "tracker-xesam-live-search.h"
+
+G_BEGIN_DECLS
+
+#define TRACKER_XESAM_ERROR_DOMAIN tracker_xesam_manager_error_quark ()
+
+typedef enum {
+	TRACKER_XESAM_ERROR_SEARCH_ID_NOT_REGISTERED = 1,
+	TRACKER_XESAM_ERROR_SESSION_ID_NOT_REGISTERED,
+	TRACKER_XESAM_ERROR_SEARCH_CLOSED,
+	TRACKER_XESAM_ERROR_SEARCH_NOT_ACTIVE,
+	TRACKER_XESAM_ERROR_PROPERTY_NOT_SUPPORTED,
+	TRACKER_XESAM_ERROR_PARSING_FAILED,
+} TrackerXesamError;
+
+GQuark	 tracker_xesam_manager_error_quark	      (void);
+
+void	 tracker_xesam_manager_init		      (void);
+void	 tracker_xesam_manager_shutdown		      (void);
+
+TrackerXesamSession*
+	 tracker_xesam_manager_create_session	      (TrackerXesam	       *xesam,
+						       gchar		      **session_id,
+						       GError		      **error);
+void	 tracker_xesam_manager_close_session	      (const gchar	       *session_id,
+						       GError		      **error);
+TrackerXesamSession*
+	 tracker_xesam_manager_get_session	      (const gchar	       *session_id,
+						       GError		      **error);
+TrackerXesamSession*
+	 tracker_xesam_manager_get_session_for_search (const gchar	       *search_id,
+						       TrackerXesamLiveSearch **search_in,
+						       GError		      **error);
+TrackerXesamLiveSearch*
+	 tracker_xesam_manager_get_live_search	      (const gchar	       *search_id,
+						       GError		      **error);
+gchar *  tracker_xesam_manager_generate_unique_key    (void);
+gboolean tracker_xesam_manager_is_uri_in_xesam_dir    (const gchar	       *uri);
+void	 tracker_xesam_manager_wakeup		      (void);
+
+G_END_DECLS
+
+#endif /* __TRACKERD_XESAM_MANAGER_H__ */

Added: trunk/src/trackerd/tracker-xesam-query.c
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-xesam-query.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,1493 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/* Tracker - indexer and metadata database engine
+ *
+ * Copyright (C) 2008, Nokia
+ * Authors: Ottela Mikael, (mikael ottela ixonos com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+/*
+ * TODO
+ * - Boost attribute support is missing completely
+ * - userQuery is missing completely
+ * - works only with the default db at the moment.
+ * - Missing checks for several field elements in one selector
+ *
+ *
+ *
+ *
+ * FIXME
+ * - The mappings
+ * - Multifield-elements work in a hackish way with a magic current_field name. An additional field is pushed to stack for fullText.
+ */
+
+
+#define FIELD_NAME_FULL_TEXT_FIELDS "fullTextFields"
+
+
+#include <string.h>
+
+#include <libtracker-common/tracker-log.h>
+#include <libtracker-common/tracker-field-data.h>
+#include <libtracker-common/tracker-type-utils.h>
+#include <libtracker-common/tracker-utils.h>
+
+#include "tracker-xesam-query.h"
+#include "tracker-db.h"
+
+/* XESAM Query Condition
+ * <query>
+ *	<and>
+ *		<greaterThan>
+ *			<field name="File:Size" />
+ *			<integer>1000000<integer>
+ *		</greaterThan>
+ *		<equals>
+ *			<field name="File:Path" />
+ *			<string>/home/jamie<string>
+ *		</equals>
+ *	</and>
+ * </query>
+*/
+
+/* Main elements */
+#define ELEMENT_XESAM_QUERY		"query"
+#define ELEMENT_XESAM_USER_QUERY	"userQuery"
+#define ELEMENT_XESAM_FIELD		"field"
+#define ELEMENT_XESAM_FULL_TEXT_FIELDS	"fullTextFields"
+#define ELEMENT_XESAM_REQUEST		"request"
+
+/* Operators */
+#define ELEMENT_XESAM_AND		"and"
+#define ELEMENT_XESAM_OR		"or"
+#define ELEMENT_XESAM_EQUALS		"equals"
+#define ELEMENT_XESAM_GREATER_THAN	"greaterThan"
+#define ELEMENT_XESAM_GREATER_OR_EQUAL	"greaterOrEqual"
+#define ELEMENT_XESAM_LESS_THAN		"lessThan"
+#define ELEMENT_XESAM_LESS_OR_EQUAL	"lessOrEqual"
+
+/* Extension operators - "contains" does a substring or full text
+ * match, "in_Set" does string in list match
+ */
+#define ELEMENT_XESAM_CONTAINS		"contains"
+#define ELEMENT_XESAM_REGEX		"regex"
+#define ELEMENT_XESAM_STARTS_WITH	"startsWith"
+#define ELEMENT_XESAM_IN_SET		"inSet"
+#define ELEMENT_XESAM_FULL_TEXT		"fullText"
+
+/* Types */
+#define ELEMENT_XESAM_INTEGER		"integer"
+#define ELEMENT_XESAM_DATE		"date"
+#define ELEMENT_XESAM_STRING		"string"
+#define ELEMENT_XESAM_FLOAT		"float"
+#define ELEMENT_XESAM_BOOLEAN		"boolean"
+
+#define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0)
+
+enum {
+	NO_ERROR,
+	PARSE_ERROR,
+};
+
+typedef enum {
+	STATE_START,
+	STATE_QUERY,
+	STATE_END_QUERY,
+	STATE_USER_QUERY,
+	STATE_END_USER_QUERY,
+	STATE_FIELD,
+	STATE_AND,
+	STATE_END_AND,
+	STATE_OR,
+	STATE_END_OR,
+	STATE_EQUALS,
+	STATE_END_EQUALS,
+	STATE_GREATER_THAN,
+	STATE_END_GREATER_THAN,
+	STATE_GREATER_OR_EQUAL,
+	STATE_END_GREATER_OR_EQUAL,
+	STATE_LESS_THAN,
+	STATE_END_LESS_THAN,
+	STATE_LESS_OR_EQUAL,
+	STATE_END_LESS_OR_EQUAL,
+	STATE_CONTAINS,
+	STATE_END_CONTAINS,
+	STATE_REGEX,
+	STATE_END_REGEX,
+	STATE_STARTS_WITH,
+	STATE_END_STARTS_WITH,
+	STATE_IN_SET,
+	STATE_END_IN_SET,
+	STATE_FULL_TEXT,
+	STATE_END_FULL_TEXT,
+	STATE_INTEGER,
+	STATE_END_INTEGER,
+	STATE_STRING,
+	STATE_END_STRING,
+	STATE_FLOAT,
+	STATE_END_FLOAT,
+	STATE_DATE,
+	STATE_END_DATE,
+	STATE_BOOLEAN,
+	STATE_END_BOOLEAN
+} ParseState;
+
+
+typedef enum {
+	OP_NONE,
+	OP_EQUALS,
+	OP_GREATER,
+	OP_GREATER_EQUAL,
+	OP_LESS,
+	OP_LESS_EQUAL,
+	OP_CONTAINS,
+	OP_REGEX,
+	OP_SET,
+	OP_FULL_TEXT,
+	OP_STARTS
+} Operators;
+
+typedef enum {
+	LOP_NONE,
+	LOP_AND,
+	LOP_OR
+} LogicOperators;
+
+typedef struct {
+	GMarkupParseContext *context;
+	GMarkupParser	    *parser;
+	GSList		    *stack;
+	GSList		    *fields;
+	gboolean	     query_okay;
+	gint		     statement_count;
+	LogicOperators	     current_logic_operator;
+	Operators	     current_operator;
+	gchar		    *current_field;
+	gchar		    *current_value;
+	TrackerDBInterface  *iface;
+	GString		    *sql_select;
+	GString		    *sql_from;
+	GString		    *sql_where;
+	GString		    *sql_order;
+	GString		    *sql_join;
+	gchar		    *service;
+} ParserData;
+
+static void start_element_handler (GMarkupParseContext	*context,
+				   const gchar		*element_name,
+				   const gchar	       **attribute_names,
+				   const gchar	       **attribute_values,
+				   gpointer		 user_data,
+				   GError	       **error);
+static void end_element_handler   (GMarkupParseContext	*context,
+				   const gchar		*element_name,
+				   gpointer		 user_data,
+				   GError	       **error);
+static void text_handler	  (GMarkupParseContext	*context,
+				   const gchar		*text,
+				   gsize		 text_len,
+				   gpointer		 user_data,
+				   GError	       **error);
+static void error_handler	  (GMarkupParseContext	*context,
+				   GError		*error,
+				   gpointer		 user_data);
+
+static GQuark error_quark;
+
+static gboolean
+is_operator (ParseState state)
+{
+	return
+		state == STATE_EQUALS ||
+		state == STATE_GREATER_THAN ||
+		state == STATE_LESS_THAN ||
+		state == STATE_CONTAINS ||
+		state == STATE_IN_SET ||
+		state == STATE_FULL_TEXT ||
+		state == STATE_LESS_OR_EQUAL ||
+		state == STATE_GREATER_OR_EQUAL ||
+		state == STATE_STARTS_WITH ||
+		state == STATE_REGEX;
+
+}
+
+static gboolean
+is_end_operator (ParseState state)
+{
+	return
+		state == STATE_END_EQUALS ||
+		state == STATE_END_GREATER_THAN ||
+		state == STATE_END_LESS_THAN ||
+		state == STATE_END_CONTAINS ||
+		state == STATE_END_IN_SET ||
+		state == STATE_END_FULL_TEXT ||
+		state == STATE_END_LESS_OR_EQUAL ||
+		state == STATE_END_GREATER_OR_EQUAL ||
+		state == STATE_END_STARTS_WITH ||
+		state == STATE_END_REGEX;
+}
+
+static gboolean
+is_logic (ParseState state)
+{
+	return
+		state == STATE_AND ||
+		state == STATE_OR;
+}
+
+static gboolean
+is_end_logic (ParseState state)
+{
+	return
+		state == STATE_END_AND ||
+		state == STATE_END_OR;
+}
+
+static void
+set_error (GError	       **err,
+	   GMarkupParseContext	*context,
+	   int			 error_code,
+	   const char		*format,
+	   ...)
+{
+	gint	 line, ch;
+	va_list  args;
+	gchar	*str;
+
+	g_markup_parse_context_get_position (context, &line, &ch);
+
+	va_start (args, format);
+	str = g_strdup_vprintf (format, args);
+	va_end (args);
+
+	g_set_error (err,
+		     error_quark,
+		     error_code,
+		     "Line %d character %d: %s",
+		     line,
+		     ch,
+		     str);
+
+	g_free (str);
+}
+
+static gboolean
+set_error_on_fail (gboolean		 condition,
+		   GMarkupParseContext	*context,
+		   const gchar		*msg,
+		   GError	       **err)
+{
+	if (!condition) {
+		set_error (err, context, 1, msg);
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+static const gchar *
+get_attribute_value (const gchar *name,
+		     const gchar **names,
+		     const gchar **values)
+{
+	gint i;
+
+	i = 0;
+
+	while (names[i]) {
+		if (strcmp (name, names[i]) == 0) {
+			return values[i];
+		}
+		i++;
+	}
+
+	return NULL;
+}
+
+static const gchar *
+get_attribute_value_required (GMarkupParseContext  *context,
+			      const gchar	   *tag,
+			      const gchar	   *name,
+			      const gchar	  **names,
+			      const gchar	  **values,
+			      GError		  **error)
+{
+	const gchar *value;
+
+	value = get_attribute_value (name, names, values);
+
+	if (!value) {
+		set_error (error,
+			   context,
+			   PARSE_ERROR,
+			   "%s must have \"%s\" attribute",
+			   tag,
+			   name);
+	}
+
+	return value;
+}
+
+static void
+push_stack (ParserData *data, ParseState state)
+{
+	data->stack = g_slist_prepend (data->stack, GINT_TO_POINTER (state));
+}
+
+static void
+pop_stack (ParserData *data)
+{
+	data->stack = g_slist_remove (data->stack, data->stack->data);
+}
+
+static ParseState
+peek_state (ParserData *data)
+{
+	g_return_val_if_fail (data->stack != NULL, STATE_START);
+
+	return GPOINTER_TO_INT (data->stack->data);
+}
+
+static void
+pop_stack_until (ParserData *data, ParseState state)
+{
+	while (data->stack != NULL) {
+		if (state == peek_state (data)) {
+			pop_stack (data);
+			break;
+		}
+
+		pop_stack (data);
+	}
+}
+
+static GList *
+add_metadata_field (ParserData	*data,
+		    const gchar *xesam_name,
+		    gboolean	 is_select,
+		    gboolean	 is_condition)
+{
+	TrackerDBResultSet *result_set;
+	TrackerFieldData   *field_data;
+	gboolean	    field_exists;
+	const GSList	   *l;
+	GList		   *reply;
+	gboolean	    valid;
+
+	reply = NULL;
+	field_exists = FALSE;
+	field_data = NULL;
+	valid = TRUE;
+
+	/* Do the xesam mapping */
+	if (!strcmp(xesam_name,FIELD_NAME_FULL_TEXT_FIELDS)) {
+		result_set = tracker_db_xesam_get_all_text_metadata_names (data->iface);
+	} else {
+		result_set = tracker_db_xesam_get_metadata_names (data->iface, xesam_name);
+	}
+
+	if (!result_set) {
+		return NULL;
+	}
+
+	while (valid) {
+		gchar *field_name;
+
+		tracker_db_result_set_get (result_set, 0, &field_name, -1);
+
+		/* Check if field is already in list */
+		for (l = data->fields; l; l = l->next) {
+			const gchar *this_field_name;
+
+			this_field_name = tracker_field_data_get_field_name (l->data);
+
+			if (!this_field_name) {
+				continue;
+			}
+
+			if (strcasecmp (this_field_name, field_name) == 0) {
+				field_data = l->data;
+				field_exists = TRUE;
+
+				tracker_field_data_set_is_condition (l->data, is_condition);
+				tracker_field_data_set_is_select (l->data, is_select);
+
+				break;
+			}
+		}
+
+		if (!field_exists) {
+			field_data = tracker_db_get_metadata_field (data->iface,
+								    data->service,
+								    field_name,
+								    g_slist_length (data->fields),
+								    is_select,
+								    is_condition);
+			if (field_data) {
+				data->fields = g_slist_prepend (data->fields, field_data);
+			}
+		}
+
+		if (field_data) {
+			reply = g_list_append (reply, field_data);
+		}
+
+		valid = tracker_db_result_set_iter_next (result_set);
+		g_free (field_name);
+	}
+
+	return reply;
+}
+
+static void
+start_element_handler (GMarkupParseContext  *context,
+		       const gchar	    *element_name,
+		       const gchar	   **attribute_names,
+		       const gchar	   **attribute_values,
+		       gpointer		     user_data,
+		       GError		   **error)
+{
+	ParserData *data;
+	ParseState state;
+
+	data = user_data;
+	state = peek_state (data);
+
+	if (ELEMENT_IS (ELEMENT_XESAM_QUERY)) {
+		const char *content;
+		const char *source;
+
+		if (set_error_on_fail (state == STATE_START,
+				       context,
+				       "Query element not expected here",
+				       error)) {
+			return;
+		}
+
+		content = get_attribute_value ("content",
+					       attribute_names,
+					       attribute_values);
+		source = get_attribute_value ("source",
+					      attribute_names,
+					      attribute_values);
+
+		/* FIXME This is a bit clumsy, check that OK and get
+		 * the defaults (all) from somewhere. CHECK MEMORY
+		 * LEAKS!
+		 */
+		if(content) {
+			TrackerDBResultSet *result_set;
+
+			result_set = tracker_db_xesam_get_service_names (data->iface,
+									 content);
+
+			content = g_strdup (content);
+		} else {
+			content = g_strdup ("Files");
+		}
+
+		data->service = g_strdup ("Files");
+
+		if (source) {
+
+		} else {
+			/* FIXME */
+			source = "Files";
+		}
+
+		g_string_append_printf (data->sql_where,
+					" WHERE (S.ServiceTypeID in (select TypeId from ServiceTypes where TypeName = '%s' or Parent = '%s')) AND ",
+					content,
+					source);
+
+		push_stack (data, STATE_QUERY);
+	} else if (ELEMENT_IS (ELEMENT_XESAM_FIELD)) {
+		const gchar *name;
+
+		if (set_error_on_fail (is_operator (state),
+				       context,
+				       "Field element not expected here",
+				       error)) {
+			return;
+		}
+
+		name = get_attribute_value_required (context,
+						     "<field>",
+						     "name",
+						     attribute_names,
+						     attribute_values,
+						     error);
+
+		if (!name) {
+			return;
+		} else {
+			if (data->current_operator == OP_NONE) {
+				set_error (error,
+					   context,
+					   PARSE_ERROR,
+					   "no operator found for field \"%s\"",
+					   name);
+				return;
+			}
+
+			data->current_field = g_strdup (name);
+			push_stack (data, STATE_FIELD);
+		}
+	} else if (ELEMENT_IS (ELEMENT_XESAM_FULL_TEXT_FIELDS)) {
+
+		if (set_error_on_fail (is_operator (state),
+				       context,
+				       "Field element (fullTextFields) not expected here",
+				       error)) {
+			return;
+		}
+
+		if (data->current_operator == OP_NONE) {
+			set_error (error,
+				   context,
+				   PARSE_ERROR,
+				   "no operator found for fullTextFields");
+			return;
+		}
+
+		data->current_field = g_strdup (FIELD_NAME_FULL_TEXT_FIELDS);
+		push_stack (data, STATE_FIELD); /* We don't need to differentiate */
+
+	} else if (ELEMENT_IS (ELEMENT_XESAM_AND)) {
+		const gchar *negate;
+
+		if (set_error_on_fail (state == STATE_QUERY ||
+				       is_logic (state) ||
+				       is_end_logic (state) ||
+				       is_end_operator (state),
+				       context,
+				       "AND element not expected here",
+				       error)) {
+			return;
+		}
+
+		if (data->statement_count > 1) {
+			if (data->current_logic_operator == LOP_AND) {
+				data->sql_where = g_string_append (data->sql_where, " AND ");
+			} else {
+				if (data->current_logic_operator == LOP_OR) {
+					data->sql_where = g_string_append (data->sql_where, " OR ");
+				}
+			}
+		}
+
+		negate = get_attribute_value ("negate",
+					      attribute_names,
+					      attribute_values);
+
+		if (negate && !strcmp (negate,"true")) {
+			data->sql_where = g_string_append (data->sql_where, " NOT ");
+		}
+
+		data->statement_count = 0;
+		data->sql_where = g_string_append (data->sql_where, " ( ");
+		data->current_logic_operator = LOP_AND;
+		push_stack (data, STATE_AND);
+	} else if (ELEMENT_IS (ELEMENT_XESAM_OR)) {
+		const gchar *negate;
+
+		if (set_error_on_fail (state == STATE_QUERY ||
+				       is_logic (state) ||
+				       is_end_logic (state) ||
+				       is_end_operator (state),
+				       context,
+				       "OR element not expected here",
+				       error)) {
+			return;
+		}
+
+		if (data->statement_count > 1) {
+			if (data->current_logic_operator == LOP_AND) {
+				data->sql_where = g_string_append (data->sql_where, " AND ");
+			} else {
+				if (data->current_logic_operator == LOP_OR) {
+					data->sql_where = g_string_append (data->sql_where, " OR ");
+				}
+			}
+		}
+
+		negate = get_attribute_value ("negate",
+					      attribute_names,
+					      attribute_values);
+
+		if (negate && !strcmp (negate,"true")) {
+			data->sql_where = g_string_append (data->sql_where, " NOT ");
+		}
+
+		data->statement_count = 0;
+		data->sql_where = g_string_append (data->sql_where, " ( ");
+		data->current_logic_operator = LOP_OR;
+		push_stack (data, STATE_OR);
+	} else if (ELEMENT_IS (ELEMENT_XESAM_EQUALS)) {
+		const gchar *negate;
+
+		if (set_error_on_fail (state == STATE_QUERY ||
+				       is_logic (state) ||
+				       ((data->current_logic_operator == LOP_AND ||
+					 data->current_logic_operator == LOP_OR) &&
+					is_end_operator (state)),
+				       context,
+				       "EQUALS element not expected here",
+				       error)) {
+			    return;
+		    }
+
+		    negate = get_attribute_value ("negate",
+						  attribute_names,
+						  attribute_values);
+
+		    if (negate && !strcmp (negate,"true")) {
+			    data->sql_where = g_string_append (data->sql_where, " NOT ");
+		    }
+
+		    data->current_operator = OP_EQUALS;
+		    push_stack (data, STATE_EQUALS);
+	} else if (ELEMENT_IS (ELEMENT_XESAM_GREATER_THAN)) {
+		const gchar *negate;
+
+		if (set_error_on_fail (state == STATE_QUERY ||
+				       is_logic (state) ||
+				       ((data->current_logic_operator == LOP_AND ||
+					 data->current_logic_operator == LOP_OR) &&
+					is_end_operator (state)),
+					context,
+				       "GREATERTHAN element not expected here",
+				       error)) {
+			return;
+		}
+
+		negate = get_attribute_value ("negate",
+					      attribute_names,
+					      attribute_values);
+
+		if (negate && !strcmp (negate,"true")) {
+			data->sql_where = g_string_append (data->sql_where, " NOT ");
+		}
+
+		data->current_operator = OP_GREATER;
+		push_stack (data, STATE_GREATER_THAN);
+	} else if (ELEMENT_IS (ELEMENT_XESAM_GREATER_OR_EQUAL)) {
+		const gchar *negate;
+
+		if (set_error_on_fail (state == STATE_QUERY ||
+				       is_logic (state) ||
+				       ((data->current_logic_operator == LOP_AND ||
+					 data->current_logic_operator == LOP_OR) &&
+					is_end_operator (state)),
+				       context,
+				       "GREATEROREQUAL element not expected here",
+				       error)) {
+			return;
+		}
+
+		negate = get_attribute_value ("negate",
+					      attribute_names,
+					      attribute_values);
+
+		if (negate && !strcmp(negate,"true")) {
+			data->sql_where = g_string_append (data->sql_where, " NOT ");
+		}
+
+		data->current_operator = OP_GREATER_EQUAL;
+		push_stack (data, STATE_GREATER_OR_EQUAL);
+	} else if (ELEMENT_IS (ELEMENT_XESAM_LESS_THAN)) {
+		const gchar *negate;
+
+		if (set_error_on_fail (state == STATE_QUERY ||
+				       is_logic (state) ||
+				       ((data->current_logic_operator == LOP_AND ||
+					 data->current_logic_operator == LOP_OR) &&
+					is_end_operator (state)),
+				       context,
+				       "LESSTHAN element not expected here",
+				       error)) {
+			return;
+		}
+
+		negate = get_attribute_value ("negate",
+					      attribute_names,
+					      attribute_values);
+		if (negate && !strcmp (negate,"true")) {
+			data->sql_where = g_string_append (data->sql_where, " NOT ");
+		}
+
+		data->current_operator = OP_LESS;
+		push_stack (data, STATE_LESS_THAN);
+	} else if (ELEMENT_IS (ELEMENT_XESAM_LESS_OR_EQUAL)) {
+		const gchar *negate;
+
+		if (set_error_on_fail (state == STATE_QUERY ||
+				       is_logic (state) ||
+				       ((data->current_logic_operator == LOP_AND ||
+					 data->current_logic_operator == LOP_OR) &&
+					is_end_operator (state)),
+				       context,
+				       "LESSOREQUAL element not expected here",
+				       error)) {
+			return;
+		}
+
+		negate = get_attribute_value ("negate",
+					      attribute_names,
+					      attribute_values);
+
+		if (negate && !strcmp(negate,"true")) {
+			data->sql_where = g_string_append (data->sql_where, " NOT ");
+		}
+
+		data->current_operator = OP_LESS_EQUAL;
+		push_stack (data, STATE_LESS_OR_EQUAL);
+	} else if (ELEMENT_IS (ELEMENT_XESAM_CONTAINS)) {
+		const gchar *negate;
+
+		if (set_error_on_fail (state == STATE_QUERY ||
+				       is_logic (state) ||
+				       ((data->current_logic_operator == LOP_AND ||
+					 data->current_logic_operator == LOP_OR) &&
+					is_end_operator (state)),
+				       context,
+				       "CONTAINS element not expected here",
+				       error)) {
+			return;
+		}
+
+		negate = get_attribute_value ("negate",
+					      attribute_names,
+					      attribute_values);
+
+		if (negate && !strcmp(negate,"true")) {
+			data->sql_where = g_string_append (data->sql_where, " NOT ");
+		}
+
+		data->current_operator = OP_CONTAINS;
+		push_stack (data, STATE_CONTAINS);
+	} else if (ELEMENT_IS (ELEMENT_XESAM_REGEX)) {
+		const gchar *negate;
+
+		if (set_error_on_fail (state == STATE_QUERY ||
+				       is_logic (state) ||
+				       ((data->current_logic_operator == LOP_AND ||
+					 data->current_logic_operator == LOP_OR) &&
+					is_end_operator (state)),
+				       context,
+				       "REGEX element not expected here",
+				       error)) {
+			return;
+		}
+
+		negate = get_attribute_value ("negate",
+					      attribute_names,
+					      attribute_values);
+		if (negate && !strcmp (negate,"true")) {
+			data->sql_where = g_string_append (data->sql_where, " NOT ");
+		}
+
+		data->current_operator = OP_REGEX;
+		push_stack (data, STATE_REGEX);
+	} else if (ELEMENT_IS (ELEMENT_XESAM_STARTS_WITH)) {
+		const gchar *negate;
+
+		if (set_error_on_fail (state == STATE_QUERY ||
+				       is_logic (state) ||
+				       ((data->current_logic_operator == LOP_AND ||
+					 data->current_logic_operator == LOP_OR) &&
+					is_end_operator (state)),
+				       context,
+				       "STARTSWITH element not expected here",
+				       error)) {
+			return;
+		}
+
+		negate = get_attribute_value ("negate",
+					      attribute_names,
+					      attribute_values);
+
+		if (negate && !strcmp(negate,"true")) {
+			data->sql_where = g_string_append (data->sql_where, " NOT ");
+		}
+
+		data->current_operator = OP_STARTS;
+		push_stack (data, STATE_STARTS_WITH);
+	} else if (ELEMENT_IS (ELEMENT_XESAM_IN_SET)) {
+		const gchar *negate;
+
+		if (set_error_on_fail (state == STATE_QUERY ||
+				       is_logic (state) ||
+				       ((data->current_logic_operator == LOP_AND ||
+					 data->current_logic_operator == LOP_OR) &&
+					is_end_operator (state)),
+				       context,
+				       "IN SET element not expected here",
+				       error)) {
+			return;
+		}
+
+		negate = get_attribute_value ("negate",
+					      attribute_names,
+					      attribute_values);
+
+		if (negate && !strcmp(negate,"true")) {
+			data->sql_where = g_string_append (data->sql_where, " NOT ");
+		}
+
+		data->current_operator = OP_SET;
+		push_stack (data, STATE_IN_SET);
+	} else if (ELEMENT_IS (ELEMENT_XESAM_FULL_TEXT)) {
+		const gchar *negate;
+
+		if (set_error_on_fail (state == STATE_QUERY ||
+				       is_logic (state) ||
+				       ((data->current_logic_operator == LOP_AND ||
+					 data->current_logic_operator == LOP_OR) &&
+					is_end_operator (state)),
+				       context,
+				       "fullText element not expected here",
+				       error)) {
+			return;
+		}
+
+		negate = get_attribute_value ("negate",
+					      attribute_names,
+					      attribute_values);
+
+		if (negate && !strcmp(negate,"true")) {
+			data->sql_where = g_string_append (data->sql_where, " NOT ");
+		}
+
+		data->current_operator = OP_FULL_TEXT;
+		data->current_field = g_strdup (FIELD_NAME_FULL_TEXT_FIELDS);
+		push_stack (data, STATE_FULL_TEXT);
+	} else if (ELEMENT_IS (ELEMENT_XESAM_INTEGER)) {
+		if (set_error_on_fail (state == STATE_FIELD || state == STATE_FULL_TEXT,
+				       context,
+				       "INTEGER element not expected here",
+				       error)) {
+			return;
+		}
+
+		push_stack (data, STATE_INTEGER);
+	} else if (ELEMENT_IS (ELEMENT_XESAM_DATE)) {
+		if (set_error_on_fail (state == STATE_FIELD || state == STATE_FULL_TEXT,
+				       context,
+				       "DATE element not expected here",
+				       error)) {
+			return;
+		}
+
+		push_stack (data, STATE_DATE);
+	} else if (ELEMENT_IS (ELEMENT_XESAM_STRING)) {
+		if (set_error_on_fail (state == STATE_FIELD || state == STATE_FULL_TEXT,
+				       context,
+				       "STRING element not expected here",
+				       error)) {
+			return;
+		}
+
+		push_stack (data, STATE_STRING);
+	} else if (ELEMENT_IS (ELEMENT_XESAM_FLOAT)) {
+		if (set_error_on_fail (state == STATE_FIELD || state == STATE_FULL_TEXT,
+				       context,
+				       "FLOAT element not expected here",
+				       error)) {
+			return;
+		}
+
+		push_stack (data, STATE_FLOAT);
+	} else if (ELEMENT_IS (ELEMENT_XESAM_BOOLEAN)) {
+		if (set_error_on_fail (state == STATE_FIELD || state == STATE_FULL_TEXT,
+				       context,
+				       "BOOLEAN element not expected here",
+				       error)) {
+			return;
+		}
+		push_stack (data, STATE_BOOLEAN);
+	} else if (ELEMENT_IS (ELEMENT_XESAM_REQUEST)) {
+		/* Ignore */
+	} else {
+		g_warning ("%s not supported", element_name);
+
+		if (set_error_on_fail (FALSE,
+				       context,
+				       "Unsupported query",
+				       error)) {
+			return;
+		}
+	}
+}
+
+static char *
+get_value (const char *value, gboolean quote)
+{
+	if (quote) {
+		return g_strconcat (" '", value, "' ", NULL);
+	} else {
+		return g_strdup (value);
+	}
+}
+
+static gboolean
+build_sql (ParserData *data)
+{
+	ParseState  state;
+	gchar	   *avalue, *value, *sub;
+	GList	   *field_data;
+	GList	   *field_data_list;
+	GString    *str;
+	gint	    i;
+
+	g_return_val_if_fail (data->current_field &&
+			      data->current_operator != OP_NONE &&
+			      data->current_value,
+			      FALSE);
+
+	value = NULL;
+	field_data = NULL;
+	field_data_list = NULL;
+	i = 0;
+
+	data->statement_count++;
+
+	state = peek_state (data);
+
+	avalue = get_value (data->current_value,
+			    (state != STATE_END_DATE &&
+			     state != STATE_END_INTEGER &&
+			     state != STATE_END_FLOAT &&
+			     state != STATE_END_BOOLEAN));
+
+	field_data_list = add_metadata_field (data, data->current_field, FALSE, TRUE);
+
+	if (!field_data_list) {
+		g_free (avalue);
+		g_free (data->current_field);
+		g_free (data->current_value);
+		data->current_field = NULL;
+		data->current_value = NULL;
+		return FALSE;
+	}
+
+	data->sql_where = g_string_append (data->sql_where, " ( ");
+
+	field_data = g_list_first (field_data_list);
+
+	while (field_data) {
+		const gchar  *where_field;
+		gchar	    **s;
+
+		i++;
+		str = g_string_new ("");
+
+		if (i>1) {
+			g_string_append (str, " OR ");
+		}
+
+		if (tracker_field_data_get_data_type (field_data->data) == TRACKER_FIELD_TYPE_DATE) {
+			gchar *bvalue;
+			gint   cvalue;
+
+			bvalue = tracker_date_format (avalue);
+			g_debug (bvalue);
+			cvalue = tracker_string_to_date (bvalue);
+			g_debug ("%d", cvalue);
+			value = tracker_gint_to_string (cvalue);
+			g_free (bvalue);
+		} else if (state == STATE_END_BOOLEAN) {
+			/* FIXME We do a state check here, because
+			 * TRACKER_FIELD_TYPE_BOOLEAN is not in db.
+			 */
+			value = tracker_string_boolean_to_string_gint (avalue);
+		} else {
+			value = g_strdup (avalue);
+		}
+
+		if (data->statement_count > 1) {
+			if (data->current_logic_operator == LOP_AND) {
+				data->sql_where = g_string_append (data->sql_where, " AND ");
+			} else {
+				if (data->current_logic_operator == LOP_OR) {
+					data->sql_where = g_string_append (data->sql_where, " OR ");
+				}
+			}
+		}
+
+		where_field = tracker_field_data_get_where_field (field_data->data);
+
+		switch (data->current_operator) {
+		case OP_EQUALS:
+			sub = strchr (data->current_value, '*');
+
+			if (sub) {
+				g_string_append_printf (str, " (%s glob '%s') ",
+							where_field,
+							data->current_value);
+			} else {
+				TrackerFieldType data_type;
+
+				data_type = tracker_field_data_get_data_type (field_data->data);
+
+				if (data_type == TRACKER_FIELD_TYPE_DATE ||
+				    data_type == TRACKER_FIELD_TYPE_INTEGER ||
+				    data_type == TRACKER_FIELD_TYPE_DOUBLE) {
+					g_string_append_printf (str, " (%s = %s) ",
+								where_field,
+								value);
+				} else {
+					g_string_append_printf (str, " (%s = '%s') ",
+								where_field,
+								value);
+				}
+			}
+			break;
+
+		case OP_GREATER:
+			g_string_append_printf (str, " (%s > %s) ",
+						where_field,
+						value);
+			break;
+
+		case OP_GREATER_EQUAL:
+			g_string_append_printf (str, " (%s >= %s) ",
+						where_field,
+						value);
+			break;
+
+		case OP_LESS:
+			g_string_append_printf (str, " (%s < %s) ",
+						where_field,
+						value);
+			break;
+
+		case OP_LESS_EQUAL:
+			g_string_append_printf (str, " (%s <= %s) ",
+						where_field,
+						value);
+			break;
+
+		case OP_CONTAINS:
+			sub = strchr (data->current_value, '*');
+
+			if (sub) {
+				g_string_append_printf (str, " (%s like '%%%s%%') ",
+							where_field,
+							data->current_value);
+			} else {
+				g_string_append_printf (str, " (%s like '%%%s%%') ",
+							where_field,
+							data->current_value);
+			}
+			break;
+
+		case OP_STARTS:
+			sub = strchr (data->current_value, '*');
+
+			if (sub) {
+				g_string_append_printf (str, " (%s like '%s') ",
+							where_field,
+							data->current_value);
+			} else {
+				g_string_append_printf (str, " (%s like '%s%%') ",
+							where_field,
+							data->current_value);
+			}
+
+			break;
+
+		case OP_REGEX:
+			g_string_append_printf (str, " (%s REGEXP '%s') ",
+						where_field,
+						data->current_value);
+			break;
+
+		case OP_SET:
+			s = g_strsplit (data->current_value, ",", 0);
+
+			if (s && s[0]) {
+				gchar **p;
+
+				g_string_append_printf (str, " (%s in ('%s'",
+							where_field,
+							s[0]);
+
+				for (p = s + 1; *p; p++) {
+					g_string_append_printf (str, ",'%s'", *p);
+				}
+
+				g_string_append_printf (str, ") ) " );
+			}
+			break;
+
+		case OP_FULL_TEXT:
+			sub = strchr (data->current_value, '*');
+
+			if (sub) {
+				g_string_append_printf (str, " (%s like '%%%s%%') ",
+							where_field,
+							data->current_value);
+			} else {
+				g_string_append_printf (str, " (%s like '%%%s%%') ",
+							where_field,
+							data->current_value);
+			}
+			break;
+
+		default:
+			break;
+		}
+
+		data->sql_where = g_string_append (data->sql_where, str->str);
+		g_string_free (str, TRUE);
+		field_data = g_list_next (field_data);
+	}
+
+	data->sql_where = g_string_append (data->sql_where, " ) ");
+
+	g_free (avalue);
+	g_free (data->current_field);
+	data->current_field = NULL;
+
+	g_free (data->current_value);
+	data->current_value = NULL;
+
+	g_free (value);
+
+	return TRUE;
+}
+
+
+static void
+end_element_handler (GMarkupParseContext *context,
+		     const gchar	 *element_name,
+		     gpointer		 user_data,
+		     GError		 **error)
+{
+	ParserData *data;
+
+	data = user_data;
+
+	if (ELEMENT_IS (ELEMENT_XESAM_QUERY)) {
+
+		push_stack (data, STATE_END_QUERY);
+		data->query_okay = TRUE;
+
+	} else if (ELEMENT_IS (ELEMENT_XESAM_AND)) {
+
+		data->sql_where = g_string_append (data->sql_where, " ) ");
+
+		pop_stack_until (data, STATE_AND);
+
+		if (peek_state (data) != STATE_AND) {
+			if (peek_state (data) == STATE_OR) {
+				data->current_logic_operator = LOP_OR;
+			} else {
+				data->current_logic_operator = LOP_NONE;
+			}
+		}
+
+	} else if (ELEMENT_IS (ELEMENT_XESAM_OR)) {
+
+		data->sql_where = g_string_append (data->sql_where, " ) ");
+
+		pop_stack_until (data, STATE_OR);
+
+		if (peek_state (data) != STATE_OR) {
+			if (peek_state (data) == STATE_AND) {
+				data->current_logic_operator = LOP_AND;
+			} else {
+				data->current_logic_operator = LOP_NONE;
+			}
+		}
+
+	} else if (ELEMENT_IS (ELEMENT_XESAM_EQUALS)) {
+
+		if (!build_sql (data)) {
+			set_error (error, context, 1, "parse error");
+			return;
+		}
+		push_stack (data, STATE_END_EQUALS);
+
+	} else if (ELEMENT_IS (ELEMENT_XESAM_GREATER_THAN)) {
+
+		if (!build_sql (data)) {
+			set_error (error, context, 1, "parse error");
+			return;
+		}
+
+		push_stack (data, STATE_END_GREATER_THAN);
+
+	} else if (ELEMENT_IS (ELEMENT_XESAM_GREATER_OR_EQUAL)) {
+
+		if (!build_sql (data)) {
+			set_error (error, context, 1, "parse error");
+			return;
+		}
+
+		push_stack (data, STATE_END_GREATER_OR_EQUAL);
+
+	} else if (ELEMENT_IS (ELEMENT_XESAM_LESS_THAN )) {
+
+		if (!build_sql (data)) {
+			set_error (error, context, 1, "parse error");
+			return;
+		}
+
+		push_stack (data, STATE_END_LESS_THAN );
+
+	} else if (ELEMENT_IS (ELEMENT_XESAM_LESS_OR_EQUAL )) {
+
+		if (!build_sql (data)) {
+			set_error (error, context, 1, "parse error");
+			return;
+		}
+
+		push_stack (data, STATE_END_LESS_OR_EQUAL );
+
+
+	} else if (ELEMENT_IS (ELEMENT_XESAM_CONTAINS)) {
+
+		if (!build_sql (data)) {
+			set_error (error, context, 1, "parse error");
+			return;
+		}
+
+		push_stack (data, STATE_END_CONTAINS);
+
+	} else if (ELEMENT_IS (ELEMENT_XESAM_REGEX)) {
+
+		if (!build_sql (data)) {
+			set_error (error, context, 1, "parse error");
+			return;
+		}
+
+		push_stack (data, STATE_END_REGEX);
+
+	} else if (ELEMENT_IS (ELEMENT_XESAM_STARTS_WITH)) {
+
+		if (!build_sql (data)) {
+			set_error (error, context, 1, "parse error");
+			return;
+		}
+
+		push_stack (data, STATE_END_STARTS_WITH);
+
+	} else if (ELEMENT_IS (ELEMENT_XESAM_IN_SET)) {
+
+		if (!build_sql (data)) {
+			set_error (error, context, 1, "parse error");
+			return;
+		}
+
+		push_stack (data, STATE_END_IN_SET);
+
+	} else if (ELEMENT_IS (ELEMENT_XESAM_FULL_TEXT)) {
+
+		if (!build_sql (data)) {
+			set_error (error, context, 1, "parse error");
+			return;
+		}
+
+		push_stack (data, STATE_END_FULL_TEXT);
+
+	} else if (ELEMENT_IS (ELEMENT_XESAM_INTEGER)) {
+
+		push_stack (data, STATE_END_INTEGER);
+
+
+	} else if (ELEMENT_IS (ELEMENT_XESAM_DATE)) {
+
+		push_stack (data, STATE_END_DATE);
+
+
+	} else if (ELEMENT_IS (ELEMENT_XESAM_STRING)) {
+
+		push_stack (data, STATE_END_STRING);
+
+
+	}  else if (ELEMENT_IS (ELEMENT_XESAM_FLOAT)) {
+
+		push_stack (data, STATE_END_FLOAT);
+
+
+	}  else if (ELEMENT_IS (ELEMENT_XESAM_BOOLEAN)) {
+
+		push_stack (data, STATE_END_BOOLEAN);
+	}
+}
+
+
+static void
+text_handler (GMarkupParseContext *context,
+	      const gchar	  *text,
+	      gsize		  text_len,
+	      gpointer		  user_data,
+	      GError		  **error)
+{
+	ParserData *data;
+	ParseState state;
+
+	data = user_data;
+	state = peek_state (data);
+
+	switch (state) {
+
+		case STATE_INTEGER:
+		case STATE_STRING:
+		case STATE_DATE:
+		case STATE_FLOAT:
+		case STATE_BOOLEAN:
+
+			data->current_value = g_strstrip (g_strndup (text, text_len));
+			break;
+
+		default :
+			break;
+	}
+}
+
+
+static void
+error_handler (GMarkupParseContext *context,
+	       GError		   *error,
+	       gpointer		   user_data)
+{
+	g_message ("Failed to parse RDF query, %s", error->message);
+}
+
+void
+tracker_xesam_query_to_sql (TrackerDBInterface	*iface,
+			    const gchar		*query,
+			    gchar	       **from,
+			    gchar	       **join,
+			    gchar	       **where,
+			    GError	       **error)
+{
+	static gboolean  inited = FALSE;
+	ParserData	 data;
+	gchar		*result;
+	gchar		*table_name;
+
+	g_return_if_fail (TRACKER_IS_DB_INTERFACE (iface));
+	g_return_if_fail (query != NULL);
+	g_return_if_fail (from != NULL);
+	g_return_if_fail (join != NULL);
+	g_return_if_fail (where != NULL);
+
+	if (!inited) {
+		error_quark = g_quark_from_static_string ("XESAM-parser-error-quark");
+		inited = TRUE;
+	}
+
+	memset (&data, 0, sizeof (data));
+	data.iface = iface;
+	data.statement_count = 0;
+
+	table_name = "Services";
+
+	data.sql_from = g_string_new ("");
+	g_string_append_printf (data.sql_from, " FROM %s S ", table_name);
+
+	data.sql_join = g_string_new ("");
+	data.sql_where = g_string_new ("");
+
+	data.parser = g_new0 (GMarkupParser, 1);
+	data.parser->start_element = start_element_handler;
+	data.parser->text = text_handler;
+	data.parser->end_element = end_element_handler;
+	data.parser->error = error_handler;
+
+	data.current_operator = OP_NONE;
+	data.current_logic_operator = LOP_NONE;
+	data.query_okay = FALSE;
+
+	data.context = g_markup_parse_context_new (data.parser, 0, &data, NULL);
+
+	push_stack (&data, STATE_START);
+
+	result = NULL;
+
+	if (!g_markup_parse_context_parse (data.context, query, -1, error)) {
+		g_string_free (data.sql_from, TRUE);
+		g_string_free (data.sql_where, TRUE);
+		g_string_free (data.sql_join, TRUE);
+
+		*from = NULL;
+		*join = NULL;
+		*where = NULL;
+	} else {
+		GSList *l;
+
+		for (l = data.fields; l; l = l->next) {
+			if (!tracker_field_data_get_is_condition (l->data)) {
+				if (tracker_field_data_get_needs_join (l->data)) {
+					g_string_append_printf (data.sql_join,
+								" LEFT OUTER JOIN %s %s ON (S.ID = %s.ServiceID and %s.MetaDataID = %s) ",
+								tracker_field_data_get_table_name (l->data),
+								tracker_field_data_get_alias (l->data),
+								tracker_field_data_get_alias (l->data),
+								tracker_field_data_get_alias (l->data),
+								tracker_field_data_get_id_field (l->data));
+				}
+			} else {
+				gchar *related_metadata;
+
+				related_metadata = tracker_db_metadata_get_related_names (iface,
+											  tracker_field_data_get_field_name (l->data));
+				g_string_append_printf (data.sql_join,
+							" INNER JOIN %s %s ON (S.ID = %s.ServiceID and %s.MetaDataID in (%s)) ",
+							tracker_field_data_get_table_name (l->data),
+							tracker_field_data_get_alias (l->data),
+							tracker_field_data_get_alias (l->data),
+							tracker_field_data_get_alias (l->data),
+							related_metadata);
+				g_free (related_metadata);
+			}
+		}
+
+		*from = g_strdup (data.sql_from->str);
+		*join = g_strdup (data.sql_join->str);
+		*where = g_strdup (data.sql_where->str);
+
+		g_string_free (data.sql_from, TRUE);
+		g_string_free (data.sql_join, TRUE);
+		g_string_free (data.sql_where, TRUE);
+	}
+
+	g_slist_foreach (data.fields, (GFunc) g_object_unref, NULL);
+	g_slist_free (data.fields);
+
+	g_slist_free (data.stack);
+	g_markup_parse_context_free (data.context);
+
+	if (data.current_field) {
+		g_free (data.current_field);
+	}
+
+	if (data.current_value) {
+		g_free (data.current_value);
+	}
+
+	g_free (data.parser);
+
+	return;
+}
+
+

Added: trunk/src/trackerd/tracker-xesam-query.h
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-xesam-query.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,40 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia
+ * Authors: Ottela Mikael, (mikael ottela ixonos com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKERD_XESAM_QUERY_H__
+#define __TRACKERD_XESAM_QUERY_H__
+
+#include <glib.h>
+
+#include <libtracker-db/tracker-db-manager.h>
+
+G_BEGIN_DECLS
+
+void tracker_xesam_query_to_sql (TrackerDBInterface  *iface,
+				 const gchar	     *query,
+				 gchar		    **from,
+				 gchar		    **join,
+				 gchar		    **where,
+				 GError		    **error);
+G_END_DECLS
+
+#endif /* __TRACKERD_XESAM_QUERY_H__ */
+

Added: trunk/src/trackerd/tracker-xesam-session.c
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-xesam-session.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,452 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia
+ * Authors: Philip Van Hoof (pvanhoof gnome org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <string.h>
+
+#include "tracker-xesam-manager.h"
+
+#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TRACKER_TYPE_XESAM_SESSION, struct _TrackerXesamSessionPriv))
+
+struct _TrackerXesamSessionPriv {
+	GHashTable *searches;
+	GHashTable *props;
+	gchar	   *session_id;
+};
+
+G_DEFINE_TYPE (TrackerXesamSession, tracker_xesam_session, G_TYPE_OBJECT)
+
+static void
+tracker_xesam_session_g_value_free (GValue *value)
+{
+	g_value_unset(value);
+	g_free(value);
+}
+
+/**
+ * tracker_xesam_session_get_props:
+ * @self: A #TrackerXesamSession
+ *
+ * Get the properties of @self. The returned value is a hashtable with key as
+ * Xesam session property key strings and value a #GValue being either a string,
+ * an integer, a boolean or an array (either TRACKER_TYPE_XESAM_STRV_ARRAY or
+ * G_TYPE_STRV).
+ *
+ * The returned value must be unreferenced using @g_hash_table_unref.
+ *
+ * returns (caller-owns): a read-only hash-table with Xesam properties
+ **/
+GHashTable *
+tracker_xesam_session_get_props (TrackerXesamSession *self)
+{
+	return g_hash_table_ref (self->priv->props);
+}
+
+static void
+tracker_xesam_session_init (TrackerXesamSession *self)
+{
+	TrackerXesamSessionPriv *priv;
+	GValue *value;
+	const gchar *hit_fields[2] = {"xesam:url", NULL};
+	const gchar *hit_fields_extended[1] = {NULL};
+	const gchar *fields[3] = {"xesam:url", "xesam:relevancyRating", NULL};
+	const gchar *contents[2] = {"xesam:Content", NULL};
+	const gchar *sources[2] = {"xesam:Source", NULL};
+	const gchar *exts[1] = {NULL};
+	const gchar *dummy_onto[4] = {"dummy-onto","0.1","/usr/share/xesam/ontologies/dummy-onto-0.1", NULL};
+	GPtrArray *ontos = g_ptr_array_new ();
+
+	priv = self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,TRACKER_TYPE_XESAM_SESSION,struct _TrackerXesamSessionPriv);
+
+	g_ptr_array_add (ontos, dummy_onto);
+
+	priv->session_id = NULL;
+
+
+	priv->searches = g_hash_table_new_full (g_str_hash, g_str_equal,
+				(GDestroyNotify) g_free,
+				(GDestroyNotify) g_object_unref);
+
+	priv->props = g_hash_table_new_full (g_str_hash, g_str_equal,
+				(GDestroyNotify) g_free,
+				(GDestroyNotify) tracker_xesam_session_g_value_free);
+
+	value = g_new0 (GValue, 1);
+	g_value_init(value, G_TYPE_BOOLEAN);
+	g_value_set_boolean (value, FALSE);
+	g_hash_table_insert (priv->props, g_strdup ("search.live"), value);
+
+
+	value = g_new0 (GValue, 1);
+	g_value_init(value, G_TYPE_STRV);
+	g_value_set_boxed (value, hit_fields);
+	g_hash_table_insert (priv->props, g_strdup ("hit.fields"), value);
+
+	value = g_new0 (GValue, 1);
+	g_value_init(value, G_TYPE_STRV);
+	g_value_set_boxed (value, hit_fields_extended);
+	g_hash_table_insert (priv->props, g_strdup ("hit.fields.extended"), value);
+
+	value = g_new0 (GValue, 1);
+	g_value_init(value, G_TYPE_UINT);
+	g_value_set_uint (value, 200);
+	g_hash_table_insert (priv->props, g_strdup ("hit.snippet.length"), value);
+
+	value = g_new0 (GValue, 1);
+	g_value_init(value, G_TYPE_STRING);
+	g_value_set_string (value, "xesam:relevancyRating");
+	g_hash_table_insert (priv->props, g_strdup ("sort.primary"), value);
+
+	value = g_new0 (GValue, 1);
+	g_value_init(value, G_TYPE_STRING);
+	g_value_set_string (value, "");
+	g_hash_table_insert (priv->props, g_strdup ("sort.secondary"), value);
+
+	value = g_new0 (GValue, 1);
+	g_value_init(value, G_TYPE_STRING);
+	g_value_set_string (value, "descending");
+	g_hash_table_insert (priv->props, g_strdup ("sort.order"), value);
+
+	value = g_new0 (GValue, 1);
+	g_value_init(value, G_TYPE_STRING);
+	g_value_set_string (value, "TrackerXesamSession");
+	g_hash_table_insert (priv->props, g_strdup ("vendor.id"), value);
+
+	value = g_new0 (GValue, 1);
+	g_value_init(value, G_TYPE_UINT);
+	g_value_set_uint (value, 1);
+	g_hash_table_insert (priv->props, g_strdup ("vendor.version"), value);
+
+	value = g_new0 (GValue, 1);
+	g_value_init(value, G_TYPE_STRING);
+	g_value_set_string (value, "Tracker Xesam Service");
+	g_hash_table_insert (priv->props, g_strdup ("vendor.display"), value);
+
+	value = g_new0 (GValue, 1);
+	g_value_init(value, G_TYPE_UINT);
+	g_value_set_uint (value, 90);
+	g_hash_table_insert (priv->props, g_strdup ("vendor.xesam"), value);
+
+	value = g_new0 (GValue, 1);
+	g_value_init(value, G_TYPE_STRV);
+	g_value_set_boxed (value, fields);
+	g_hash_table_insert (priv->props, g_strdup ("vendor.ontology.fields"), value);
+
+	value = g_new0 (GValue, 1);
+	g_value_init(value, G_TYPE_STRV);
+	g_value_set_boxed (value, contents);
+	g_hash_table_insert (priv->props, g_strdup ("vendor.ontology.contents"), value);
+
+	value = g_new0 (GValue, 1);
+	g_value_init(value, G_TYPE_STRV);
+	g_value_set_boxed (value, sources);
+	g_hash_table_insert (priv->props, g_strdup ("vendor.ontology.sources"), value);
+
+	value = g_new0 (GValue, 1);
+	g_value_init(value, G_TYPE_STRV);
+	g_value_set_boxed (value, exts);
+	g_hash_table_insert (priv->props, g_strdup ("vendor.extensions"), value);
+
+	value = g_new0 (GValue, 1);
+	g_value_init(value, TRACKER_TYPE_XESAM_STRV_ARRAY);
+	g_value_set_boxed(value, ontos);
+	/* Comment by Philip Van Hoof: I don't see how we are freeing this one ,
+	 * up. So we are most likely just leaking the GPtrArray per session ... */
+	g_hash_table_insert (priv->props, g_strdup ("vendor.ontologies"), value);
+
+	value = g_new0 (GValue, 1);
+	g_value_init(value, G_TYPE_UINT);
+	g_value_set_uint (value, 50);
+	g_hash_table_insert (priv->props, g_strdup ("vendor.maxhits"), value);
+
+}
+
+static void
+tracker_xesam_session_finalize (GObject *object)
+{
+	TrackerXesamSession *self = (TrackerXesamSession *) object;
+	TrackerXesamSessionPriv *priv = self->priv;
+
+	g_free (priv->session_id);
+	g_hash_table_destroy (priv->searches);
+	g_hash_table_destroy (priv->props);
+
+}
+
+static void
+tracker_xesam_session_class_init (TrackerXesamSessionClass *klass)
+{
+	GObjectClass *object_class;
+	object_class = G_OBJECT_CLASS (klass);
+	object_class->finalize = tracker_xesam_session_finalize;
+
+	g_type_class_add_private( klass, sizeof(struct _TrackerXesamSessionPriv) );
+}
+
+/**
+ * tracker_xesam_session_get_id:
+ * @self: A #TrackerXesamSession
+ * @session_id: a unique ID string for @self
+ *
+ * Set a read-only unique ID string for @self.
+ **/
+void
+tracker_xesam_session_set_id (TrackerXesamSession *self,
+			      const gchar	  *session_id)
+{
+	TrackerXesamSessionPriv *priv = self->priv;
+
+	if (priv->session_id)
+		g_free (priv->session_id);
+	priv->session_id = g_strdup (session_id);
+}
+
+/**
+ * tracker_xesam_session_get_id:
+ * @self: A #TrackerXesamSession
+ *
+ * Get the read-only unique ID string for @self.
+ *
+ * returns: a unique id
+ **/
+const gchar*
+tracker_xesam_session_get_id (TrackerXesamSession *self)
+{
+	TrackerXesamSessionPriv *priv = self->priv;
+
+	return (const gchar*) priv->session_id;
+}
+
+/**
+ * tracker_xesam_session_get_searches:
+ * @self: A #TrackerXesamSession
+ *
+ * Get all searches in @self as a doubly linked list containing
+ * #TrackerXesamLiveSearch objects.
+ *
+ * @returns: (caller-owns) (null-ok): all searches in @self
+ **/
+GList *
+tracker_xesam_session_get_searches (TrackerXesamSession *self)
+{
+	TrackerXesamSessionPriv *priv = self->priv;
+
+	return g_hash_table_get_values (priv->searches);
+}
+
+/**
+ * tracker_xesam_session_set_property:
+ * @self: A #TrackerXesamSession
+ * @prop: The name or the property to set, see the list of session properties
+ * for valid property names at http://xesam.org/main/XesamSearchAPI#properties
+ * @val: The value to set the property to
+ * @new_val: (out) (caller-owns): The actual value the search engine will use.
+ * As noted above it is  not guaranteed that the requested value will be
+ * respected
+ * @error: (null-ok) (out): a #GError
+ *
+ * Set a property on the session. It is not guaranteed that the session property
+ * will actually be used, the return value is the property value that will be
+ * used. Search engines must respect the default property values however. For a
+ * list of properties and descriptions see below.
+ *
+ * Calling this method after the first search has been created with
+ * @tracker_xesam_session_create_search is illegal. The server will raise an
+ * error if you do. Ie. once you create the first search the properties are set
+ * in stone for the parent session. The search engine will also throw an error
+ * if the session handle has been closed or is invalid.
+ *
+ * An error will also be thrown if the prop parameter is not a valid session
+ * property, if it is a property marked as read-only, or if the requested value
+ * is invalid.
+ **/
+void
+tracker_xesam_session_set_property (TrackerXesamSession  *self,
+				    const gchar		 *prop,
+				    const GValue	 *val,
+				    GValue		**new_val,
+				    GError		**error)
+{
+	TrackerXesamSessionPriv *priv = self->priv;
+	const gchar *read_only[11] = {"vendor.id", "vendor.version", "vendor.display",
+		"vendor.xesam", "vendor.ontology.fields", "vendor.ontology.contents",
+		"vendor.ontology.sources", "vendor.extensions", "vendor.ontologies",
+		"vendor.maxhits", NULL};
+	GValue *property = NULL;
+	gboolean found = FALSE;
+	gint i = 0;
+
+	while (read_only[i]) {
+		if (!strcmp (prop, read_only[i])) {
+			found = TRUE;
+			break;
+		}
+		i++;
+	}
+
+	if (!found)
+		property = g_hash_table_lookup (priv->props, prop);
+
+	if (!property) {
+		g_set_error (error, TRACKER_XESAM_ERROR_DOMAIN,
+				TRACKER_XESAM_ERROR_PROPERTY_NOT_SUPPORTED,
+				"Property not supported");
+		*new_val = NULL;
+	} else {
+		GValue *target_val = g_new0 (GValue, 1);
+		g_value_init (target_val, G_VALUE_TYPE (property));
+		g_value_transform (val, property);
+		g_value_transform (val, target_val);
+		*new_val = target_val;
+	}
+}
+
+/**
+ * tracker_xesam_session_get_property:
+ * @self: A #TrackerXesamSession
+ * @prop: The name or the property to set, see the list of session properties
+ * for valid property names at http://xesam.org/main/XesamSearchAPI#properties
+ * @value: (out) (caller-owns): The value of a session property
+ * @error: (null-ok) (out): a #GError
+ *
+ * Get the value of a session property. The server should throw an error if the
+ * session handle is closed or does not exist. An error should also be raised if
+ * prop is not a valid session property.
+ **/
+void
+tracker_xesam_session_get_property (TrackerXesamSession  *self,
+				    const gchar		 *prop,
+				    GValue		**value,
+				    GError		**error)
+{
+	TrackerXesamSessionPriv *priv = self->priv;
+
+	GValue *property = g_hash_table_lookup (priv->props, prop);
+
+	if (!property) {
+		g_set_error (error, TRACKER_XESAM_ERROR_DOMAIN,
+				TRACKER_XESAM_ERROR_PROPERTY_NOT_SUPPORTED,
+				"Property not supported");
+		*value = NULL;
+	} else {
+		GValue *target_val = g_new0 (GValue, 1);
+		g_value_init (target_val, G_VALUE_TYPE (property));
+		g_value_transform (property, target_val);
+		*value = target_val;
+	}
+
+	return;
+}
+
+/**
+ * tracker_xesam_session_create_search:
+ * @self: A #TrackerXesamSession
+ * @query_xml: A string in the xesam query language
+ * @search_id: (out) (caller-owns): An opaque handle for the Search object
+ * @error: (null-ok) (out): a #GError
+ *
+ * Create a new search from @query_xml. If there are errors parsing the
+ * @query_xml parameter an error will be set in @error.
+ *
+ * Notifications of hits can be obtained by listening to the @hits-added signal.
+ * Signals will not be emitted before a call to @tracker_xesam_live_search_activate
+ * has been made.
+ *
+ * @returns: (null-ok) (caller-owns): a new non-activated #TrackerXesamLiveSearch
+ **/
+TrackerXesamLiveSearch*
+tracker_xesam_session_create_search (TrackerXesamSession  *self,
+				     const gchar	  *query_xml,
+				     gchar		 **search_id,
+				     GError		 **error)
+{
+	TrackerXesamLiveSearch	*search;
+	TrackerXesamSessionPriv *priv = self->priv;
+
+	g_debug ("Creating search for xesam session: \n %s", query_xml);
+
+	search = tracker_xesam_live_search_new (query_xml);
+
+	tracker_xesam_live_search_set_session (search, self);
+	tracker_xesam_live_search_set_id (search,
+					  tracker_xesam_manager_generate_unique_key ());
+
+	if (tracker_xesam_live_search_parse_query (search, error)) {
+
+		g_debug ("Xesam live search added");
+		g_hash_table_insert (priv->searches,
+			g_strdup (tracker_xesam_live_search_get_id (search)),
+			g_object_ref (search));
+
+		if (search_id)
+			*search_id = g_strdup (tracker_xesam_live_search_get_id (search));
+
+	} else {
+		g_message ("Xesam search parse failed");
+		g_object_unref (search);
+		search = NULL;
+	}
+
+	return search;
+}
+
+/**
+ * tracker_xesam_session_get_search:
+ * @self: A #TrackerXesamSession
+ * @search_id: (in): An opaque handle for the Search object
+ * @error: (null-ok) (out): a #GError
+ *
+ * Get the #TrackerXesamLiveSearch identified by @search_id in @self.
+ *
+ * @returns: (null-ok) (caller-owns): a #TrackerXesamLiveSearch or NULL
+ **/
+TrackerXesamLiveSearch*
+tracker_xesam_session_get_search (TrackerXesamSession  *self,
+				  const gchar	       *search_id,
+				  GError	      **error)
+{
+	TrackerXesamSessionPriv *priv = self->priv;
+	TrackerXesamLiveSearch *search = g_hash_table_lookup (priv->searches, search_id);
+
+	if (search)
+		g_object_ref (search);
+	else {
+		g_set_error (error, TRACKER_XESAM_ERROR_DOMAIN,
+				TRACKER_XESAM_ERROR_SEARCH_ID_NOT_REGISTERED,
+				"SearchID not registered");
+	}
+
+	return search;
+}
+
+/**
+ * tracker_xesam_session_new:
+ *
+ * Create a new #TrackerXesamSession
+ *
+ * @returns: (caller-owns): a new #TrackerXesamSession
+ **/
+TrackerXesamSession*
+tracker_xesam_session_new (void)
+{
+	return g_object_newv (TRACKER_TYPE_XESAM_SESSION, 0, NULL);
+}
+
+

Added: trunk/src/trackerd/tracker-xesam-session.h
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-xesam-session.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,81 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia
+ * Authors: Philip Van Hoof (pvanhoof gnome org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKERD_XESAM_SESSION_H__
+#define __TRACKERD_XESAM_SESSION_H__
+
+#include <glib-object.h>
+
+#include "tracker-xesam-manager.h"
+#include "tracker-xesam-live-search.h"
+#include "tracker-xesam.h"
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_XESAM_SESSION (tracker_xesam_session_get_type ())
+#define TRACKER_XESAM_SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRACKER_TYPE_XESAM_SESSION, TrackerXesamSession))
+#define TRACKER_XESAM_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TRACKER_TYPE_XESAM_SESSION, TrackerXesamSessionClass))
+#define TRACKER_IS_XESAM_SESSION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRACKER_TYPE_XESAM_SESSION))
+#define TRACKER_IS_XESAM_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TRACKER_TYPE_XESAM_SESSION))
+#define TRACKER_XESAM_SESSION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TRACKER_TYPE_XESAM_SESSION, TrackerXesamSessionClass))
+
+#define TRACKER_TYPE_XESAM_STRV_ARRAY (dbus_g_type_get_collection ("GPtrArray", G_TYPE_STRV))
+
+typedef struct _TrackerXesamSession	 TrackerXesamSession;
+typedef struct _TrackerXesamSessionClass TrackerXesamSessionClass;
+typedef struct _TrackerXesamSessionPriv  TrackerXesamSessionPriv;
+
+struct _TrackerXesamSession {
+	GObject parent_instance;
+	TrackerXesamSessionPriv *priv;
+};
+
+struct _TrackerXesamSessionClass {
+	GObjectClass parent_class;
+};
+
+void			tracker_xesam_session_set_property  (TrackerXesamSession  *self,
+							     const gchar	  *prop,
+							     const GValue	  *val,
+							     GValue		 **new_val,
+							     GError		 **error);
+void			tracker_xesam_session_get_property  (TrackerXesamSession  *self,
+							     const gchar	  *prop,
+							     GValue		 **value,
+							     GError		 **error);
+TrackerXesamLiveSearch* tracker_xesam_session_create_search (TrackerXesamSession  *self,
+							     const gchar	  *query_xml,
+							     gchar		 **search_id,
+							     GError		 **error);
+TrackerXesamLiveSearch* tracker_xesam_session_get_search    (TrackerXesamSession  *self,
+							     const gchar	  *search_id,
+							     GError		 **error);
+GList *			tracker_xesam_session_get_searches  (TrackerXesamSession  *self);
+void			tracker_xesam_session_set_id	    (TrackerXesamSession  *self,
+							     const gchar	  *session_id);
+const gchar*		tracker_xesam_session_get_id	    (TrackerXesamSession  *self);
+TrackerXesamSession*	tracker_xesam_session_new	    (void);
+GType			tracker_xesam_session_get_type	    (void);
+GHashTable*		tracker_xesam_session_get_props     (TrackerXesamSession *self);
+
+G_END_DECLS
+
+#endif /* __TRACKERD_XESAM_SESSION_H__ */

Added: trunk/src/trackerd/tracker-xesam.c
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-xesam.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,793 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia
+ * Authors: Philip Van Hoof (pvanhoof gnome org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libtracker-common/tracker-config.h>
+#include <libtracker-common/tracker-dbus.h>
+#include <libtracker-common/tracker-log.h>
+#include <libtracker-common/tracker-ontology.h>
+#include <libtracker-common/tracker-type-utils.h>
+#include <libtracker-common/tracker-utils.h>
+
+#include <libtracker-db/tracker-db-index.h>
+
+#include "tracker-dbus.h"
+#include "tracker-xesam.h"
+#include "tracker-status.h"
+#include "tracker-xesam-manager.h"
+#include "tracker-rdf-query.h"
+#include "tracker-query-tree.h"
+#include "tracker-marshal.h"
+
+enum {
+	XESAM_HITS_ADDED,
+	XESAM_HITS_REMOVED,
+	XESAM_HITS_MODIFIED,
+	XESAM_SEARCH_DONE,
+	XESAM_STATE_CHANGED,
+	XESAM_LAST_SIGNAL
+};
+
+static GHashTable *sessions = NULL;
+static guint	   signals[XESAM_LAST_SIGNAL] = {0};
+
+G_DEFINE_TYPE(TrackerXesam, tracker_xesam, G_TYPE_OBJECT)
+
+static void
+tracker_xesam_class_init (TrackerXesamClass *klass)
+{
+	signals[XESAM_HITS_ADDED] =
+		g_signal_new ("hits-added",
+			G_TYPE_FROM_CLASS (klass),
+			G_SIGNAL_RUN_LAST,
+			0,
+			NULL, NULL,
+			tracker_marshal_VOID__STRING_UINT,
+			G_TYPE_NONE,
+			2,
+			G_TYPE_STRING,
+			G_TYPE_UINT);
+
+	signals[XESAM_HITS_REMOVED] =
+		g_signal_new ("hits-removed",
+			G_TYPE_FROM_CLASS (klass),
+			G_SIGNAL_RUN_LAST,
+			0,
+			NULL, NULL,
+			tracker_marshal_VOID__STRING_BOXED,
+			G_TYPE_NONE,
+			2,
+			G_TYPE_STRING,
+			DBUS_TYPE_G_UINT_ARRAY);
+
+	signals[XESAM_HITS_MODIFIED] =
+		g_signal_new ("hits-modified",
+			G_TYPE_FROM_CLASS (klass),
+			G_SIGNAL_RUN_LAST,
+			0,
+			NULL, NULL,
+			tracker_marshal_VOID__STRING_BOXED,
+			G_TYPE_NONE,
+			2,
+			G_TYPE_STRING,
+			DBUS_TYPE_G_UINT_ARRAY);
+
+	signals[XESAM_SEARCH_DONE] =
+		g_signal_new ("search-done",
+			G_TYPE_FROM_CLASS (klass),
+			G_SIGNAL_RUN_LAST,
+			0,
+			NULL, NULL,
+			g_cclosure_marshal_VOID__STRING,
+			G_TYPE_NONE,
+			1,
+			G_TYPE_STRING);
+
+
+	signals[XESAM_STATE_CHANGED] =
+		g_signal_new ("state-changed",
+			G_TYPE_FROM_CLASS (klass),
+			G_SIGNAL_RUN_LAST,
+			0,
+			NULL, NULL,
+			g_cclosure_marshal_VOID__BOXED,
+			G_TYPE_NONE,
+			1,
+			G_TYPE_STRV);
+}
+
+static void
+tracker_xesam_init (TrackerXesam *object)
+{
+}
+
+TrackerXesam *
+tracker_xesam_new (void)
+{
+	return g_object_new (TRACKER_TYPE_XESAM, NULL);
+}
+
+static void
+tracker_xesam_close_session_interal (const gchar  *session_id,
+				     GError	 **error)
+{
+	TrackerXesamSession *session;
+
+	session = tracker_xesam_manager_get_session (session_id, error);
+
+	if (session) {
+		GList *searches = tracker_xesam_session_get_searches (session);
+		while (searches) {
+			TrackerXesamLiveSearch *search = searches->data;
+			tracker_xesam_live_search_close (search, NULL);
+			searches = g_list_next (searches);
+		}
+		g_list_free (searches);
+
+		tracker_xesam_manager_close_session (session_id, error);
+		g_object_unref (session);
+	}
+}
+
+static void
+my_sessions_cleanup (GList *data)
+{
+	g_list_foreach (data, (GFunc) g_free, NULL);
+	g_list_free (data);
+}
+
+void
+tracker_xesam_name_owner_changed (DBusGProxy   *proxy,
+				  const char   *name,
+				  const char   *prev_owner,
+				  const char   *new_owner,
+				  TrackerXesam *self)
+{
+	if (sessions) {
+		GList *my_sessions;
+
+		my_sessions = g_hash_table_lookup (sessions, prev_owner);
+
+		if (my_sessions) {
+			GList *copy;
+
+			copy = my_sessions;
+
+			while (copy) {
+				gchar *session_id;
+
+				session_id = copy->data;
+				tracker_xesam_close_session_interal (session_id, NULL);
+				copy = g_list_next (copy);
+			}
+
+			my_sessions_cleanup (my_sessions);
+		}
+
+		g_hash_table_remove (sessions, prev_owner);
+	}
+}
+
+/*
+ * Functions
+ */
+void
+tracker_xesam_new_session (TrackerXesam		 *object,
+			   DBusGMethodInvocation *context)
+{
+	GList	 *my_sessions;
+	GError	 *error = NULL;
+	gchar	 *session_id = NULL;
+	guint	  request_id;
+	gchar	 *key;
+	gboolean  insert = FALSE;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	key = dbus_g_method_get_sender (context);
+
+	if (!sessions)
+		sessions = g_hash_table_new_full (g_str_hash,
+						  g_str_equal,
+						  (GDestroyNotify) g_free,
+						  NULL);
+
+	my_sessions = g_hash_table_lookup (sessions, key);
+
+	if (!my_sessions)
+		insert = TRUE;
+
+	tracker_xesam_manager_create_session (object, &session_id, &error);
+
+	if (error) {
+		dbus_g_method_return_error (context, error);
+		g_error_free (error);
+	} else {
+		my_sessions = g_list_prepend (my_sessions,
+					      g_strdup (session_id));
+
+		if (insert)
+			g_hash_table_insert (sessions,
+					     g_strdup (key),
+					     my_sessions);
+		else
+			g_hash_table_replace (sessions,
+					      g_strdup (key),
+					      my_sessions);
+
+		dbus_g_method_return (context, session_id);
+
+		g_message ("Created new xesam session: %s", session_id);
+	}
+
+	g_free (session_id);
+	g_free (key);
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_xesam_close_session (TrackerXesam	   *object,
+			     const gchar	   *session_id,
+			     DBusGMethodInvocation *context)
+{
+	GError *error = NULL;
+	gchar  *key;
+	guint	request_id;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	key = dbus_g_method_get_sender (context);
+	tracker_xesam_close_session_interal (session_id, &error);
+
+	if (error) {
+		dbus_g_method_return_error (context, error);
+		g_error_free (error);
+	} else {
+		if (sessions) {
+			GList *my_sessions;
+
+			my_sessions = g_hash_table_lookup (sessions, key);
+
+			if (my_sessions) {
+				GList *found;
+
+				found = g_list_find_custom (my_sessions,
+							    session_id,
+							    (GCompareFunc) strcmp);
+
+				if (found) {
+					g_free (found->data);
+					my_sessions = g_list_delete_link (my_sessions, found);
+					g_hash_table_replace (sessions,
+							      g_strdup (key),
+							      my_sessions);
+				}
+			}
+
+			g_hash_table_remove (sessions, key);
+		}
+
+		dbus_g_method_return (context);
+	}
+
+	g_free (key);
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_xesam_set_property (TrackerXesam	  *object,
+			    const gchar		  *session_id,
+			    const gchar		  *prop,
+			    GValue		  *val,
+			    DBusGMethodInvocation *context)
+{
+	TrackerXesamSession *session;
+	GError		    *error = NULL;
+	guint		     request_id;
+
+	request_id = tracker_dbus_get_next_request_id ();
+	session = tracker_xesam_manager_get_session (session_id, &error);
+
+	if (session) {
+		GValue *new_val = NULL;
+
+		g_clear_error (&error);
+
+		tracker_xesam_session_set_property (session,
+						    prop,
+						    val,
+						    &new_val,
+						    &error);
+
+		if (error) {
+			dbus_g_method_return_error (context, error);
+			g_error_free (error);
+		} else if (new_val)
+			dbus_g_method_return (context, new_val);
+
+		if (new_val)
+			g_value_unset (new_val);
+
+	} else if (error) {
+		dbus_g_method_return_error (context, error);
+		g_error_free (error);
+	}
+
+	if (session)
+		g_object_unref (session);
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_xesam_get_property (TrackerXesam	  *object,
+			    const gchar		  *session_id,
+			    const gchar		  *prop,
+			    DBusGMethodInvocation *context)
+{
+	TrackerXesamSession *session;
+	GError		    *error = NULL;
+	guint		     request_id;
+
+	request_id = tracker_dbus_get_next_request_id ();
+	session = tracker_xesam_manager_get_session (session_id, &error);
+
+	if (session) {
+		GValue *value = NULL;
+
+		g_clear_error (&error);
+
+		tracker_xesam_session_get_property (session,
+						    prop,
+						    &value,
+						    &error);
+
+		if (error) {
+			dbus_g_method_return_error (context, error);
+			g_error_free (error);
+		} else
+			dbus_g_method_return (context, value);
+
+		if (value) {
+			g_value_unset (value);
+			g_free (value);
+		}
+
+	} else if (error) {
+		dbus_g_method_return_error (context, error);
+		g_error_free (error);
+	}
+
+	if (session)
+		g_object_unref (session);
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_xesam_new_search (TrackerXesam		*object,
+			  const gchar		*session_id,
+			  const gchar		*query_xml,
+			  DBusGMethodInvocation *context)
+{
+	TrackerXesamSession *session;
+	GError		    *error = NULL;
+	guint		     request_id;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	session = tracker_xesam_manager_get_session (session_id, &error);
+
+	if (session) {
+		TrackerXesamLiveSearch *search;
+		gchar		       *search_id = NULL;
+
+		g_clear_error (&error);
+
+		search = tracker_xesam_session_create_search (session,
+							      query_xml,
+							      &search_id,
+							      &error);
+
+		if (error) {
+			dbus_g_method_return_error (context, error);
+			g_error_free (error);
+		} else {
+			g_debug ("Created new xesam search: %s	for session: %s",
+				search_id, session_id);
+			dbus_g_method_return (context, search_id);
+		}
+
+		if (search)
+			g_object_unref (search);
+
+		g_free (search_id);
+
+	} else if (error) {
+		dbus_g_method_return_error (context, error);
+		g_error_free (error);
+	}
+
+	if (session)
+		g_object_unref (session);
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_xesam_start_search (TrackerXesam	  *object,
+			    const gchar		  *search_id,
+			    DBusGMethodInvocation *context)
+{
+	TrackerXesamLiveSearch *search;
+	GError		       *error = NULL;
+	guint			request_id;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	search = tracker_xesam_manager_get_live_search (search_id, &error);
+
+	if (search) {
+		g_clear_error (&error);
+
+		tracker_xesam_live_search_activate (search, &error);
+
+		if (error) {
+			dbus_g_method_return_error (context, error);
+			g_error_free (error);
+		} else {
+			dbus_g_method_return (context);
+		}
+	} else if (error) {
+		dbus_g_method_return_error (context, error);
+		g_error_free (error);
+	}
+
+	if (search)
+		g_object_unref (search);
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_xesam_get_hit_count (TrackerXesam	   *object,
+			     const gchar	   *search_id,
+			     DBusGMethodInvocation *context)
+{
+	TrackerXesamLiveSearch *search;
+	GError		       *error = NULL;
+	guint			request_id;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	search = tracker_xesam_manager_get_live_search (search_id, &error);
+
+	if (search) {
+		guint count = -1;
+
+		g_clear_error (&error);
+
+		tracker_xesam_live_search_get_hit_count (search, &count, &error);
+
+		if (error) {
+			dbus_g_method_return_error (context, error);
+			g_error_free (error);
+		} else {
+			dbus_g_method_return (context, count);
+		}
+	} else if (error) {
+		dbus_g_method_return_error (context, error);
+		g_error_free (error);
+	}
+
+	if (search)
+		g_object_unref (search);
+
+	tracker_dbus_request_success (request_id);
+}
+
+inline static void
+unsetvalue (gpointer data,
+	    gpointer user_data)
+{
+	g_value_unset (data);
+	g_free (data);
+}
+
+inline static void
+foreach_hits_data (gpointer hits_data,
+		   gpointer user_data)
+{
+	g_ptr_array_foreach ((GPtrArray *) hits_data, unsetvalue, NULL);
+}
+
+inline static void
+freeup_hits_data (GPtrArray *hits_data)
+{
+	g_ptr_array_foreach (hits_data, foreach_hits_data, NULL);
+}
+
+void
+tracker_xesam_get_hits (TrackerXesam	      *object,
+			const gchar	      *search_id,
+			guint		       count,
+			DBusGMethodInvocation *context)
+{
+	TrackerXesamLiveSearch *search;
+	GError		       *error = NULL;
+	guint			request_id;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	search = tracker_xesam_manager_get_live_search (search_id, &error);
+
+	if (search) {
+		GPtrArray *hits = NULL;
+
+		g_clear_error (&error);
+
+		tracker_xesam_live_search_get_hits (search,
+						    count,
+						    &hits,
+						    &error);
+
+		if (error) {
+			dbus_g_method_return_error (context, error);
+			g_error_free (error);
+		} else {
+			dbus_g_method_return (context, hits);
+		}
+
+		if (hits)
+			freeup_hits_data (hits);
+	} else if (error) {
+		dbus_g_method_return_error (context, error);
+		g_error_free (error);
+	}
+
+	if (search)
+		g_object_unref (search);
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_xesam_get_range_hits (TrackerXesam	    *object,
+			      const gchar	    *search_id,
+			      guint		     a,
+			      guint		     b,
+			      DBusGMethodInvocation *context)
+{
+	TrackerXesamLiveSearch *search;
+	GError		       *error = NULL;
+	guint			request_id;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	search = tracker_xesam_manager_get_live_search (search_id, &error);
+
+	if (search) {
+		GPtrArray *hits = NULL;
+
+		g_clear_error (&error);
+
+		tracker_xesam_live_search_get_range_hits (search,
+							  a,
+							  b,
+							  &hits,
+							  &error);
+
+		if (error) {
+			dbus_g_method_return_error (context, error);
+			g_error_free (error);
+		} else	{
+			dbus_g_method_return (context, hits);
+		}
+
+		if (hits)
+			freeup_hits_data (hits);
+	} else if (error) {
+		dbus_g_method_return_error (context, error);
+		g_error_free (error);
+	}
+
+	if (search)
+		g_object_unref (search);
+
+	tracker_dbus_request_success (request_id);
+}
+
+
+void
+tracker_xesam_get_hit_data (TrackerXesam	  *object,
+			    const gchar		  *search_id,
+			    GArray		  *hit_ids,
+			    GStrv		   fields,
+			    DBusGMethodInvocation *context)
+{
+	TrackerXesamLiveSearch *search;
+	GError		       *error = NULL;
+	guint			request_id;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	search = tracker_xesam_manager_get_live_search (search_id, &error);
+
+	if (search) {
+		GPtrArray *hit_data = NULL;
+
+		g_clear_error (&error);
+
+		tracker_xesam_live_search_get_hit_data (search,
+							hit_ids,
+							fields,
+							&hit_data,
+							&error);
+
+		if (error) {
+			dbus_g_method_return_error (context, error);
+			g_error_free (error);
+		} else {
+			dbus_g_method_return (context, hit_data);
+		}
+
+		if (hit_data)
+			freeup_hits_data (hit_data);
+	} else if (error) {
+		dbus_g_method_return_error (context, error);
+		g_error_free (error);
+	}
+
+	if (search)
+		g_object_unref (search);
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_xesam_get_range_hit_data (TrackerXesam		*object,
+				  const gchar		*search_id,
+				  guint			 a,
+				  guint			 b,
+				  GStrv			 fields,
+				  DBusGMethodInvocation *context)
+{
+	TrackerXesamLiveSearch *search;
+	GError		       *error = NULL;
+	guint			request_id;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	search = tracker_xesam_manager_get_live_search (search_id, &error);
+
+	if (search) {
+		GPtrArray *hit_data = NULL;
+
+		g_clear_error (&error);
+
+		tracker_xesam_live_search_get_range_hit_data (search,
+							      a,
+							      b,
+							      fields,
+							      &hit_data,
+							      &error);
+
+		if (error) {
+			dbus_g_method_return_error (context, error);
+			g_error_free (error);
+		} else {
+			dbus_g_method_return (context, hit_data);
+		}
+
+		if (hit_data)
+			freeup_hits_data (hit_data);
+	} else if (error) {
+		dbus_g_method_return_error (context, error);
+		g_error_free (error);
+	}
+
+	if (search)
+		g_object_unref (search);
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_xesam_close_search (TrackerXesam	  *object,
+			    const gchar		  *search_id,
+			    DBusGMethodInvocation *context)
+{
+	TrackerXesamLiveSearch *search;
+	GError		       *error = NULL;
+	guint			request_id;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	search = tracker_xesam_manager_get_live_search (search_id, &error);
+
+	if (search) {
+		g_clear_error (&error);
+
+		tracker_xesam_live_search_close (search, &error);
+
+		if (error) {
+			dbus_g_method_return_error (context, error);
+			g_error_free (error);
+		} else {
+			dbus_g_method_return (context);
+		}
+	} else if (error) {
+		dbus_g_method_return_error (context, error);
+		g_error_free (error);
+	}
+
+	if (search)
+		g_object_unref (search);
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_xesam_get_state (TrackerXesam	       *object,
+			 DBusGMethodInvocation *context)
+{
+	GStrv	     strv;
+	const gchar *state;
+	guint	     request_id;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	state = tracker_status_get_as_string ();
+	strv = tracker_string_to_string_list (state);
+
+	dbus_g_method_return (context, strv);
+
+	g_strfreev (strv);
+
+	tracker_dbus_request_success (request_id);
+}
+
+/**
+ * tracker_xesam_emit_state_changed:
+ * @self: A #TrackerXesam
+ * @state_info: (in): an array of strings that contain the state
+ *
+ * Emits the @state-changed signal on the DBus proxy for Xesam.
+ *
+ * When the state as returned by @tracker_get_state changes this @state-changed
+ * signal must be fired with an argument as described in said method. If the
+ * indexer expects to only enter the UPDATE state for a very brief period
+ * - indexing one changed file - it is not required that the @state-changed
+ * signal is fired. The signal only needs to be fired if the process of updating
+ * the index is going to be non-negligible. The purpose of this signal is not to
+ * provide exact details on the engine, just to provide hints for a user
+ * interface.
+ **/
+void
+tracker_xesam_emit_state_changed (TrackerXesam *self,
+				  GStrv		state_info)
+{
+	g_signal_emit (self, signals[XESAM_STATE_CHANGED], 0, state_info);
+}

Added: trunk/src/trackerd/tracker-xesam.h
==============================================================================
--- (empty file)
+++ trunk/src/trackerd/tracker-xesam.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,118 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia
+ * Authors: Philip Van Hoof (pvanhoof gnome org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKERD_XESAM_H__
+#define __TRACKERD_XESAM_H__
+
+#include <glib-object.h>
+
+#include <dbus/dbus-glib-bindings.h>
+
+#include <libtracker-db/tracker-db-index.h>
+
+#include "tracker-db.h"
+
+#define TRACKER_XESAM_SERVICE	      "org.freedesktop.xesam.searcher"
+#define TRACKER_XESAM_PATH	      "/org/freedesktop/xesam/searcher/main"
+#define TRACKER_XESAM_INTERFACE       "org.freedesktop.xesam.Search"
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_XESAM	      (tracker_xesam_get_type ())
+#define TRACKER_XESAM(object)	      (G_TYPE_CHECK_INSTANCE_CAST ((object), TRACKER_TYPE_XESAM, TrackerXesam))
+#define TRACKER_XESAM_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), TRACKER_TYPE_XESAM, TrackerXesamClass))
+#define TRACKER_IS_XESAM(object)      (G_TYPE_CHECK_INSTANCE_TYPE ((object), TRACKER_TYPE_XESAM))
+#define TRACKER_IS_XESAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TRACKER_TYPE_XESAM))
+#define TRACKER_XESAM_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), TRACKER_TYPE_XESAM, TrackerXesamClass))
+
+typedef struct TrackerXesam	 TrackerXesam;
+typedef struct TrackerXesamClass TrackerXesamClass;
+
+struct TrackerXesam {
+	GObject parent;
+};
+
+struct TrackerXesamClass {
+	GObjectClass parent;
+};
+
+GType	      tracker_xesam_get_type	       (void);
+TrackerXesam *tracker_xesam_new		       (void);
+void	      tracker_xesam_new_session        (TrackerXesam	      *object,
+						DBusGMethodInvocation *context);
+void	      tracker_xesam_set_property       (TrackerXesam	      *object,
+						const gchar	      *session_id,
+						const gchar	      *prop,
+						GValue		      *val,
+						DBusGMethodInvocation *context);
+void	      tracker_xesam_get_property       (TrackerXesam	      *object,
+						const gchar	      *session_id,
+						const gchar	      *prop,
+						DBusGMethodInvocation *context);
+void	      tracker_xesam_close_session      (TrackerXesam	      *object,
+						const gchar	      *session_id,
+						DBusGMethodInvocation *context);
+void	      tracker_xesam_new_search	       (TrackerXesam	      *object,
+						const gchar	      *session_id,
+						const gchar	      *query_xml,
+						DBusGMethodInvocation *context);
+void	      tracker_xesam_start_search       (TrackerXesam	      *object,
+						const gchar	      *search_id,
+						DBusGMethodInvocation *context);
+void	      tracker_xesam_get_hit_count      (TrackerXesam	      *object,
+						const gchar	      *search_id,
+						DBusGMethodInvocation *context);
+void	      tracker_xesam_get_hits	       (TrackerXesam	      *object,
+						const gchar	      *search_id,
+						guint		       count,
+						DBusGMethodInvocation *context);
+void	      tracker_xesam_get_range_hits     (TrackerXesam	      *object,
+						const gchar	      *search_id,
+						guint		       a,
+						guint		       b,
+						DBusGMethodInvocation *context);
+void	      tracker_xesam_get_hit_data       (TrackerXesam	      *object,
+						const gchar	      *search_id,
+						GArray		      *hit_ids,
+						GStrv		       fields,
+						DBusGMethodInvocation *context);
+void	      tracker_xesam_get_range_hit_data (TrackerXesam	      *object,
+						const gchar	      *search_id,
+						guint		       a,
+						guint		       b,
+						GStrv		       fields,
+						DBusGMethodInvocation *context);
+void	      tracker_xesam_close_search       (TrackerXesam	      *object,
+						const gchar	      *search_id,
+						DBusGMethodInvocation *context);
+void	      tracker_xesam_get_state	       (TrackerXesam	      *object,
+						DBusGMethodInvocation *context);
+void	      tracker_xesam_emit_state_changed (TrackerXesam	      *self,
+						GStrv		       state_info);
+void	      tracker_xesam_name_owner_changed (DBusGProxy	      *proxy,
+						const char	      *name,
+						const char	      *prev_owner,
+						const char	      *new_owner,
+						TrackerXesam	      *self);
+
+G_END_DECLS
+
+#endif /* __TRACKERD_XESAM_H__ */

Added: trunk/tests/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/tests/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,14 @@
+include $(top_srcdir)/Makefile.decl
+
+if ENABLE_SQLITE_FTS
+build_sqlite_fts = tracker-fts
+endif
+
+SUBDIRS = common 			\
+	libtracker-common		\
+	libtracker-db 			\
+	trackerd 			\
+	scripts 			\
+	$(build_sqlite_fts)		\
+	tracker-indexer
+

Added: trunk/tests/common/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/tests/common/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,16 @@
+include $(top_srcdir)/Makefile.decl
+
+noinst_LTLIBRARIES = libtracker-testcommon.la
+
+INCLUDES = 				\
+	-DTEST_TEXT=\""$(top_srcdir)"/tests/libtracker-common/non-utf8.txt\"	\
+	-I.				\
+	$(GLIB2_CFLAGS)
+
+libtracker_testcommon_la_SOURCES = 	\
+	tracker-test-helpers.c 		\
+	tracker-test-helpers.h
+
+libtracker_testcommon_la_LIBDADD = 	\
+	$(GLIB2_LIBS)
+

Added: trunk/tests/common/tracker-test-helpers.c
==============================================================================
--- (empty file)
+++ trunk/tests/common/tracker-test-helpers.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,68 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+#include "tracker-test-helpers.h"
+
+static gchar *nonutf8_str = NULL;
+
+gboolean
+tracker_test_helpers_cmpstr_equal (const gchar *obtained,
+				   const gchar *expected)
+{
+	gboolean result;
+
+	if (expected == obtained) {
+		return TRUE;
+	}
+
+	if (expected && obtained) {
+		result = !g_utf8_collate (expected, obtained);
+		if (!result) {
+			g_warning ("Expected %s - obtained %s", expected, obtained);
+		}
+		return result;
+	} else {
+		g_warning ("\n Only one of the strings is NULL\n");
+		return FALSE;
+	}
+}
+
+const gchar *
+tracker_test_helpers_get_nonutf8 (void)
+{
+	GMappedFile *file = NULL;
+
+	if (!nonutf8_str) {
+		file = g_mapped_file_new (TEST_TEXT, FALSE, NULL);
+		nonutf8_str = g_strdup (g_mapped_file_get_contents (file));
+		nonutf8_str [g_mapped_file_get_length (file) -1] = '\0';
+		g_mapped_file_free (file);
+	}
+
+	return nonutf8_str;
+}
+
+void
+tracker_test_helpers_free_nonutf8 (void)
+{
+	if (nonutf8_str) {
+		g_free (nonutf8_str);
+		nonutf8_str = NULL;
+	}
+}

Added: trunk/tests/common/tracker-test-helpers.h
==============================================================================
--- (empty file)
+++ trunk/tests/common/tracker-test-helpers.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,34 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+#ifndef __TRACKER_TEST_HELPERS_H__
+#define __TRACKER_TEST_HELPERS_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+gboolean     tracker_test_helpers_cmpstr_equal (const gchar *obtained,
+						const gchar *expected);
+const gchar *tracker_test_helpers_get_nonutf8  (void);
+void	     tracker_test_helpers_free_nonutf8 (void);
+
+G_END_DECLS
+
+#endif /* __TRACKER_TEST_HELPERS_H__ */

Added: trunk/tests/libtracker-common/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/tests/libtracker-common/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,97 @@
+include $(top_srcdir)/Makefile.decl
+
+noinst_PROGRAMS = $(TEST_PROGS)
+
+#
+# NOTE: Some of these tests have been disabled (by Martyn) temporarily
+# 	because make distcheck fails. The reason for this is that it
+# 	depends files being installed to $prefix/share which have not
+# 	been installed at this point.
+# 
+#       These tests include:
+#
+#         tracker-parser
+#	  tracker-field
+#
+
+TEST_PROGS += 								\
+	tracker-ontology 						\
+	tracker-dbus 							\
+	tracker-type-utils 						\
+	tracker-file-utils
+
+INCLUDES = 								\
+	-DG_LOG_DOMAIN=\"Tracker\"					\
+	-I$(top_srcdir)/src						\
+	-I$(top_srcdir)/tests/common					\
+	$(DBUS_CFLAGS)							\
+	$(GMODULE_CFLAGS)						\
+	$(GTHREAD_CFLAGS)						\
+	$(PANGO_CFLAGS)							\
+	$(GLIB2_CFLAGS)
+
+tracker_ontology_SOURCES = 						\
+	tracker-ontology-test.c 
+
+tracker_ontology_LDADD =						\
+	$(top_builddir)/src/libtracker-common/libtracker-common.la 	\
+	$(top_builddir)/tests/common/libtracker-testcommon.la 		\
+	$(GMODULE_LIBS)							\
+	$(GTHREAD_LIBS)							\
+	$(GLIB2_LIBS)							
+
+tracker_dbus_SOURCES = \
+	tracker-dbus-test.c
+
+tracker_dbus_LDADD =							\
+	$(top_builddir)/src/libtracker-common/libtracker-common.la 	\
+	$(top_builddir)/tests/common/libtracker-testcommon.la 		\
+	$(DBUS_LIBS)							\
+	$(GMODULE_LIBS)							\
+	$(GTHREAD_LIBS)							\
+	$(GLIB2_LIBS)							
+
+tracker_type_utils_SOURCES = \
+	tracker-type-utils-test.c 
+
+tracker_type_utils_LDADD =						\
+	$(top_builddir)/src/libtracker-common/libtracker-common.la 	\
+	$(top_builddir)/tests/common/libtracker-testcommon.la 		\
+	$(GMODULE_LIBS)							\
+	$(GTHREAD_LIBS)							\
+	$(GLIB2_LIBS)							
+
+tracker_file_utils_SOURCES = 						\
+	tracker-file-utils-test.c 
+
+tracker_file_utils_LDADD =						\
+	$(top_builddir)/src/libtracker-common/libtracker-common.la 	\
+	$(top_builddir)/tests/common/libtracker-testcommon.la 		\
+	$(GMODULE_LIBS)							\
+	$(GTHREAD_LIBS)							\
+	$(GLIB2_LIBS)							
+
+# tracker_parser_SOURCES = 						\
+# 	tracker-parser-test.c 
+#
+# tracker_parser_LDADD =						\
+# 	$(top_builddir)/src/libtracker-common/libtracker-common.la 	\
+# 	$(top_builddir)/tests/common/libtracker-testcommon.la 		\
+# 	$(top_builddir)/src/libstemmer/libstemmer.la	 		\
+# 	$(GMODULE_LIBS)							\
+# 	$(GTHREAD_LIBS)							\
+# 	$(PANGO_LIBS)							\
+# 	$(GLIB2_LIBS)							
+#
+# tracker_field_SOURCES = 						\
+# 	tracker-field-test.c 
+#
+# tracker_field_LDADD =							\
+# 	$(top_builddir)/src/libtracker-common/libtracker-common.la 	\
+# 	$(top_builddir)/tests/common/libtracker-testcommon.la 		\
+# 	$(GMODULE_LIBS)							\
+# 	$(GTHREAD_LIBS)							\
+# 	$(PANGO_LIBS)							\
+# 	$(GLIB2_LIBS)							
+
+EXTRA_DIST = non-utf8.txt

Added: trunk/tests/libtracker-common/non-utf8.txt
==============================================================================
--- (empty file)
+++ trunk/tests/libtracker-common/non-utf8.txt	Fri Sep 26 10:47:33 2008
@@ -0,0 +1 @@
+/invalid/file/ä90808.

Added: trunk/tests/libtracker-common/tracker-dbus-test.c
==============================================================================
--- (empty file)
+++ trunk/tests/libtracker-common/tracker-dbus-test.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,187 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+#include <glib.h>
+#include <glib/gtestutils.h>
+#include <libtracker-common/tracker-dbus.h>
+#include <tracker-test-helpers.h>
+
+
+static void
+slist_to_strv (gboolean utf8)
+{
+	GSList *input = NULL;
+	gint	i;
+	gchar **input_as_strv;
+	gint	strings = 5;
+
+	for (i = 0; i < strings; i++) {
+		if (utf8) {
+			input = g_slist_prepend (input, g_strdup_printf ("%d", i));
+		} else {
+			input = g_slist_prepend (input, g_strdup (tracker_test_helpers_get_nonutf8 ()));
+		}
+	}
+	g_assert_cmpint (g_slist_length (input), ==, strings);
+
+	input_as_strv = tracker_dbus_slist_to_strv (input);
+
+	g_assert_cmpint (g_strv_length (input_as_strv), ==, (utf8 ? strings : 0));
+
+	g_slist_foreach (input, (GFunc)g_free, NULL);
+	g_slist_free (input);
+
+	g_strfreev (input_as_strv);
+}
+
+static void
+test_slist_to_strv (void)
+{
+	slist_to_strv (TRUE);
+}
+
+static void
+test_slist_to_strv_nonutf8 (void)
+{
+	slist_to_strv (FALSE);
+}
+
+static void
+async_queue_to_strv (gboolean utf8)
+{
+	GQueue *queue;
+	gint i;
+	gchar **queue_as_strv;
+	gint strings = 5;
+
+	queue = g_queue_new ();
+
+	for (i = 0; i < strings; i++) {
+		if (utf8) {
+			g_queue_push_tail (queue, g_strdup_printf ("%d", i));
+		} else {
+			g_queue_push_tail (queue, g_strdup (tracker_test_helpers_get_nonutf8 ()));
+		}
+	}
+	g_assert_cmpint (g_queue_get_length (queue), ==, strings);
+
+	queue_as_strv = tracker_dbus_queue_str_to_strv (queue, g_queue_get_length (queue));
+
+	g_assert_cmpint (g_strv_length (queue_as_strv), ==, (utf8 ? strings : 0));
+
+	// Queue empty by tracker_dbus_async_queue_to_strv
+	g_queue_free (queue);
+	g_strfreev (queue_as_strv);
+
+}
+
+
+static void
+test_async_queue_to_strv (void)
+{
+	async_queue_to_strv (TRUE);
+}
+
+static void
+test_async_queue_to_strv_nonutf8 (void)
+{
+	async_queue_to_strv (FALSE);
+}
+
+static void
+test_results_ptr_array_free (void)
+{
+	GPtrArray *array = NULL;
+
+	/* NULL */
+	tracker_dbus_results_ptr_array_free (&array);
+
+	/* Empty */
+	array = g_ptr_array_new ();
+	g_assert (array != NULL);
+
+	tracker_dbus_results_ptr_array_free (&array);
+	g_assert (array == NULL);
+
+	/* With contents */
+	array = g_ptr_array_new ();
+	g_ptr_array_add (array, g_strsplit ("one two three", " ", -1));
+
+	tracker_dbus_results_ptr_array_free (&array);
+	g_assert (array == NULL);
+}
+
+static void
+test_dbus_request_failed (void)
+{
+	GError *error = NULL;
+
+	/* Default case: we set the error */
+	if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR)) {
+		tracker_dbus_request_failed (1, &error, "Test Error message");
+	}
+	g_test_trap_assert_stderr ("*Test Error message*");
+
+	/* Second common case: we have already the error and want only the log line */
+	error = g_error_new (1000, -1, "The indexer founded an error");
+	if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR)) {
+		tracker_dbus_request_failed (1, &error, NULL);
+	}
+	g_test_trap_assert_stderr ("*The indexer founded an error*");
+	g_error_free (error);
+
+
+	/* Wrong use: error set and we add a new message */
+	error = g_error_new (1000, -1, "The indexer founded an error");
+	if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR)) {
+		tracker_dbus_request_failed (1, &error, "Dont do this");
+	}
+	g_test_trap_assert_stderr ("*GError set over the top of a previous GError or uninitialized memory*");
+	g_error_free (error);
+
+	error = NULL;
+	/* Wrong use: no error, no message */
+	if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR)) {
+		tracker_dbus_request_failed (1, &error, NULL);
+	}
+	g_test_trap_assert_stderr ("*Unset error and no error message*");
+}
+
+int
+main (int argc, char **argv) {
+
+	int result;
+
+	g_type_init ();
+	g_thread_init (NULL);
+	g_test_init (&argc, &argv, NULL);
+
+	g_test_add_func ("/libtracker-common/tracker-dbus/slist_to_strv_ok", test_slist_to_strv);
+	g_test_add_func ("/libtracker-common/tracker-dbus/slist_to_strv_nonutf8", test_slist_to_strv_nonutf8);
+	g_test_add_func ("/libtracker-common/tracker-dbus/async_queue_to_strv_ok", test_async_queue_to_strv);
+	g_test_add_func ("/libtracker-common/tracker-dbus/async_queue_to_strv_nonutf8", test_async_queue_to_strv_nonutf8);
+	g_test_add_func ("/libtracker-common/tracker-dbus/free_ptr_array", test_results_ptr_array_free);
+	g_test_add_func ("/libtracker-common/tracker-dbus/dbus_request_failed", test_dbus_request_failed);
+
+	result = g_test_run ();
+
+	tracker_test_helpers_free_nonutf8 ();
+
+	return result;
+}

Added: trunk/tests/libtracker-common/tracker-field-test.c
==============================================================================
--- (empty file)
+++ trunk/tests/libtracker-common/tracker-field-test.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,76 @@
+#include <glib.h>
+#include <glib/gtestutils.h>
+#include <libtracker-common/tracker-field.h>
+#include <tracker-test-helpers.h>
+
+static void
+test_type_to_string ()
+{
+	const gchar *result;
+	TrackerFieldType type;
+
+	type = TRACKER_FIELD_TYPE_KEYWORD;
+	result = tracker_field_type_to_string (type);
+	g_assert (tracker_test_helpers_cmpstr_equal (result, "keyword"));
+
+	type = TRACKER_FIELD_TYPE_INDEX;
+	result = tracker_field_type_to_string (type);
+	g_assert (tracker_test_helpers_cmpstr_equal (result, "index"));
+
+	type = TRACKER_FIELD_TYPE_FULLTEXT;
+	result = tracker_field_type_to_string (type);
+	g_assert (tracker_test_helpers_cmpstr_equal (result, "fulltext"));
+
+	type = TRACKER_FIELD_TYPE_STRING;
+	result = tracker_field_type_to_string (type);
+	g_assert (tracker_test_helpers_cmpstr_equal (result, "string"));
+
+	type = TRACKER_FIELD_TYPE_INTEGER;
+	result = tracker_field_type_to_string (type);
+	g_assert (tracker_test_helpers_cmpstr_equal (result, "integer"));
+
+	type = TRACKER_FIELD_TYPE_DOUBLE;
+	result = tracker_field_type_to_string (type);
+	g_assert (tracker_test_helpers_cmpstr_equal (result, "double"));
+
+	type = TRACKER_FIELD_TYPE_DATE;
+	result = tracker_field_type_to_string (type);
+	g_assert (tracker_test_helpers_cmpstr_equal (result, "date"));
+
+	type =	TRACKER_FIELD_TYPE_BLOB;
+	result = tracker_field_type_to_string (type);
+	g_assert (tracker_test_helpers_cmpstr_equal (result, "blob"));
+
+	type =	TRACKER_FIELD_TYPE_STRUCT;
+	result = tracker_field_type_to_string (type);
+	g_assert (tracker_test_helpers_cmpstr_equal (result, "struct"));
+
+	type =	TRACKER_FIELD_TYPE_LINK;
+	result = tracker_field_type_to_string (type);
+	g_assert (tracker_test_helpers_cmpstr_equal (result, "link"));
+
+}
+
+int
+main (int argc, char **argv) {
+
+	int result;
+
+	g_type_init ();
+	g_test_init (&argc, &argv, NULL);
+
+	/* Something is not initialized without these lines */
+	TrackerField *field = tracker_field_new ();
+	g_object_unref (field);
+
+	/* Init */
+
+	g_test_add_func ("/libtracker-common/tracker-field/type_to_string",
+			 test_type_to_string);
+
+	result = g_test_run ();
+
+	/* End */
+
+	return result;
+}

Added: trunk/tests/libtracker-common/tracker-file-utils-test.c
==============================================================================
--- (empty file)
+++ trunk/tests/libtracker-common/tracker-file-utils-test.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,259 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gtestutils.h>
+#include <gio/gio.h>
+
+#include <libtracker-common/tracker-file-utils.h>
+
+#include <tracker-test-helpers.h>
+
+static GSList *
+array_as_list (gchar **array)
+{
+	gint i;
+	GSList *result = NULL;
+
+	for (i = 0; array[i] != NULL; i++) {
+		result = g_slist_prepend (result, g_strdup(array[i]));
+
+	}
+
+	return result;
+}
+
+static gboolean
+string_in_list (GSList *list, const gchar *string)
+{
+	GSList *it;
+	for ( it = list; it != NULL; it = it->next) {
+		if (strcmp (it->data, string) == 0) {
+			return TRUE;
+		}
+	}
+	return FALSE;
+}
+
+static void
+test_path_list_filter_duplicates (void)
+{
+	gchar *input_roots [] = {"/home", "/home/ivan", "/tmp", "/usr/", "/usr/share/local", NULL};
+
+	GSList *input_as_list = NULL;
+	GSList *result;
+
+	input_as_list = array_as_list (input_roots);
+
+
+	result = tracker_path_list_filter_duplicates (input_as_list);
+	g_assert_cmpint (3, ==, g_slist_length (result));
+
+	g_assert (string_in_list (result, "/home"));
+	g_assert (string_in_list (result, "/tmp"));
+	g_assert (string_in_list (result, "/usr"));
+
+	g_slist_foreach (input_as_list, (GFunc) g_free, NULL);
+}
+
+static void
+test_path_evaluate_name (void)
+{
+	gchar *result, *expected;
+
+	const gchar *home = g_getenv ("HOME");
+	const gchar *pwd = g_getenv ("PWD");
+
+	const gchar *test = "/one/two";
+	gchar *parent_dir;
+
+	g_setenv ("TEST_TRACKER_DIR", test, TRUE);
+
+
+	result = tracker_path_evaluate_name ("/home/user/all/ok");
+	tracker_test_helpers_cmpstr_equal (result, "/home/user/all/ok");
+	g_free (result);
+
+	/* The result of this test and the next one are not consistent!
+	 * Must it remove the end '/' or not?
+	 */
+	result = tracker_path_evaluate_name ("/home/user/all/dir/");
+	tracker_test_helpers_cmpstr_equal (result, "/home/user/all/dir");
+	g_free (result);
+
+
+	/*
+	 * TODO: In valgrind this test shows a memory leak
+	 */
+	result = tracker_path_evaluate_name ("~/all/dir/");
+	expected = g_build_path (G_DIR_SEPARATOR_S, home, "/all/dir/", NULL);
+	tracker_test_helpers_cmpstr_equal (result, expected);
+
+	g_free (result);
+	g_free (expected);
+
+	result = tracker_path_evaluate_name ("$HOME/all/dir/");
+	expected = g_build_path (G_DIR_SEPARATOR_S, home, "/all/dir", NULL);
+	tracker_test_helpers_cmpstr_equal (result, expected);
+
+	g_free (result);
+	g_free (expected);
+
+	result = tracker_path_evaluate_name ("${HOME}/all/dir/");
+	expected = g_build_path (G_DIR_SEPARATOR_S, home, "/all/dir", NULL);
+	tracker_test_helpers_cmpstr_equal (result, expected);
+
+	g_free (result);
+	g_free (expected);
+
+	result = tracker_path_evaluate_name ("./test/current/dir");
+	expected = g_build_path (G_DIR_SEPARATOR_S, pwd, "/test/current/dir", NULL);
+	tracker_test_helpers_cmpstr_equal (result, expected);
+
+	g_free (result);
+	g_free (expected);
+
+	result = tracker_path_evaluate_name ("$TEST_TRACKER_DIR/test/dir");
+	expected = g_build_path (G_DIR_SEPARATOR_S, test, "/test/dir", NULL);
+	tracker_test_helpers_cmpstr_equal (result, expected);
+
+	g_free (result);
+	g_free (expected);
+
+	result = tracker_path_evaluate_name ("../test/dir");
+	parent_dir = g_path_get_dirname (pwd);
+	expected = g_build_path (G_DIR_SEPARATOR_S, parent_dir, "/test/dir", NULL);
+	tracker_test_helpers_cmpstr_equal (result, expected);
+
+	g_free (result);
+	g_free (parent_dir);
+	g_free (expected);
+
+	result = tracker_path_evaluate_name ("");
+	g_assert (!result);
+
+	result = tracker_path_evaluate_name (NULL);
+	g_assert (!result);
+
+	result = tracker_path_evaluate_name (tracker_test_helpers_get_nonutf8 ());
+	tracker_test_helpers_cmpstr_equal (result,
+					   tracker_test_helpers_get_nonutf8 ());
+
+	g_unsetenv ("TEST_TRACKER_DIR");
+}
+
+
+static void
+test_file_get_mime_type (void)
+{
+	gchar *dir_name, *result;
+	GFile *dir;
+
+	/* Create test directory */
+	dir_name = g_build_filename (g_get_tmp_dir (), "tracker-test", NULL);
+	dir = g_file_new_for_path (dir_name);
+	g_file_make_directory (dir, NULL, NULL);
+
+	result = tracker_file_get_mime_type (dir_name);
+
+	g_assert (tracker_test_helpers_cmpstr_equal (result, "inode/directory"));
+
+	/* Remove test directory */
+	g_file_delete (dir, NULL, NULL);
+	g_object_unref (dir);
+	g_free (dir_name);
+}
+
+static void
+test_file_get_path_and_name ()
+{
+
+	gchar *name = NULL;
+	gchar *path = NULL;
+
+	tracker_file_get_path_and_name ("/home/ivan/test/file.txt",
+					&path,
+					&name);
+
+	g_assert_cmpint (g_strcmp0 (name, "file.txt"), ==, 0);
+	g_assert_cmpint (g_strcmp0 (path, "/home/ivan/test"), ==, 0);
+
+	g_free (name);
+	g_free (path);
+	name = NULL;
+	path = NULL;
+
+	tracker_file_get_path_and_name ("/home/ivan//test/file.txt",
+					&path,
+					&name);
+
+	g_assert_cmpint (g_strcmp0 (name, "file.txt"), ==, 0);
+	g_assert_cmpint (g_strcmp0 (path, "/home/ivan/test"), ==, 0);
+
+	g_free (name);
+	g_free (path);
+	name = NULL;
+	path = NULL;
+/*
+ *	TODO: Fix this case
+ *
+	tracker_file_get_path_and_name ("file:///home/ivan//test/file.txt",
+					&path,
+					&name);
+
+	g_assert_cmpint (g_strcmp0 (name, "file.txt"), ==, 0);
+	g_print ("%s\n", path);
+	g_assert_cmpint (g_strcmp0 (path, "file:///home/ivan/test"), ==, 0);
+
+	g_free (name);
+	g_free (path);
+	name = NULL;
+	path = NULL;
+*/
+
+}
+
+int
+main (int argc, char **argv)
+{
+	int result;
+
+	g_type_init ();
+	g_test_init (&argc, &argv, NULL);
+
+
+	g_test_add_func ("/tracker/libtracker-common/tracker-file-utils/path_evaluate_name",
+			 test_path_evaluate_name);
+
+	g_test_add_func ("/tracker/libtracker-common/tracker-file-utils/path_list_filter_duplicates",
+			 test_path_list_filter_duplicates);
+
+	g_test_add_func ("/tracker/libtracker-common/tracker-file-utils/file_get_mime_type",
+			 test_file_get_mime_type);
+
+	g_test_add_func ("/libtracker_common/tracker-file-utils/file_get_path_and_name",
+			 test_file_get_path_and_name);
+
+	result = g_test_run ();
+
+	return result;
+}

Added: trunk/tests/libtracker-common/tracker-ontology-test.c
==============================================================================
--- (empty file)
+++ trunk/tests/libtracker-common/tracker-ontology-test.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,437 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gtestutils.h>
+
+#include <libtracker-common/tracker-service.h>
+#include <libtracker-common/tracker-field.h>
+#include <libtracker-common/tracker-ontology.h>
+
+#include <tracker-test-helpers.h>
+
+static gboolean
+test_cmp_servicedef_equals (TrackerService *one,
+			    TrackerService *two)
+{
+	if (one && !two) {
+		return FALSE;
+	}
+
+	if (!one && two) {
+		return FALSE;
+	}
+
+	if (!one && one == two) {
+		return TRUE;
+	}
+
+	return (tracker_service_get_id (one) == tracker_service_get_id (two) &&
+		tracker_test_helpers_cmpstr_equal (tracker_service_get_name (one),
+						   tracker_service_get_name (two)) &&
+		tracker_test_helpers_cmpstr_equal (tracker_service_get_parent (one),
+						   tracker_service_get_parent (two)) &&
+		tracker_service_get_db_type (one) == tracker_service_get_db_type (two) &&
+		tracker_service_get_embedded (one) == tracker_service_get_embedded (two));
+}
+
+static gboolean
+element_in_list (GSList *list, gchar *element)
+{
+	return (g_slist_find_custom (list, element, (GCompareFunc) strcmp) != NULL);
+}
+
+static GSList *
+array_to_list (char **array)
+{
+	GSList	*list = NULL;
+	int	i;
+
+	for (i = 0; array[i] != NULL; i++) {
+		list = g_slist_prepend (list, g_strdup (array[i]));
+	}
+
+	return list;
+}
+
+static TrackerField *
+create_field_definition (const gchar *id,
+			 const gchar *name,
+			 TrackerFieldType data_type,
+			 const gchar *field_name,
+			 gboolean multiple_values,
+			 GSList *child_ids)
+{
+	TrackerField *field;
+
+	field = tracker_field_new ();
+
+	tracker_field_set_id (field, id);
+	tracker_field_set_name (field, name);
+	tracker_field_set_data_type (field, data_type);
+	tracker_field_set_field_name (field, field_name);
+	tracker_field_set_multiple_values (field, multiple_values);
+	tracker_field_set_child_ids (field, child_ids);
+
+	return field;
+}
+
+static TrackerService *
+create_service_definition (gint		id,
+			   const gchar *name,
+			   const gchar *parent,
+			   const gchar *prefix,
+			   gboolean	embedded)
+{
+	TrackerService *def;
+	/* array_to_list use prepend, so use reverse order here  */
+	gchar *key_metadata [] = {
+		"Key:Metadata2",
+		"Key:MetaData1",
+		NULL
+	};
+
+	def = tracker_service_new ();
+	tracker_service_set_id (def, id);
+	tracker_service_set_name (def, name);
+	tracker_service_set_parent (def, parent);
+	tracker_service_set_property_prefix (def, prefix);
+	tracker_service_set_db_type (def, TRACKER_DB_TYPE_CONTENT);
+	tracker_service_set_enabled (def, FALSE);
+	tracker_service_set_embedded (def, embedded);
+	tracker_service_set_has_thumbs (def, TRUE);
+	tracker_service_set_has_full_text (def, TRUE);
+	tracker_service_set_has_metadata (def, FALSE);
+	tracker_service_set_key_metadata (def, array_to_list (key_metadata));
+
+	return def;
+}
+
+typedef struct {
+	TrackerService *def;
+	TrackerService *parent_def;
+} ExpectedResults;
+
+static ExpectedResults *expected_results = NULL;
+
+static void
+tracker_services_general_setup ()
+{
+	TrackerService *def, *parent_def, *other_def;
+	TrackerService *conv_def, *gaim_def, *gossip_def, *new_gaim_def;
+	TrackerField *field_title;
+
+	GSList *mimes, *mime_prefixes;
+
+	def = create_service_definition (0, "Test service", "Parent service", NULL, TRUE);
+	parent_def = create_service_definition (1, "Parent service", NULL, NULL, FALSE);
+	other_def = create_service_definition (2, "Applications", NULL, "App", FALSE);
+	conv_def = create_service_definition (3, "Conversations", NULL, NULL, FALSE);
+	gaim_def = create_service_definition (4, "GaimConversations", "Conversations", NULL, FALSE);
+	gossip_def = create_service_definition (5, "GossipConversations", "Conversations", NULL, FALSE);
+	new_gaim_def = create_service_definition (6, "NewGaimConversations", "GaimConversations", NULL, FALSE);
+
+	field_title = create_field_definition ("0",
+					       "App.Title",
+					       TRACKER_FIELD_TYPE_INDEX,
+					       "Title",
+					       TRUE,
+					       NULL);
+
+	char * m[] = {"application/rtf", "text/joke", "test/1", NULL};
+	mimes = array_to_list (m);
+
+	char *mp[] = {"images/", "video/", "other.mimes.", NULL};
+	mime_prefixes = array_to_list (mp);
+	tracker_ontology_init ();
+
+	expected_results = g_new0 (ExpectedResults, 1);
+	expected_results->def = def;
+	expected_results->parent_def = parent_def;
+
+	tracker_ontology_service_add (def, NULL, NULL);
+	tracker_ontology_service_add (parent_def, mimes, mime_prefixes);
+	tracker_ontology_service_add (other_def, NULL, NULL);
+	tracker_ontology_service_add (conv_def, NULL, NULL);
+	tracker_ontology_service_add (gaim_def, NULL, NULL);
+	tracker_ontology_service_add (gossip_def, NULL, NULL);
+	tracker_ontology_service_add (new_gaim_def, NULL, NULL);
+
+	tracker_ontology_field_add (field_title);
+
+	g_slist_free (mimes);
+	g_slist_free (mime_prefixes);
+
+}
+
+static void
+test_get_id_for_service (void)
+{
+	gint result_int;
+
+	result_int = tracker_ontology_get_service_id_by_name ("Test service");
+	g_assert_cmpint (result_int, ==, 0);
+	result_int = tracker_ontology_get_service_id_by_name ("trash");
+	g_assert_cmpint (result_int, ==, -1);
+}
+
+
+static void
+test_get_service_by_id (void)
+{
+	gchar *result_string;
+
+	result_string = tracker_ontology_get_service_by_id (0);
+	g_assert ( g_str_equal (result_string, "Test service"));
+	g_free (result_string);
+	result_string = tracker_ontology_get_service_by_id (20);
+	g_assert (!result_string);
+}
+
+
+static void
+test_get_parent_service_by_id (void)
+{
+	gchar *result_string;
+
+	result_string = tracker_ontology_get_service_parent_by_id (0);
+	g_assert ( g_str_equal (result_string, "Parent service"));
+	g_free (result_string);
+	result_string = tracker_ontology_get_service_parent_by_id (1);
+	g_assert (!result_string);
+}
+
+static void
+test_get_parent_id_for_service_id (void)
+{
+	gint result_int;
+
+	result_int = tracker_ontology_get_service_parent_id_by_id (0);
+	g_assert_cmpint (result_int, ==, 1);
+	result_int = tracker_ontology_get_service_parent_id_by_id (1);
+	g_assert_cmpint (result_int, ==, -1);
+}
+
+static void
+test_get_parent_service (void)
+{
+	gchar *result_string;
+
+	result_string = tracker_ontology_get_service_parent ("Test service");
+	g_assert (g_str_equal (result_string, "Parent service"));
+	g_free (result_string);
+	result_string = tracker_ontology_get_service_parent ("Parent service");
+	g_assert (!result_string);
+}
+
+static void
+test_get_service_type_for_mime (void)
+{
+	gchar *value;
+
+	value = tracker_ontology_get_service_by_mime ("application/rtf");
+	g_assert ( g_str_equal ("Parent service", value));
+	g_free (value);
+
+	value = tracker_ontology_get_service_by_mime ("images/jpeg");
+	g_assert ( g_str_equal ("Parent service", value));
+	g_free (value);
+
+	value = tracker_ontology_get_service_by_mime ("noexists/bla");
+	g_assert ( g_str_equal ("Other", value));
+	g_free (value);
+}
+
+static void
+test_get_service (void)
+{
+	TrackerService *result_def;
+
+	result_def = tracker_ontology_get_service_by_name ("Test service");
+	g_assert (test_cmp_servicedef_equals (result_def, expected_results->def));
+	result_def = tracker_ontology_get_service_by_name ("No no no");
+	g_assert (!test_cmp_servicedef_equals (result_def, expected_results->def));
+	result_def = tracker_ontology_get_service_by_name ("Parent service");
+	g_assert (test_cmp_servicedef_equals (result_def, expected_results->parent_def));
+}
+
+static void
+test_get_db_for_service (void)
+{
+	TrackerDBType result_db;
+
+	result_db = tracker_ontology_get_service_db_by_name ("Test service");
+	g_assert (result_db == TRACKER_DB_TYPE_FILES); // ????? HARDCODED IN tracker-ontology!!!!!
+	result_db = tracker_ontology_get_service_db_by_name ("trash");
+	g_assert (result_db == TRACKER_DB_TYPE_FILES);
+}
+
+static void
+test_is_service_embedded (void)
+{
+	g_assert (tracker_ontology_service_has_embedded ("Test service"));
+	g_assert (!tracker_ontology_service_has_embedded ("Parent service"));
+	g_assert (!tracker_ontology_service_has_embedded ("Trash"));
+}
+
+static void
+test_has_thumbnails (void)
+{
+	g_assert (tracker_ontology_service_has_thumbnails ("Test service"));
+	g_assert (!tracker_ontology_service_has_thumbnails ("trash"));
+}
+
+static void
+test_has_text (void)
+{
+	g_assert (tracker_ontology_service_has_text ("Test service"));
+	g_assert (!tracker_ontology_service_has_text ("trash"));
+}
+
+static void
+test_has_metadata (void)
+{
+	g_assert (!tracker_ontology_service_has_metadata ("Test service"));
+	g_assert (!tracker_ontology_service_has_metadata ("trash"));
+}
+
+static void
+test_field_in_ontology (void)
+{
+	TrackerField *field;
+
+	field = tracker_ontology_get_field_by_name ("App.Title");
+	g_assert (field);
+	g_assert (!tracker_ontology_get_field_by_name ("nooooo"));
+}
+
+static void
+test_get_registered_service_types (void)
+{
+	GSList *service_types = NULL;
+
+	service_types = tracker_ontology_get_service_names_registered ();
+
+	g_assert_cmpint (7, ==, g_slist_length (service_types));
+
+	g_assert (element_in_list (service_types, "Applications"));
+
+	g_slist_foreach (service_types, (GFunc)g_free, NULL);
+	g_slist_free (service_types);
+}
+
+static void
+test_get_registered_field_types (void)
+{
+	GSList *field_types = NULL;
+
+	/* All registered field types */
+	field_types = tracker_ontology_get_field_names_registered (NULL);
+
+	g_assert_cmpint (1 ,==, g_slist_length (field_types));
+
+	g_assert (element_in_list (field_types, "App.Title"));
+
+	g_slist_foreach (field_types, (GFunc)g_free, NULL);
+	g_slist_free (field_types);
+
+	/* Music field types */
+	field_types = tracker_ontology_get_field_names_registered ("Music");
+
+	g_assert (!field_types);
+
+	/* App field types */
+	field_types = tracker_ontology_get_field_names_registered ("Applications");
+
+	g_assert_cmpint (1 ,==, g_slist_length (field_types));
+
+	g_assert (element_in_list (field_types, "App.Title"));
+
+	g_slist_foreach (field_types, (GFunc)g_free, NULL);
+	g_slist_free (field_types);
+}
+
+static void
+test_metadata_key_in_service (void)
+{
+	gint key;
+
+	key = tracker_ontology_service_get_key_metadata ("Applications",
+							 "Key:MetaData1");
+	g_assert_cmpint (key, ==, 1);
+
+	key = tracker_ontology_service_get_key_metadata ("Applications",
+							 "Key:MetaDataUnknown");
+	g_assert_cmpint (key, ==, 0);
+}
+
+int
+main (int argc, char **argv)
+{
+	int result;
+
+	g_type_init ();
+	g_test_init (&argc, &argv, NULL);
+
+	tracker_services_general_setup ();
+
+	g_test_add_func ("/libtracker-common/tracker-ontology/get_id_for_service",
+			 test_get_id_for_service);
+	g_test_add_func ("/libtracker-common/tracker-ontology/get_service_for_id",
+			 test_get_service_by_id);
+	g_test_add_func ("/libtracker-common/tracker-ontology/get_parent_service_by_id",
+			  test_get_parent_service_by_id);
+	g_test_add_func ("/libtracker-common/tracker-ontology/get_parent_id_for_service_id",
+			 test_get_parent_id_for_service_id);
+	g_test_add_func ("/libtracker-common/tracker-ontology/get_parent_service",
+			 test_get_parent_service);
+	g_test_add_func ("/libtracker-common/tracker-ontology/get_service_type_for_mime",
+			 test_get_service_type_for_mime);
+	g_test_add_func ("/libtracker-common/tracker-ontology/get_service",
+			 test_get_service);
+	g_test_add_func ("/libtracker-common/tracker-ontology/get_db_for_service",
+			 test_get_db_for_service);
+	g_test_add_func ("/libtracker-common/tracker-ontology/is_service_embedded",
+			 test_is_service_embedded);
+	g_test_add_func ("/libtracker-common/tracker-ontology/has_thumbnails",
+			 test_has_thumbnails);
+	g_test_add_func ("/libtracker-common/tracker-ontology/has_text",
+			 test_has_text);
+	g_test_add_func ("/libtracker-common/tracker-ontology/has_metadata",
+			 test_has_metadata);
+	g_test_add_func ("/libtracker-common/tracker-ontology/test_field_in_ontology",
+			 test_field_in_ontology);
+
+	g_test_add_func ("/libtracker-common/tracker-ontology/test_get_all_registered_service_types",
+			 test_get_registered_service_types);
+	g_test_add_func ("/libtracker-common/tracker-ontology/test_get_all_registered_field_types",
+			 test_get_registered_field_types);
+
+	g_test_add_func ("/libtracker-common/tracker-ontology/test_metadata_key_in_service",
+			 test_metadata_key_in_service);
+
+	result = g_test_run ();
+
+	tracker_ontology_shutdown ();
+
+	return result;
+}

Added: trunk/tests/libtracker-common/tracker-parser-test.c
==============================================================================
--- (empty file)
+++ trunk/tests/libtracker-common/tracker-parser-test.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,194 @@
+#include <glib.h>
+#include <glib/gtestutils.h>
+#include <string.h>
+
+#include <libtracker-common/tracker-config.h>
+#include <libtracker-common/tracker-language.h>
+#include <libtracker-common/tracker-parser.h>
+
+/*
+ * len(word) > 3 : 6 words
+ * longest word: 10 chars
+ * stop words ("here", "a", "of", "various", "to", "after")
+ */
+#define SAMPLE_TEXT "Here a good collection of various words to parse 12345678 after"
+
+static TrackerConfig *config;
+static TrackerLanguage *language;
+
+static void
+assert_key_length (gpointer key, gpointer value, gpointer user_data)
+{
+	gint max_length = GPOINTER_TO_INT (user_data);
+
+	g_assert_cmpint (strlen (key), <=, max_length);
+}
+
+/*
+ * Test max_words_to_index and min_length of the word
+ */
+static void
+test_parser_text_max_words_to_index (void)
+{
+	GHashTable *result = NULL;
+
+	result = tracker_parser_text (result,
+				      SAMPLE_TEXT,
+				      1,
+				      language,
+				      5, /* max words to index */
+				      18, /* max length of the word */
+				      3, /* min length of the word */
+				      FALSE, FALSE); /* Filter / Delimit */
+
+	g_assert_cmpint (g_hash_table_size (result), ==, 5);
+
+	g_hash_table_unref (result);
+}
+
+/*
+ * Test max length of the word.
+ */
+static void
+test_parser_text_max_length (void)
+{
+	GHashTable *result = NULL;
+	gint max_length;
+
+	max_length = 6;
+	result = tracker_parser_text (result,
+				      SAMPLE_TEXT,
+				      1,
+				      language,
+				      10, /* max words to index */
+				      max_length, /* max length of the word */
+				      3, /* min length of the word */
+				      FALSE, FALSE); /* Filter / Delimit */
+	g_hash_table_foreach (result, assert_key_length, GINT_TO_POINTER (max_length));
+	g_assert_cmpint (g_hash_table_size (result), ==, 8);
+
+	g_hash_table_unref (result);
+}
+
+/*
+ * Filter numbers
+ */
+static void
+test_parser_text_filter_numbers_stop_words (void)
+{
+	GHashTable *result = NULL;
+
+	/* Filtering numbers */
+	result = tracker_parser_text (result,
+				      SAMPLE_TEXT,
+				      1,
+				      language,
+				      100, /* max words to index */
+				      100, /* max length of the word */
+				      0, /* min length of the word */
+				      TRUE, FALSE); /* Filter / Delimit */
+
+	g_assert (!g_hash_table_lookup (result, "12345678"));
+
+	g_assert_cmpint (g_hash_table_size (result), ==, 4);
+
+	g_hash_table_unref (result);
+	result = NULL;
+
+	/* No filter */
+	result = tracker_parser_text (result,
+				      SAMPLE_TEXT,
+				      1,
+				      language,
+				      100, /* max words to index */
+				      100, /* max length of the word */
+				      0, /* min length of the word */
+				      FALSE, FALSE); /* Filter / Delimit */
+
+	g_assert_cmpint (g_hash_table_size (result), ==, 11);
+
+	g_assert (g_hash_table_lookup (result, "12345678"));
+
+	g_hash_table_unref (result);
+	result = NULL;
+}
+
+static void
+test_parser_stop_words (void)
+{
+	GHashTable *stop_words, *result = NULL;
+
+	/* Check we have the default stop words */
+	stop_words = tracker_language_get_stop_words (language);
+	g_assert (stop_words);
+	g_assert_cmpint (g_hash_table_size (stop_words), >, 1);
+
+	/* Set specific stop words to test */
+	tracker_config_set_language (config, "en");
+	g_assert (g_hash_table_lookup (stop_words, "after"));
+
+	result = tracker_parser_text (result,
+				      SAMPLE_TEXT,
+				      1,
+				      language,
+				      100, /* max words to index */
+				      100, /* max length of the word */
+				      1, /* min length of the word */
+				      TRUE, FALSE); /* Filter / Delimit */
+}
+
+static void
+test_parser_text_fast (void)
+{
+	GHashTable  *result = NULL;
+	const gchar *contents = "one two three four five six seven eight";
+
+	result = tracker_parser_text_fast (result, NULL, 1);
+
+	g_assert (result);
+	g_assert_cmpint (g_hash_table_size (result), ==, 0);
+
+	result = tracker_parser_text_fast (result, contents, 1);
+	g_assert_cmpint (g_hash_table_size (result), ==, 8);
+
+	result = tracker_parser_text_fast (result, contents, 1);
+	g_assert_cmpint (g_hash_table_size (result), ==, 8);
+
+}
+
+int
+main (int argc, char **argv) {
+
+	int result;
+
+	g_type_init ();
+	g_thread_init (NULL);
+	g_test_init (&argc, &argv, NULL);
+
+	/* Init */
+	config = tracker_config_new ();
+	language = tracker_language_new (config);
+
+	g_test_add_func ("/libtracker-common/tracker-parser/parser_text/max_words_to_index",
+			 test_parser_text_max_words_to_index);
+
+	g_test_add_func ("/libtracker-common/tracker-parser/parser_text/max_length",
+			 test_parser_text_max_length);
+
+	g_test_add_func ("/libtracker-common/tracker-parser/parser_text/filter_numbers",
+			 test_parser_text_filter_numbers_stop_words);
+
+	g_test_add_func ("/libtracker-common/tracker-parser/stop_words",
+			 test_parser_stop_words);
+
+	g_test_add_func ("/libtracker-common/tracker-parser/parser_text_fast",
+			 test_parser_text_fast);
+
+	result = g_test_run ();
+
+	/* End */
+	g_object_unref (config);
+	g_object_unref (language);
+
+	return result;
+}

Added: trunk/tests/libtracker-common/tracker-type-utils-test.c
==============================================================================
--- (empty file)
+++ trunk/tests/libtracker-common/tracker-type-utils-test.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,424 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <time.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib-object.h>
+#include <glib/gtestutils.h>
+
+#include <libtracker-common/tracker-type-utils.h>
+
+#include <tracker-test-helpers.h>
+
+static void
+test_date_format ()
+{
+	gchar *result;
+
+	result = tracker_date_format ("");
+	g_assert (result == NULL);
+
+
+	/* Fails
+	result = tracker_date_format ("1978"); //Audio.ReleaseDate
+	g_assert (tracker_test_helpers_cmpstr_equal (result, "1978-01-01T00:00:00"));
+	*/
+
+	result = tracker_date_format ("2008-06-14");
+	g_assert (tracker_test_helpers_cmpstr_equal (result, "2008-06-14T00:00:00"));
+	g_free (result);
+
+	result = tracker_date_format ("20080614000000");
+	g_assert (tracker_test_helpers_cmpstr_equal (result, "2008-06-14T00:00:00"));
+	g_free (result);
+
+	result = tracker_date_format ("20080614000000Z");
+	g_assert (tracker_test_helpers_cmpstr_equal (result, "2008-06-14T00:00:00Z"));
+	g_free (result);
+
+	result = tracker_date_format ("Mon Jun 14 04:20:20 2008"); //MS Office
+	g_assert (tracker_test_helpers_cmpstr_equal (result, "2008-06-14T04:20:20"));
+	g_free (result);
+
+	result = tracker_date_format ("2008:06:14 04:20:20"); //Exif style
+	g_assert (tracker_test_helpers_cmpstr_equal (result, "2008-06-14T04:20:20"));
+	g_free (result);
+
+	if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR)) {
+		result = tracker_date_format (NULL);
+	}
+	g_test_trap_assert_failed ();
+
+}
+
+static void
+test_string_to_date ()
+{
+	GDate	  *expected;
+	GDate	  *result;
+	time_t	   result_time_t;
+	gchar	  *input = "2008-06-16T11:10:10+0600";
+
+	expected = g_date_new_dmy (16, G_DATE_JUNE, 2008);
+
+	result_time_t = tracker_string_to_date (input);
+
+	result = g_date_new ();
+	g_date_set_time_t (result, result_time_t);
+
+	g_assert_cmpint (g_date_get_year (expected), ==, g_date_get_year (result));
+	g_assert_cmpint (g_date_get_day (expected), ==, g_date_get_day (result));
+	g_assert_cmpint (g_date_get_month (expected), ==, g_date_get_month (result));
+
+	if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR)) {
+		result_time_t = tracker_string_to_date (NULL);
+	}
+	g_test_trap_assert_failed ();
+
+	result_time_t = tracker_string_to_date ("");
+	g_assert_cmpint (result_time_t, ==, -1);
+
+	result_time_t = tracker_string_to_date ("i am not a date");
+	g_assert_cmpint (result_time_t, ==, -1);
+
+	/* Fails! Check the code
+	result_time_t = tracker_string_to_date ("2008-06-32T04:23:10+0000");
+	g_assert_cmpint (result_time_t, ==, -1);
+	*/
+}
+
+static void
+test_date_to_string ()
+{
+	struct tm *original;
+	time_t	   input;
+	gchar	  *result;
+
+	original = g_new0 (struct tm, 1);
+	original->tm_sec = 10;
+	original->tm_min = 53;
+	original->tm_hour = 23;
+	original->tm_mday = 16;
+	original->tm_mon = 5;
+	original->tm_year = 108;
+	original->tm_isdst = 1;
+
+	input = mktime (original);
+
+	result = tracker_date_to_string (input);
+
+	g_assert (result != NULL && strncmp (result, "2008-06-16T23:53:10", 19) == 0);
+}
+
+
+static void
+test_long_to_string ()
+{
+	glong n;
+	gchar *result;
+
+	n = 10050;
+	result = tracker_glong_to_string (n);
+	g_assert (tracker_test_helpers_cmpstr_equal (result, "10050"));
+	g_free (result);
+
+	n = -9950;
+	result = tracker_glong_to_string (n);
+	g_assert (tracker_test_helpers_cmpstr_equal (result, "-9950"));
+	g_free (result);
+}
+
+static void
+test_int_to_string ()
+{
+	gint n;
+	gchar *result;
+
+	n = 654;
+	result = tracker_gint_to_string (n);
+	g_assert (tracker_test_helpers_cmpstr_equal (result, "654"));
+	g_free (result);
+
+	n = -963;
+	result = tracker_gint_to_string (n);
+	g_assert (tracker_test_helpers_cmpstr_equal (result, "-963"));
+	g_free (result);
+
+}
+
+
+static void
+test_uint_to_string ()
+{
+	guint n;
+	gchar *result;
+
+	n = 100;
+	result = tracker_guint_to_string (n);
+	g_assert (tracker_test_helpers_cmpstr_equal (result, "100"));
+	g_free (result);
+}
+
+static void
+test_gint32_to_string ()
+{
+	gint32 n;
+	gchar *result;
+
+	n = 100;
+	result = tracker_gint32_to_string (n);
+	g_assert (tracker_test_helpers_cmpstr_equal (result, "100"));
+	g_free (result);
+
+	n = -96;
+	result = tracker_gint32_to_string (n);
+	g_assert (tracker_test_helpers_cmpstr_equal (result, "-96"));
+	g_free (result);
+
+}
+
+
+static void
+test_guint32_to_string ()
+{
+	guint32 n;
+	gchar *result;
+
+	n = 100;
+	result = tracker_guint32_to_string (n);
+	g_assert (tracker_test_helpers_cmpstr_equal (result, "100"));
+	g_free (result);
+
+}
+
+
+static void
+test_string_to_uint ()
+{
+	guint num_result, rc;
+
+	rc = tracker_string_to_uint ("10", &num_result);
+
+	g_assert (rc);
+	g_assert_cmpint (num_result, ==, 10);
+
+
+	if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR)) {
+		rc = tracker_string_to_uint (NULL, &num_result);
+	}
+	g_test_trap_assert_failed ();
+
+	// ???? FIXME
+	rc = tracker_string_to_uint ("-20", &num_result);
+	// ????
+
+	if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR)) {
+		tracker_string_to_uint (NULL, &num_result);
+	}
+	g_test_trap_assert_failed ();
+
+	if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR)) {
+		tracker_string_to_uint ("199", NULL);
+	}
+	g_test_trap_assert_failed ();
+
+	rc = tracker_string_to_uint ("i am not a number", &num_result);
+	g_assert (!rc);
+	g_assert_cmpint (rc, ==, 0);
+}
+
+
+static void
+test_string_in_string_list ()
+{
+	gchar *complete = "This is an extract of text with different terms an props like Audio:Title ...";
+	gchar **pieces;
+
+	pieces = g_strsplit (complete, " ", -1);
+
+	g_assert_cmpint (tracker_string_in_string_list ("is", pieces), ==, 1);
+	g_assert_cmpint (tracker_string_in_string_list ("Audio:Title", pieces), ==, 12);
+
+	if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR)) {
+		g_assert_cmpint (tracker_string_in_string_list (NULL, pieces), ==, -1);
+	}
+	g_test_trap_assert_failed ();
+
+	if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR)) {
+		g_assert_cmpint (tracker_string_in_string_list ("terms", NULL), ==, -1);
+	}
+	g_test_trap_assert_failed ();
+}
+
+static void
+test_gslist_to_string_list ()
+{
+	GSList *input = NULL;
+	gchar **result;
+
+	input = g_slist_prepend (input, "four");
+	input = g_slist_prepend (input, "three");
+	input = g_slist_prepend (input, "two");
+	input = g_slist_prepend (input, "one");
+
+	result = tracker_gslist_to_string_list (input);
+
+	g_assert (tracker_test_helpers_cmpstr_equal (result[0], "one")
+		  && tracker_test_helpers_cmpstr_equal (result[1], "two")
+		  && tracker_test_helpers_cmpstr_equal (result[2], "three")
+		  && tracker_test_helpers_cmpstr_equal (result[3], "four"));
+
+	g_strfreev (result);
+
+	if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR)) {
+		result = tracker_gslist_to_string_list (NULL);
+	}
+	g_test_trap_assert_failed ();
+}
+
+
+static void
+test_string_list_to_string ()
+{
+	gchar *input = "one two three four";
+	gchar **pieces;
+	gchar *result;
+
+	pieces = g_strsplit (input, " ", 4);
+
+	result = tracker_string_list_to_string (pieces, 4, ' ');
+	g_assert (tracker_test_helpers_cmpstr_equal (input, result));
+	g_free (result);
+
+	result = tracker_string_list_to_string (pieces, 3, '_');
+	g_assert (tracker_test_helpers_cmpstr_equal ("one_two_three", result));
+	g_free (result);
+
+
+	if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR)) {
+		result = tracker_string_list_to_string (NULL, 6, 'x');
+	}
+	g_test_trap_assert_failed ();
+
+	result = tracker_string_list_to_string (pieces, -1, ' ');
+	g_assert (tracker_test_helpers_cmpstr_equal (input, result));
+	g_free (result);
+
+	result = tracker_string_list_to_string (pieces, 6, ' ');
+	g_assert (tracker_test_helpers_cmpstr_equal (input, result));
+	g_free (result);
+
+	g_strfreev (pieces);
+}
+
+
+static void
+test_boolean_as_text_to_number ()
+{
+	gchar *result;
+
+	/* Correct true values */
+	result = tracker_string_boolean_to_string_gint ("True");
+	g_assert (tracker_test_helpers_cmpstr_equal (result, "1"));
+	g_free (result);
+
+
+	result = tracker_string_boolean_to_string_gint ("TRUE");
+	g_assert (tracker_test_helpers_cmpstr_equal (result, "1"));
+	g_free (result);
+
+	result = tracker_string_boolean_to_string_gint ("true");
+	g_assert (tracker_test_helpers_cmpstr_equal (result, "1"));
+	g_free (result);
+
+	/* Correct false values */
+	result = tracker_string_boolean_to_string_gint ("False");
+	g_assert (tracker_test_helpers_cmpstr_equal (result, "0"));
+	g_free (result);
+
+	result = tracker_string_boolean_to_string_gint ("FALSE");
+	g_assert (tracker_test_helpers_cmpstr_equal (result, "0"));
+	g_free (result);
+
+	result = tracker_string_boolean_to_string_gint ("false");
+	g_assert (tracker_test_helpers_cmpstr_equal (result, "0"));
+	g_free (result);
+
+	/* Invalid values */
+	result = tracker_string_boolean_to_string_gint ("Thrue");
+	g_assert (tracker_test_helpers_cmpstr_equal (result, "Thrue"));
+	g_free (result);
+
+	result = tracker_string_boolean_to_string_gint ("Falsez");
+	g_assert (tracker_test_helpers_cmpstr_equal (result, "Falsez"));
+	g_free (result);
+
+	result = tracker_string_boolean_to_string_gint ("Other invalid value");
+	g_assert (tracker_test_helpers_cmpstr_equal (result, "Other invalid value"));
+	g_free (result);
+
+
+	if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR)) {
+		result = tracker_string_boolean_to_string_gint (NULL);
+	}
+	g_test_trap_assert_failed ();
+}
+
+
+int
+main (int argc, char **argv) {
+
+	int result;
+
+	g_type_init ();
+	g_test_init (&argc, &argv, NULL);
+
+	g_test_add_func ("/libtracker-common/tracker-type-utils/boolean_as_text_to_number",
+			 test_boolean_as_text_to_number);
+	g_test_add_func ("/libtracker-common/tracker-type-utils/string_list_as_list",
+			 test_string_list_to_string);
+	g_test_add_func ("/libtracker-common/tracker-type-utils/gslist_to_string_list",
+			 test_gslist_to_string_list);
+	g_test_add_func ("/libtracker-common/tracker-type-utils/string_in_string_list",
+			 test_string_in_string_list);
+	g_test_add_func ("/libtracker-common/tracker-type-utils/string_to_uint",
+			 test_string_to_uint);
+	g_test_add_func ("/libtracker-common/tracker-type-utils/guint32_to_string",
+			 test_guint32_to_string);
+	g_test_add_func ("/libtracker-common/tracker-type-utils/gint32_to_string",
+			 test_gint32_to_string);
+	g_test_add_func ("/libtracker-common/tracker-type-utils/uint_to_string",
+			 test_uint_to_string);
+	g_test_add_func ("/libtracker-common/tracker-type-utils/int_to_string",
+			 test_int_to_string);
+	g_test_add_func ("/libtracker-common/tracker-type-utils/long_to_string",
+			 test_long_to_string);
+	g_test_add_func ("/libtracker-common/tracker-type-utils/date_format",
+			 test_date_format);
+	g_test_add_func ("/libtracker-common/tracker-type-utils/date_to_string",
+			 test_date_to_string);
+	g_test_add_func ("/libtracker-common/tracker-type-utils/string_to_date",
+			 test_string_to_date);
+	result = g_test_run ();
+
+	return result;
+}

Added: trunk/tests/libtracker-db/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/tests/libtracker-db/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,145 @@
+include $(top_srcdir)/Makefile.decl
+
+noinst_PROGRAMS = $(TEST_PROGS)
+
+#
+# NOTE: Some of these tests have been disabled (by Martyn) temporarily
+# 	because make distcheck fails. The reason for this is that it
+# 	depends files being installed to $prefix/share which have not
+# 	been installed at this point.
+# 
+#       These tests include:
+#
+#	  union-performance
+#         tracker-db-dbus
+#	  tracker-db-manager-unattach
+#	  tracker-db-manager-attach
+#	  tracker-db-manager-custom
+#
+
+TEST_PROGS += 								\
+	tracker-index-writer
+
+INCLUDES = 								\
+	-DG_LOG_DOMAIN=\"Tracker\"					\
+	-I$(top_srcdir)/src						\
+	-I$(top_srcdir)/tests/common					\
+	$(DBUS_CFLAGS)							\
+	$(SQLITE3_CFLAGS)						\
+	$(QDBM_CFLAGS)							\
+	$(GMODULE_CFLAGS)						\
+	$(GTHREAD_CFLAGS)						\
+	$(GLIB2_CFLAGS)
+
+# TEST_PROGS +=								\
+#	union-performance						\
+#	tracker-index-reader						\
+# 	tracker-db-dbus							\
+# 	tracker-db-manager-unattach					\
+# 	tracker-db-manager-attach					\
+# 	tracker-db-manager-custom					\
+#
+# union_performance_SOURCES =						\
+# 	tracker-db-manager-common.h					\
+# 	tracker-db-manager-common.c					\
+# 	union-performance.c
+#
+# union_performance_LDADD =                                            	\
+# 	$(top_builddir)/src/libtracker-common/libtracker-common.la      \
+# 	$(top_builddir)/src/libtracker-db/libtracker-db.la              \
+# 	$(top_builddir)/tests/common/libtracker-testcommon.la           \
+# 	$(SQLITE3_LIBS)                                                 \
+# 	$(QDBM_LIBS)                                                    \
+# 	$(GMODULE_LIBS)                                                 \
+# 	$(GTHREAD_LIBS)                                                 \
+# 	$(GLIB2_LIBS)                                                   \
+# 	-lz
+#
+# tracker_db_manager_attach_SOURCES = 					\
+# 	tracker-db-manager-test-attach.c 				\
+# 	tracker-db-manager-common.c					\
+# 	tracker-db-manager-common.h
+#
+# tracker_db_manager_attach_LDADD =					\
+# 	$(top_builddir)/src/libtracker-db/libtracker-db.la 		\
+# 	$(top_builddir)/src/libtracker-common/libtracker-common.la 	\
+# 	$(top_builddir)/tests/common/libtracker-testcommon.la 		\
+# 	$(SQLITE3_LIBS)							\
+# 	$(QDBM_LIBS)							\
+# 	$(GMODULE_LIBS)							\
+# 	$(GTHREAD_LIBS)							\
+# 	$(GLIB2_LIBS)							\
+# 	-lz
+#
+# tracker_db_manager_unattach_SOURCES = 				\
+# 	tracker-db-manager-test-unattach.c 				\
+# 	tracker-db-manager-common.c					\
+# 	tracker-db-manager-common.h
+#
+# tracker_db_manager_unattach_LDADD =					\
+# 	$(top_builddir)/src/libtracker-db/libtracker-db.la 		\
+# 	$(top_builddir)/src/libtracker-common/libtracker-common.la 	\
+# 	$(top_builddir)/tests/common/libtracker-testcommon.la 		\
+# 	$(SQLITE3_LIBS)							\
+# 	$(QDBM_LIBS)							\
+# 	$(GMODULE_LIBS)							\
+# 	$(GTHREAD_LIBS)							\
+# 	$(GLIB2_LIBS)							\
+# 	-lz
+#
+# tracker_db_manager_custom_SOURCES = 					\
+# 	tracker-db-manager-test-custom.c 				\
+# 	tracker-db-manager-common.c					\
+# 	tracker-db-manager-common.h
+#
+# tracker_db_manager_custom_LDADD =					\
+# 	$(top_builddir)/src/libtracker-db/libtracker-db.la 		\
+# 	$(top_builddir)/src/libtracker-common/libtracker-common.la 	\
+# 	$(top_builddir)/tests/common/libtracker-testcommon.la 		\
+# 	$(SQLITE3_LIBS)							\
+# 	$(QDBM_LIBS)							\
+# 	$(GMODULE_LIBS)							\
+# 	$(GTHREAD_LIBS)							\
+# 	$(GLIB2_LIBS)							\
+# 	-lz
+#
+# tracker_db_dbus_SOURCES = 						\
+# 	tracker-db-dbus-test.c 		
+#
+# tracker_db_dbus_LDADD =						\
+# 	$(top_builddir)/src/libtracker-common/libtracker-common.la 	\
+# 	$(top_builddir)/src/libtracker-db/libtracker-db.la 		\
+# 	$(top_builddir)/tests/common/libtracker-testcommon.la 		\
+# 	$(SQLITE3_LIBS)							\
+# 	$(GMODULE_LIBS)							\
+# 	$(GTHREAD_LIBS)							\
+# 	$(GLIB2_LIBS)							\
+# 	-lz
+#
+#
+# tracker_index_reader_SOURCES = 					\
+# 	tracker-index-reader-test.c 
+#
+# tracker_index_reader_LDADD = 						\
+# 	$(top_builddir)/src/libtracker-common/libtracker-common.la 	\
+# 	$(top_builddir)/src/libtracker-db/libtracker-db.la 		\
+# 	$(top_builddir)/tests/common/libtracker-testcommon.la 		\
+# 	$(GMODULE_LIBS) 						\
+# 	$(GTHREAD_LIBS) 						\
+# 	$(GLIB2_LIBS) 							\
+# 	$(DBUS_LIBS) 							\
+# 	$(GIO_LIBS) 							\
+# 	$(QDBM_LIBS) 
+
+tracker_index_writer_SOURCES = 						\
+	tracker-index-writer-test.c
+
+tracker_index_writer_LDADD =						\
+	$(top_builddir)/src/libtracker-common/libtracker-common.la 	\
+	$(top_builddir)/src/libtracker-db/libtracker-db.la	 	\
+	$(top_builddir)/tests/common/libtracker-testcommon.la 		\
+	$(QDBM_LIBS)							\
+	$(GMODULE_LIBS)							\
+	$(GTHREAD_LIBS)							\
+	$(GLIB2_LIBS)							
+

Added: trunk/tests/libtracker-db/example.index
==============================================================================
Binary file. No diff available.

Added: trunk/tests/libtracker-db/run-test-in-tmp-dir.sh
==============================================================================
--- (empty file)
+++ trunk/tests/libtracker-db/run-test-in-tmp-dir.sh	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+GLIB_DIR=`pkg-config --variable=prefix glib-2.0`
+. ../scripts/xdg_dirs.source
+
+# Ensure we have gtester in PATH
+export PATH=$PATH:$GLIB_DIR/bin
+
+make test 2> /dev/null
+
+#./tracker-db-manager-unattach
+#./tracker-db-manager-attach
+#./tracker-db-manager-custom
+
+. ../scripts/xdg_dirs.unsource

Added: trunk/tests/libtracker-db/tracker-db-dbus-test.c
==============================================================================
--- (empty file)
+++ trunk/tests/libtracker-db/tracker-db-dbus-test.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,180 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+#include <glib.h>
+#include <glib/gtestutils.h>
+
+#include <libtracker-db/tracker-db-dbus.h>
+#include <libtracker-db/tracker-db-interface.h>
+
+
+static TrackerDBResultSet *
+get_mock_tracker_db_result (gint results, gint columns, gboolean set_null) {
+
+	TrackerDBResultSet *mock;
+	gint i, j;
+
+	mock = _tracker_db_result_set_new (columns);
+
+	for (i = 0; i < results; i++) {
+		_tracker_db_result_set_append (mock);
+
+		for (j = 0; j < columns; j++) {
+
+			GValue value = {0,};
+			gchar * text = g_strdup_printf ("value %d", i);
+
+			g_value_init (&value, G_TYPE_STRING);
+			g_value_set_string (&value, (set_null ? NULL : text));
+			_tracker_db_result_set_set_value (mock, j, &value);
+
+			g_value_unset (&value);
+			g_free (text);
+		}
+	}
+
+	tracker_db_result_set_rewind (mock);
+
+	return mock;
+
+}
+
+
+
+static void
+test_dbus_query_result_to_strv ()
+{
+
+	TrackerDBResultSet *result_set = NULL;
+	gchar **result;
+	gint	count;
+
+	/* NULL */
+	result = tracker_dbus_query_result_to_strv (result_set, 0, &count);
+	g_assert (result == NULL);
+
+	/* 5 results, 1 column */
+	result_set = get_mock_tracker_db_result (5, 1, FALSE);
+	result = tracker_dbus_query_result_to_strv (result_set, 0, &count);
+
+	g_assert_cmpint (count, ==, 5);
+	g_assert_cmpint (g_strv_length (result), ==, 5);
+
+	g_strfreev (result);
+	g_object_unref (result_set);
+
+	/* 0 results, 1 columns */
+	result_set = get_mock_tracker_db_result (0, 1, FALSE);
+	if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR)) {
+		result = tracker_dbus_query_result_to_strv (result_set, 0, &count);
+	}
+	g_test_trap_assert_failed ();
+	/* Should raise g_critical (priv->array...); */
+
+	g_object_unref (result_set);
+
+
+	/* 1 result ... NULL */
+	result_set = get_mock_tracker_db_result (1, 1, TRUE);
+	result = tracker_dbus_query_result_to_strv (result_set, 0, &count);
+
+	g_assert_cmpint (count, ==, 0);
+
+	g_strfreev (result);
+	g_object_unref (result_set);
+
+}
+
+static void
+test_dbus_query_result_to_hash_table ()
+{
+	/* TODO: Implement */
+	g_print ("- Unimplemented -\n");
+}
+
+static void
+free_string_ptr_array (GPtrArray *array)
+{
+	g_ptr_array_foreach (array, (GFunc)g_strfreev, NULL);
+	g_ptr_array_free (array, TRUE);
+}
+
+static void
+test_dbus_query_result_to_ptr_array ()
+{
+	TrackerDBResultSet *result_set = NULL;
+	GPtrArray *result = NULL;
+
+	/* NULL */
+	result = tracker_dbus_query_result_to_ptr_array (result_set);
+	g_assert_cmpint (result->len, ==, 0);
+	free_string_ptr_array (result);
+
+	/* 5 results, 1 column */
+	result_set = get_mock_tracker_db_result (5, 1, FALSE);
+	result = tracker_dbus_query_result_to_ptr_array (result_set);
+
+	g_assert_cmpint (result->len, ==, 5);
+	free_string_ptr_array (result);
+
+	g_object_unref (result_set);
+
+	/* 0 results, 1 columns */
+	result_set = get_mock_tracker_db_result (0, 1, FALSE);
+	if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR)) {
+		result = tracker_dbus_query_result_to_ptr_array (result_set);
+		free_string_ptr_array (result);
+	}
+	g_test_trap_assert_failed ();
+	/* Should raise g_critical (priv->array...); */
+
+	g_object_unref (result_set);
+
+	/*  1 result ... NULL */
+	result_set = get_mock_tracker_db_result (1, 1, TRUE);
+	result = tracker_dbus_query_result_to_ptr_array (result_set);
+	g_assert_cmpint (result->len, ==, 1);
+	free_string_ptr_array (result);
+
+	g_object_unref (result_set);
+}
+
+gint
+main (gint argc, gchar **argv)
+{
+	int result;
+
+	g_type_init ();
+	g_thread_init (NULL);
+	g_test_init (&argc, &argv, NULL);
+
+	g_test_add_func ("/libtracker-db/tracker-db-dbus/query_result_to_strv",
+			 test_dbus_query_result_to_strv);
+	g_test_add_func ("/libtracker-db/tracker-db-dbus/query_result_to_hash_table",
+			 test_dbus_query_result_to_hash_table);
+	g_test_add_func ("/libtracker-db/tracker-db-dbus/query_result_to_ptr_array",
+			 test_dbus_query_result_to_ptr_array);
+
+
+	result = g_test_run ();
+
+	/* End */
+
+	return result;
+}

Added: trunk/tests/libtracker-db/tracker-db-manager-common.c
==============================================================================
--- (empty file)
+++ trunk/tests/libtracker-db/tracker-db-manager-common.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,50 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+#include "tracker-db-manager-common.h"
+
+gboolean
+test_assert_query_run (TrackerDB db, const gchar *query)
+{
+	TrackerDBInterface *iface;
+
+	iface = tracker_db_manager_get_db_interface (db);
+
+	return test_assert_query_run_on_iface (iface, query);
+
+}
+
+gboolean
+test_assert_query_run_on_iface (TrackerDBInterface *iface, const gchar *query)
+{
+	TrackerDBResultSet *result_set;
+	GError *error = NULL;
+
+	result_set = tracker_db_interface_execute_query (iface,
+							 &error,
+							 query);
+
+	if (error && error->message) {
+		g_warning ("Error loading query:'%s' - %s", query, error->message);
+		g_error_free (error);
+		return FALSE;
+	}
+
+	return TRUE;
+}

Added: trunk/tests/libtracker-db/tracker-db-manager-common.h
==============================================================================
--- (empty file)
+++ trunk/tests/libtracker-db/tracker-db-manager-common.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,29 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+#ifndef __TRACKER_DB_MANAGER_TEST_COMMON__
+#define __TRACKER_DB_MANAGER_TEST_COMMON__
+
+#include <glib.h>
+#include <libtracker-db/tracker-db-manager.h>
+
+gboolean test_assert_query_run (TrackerDB db, const gchar *query);
+gboolean test_assert_query_run_on_iface (TrackerDBInterface *iface, const gchar *query);
+
+#endif

Added: trunk/tests/libtracker-db/tracker-db-manager-test-attach.c
==============================================================================
--- (empty file)
+++ trunk/tests/libtracker-db/tracker-db-manager-test-attach.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,160 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+#include <glib.h>
+#include <glib/gtestutils.h>
+
+#include <libtracker-db/tracker-db-manager.h>
+#include "tracker-db-manager-common.h"
+
+
+typedef enum {
+	NO_INIT,
+	INIT_NO_REINDEX,
+	INIT_REINDEX
+} Status;
+
+static gboolean db_manager_status = NO_INIT;
+
+void
+ensure_db_manager_is_reindex (gboolean must_reindex)
+{
+	gint first;
+
+	if (db_manager_status == NO_INIT) {
+		if (must_reindex) {
+			tracker_db_manager_init (TRACKER_DB_MANAGER_FORCE_REINDEX,
+						 &first);
+			db_manager_status = INIT_REINDEX;
+		} else {
+			tracker_db_manager_init (0, &first);
+			db_manager_status = INIT_NO_REINDEX;
+		}
+		return;
+	}
+
+	if (db_manager_status == INIT_NO_REINDEX && !must_reindex) {
+		// tracker_db_manager is already correctly initialised
+		return;
+	}
+
+	if (db_manager_status == INIT_REINDEX && must_reindex) {
+		// tracker_db_manager is already correctly initialised
+		return ;
+	}
+
+	tracker_db_manager_shutdown ();
+	if (must_reindex) {
+		tracker_db_manager_init (TRACKER_DB_MANAGER_FORCE_REINDEX,
+					 &first);
+		db_manager_status = INIT_REINDEX;
+	} else {
+		tracker_db_manager_init (0, &first);
+		db_manager_status = INIT_NO_REINDEX;
+	}
+}
+
+
+
+
+
+void
+test_assert_tables_in_db (TrackerDB db, gchar *query)
+{
+	g_assert (test_assert_query_run (db, query));
+}
+
+static void
+test_creation_common_db_no_reindex ()
+{
+	ensure_db_manager_is_reindex (FALSE);
+	test_assert_tables_in_db (TRACKER_DB_COMMON, "SELECT * FROM MetaDataTypes");
+}
+
+
+static void
+test_creation_xesam_db_no_reindex_multiple_interfaces ()
+{
+	TrackerDBInterface *iface;
+
+	ensure_db_manager_is_reindex (FALSE);
+
+	iface = tracker_db_manager_get_db_interfaces (2,
+												  TRACKER_DB_XESAM,
+												  TRACKER_DB_COMMON);
+
+	test_assert_query_run_on_iface (iface, "SELECT * FROM XesamServiceTypes");
+}
+
+
+static void
+test_creation_xesam_db_no_reindex ()
+{
+	ensure_db_manager_is_reindex (FALSE);
+	test_assert_tables_in_db (TRACKER_DB_XESAM, "SELECT * FROM XesamServiceTypes");
+}
+
+static void
+test_creation_file_meta_db_no_reindex ()
+{
+	ensure_db_manager_is_reindex (FALSE);
+	test_assert_tables_in_db (TRACKER_DB_FILE_METADATA, "SELECT * FROM ServiceMetaData");
+}
+
+static void
+test_creation_file_contents_db_no_reindex ()
+{
+	ensure_db_manager_is_reindex (FALSE);
+	test_assert_tables_in_db (TRACKER_DB_FILE_CONTENTS, "SELECT * FROM ServiceContents");
+}
+
+
+int
+main (int argc, char **argv) {
+
+	int result;
+
+	g_type_init ();
+	g_thread_init (NULL);
+	g_test_init (&argc, &argv, NULL);
+
+
+	// Tests with attach and no-reindex
+	g_test_add_func ("/libtracker-db/tracker-db-manager/attach/no-reindex/common_db_tables",
+			test_creation_common_db_no_reindex);
+
+	g_test_add_func ("/libtracker-db/tracker-db-manager/attach/no-reindex/xesam_db_tables",
+			 test_creation_xesam_db_no_reindex);
+
+	g_test_add_func ("/libtracker-db/tracker-db-manager/attach/no-reindex/xesam_db_tables/multiple_interfaces",
+			 test_creation_xesam_db_no_reindex_multiple_interfaces);
+
+	g_test_add_func ("/libtracker-db/tracker-db-manager/attach/no-reindex/file_meta_db_tables",
+			 test_creation_file_meta_db_no_reindex);
+
+	g_test_add_func ("/libtracker-db/tracker-db-manager/attach/no-reindex/file_contents_db_tables",
+			 test_creation_file_contents_db_no_reindex);
+
+
+	result = g_test_run ();
+
+	/* End */
+
+	return result;
+}

Added: trunk/tests/libtracker-db/tracker-db-manager-test-custom.c
==============================================================================
--- (empty file)
+++ trunk/tests/libtracker-db/tracker-db-manager-test-custom.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,88 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+#include <glib.h>
+#include <glib/gtestutils.h>
+
+
+#include <libtracker-db/tracker-db-manager.h>
+#include "tracker-db-manager-common.h"
+
+void
+test_assert_tables_in_db (TrackerDBInterface *iface, gchar *query)
+{
+	g_assert (test_assert_query_run_on_iface (iface, query));
+}
+
+static void
+test_custom_common_filemeta_filecontents ()
+{
+	TrackerDBInterface *iface;
+
+	iface = tracker_db_manager_get_db_interfaces (3,
+						      TRACKER_DB_COMMON,
+						      TRACKER_DB_FILE_METADATA,
+						      TRACKER_DB_FILE_CONTENTS);
+
+	test_assert_tables_in_db (iface, "SELECT * FROM MetadataTypes");
+	test_assert_tables_in_db (iface, "SELECT * FROM ServiceMetadata");
+	test_assert_tables_in_db (iface, "SELECT * FROM ServiceContents");
+}
+
+
+static void
+test_custom_xesam_no_common ()
+{
+	TrackerDBInterface *iface;
+
+	iface = tracker_db_manager_get_db_interfaces (1,
+						      TRACKER_DB_XESAM);
+
+	test_assert_tables_in_db (iface, "SELECT * FROM XesamMetaDataTypes");
+}
+
+
+
+int
+main (int argc, char **argv) {
+
+	int result;
+	gint first_time;
+
+	g_type_init ();
+	g_thread_init (NULL);
+	g_test_init (&argc, &argv, NULL);
+
+	/* Init */
+	tracker_db_manager_init (TRACKER_DB_MANAGER_FORCE_REINDEX,
+				 &first_time);
+
+	g_test_add_func ("/libtracker-db/tracker-db-manager/custom/common_filemeta_filecontents",
+			test_custom_common_filemeta_filecontents);
+
+	g_test_add_func ("/libtracker-db/tracker-db-manager/custom/xesam_no_common",
+			 test_custom_xesam_no_common);
+
+	result = g_test_run ();
+
+	/* End */
+	tracker_db_manager_shutdown ();
+
+	return result;
+}

Added: trunk/tests/libtracker-db/tracker-db-manager-test-unattach.c
==============================================================================
--- (empty file)
+++ trunk/tests/libtracker-db/tracker-db-manager-test-unattach.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,110 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+#include <glib.h>
+#include <glib/gtestutils.h>
+
+
+#include <libtracker-db/tracker-db-manager.h>
+#include "tracker-db-manager-common.h"
+
+void
+test_assert_tables_in_db (TrackerDB db, gchar *query)
+{
+	g_assert (test_assert_query_run (db, query));
+}
+
+static void
+test_creation_common_db ()
+{
+	test_assert_tables_in_db (TRACKER_DB_COMMON, "SELECT * FROM MetaDataTypes");
+}
+
+static void
+test_creation_cache_db ()
+{
+	test_assert_tables_in_db (TRACKER_DB_CACHE, "SELECT * FROM FilePending");
+}
+
+static void
+test_creation_file_meta_db ()
+{
+	test_assert_tables_in_db (TRACKER_DB_FILE_METADATA, "SELECT * FROM ServiceMetaData");
+}
+
+static void
+test_creation_file_contents_db ()
+{
+	test_assert_tables_in_db (TRACKER_DB_FILE_CONTENTS, "SELECT * FROM ServiceContents");
+}
+
+static void
+test_creation_email_meta_db ()
+{
+	test_assert_tables_in_db (TRACKER_DB_EMAIL_METADATA, "SELECT * FROM ServiceMetadata");
+}
+
+static void
+test_creation_email_contents_db ()
+{
+	test_assert_tables_in_db (TRACKER_DB_FILE_CONTENTS, "SELECT * FROM ServiceContents");
+}
+
+
+int
+main (int argc, char **argv) {
+
+	int result;
+	gint first_time;
+
+	g_type_init ();
+	g_thread_init (NULL);
+	g_test_init (&argc, &argv, NULL);
+
+	/* Init */
+	tracker_db_manager_init (TRACKER_DB_MANAGER_FORCE_REINDEX,
+				 &first_time);
+
+	g_test_add_func ("/libtracker-db/tracker-db-manager/unattach/common_db_tables",
+			test_creation_common_db);
+
+	// XESAM is not available
+
+	g_test_add_func ("/libtracker-db/tracker-db-manager/unattach/cache_db_tables",
+			test_creation_cache_db);
+
+	g_test_add_func ("/libtracker-db/tracker-db-manager/unattach/file_meta_db_tables",
+			 test_creation_file_meta_db);
+
+	g_test_add_func ("/libtracker-db/tracker-db-manager/unattach/file_contents_db_tables",
+			 test_creation_file_contents_db);
+
+	g_test_add_func ("/libtracker-db/tracker-db-manager/unattach/email_meta_db_tables",
+			 test_creation_email_meta_db);
+
+	g_test_add_func ("/libtracker-db/tracker-db-manager/unattach/email_contents_db_tables",
+			 test_creation_email_contents_db);
+
+	result = g_test_run ();
+
+	/* End */
+	tracker_db_manager_shutdown ();
+
+	return result;
+}

Added: trunk/tests/libtracker-db/tracker-index-reader-test.c
==============================================================================
--- (empty file)
+++ trunk/tests/libtracker-db/tracker-index-reader-test.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,133 @@
+#include <glib.h>
+#include <glib/gtestutils.h>
+#include <tracker-test-helpers.h>
+#include <gio/gio.h>
+
+#include <libtracker-db/tracker-db-index.h>
+#include <libtracker-db/tracker-db-index-item.h>
+
+/* From libtracker-common/tracker-config.c */
+#define DEFAULT_MAX_BUCKET_COUNT		 524288
+#define DEFAULT_MIN_BUCKET_COUNT		 65536
+
+static void
+test_get_suggestion ()
+{
+	TrackerDBIndex *index;
+	gchar	       *suggestion;
+
+	index = tracker_db_index_new ("./example.index",
+				      DEFAULT_MIN_BUCKET_COUNT,
+				      DEFAULT_MAX_BUCKET_COUNT,
+				      TRUE);
+
+	g_assert (!tracker_db_index_get_reload (index));
+
+	suggestion = tracker_db_index_get_suggestion (index, "Thiz", 9);
+
+	g_assert (tracker_test_helpers_cmpstr_equal (suggestion, "this"));
+
+	g_free (suggestion);
+
+	g_object_unref (index);
+}
+
+static void
+test_reloading ()
+{
+	TrackerDBIndex	 *index;
+	TrackerDBIndexItem *hits = NULL;
+	guint		  count;
+
+	index = tracker_db_index_new ("./example.index",
+				      DEFAULT_MIN_BUCKET_COUNT,
+				      DEFAULT_MAX_BUCKET_COUNT,
+				      TRUE);
+
+	tracker_db_index_set_reload (index, TRUE);
+	g_assert (tracker_db_index_get_reload (index)); /* Trivial check of get/set */
+
+	hits = tracker_db_index_get_word_hits (index, "this", &count);
+	g_assert (hits);
+	g_free (hits);
+
+	g_assert (!tracker_db_index_get_reload (index)); /* Trivial check of get/set */
+}
+
+static void
+test_bad_index ()
+{
+	TrackerDBIndex *index;
+	guint		count;
+
+	index = tracker_db_index_new ("unknown-index",
+				      DEFAULT_MIN_BUCKET_COUNT,
+				      DEFAULT_MAX_BUCKET_COUNT,
+				      TRUE);
+
+	/* Reload true: the index doesnt exists */
+	g_assert (tracker_db_index_get_reload (index));
+
+	/* Return NULL, the index cannot reload the file */
+	g_assert (!tracker_db_index_get_word_hits (index, "this", &count));
+
+	/* Return NULL, the index cannot reload the file */
+	g_assert (!tracker_db_index_get_suggestion (index, "Thiz", 9));
+
+}
+
+static void
+test_created_file_in_the_mean_time ()
+{
+	TrackerDBIndex *index;
+	GFile	       *good, *bad;
+	guint		count;
+
+	index = tracker_db_index_new ("./unknown-index",
+				      DEFAULT_MIN_BUCKET_COUNT,
+				      DEFAULT_MAX_BUCKET_COUNT,
+				      TRUE);
+
+	/* Reload true: the index doesnt exists */
+	g_assert (tracker_db_index_get_reload (index));
+
+	good = g_file_new_for_path ("./example.index");
+	bad = g_file_new_for_path ("./unknown-index");
+
+	g_file_copy (good, bad, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, NULL);
+
+	/* Now the first operation reload the index */
+	g_assert (tracker_db_index_get_word_hits (index, "this", &count));
+
+	/* Reload false: It is already reloaded */
+	g_assert (!tracker_db_index_get_reload (index));
+
+	g_file_delete (bad, NULL, NULL);
+}
+
+
+int
+main (int argc, char **argv) {
+
+	int result;
+
+	g_type_init ();
+	g_thread_init (NULL);
+	g_test_init (&argc, &argv, NULL);
+
+	/* Init */
+
+	g_test_add_func ("/libtracker-db/tracker-index/get_suggestion",
+			 test_get_suggestion );
+	g_test_add_func ("/libtracker-db/tracker-index/reloading",
+			 test_reloading );
+	g_test_add_func ("/libtracker-db/tracker-index/bad_index",
+			 test_bad_index );
+	g_test_add_func ("/libtracker-db/tracker-index/created_file_in_the_mean_time",
+			 test_created_file_in_the_mean_time);
+	result = g_test_run ();
+
+	/* End */
+
+	return result;
+}

Added: trunk/tests/libtracker-db/tracker-index-writer-test.c
==============================================================================
--- (empty file)
+++ trunk/tests/libtracker-db/tracker-index-writer-test.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,351 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <qdbm/depot.h>
+
+#include <glib.h>
+#include <glib-object.h>
+#include <glib/gtestutils.h>
+#include <glib/gstdio.h>
+
+#include <libtracker-db/tracker-db-index.h>
+#include <libtracker-db/tracker-db-index-item.h>
+
+#define MIN_BUCKET_COUNT 1
+#define MAX_BUCKET_COUNT 100
+
+/* Helper functions to read the index */
+static gint
+get_number_words_in_index (const gchar *index_file)
+{
+	DEPOT *index;
+	gint   words;
+
+	index = dpopen (index_file, DP_OREADER, MAX_BUCKET_COUNT);
+
+	words = dprnum (index);
+
+	dpclose (index);
+
+	return words;
+}
+
+static gint
+get_results_for_word (const gchar *index_file, const gchar *word)
+{
+	DEPOT *index;
+	gint result;
+
+	index = dpopen (index_file, DP_OREADER, MAX_BUCKET_COUNT);
+
+	result = dpvsiz (index, word, -1);
+
+	dpclose (index);
+
+	return result / sizeof (TrackerDBIndexItem);
+}
+
+static gint
+get_score_for_word (const gchar *index_file, const gchar *word)
+{
+	DEPOT *index;
+	gint tsiz;
+	TrackerDBIndexItem *results;
+	gint score;
+
+	index = dpopen (index_file, DP_OREADER, MAX_BUCKET_COUNT);
+
+	results = (TrackerDBIndexItem *)dpget (index, word, -1, 0, -1, &tsiz);
+
+	dpclose (index);
+
+	g_return_val_if_fail ((tsiz / sizeof (TrackerDBIndexItem)) == 1, -1);
+	g_return_val_if_fail (results, -1);
+
+	score = tracker_db_index_item_get_score (&results[0]);
+
+	g_free (results);
+	return score;
+}
+
+static void
+test_add_one_word (void)
+{
+	TrackerDBIndex *index;
+	const gchar *indexname = "test-add-one-word.index";
+
+	g_remove (indexname);
+	index = tracker_db_index_new (indexname, MIN_BUCKET_COUNT, MAX_BUCKET_COUNT, FALSE);
+
+	tracker_db_index_add_word (index, "word1", 1, 1, 1);
+	tracker_db_index_flush (index);
+	g_object_unref (index);
+
+	g_assert_cmpint (get_number_words_in_index (indexname), ==, 1);
+	g_assert_cmpint (get_results_for_word (indexname, "word1"), ==, 1);
+
+	g_remove (indexname);
+}
+
+
+static void
+test_add_n_words ()
+{
+	TrackerDBIndex *index;
+	const gchar  *indexname = "test-add-n-words.index";
+	gint i;
+	gchar *word;
+
+	g_remove (indexname);
+
+	g_remove (indexname);
+	index = tracker_db_index_new (indexname, MIN_BUCKET_COUNT, MAX_BUCKET_COUNT, FALSE);
+
+	for ( i = 0; i < 20; i++) {
+		word = g_strdup_printf ("word%d", i);
+		tracker_db_index_add_word (index, word, 1, 1, 1);
+		g_free (word);
+	}
+
+	tracker_db_index_flush (index);
+	g_object_unref (index);
+
+	g_assert_cmpint (get_number_words_in_index (indexname), ==, 20);
+	g_assert_cmpint (get_results_for_word (indexname, "word5"), ==, 1);
+	g_remove (indexname);
+}
+
+static void
+test_add_word_n_times ()
+{
+	TrackerDBIndex *index;
+	gint i;
+	const gchar *indexname = "test-add-word-n-times.index";
+
+	g_remove (indexname);
+	index = tracker_db_index_new (indexname, MIN_BUCKET_COUNT, MAX_BUCKET_COUNT, FALSE);
+
+	for ( i = 0; i < 20; i++) {
+		tracker_db_index_add_word (index, "test-word", i, 1, 1);
+	}
+
+	tracker_db_index_flush (index);
+	g_object_unref (index);
+
+	g_assert_cmpint (get_number_words_in_index (indexname), ==, 1);
+	g_assert_cmpint (get_results_for_word (indexname, "test-word"), ==, 20);
+
+	g_remove (indexname);
+}
+
+static void
+test_add_word_multiple_occurrences ()
+{
+	TrackerDBIndex *index;
+	gint i;
+	const gchar *indexname = "test-word-multiple-ocurrences.index";
+
+	g_remove (indexname);
+	index = tracker_db_index_new (indexname, MIN_BUCKET_COUNT, MAX_BUCKET_COUNT, FALSE);
+
+	for ( i = 0; i < 20; i++) {
+		tracker_db_index_add_word (index, "test-word", 1, 1, 1);
+	}
+
+	tracker_db_index_flush (index);
+	g_object_unref (index);
+
+	g_assert_cmpint (get_number_words_in_index (indexname), ==, 1);
+
+	// There must be only ONE result with a high score
+	g_assert_cmpint (get_results_for_word (indexname, "test-word"), ==, 1);
+	g_assert_cmpint (get_score_for_word (indexname, "test-word"), ==, 20);
+
+	g_remove (indexname);
+
+}
+
+static gint
+insert_in_index (TrackerDBIndex *index, const gchar *text)
+{
+	gchar **pieces;
+	gint i;
+	static gint doc = 0;
+
+	doc += 1;
+
+	pieces = g_strsplit (text, " ", -1);
+	for (i = 0; pieces[i] != NULL; i++) {
+		tracker_db_index_add_word (index, pieces[i], doc, 1, 1);
+	}
+	g_strfreev (pieces);
+
+	return doc;
+}
+
+static void
+test_add_with_flushs ()
+{
+
+	TrackerDBIndex *index;
+	const gchar *indexname = "test-add-with-flush.index";
+
+	const gchar *text1 = "this is a text to try a kind of real use case of the indexer";
+	const gchar *text2 = "this is another text with some common words";
+
+	g_remove (indexname);
+	index = tracker_db_index_new (indexname, MIN_BUCKET_COUNT, MAX_BUCKET_COUNT, FALSE);
+
+	/* Text 1 */
+	insert_in_index (index, text1);
+	tracker_db_index_flush (index);
+
+	/* Text 2 */
+	insert_in_index (index, text2);
+	tracker_db_index_flush (index);
+
+	g_object_unref (index);
+
+	g_assert_cmpint (get_number_words_in_index (indexname), ==, 18);
+	g_assert_cmpint (get_results_for_word (indexname, "this"), ==, 2);
+	g_assert_cmpint (get_results_for_word (indexname, "common"), ==, 1);
+	g_assert_cmpint (get_score_for_word (indexname, "a"), ==, 2);
+	g_remove (indexname);
+
+}
+
+static void
+remove_in_index (TrackerDBIndex *index, const gchar *text, gint docid)
+{
+	gchar **pieces;
+	gint i;
+	static gint doc = 1;
+
+	pieces = g_strsplit (text, " ", -1);
+	for (i = 0; pieces[i] != NULL; i++) {
+		tracker_db_index_add_word (index, pieces[i], docid, 1, -1);
+	}
+	g_strfreev (pieces);
+
+	doc += 1;
+}
+
+static void
+test_remove_document (void)
+{
+	TrackerDBIndex *index;
+	const gchar *indexname = "test-remove-document.index";
+	gint id1, id2;
+
+	const gchar *doc1 = "this is a text to try a kind of real use case of the indexer";
+	const gchar *doc2 = "this is another text with some common words";
+
+	g_remove (indexname);
+
+	index = tracker_db_index_new (indexname, MIN_BUCKET_COUNT, MAX_BUCKET_COUNT, FALSE);
+
+	/* Doc 1 */
+	id1 = insert_in_index (index, doc1);
+	tracker_db_index_flush (index);
+
+	/* Doc 2 */
+	id2 = insert_in_index (index, doc2);
+	tracker_db_index_flush (index);
+
+	g_object_unref (index);
+
+	g_assert_cmpint (get_number_words_in_index (indexname), ==, 18);
+
+	index = tracker_db_index_new (indexname, MIN_BUCKET_COUNT, MAX_BUCKET_COUNT, FALSE);
+
+	/* Remove doc1 */
+	remove_in_index (index, doc1, id1);
+	tracker_db_index_flush (index);
+
+	g_object_unref (index);
+
+	g_assert_cmpint (get_number_words_in_index (indexname), ==, 8);
+
+	g_remove (indexname);
+}
+
+static void
+test_remove_before_flush (void)
+{
+	TrackerDBIndex *index;
+	const gchar *indexname = "test-remove-before-flush.index";
+	gint id1;
+
+	const gchar *doc1 = "this is a text";
+
+	g_remove (indexname);
+
+	index = tracker_db_index_new (indexname, MIN_BUCKET_COUNT, MAX_BUCKET_COUNT, FALSE);
+
+	/* Doc 1 */
+	id1 = insert_in_index (index, doc1);
+
+	/* Remove before flush */
+	remove_in_index (index, doc1, id1);
+
+	tracker_db_index_flush (index);
+
+	g_object_unref (index);
+
+	g_assert_cmpint (get_number_words_in_index (indexname), ==, 0);
+
+	g_remove (indexname);
+}
+
+int
+main (int argc, char **argv)
+{
+	int result;
+
+	g_type_init ();
+	g_thread_init (NULL);
+
+	g_test_init (&argc, &argv, NULL);
+
+	g_test_add_func ("/libtracker-db/tracker-index/add_word",
+			 test_add_one_word);
+
+	g_test_add_func ("/libtracker-db/tracker-index/add_n_words",
+			 test_add_n_words);
+
+	g_test_add_func ("/libtracker-db/tracker-index/add_word_n_times",
+			 test_add_word_n_times);
+
+	g_test_add_func ("/libtracker-db/tracker-index/add_word_multiple_occurrences",
+			 test_add_word_multiple_occurrences);
+
+	g_test_add_func ("/libtracker-db/tracker-index/add_with_flush",
+			 test_add_with_flushs);
+
+	g_test_add_func ("/libtracker-db/tracker-index/remove_document",
+			 test_remove_document);
+
+	g_test_add_func ("/libtracker-db/tracker-index/remove_before_flush",
+			 test_remove_before_flush);
+
+	result = g_test_run ();
+
+	return result;
+}

Added: trunk/tests/libtracker-db/union-performance.c
==============================================================================
--- (empty file)
+++ trunk/tests/libtracker-db/union-performance.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,151 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+#include <glib.h>
+#include <glib/gtestutils.h>
+
+
+#include <libtracker-db/tracker-db-manager.h>
+#include "tracker-db-manager-common.h"
+
+
+static void
+test_union_performance_xesam_view ()
+{
+	TrackerDBInterface *iface;
+	TrackerDBResultSet *result_set;
+	GError		   *error = NULL;
+
+	iface = tracker_db_manager_get_db_interfaces (7, TRACKER_DB_COMMON,
+							  TRACKER_DB_XESAM,
+							  TRACKER_DB_FILE_METADATA,
+							  TRACKER_DB_FILE_CONTENTS,
+							  TRACKER_DB_EMAIL_CONTENTS,
+							  TRACKER_DB_EMAIL_METADATA,
+							  TRACKER_DB_CACHE);
+
+	g_assert (iface);
+
+	result_set = tracker_db_interface_execute_query (iface, &error,
+		"CREATE TEMPORARY VIEW XesamServicesView AS SELECT * FROM 'file-meta'.Services UNION SELECT * FROM 'email-meta'.Services");
+	if (result_set)
+		g_object_unref (result_set);
+
+	g_assert (!error);
+
+	result_set = tracker_db_interface_execute_query (iface, &error,
+		"CREATE TEMPORARY VIEW XesamServiceMetaDataView AS SELECT * FROM 'file-meta'.ServiceMetaData UNION SELECT * FROM 'email-meta'.ServiceMetaData");
+	if (result_set)
+		g_object_unref (result_set);
+
+	g_assert (!error);
+
+	/* TODO: Start timer  */
+	result_set = tracker_db_interface_execute_query (iface, &error,
+		"SELECT M0.MetaDataValue "
+		"FROM XesamServicesView S "
+		"INNER JOIN XesamServiceMetaDataView M0 ON (S.ID = M0.ServiceID and "
+		"M0.MetaDataID in (82)) WHERE (S.ServiceTypeID in (select TypeId from "
+		"ServiceTypes where TypeName = 'Files' or Parent = 'Files')) AND "
+		" (  (M0.MetaDataValue like '%%test%%')  )");
+
+	/* TODO:  Stop timer  */
+	if (result_set)
+		g_object_unref (result_set);
+
+	g_assert (!error);
+
+	g_object_unref (iface);
+}
+
+
+static void
+test_union_performance_xesam_union ()
+{
+	TrackerDBInterface *iface;
+	TrackerDBResultSet *result_set;
+	GError		   *error = NULL;
+
+	iface = tracker_db_manager_get_db_interfaces (7, TRACKER_DB_COMMON,
+							  TRACKER_DB_XESAM,
+							  TRACKER_DB_FILE_METADATA,
+							  TRACKER_DB_FILE_CONTENTS,
+							  TRACKER_DB_EMAIL_CONTENTS,
+							  TRACKER_DB_EMAIL_METADATA,
+							  TRACKER_DB_CACHE);
+
+	g_assert (iface);
+
+	g_assert (!error);
+
+	/* TODO: Start timer  */
+	result_set = tracker_db_interface_execute_query (iface, &error,
+		"SELECT M0.MetaDataValue "
+		"FROM 'file-meta'.Services S "
+		"INNER JOIN 'file-meta'.ServiceMetaData M0 ON (S.ID = M0.ServiceID and "
+		"M0.MetaDataID in (82)) WHERE (S.ServiceTypeID in (select TypeId from "
+		"ServiceTypes where TypeName = 'Files' or Parent = 'Files')) AND "
+		" (  (M0.MetaDataValue like '%%test%%')  ) "
+
+		"UNION "
+
+		"SELECT M0.MetaDataValue "
+		"FROM 'email-meta'.Services S "
+		"INNER JOIN 'email-meta'.ServiceMetaData M0 ON (S.ID = M0.ServiceID and "
+		"M0.MetaDataID in (82)) WHERE (S.ServiceTypeID in (select TypeId from "
+		"ServiceTypes where TypeName = 'Email' or Parent = 'Email')) AND "
+		" (  (M0.MetaDataValue like '%%test%%')  ) ");
+
+	/* TODO:  Stop timer  */
+	if (result_set)
+		g_object_unref (result_set);
+
+	g_assert (!error);
+
+	g_object_unref (iface);
+}
+
+
+int
+main (int argc, char **argv)
+{
+	int result;
+	gint first_time;
+
+	g_type_init ();
+	g_thread_init (NULL);
+	g_test_init (&argc, &argv, NULL);
+
+	/* Init */
+	tracker_db_manager_init (TRACKER_DB_MANAGER_FORCE_REINDEX,
+							 &first_time);
+
+	g_test_add_func ("/libtracker-db/union-performance/xesam/view",
+					 test_union_performance_xesam_view);
+
+	g_test_add_func ("/libtracker-db/union-performance/xesam/union",
+					 test_union_performance_xesam_union);
+
+	result = g_test_run ();
+
+	/* End */
+	tracker_db_manager_shutdown ();
+
+	return result;
+}

Added: trunk/tests/scripts/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/tests/scripts/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,3 @@
+include $(top_srcdir)/Makefile.decl
+
+noinst_SCRIPTS = dummy_data_start.sh dummy_data_stop.sh

Added: trunk/tests/scripts/data/common.sql
==============================================================================
--- (empty file)
+++ trunk/tests/scripts/data/common.sql	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,609 @@
+BEGIN TRANSACTION;
+ANALYZE sqlite_master;
+CREATE TABLE Options (	
+	OptionKey 	Text COLLATE NOCASE not null,	
+	OptionValue	Text COLLATE NOCASE
+);
+INSERT INTO "Options" VALUES('DBVersion','20');
+INSERT INTO "Options" VALUES('Sequence','1');
+INSERT INTO "Options" VALUES('EventSequence','1');
+INSERT INTO "Options" VALUES('UpdateCount','0');
+INSERT INTO "Options" VALUES('1','IntegrityCheck');
+INSERT INTO "Options" VALUES('1','InitialIndex');
+CREATE TABLE Volumes
+(
+	VolumeID 	Integer primary key AUTOINCREMENT not null,
+	UDI		Text,
+	VolumeName	Text,
+	MountPath	Text,
+	Enabled		Integer default 0
+
+);
+DELETE FROM sqlite_sequence;
+INSERT INTO "sqlite_sequence" VALUES('MetaDataTypes',117);
+INSERT INTO "sqlite_sequence" VALUES('ServiceTypes',23);
+CREATE TABLE ServiceLinks
+(
+	ID			Integer primary key AUTOINCREMENT not null,
+	MetadataID		Integer not null,
+	SourcePath		Text,
+	SourceName		Text,
+	DestPath		Text,
+	DestName		Text
+);
+CREATE TABLE BackupServices
+(
+	ID            		Integer primary key AUTOINCREMENT not null,
+	Path 			Text  not null, 
+	Name	 		Text,
+
+	unique (Path, Name)
+
+);
+CREATE TABLE BackupMetaData
+(
+	ID			Integer primary key  AUTOINCREMENT not null,
+	ServiceID		Integer not null,
+	MetaDataID 		Integer  not null,
+	UserValue		Text
+	
+	 
+);
+CREATE TABLE KeywordImages
+(
+	Keyword 	Text primary key,
+	Image		Text
+);
+CREATE TABLE VFolders
+(
+	Path			Text  not null,
+	Name			Text  not null,
+	Query			text not null,
+	RDF			text,
+	Type			Integer default 0,
+	active			Integer,
+
+	primary key (Path, Name)
+
+);
+CREATE TABLE MetaDataTypes 
+(
+	ID	 		Integer primary key AUTOINCREMENT not null,
+	MetaName		Text not null  COLLATE NOCASE, 
+	DataTypeID		Integer default 1,    /* 0=Keyword, 1=indexable, 2=Clob (compressed indexable text),  3=String, 4=Integer, 5=Double,  6=DateTime, 7=Blob, 8=Struct, 9=ServiceLink */
+	DisplayName		text,
+	Description		text default ' ',
+	Enabled			integer default 1, /* used to prevent use of this metadata type */
+	UIVisible		integer default 0, /* should this metadata type be visible in a search criteria UI  */
+	WriteExec		text default ' ', /* used to specify an external program that can write an *embedded* metadata to a file */
+	Alias			text default ' ', /* alternate name for this type (XESAM specs?) */
+	FieldName		text default ' ', /* filedname if present in the services table */
+	Weight			Integer default 1, /* weight of metdata type in ranking */
+	Embedded		Integer default 1, /* 1 if metadata extracted from the file by the indexer and is not updateable by the user. 0 - this metadata can be updated by the user and is external to the file */
+	MultipleValues		Integer default 0, /* 0= type cannot have multiple values per entity, 1= type can have more than 1 value per entity */
+	Delimited		Integer default 0, /* if 1, extra delimiters (hyphen and underscore) are used to break word */
+	Filtered		Integer default 1, /* if 1, words are filtered for numerics (if numeric indexing is disabled), stopwords and min length */
+	Abstract		Integer default 0, /* if 0, can be used for storing metadata - Abstract type classes cannot store metadata and can only be used for searching its decendants */
+	StemMetadata		Integer default 1, /* 1 if metadata should be stemmed */
+	SideCar			Integer default 0, /* should this metadata be backed up in an xmp sidecar file */
+	FileName		Text default ' ',
+
+	Unique (MetaName)
+);
+INSERT INTO "MetaDataTypes" VALUES(1,'default',1,NULL,' ',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(2,'DC:Contributor',1,'Contributor','Contributors to the resource (other than the authors)',1,0,' ',' ',' ',1,1,0,0,1,1,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(3,'DC:Coverage',1,'Coverage','The extent or scope of the resource',1,0,' ',' ',' ',1,1,0,0,1,1,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(4,'DC:Creator',1,'Author','The authors of the resource',1,0,' ',' ',' ',1,1,0,0,1,1,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(5,'DC:Date',6,'Date','Date that something interesting happened to the resource',1,0,' ',' ',' ',1,1,0,0,1,1,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(6,'DC:Description',1,'Description','A textual description of the content of the resource',1,0,' ',' ',' ',1,1,0,0,1,1,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(7,'DC:Format',0,'Format','The file format(mime) or type used when saving the resource',1,0,' ',' ',' ',1,1,0,0,1,1,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(8,'DC:Identifier',1,'Identifier','Unique identifier of the resource',1,0,' ',' ',' ',1,1,0,0,1,1,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(9,'DC:Language',1,'Langauge','Language used in the resource',1,0,' ',' ',' ',1,1,0,0,1,1,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(10,'DC:Publishers',1,'Publishers','Publishers of the resource',1,0,' ',' ',' ',1,1,0,0,1,1,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(11,'DC:Relation',1,'Relationship','Relationships to other resources',1,0,' ',' ',' ',1,1,0,0,1,1,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(12,'DC:Rights',1,'Rights','Informal rights statement of the resource',1,0,' ',' ',' ',1,1,0,0,1,1,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(13,'DC:Source',1,'Source','Unique identifier of the work from which this resource was derived',1,0,' ',' ',' ',1,1,0,0,1,1,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(14,'DC:Subject',1,'Subject','specifies the topic of the content of the resource',1,0,' ',' ',' ',1,1,0,0,1,1,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(15,'DC:Keywords',0,'Keywords','Keywords that are used to tag a resource',1,0,' ',' ',' ',1,1,0,0,1,1,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(16,'DC:Title',1,'Title','specifies the topic of the content of the resource',1,0,' ',' ',' ',1,1,0,0,1,1,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(17,'DC:Type',1,'Type','specifies the type of the content of the resource',1,0,' ',' ',' ',1,1,0,0,1,1,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(18,'User:Rank',4,'Rank','User settable rank or score of the resource',1,0,' ',' ','Rank',1,0,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(19,'User:Keywords',0,'Keywords','User settable keywords which are used to tag a resource',1,0,' ',' ',' ',50,0,1,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(20,'File:Name',1,'Filename','Name of File',1,0,' ',' ','Name',10,1,0,0,0,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(21,'File:Ext',1,'Extension','File extension',1,0,' ',' ',' ',15,1,0,0,0,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(22,'File:Path',1,'Path','File Path',1,0,' ',' ','Path',1,1,0,0,0,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(23,'File:NameDelimited',1,'Keywords','Name of File',1,0,' ',' ',' ',5,1,0,1,0,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(24,'File:Contents',2,'Contents','File Contents',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(25,'File:Link',1,'Link','File Link',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(26,'File:Mime',0,'Mime Type','File Mime Type',1,0,' ',' ','Mime',10,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(27,'File:Size',4,'Size','File size in bytes',1,0,' ',' ','Size',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(28,'File:License',1,'License','File License Type',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(29,'File:Copyright',1,'Copyright','Copyright owners of the file',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(30,'File:Modified',6,'Modified','Last modified date',1,0,' ',' ','IndexTime',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(31,'File:Accessed',6,'Accessed','Last acessed date',1,0,' ',' ','Accessed',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(32,'File:Other',1,'Other','Other details about a file',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(33,'Audio:Title',1,'Title','Track title',1,0,' ',' ',' ',20,1,0,0,0,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(34,'Audio:Artist',1,'Artist','Track artist',1,0,' ',' ',' ',15,1,0,0,0,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(35,'Audio:Album',1,'Title','Track title',1,0,' ',' ',' ',10,1,0,0,0,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(36,'Audio:Genre',0,'Genre','The type or genre of the music track',1,0,' ',' ',' ',5,1,1,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(37,'Audio:Duration',4,'Duration','The length in seconds of the music track',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(38,'Audio:ReleaseDate',6,'Release date','The date the track was released',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(39,'Audio:AlbumArtist',1,'Album artist',' ',1,0,' ',' ',' ',10,1,0,0,0,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(40,'Audio:AlbumTrackCount',4,'Album track count','The number of tracks in the album',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(41,'Audio:TrackNo',4,'Track number','The position of the track relative to the others',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(42,'Audio:DiscNo',4,'Disc number','On which disc the track is located',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(43,'Audio:Performer',1,'Performer','The individual or group performing the track',1,0,' ',' ',' ',5,1,1,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(44,'Audio:TrackGain',5,'Track gain','The amount of gain needed for the track',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(45,'Audio:PeakTrackGain',5,'Peak track gain','The peak amount of gain needed for the track',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(46,'Audio:AlbumGain',5,'Album gain','The amount of gain needed for the entire album',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(47,'Audio:AlbumPeakGain',5,'Peak album gain','The peak amount of gain needed for the entire album',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(48,'Audio:Comment',1,'Comments','General purpose comments',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(49,'Audio:Codec',1,'Codec','Codec name',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(50,'Audio:CodecVersion',3,'Codec version','Codec version string',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(51,'Audio:Samplerate',5,'Sample rate','Sample rate of track in Hz',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(52,'Audio:Bitrate',5,'Bitrate','Bitrate in bits/sec',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(53,'Audio:Channels',4,'Channels','The number of channels in the track',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(54,'Audio:LastPlay',6,'Last Played','The date and time the track was last played',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(55,'Audio:PlayCount',4,'Play Count','Number of times the track has been played',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(56,'Audio:DateAdded',6,'Date Added','Date track was first added',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(57,'Audio:Lyrics',1,'Lyrics','Lyrics of the track',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(58,'Audio:MBAlbumID',3,'MusicBrainz album ID','The MusicBrainz album ID for the track',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(59,'Audio:MBArtistID',3,'MusicBrainz artist ID','The MusicBrainz artist ID for the track',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(60,'Audio:MBAlbumArtistID',3,'MusicBrainz album artist ID','The MusicBrainz album artist ID for the track',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(61,'Audio:MBTrackID',3,'MusicBrainz track ID','The MusicBrainz track ID',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(62,'App:Name',1,'Name','Application name',1,0,' ',' ',' ',25,1,0,0,0,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(63,'App:DisplayName',1,'Display name','Application display name',1,0,' ',' ',' ',10,1,0,0,0,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(64,'App:GenericName',1,'Generic name','Application generic name',1,0,' ',' ',' ',10,1,0,0,0,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(65,'App:Comment',1,'Comments','Application comments',1,0,' ',' ',' ',5,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(66,'App:Exec',3,'Name','Application name',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(67,'App:Icon',3,'Icon','Application icon name',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(68,'App:MimeType',0,'Mime type','Application supported mime types',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(69,'App:Categories',0,'Categories','Application categories',1,0,' ',' ',' ',5,1,1,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(70,'Doc:Title',1,'Title','The title of the document',1,0,' ',' ',' ',25,1,0,0,0,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(71,'Doc:Subject',1,'Subject','The subject or topic of the document',1,0,' ',' ',' ',20,1,0,0,0,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(72,'Doc:Author',1,'Author','The author of the document',1,0,' ',' ',' ',20,1,0,0,0,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(73,'Doc:Keywords',0,'Keywords','keywords embedded in the document',1,0,' ',' ',' ',25,1,0,1,0,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(74,'Doc:Comments',1,'Comments','The comments embedded in the document',1,0,' ',' ',' ',10,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(75,'Doc:PageCount',4,'Page count','Number of pages in the document',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(76,'Doc:WordCount',4,'Word count','Number of words in the document',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(77,'Doc:Created',6,'Created','Date document was created',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(78,'Doc:URL',1,'URL','URL to this Doc',1,0,' ',' ',' ',20,1,0,0,0,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(79,'Email:Recipient',1,'Recipient','The recepient of an email',1,0,' ',' ',' ',1,1,0,0,1,1,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(80,'Email:Body',1,'Body','The body contents of the email',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(81,'Email:Date',1,'Date','Date email was sent',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(82,'Email:Sender',1,'Sender','The sender of the email',1,0,' ',' ',' ',10,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(83,'Email:Subject',1,'Subject','The subject of the email',1,0,' ',' ',' ',20,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(84,'Email:SentTo',1,'Sent to','The group of people the email was sent to',1,0,' ',' ',' ',10,1,1,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(85,'Email:CC',1,'CC','The CC recipients of the email',1,0,' ',' ',' ',5,1,1,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(86,'Email:Attachments',1,'Attachments','The names of the attachments',1,0,' ',' ',' ',5,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(87,'Email:AttachmentsDelimited',1,'AttachmentsDelimited','The names of the attachments with extra delimiting',1,0,' ',' ',' ',5,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(88,'Image:Title',1,'Title','The title of the image',1,0,' ',' ',' ',10,1,0,0,0,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(89,'Image:Keywords',1,'Keywords','The keywords embedded in the image',1,0,' ',' ',' ',20,1,0,1,0,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(90,'Image:Height',1,'Height','Height in pixels of the image',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(91,'Image:Width',1,'Width','Width in pixels of the image',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(92,'Image:Album',1,'Album','The name of the album in which the image resides',1,0,' ',' ',' ',5,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(93,'Image:Date',1,'Created','Date image was created or shot',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(94,'Image:Creator',1,'Creator','The person who created the image',1,0,' ',' ',' ',10,1,0,0,0,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(95,'Image:Comments',1,'Comments','The comments embedded in the image',1,0,' ',' ',' ',5,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(96,'Image:Description',1,'Description','The description embedded in the image',1,0,' ',' ',' ',5,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(97,'Image:Software',1,'Software','The software used to create the image',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(98,'Image:CameraMake',1,'Camera make','The camera used to create the image',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(99,'Image:CameraModel',1,'Camera model','The model number of the camera used to create the image',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(100,'Image:Orientation',1,'Orientation','The Orientation mode of the image (portrait/landscape)',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(101,'Image:ExposureProgram',1,'Exposure program','The class of the program used by the camera to set exposure when the picture is taken',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(102,'Image:ExposureTime',1,'Exposure time','Exposure time in seconds',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(103,'Image:FNumber',1,'F number','The F number',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(104,'Image:Flash',1,'Flash','Indicates the status of flash when the image was shot (0=off, 1=on)',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(105,'Image:FocalLength',1,'Focal length','The actual focal length of the lens in mm',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(106,'Image:ISOSpeed',1,'ISO speed','Indicates the ISO Speed and ISO Latitude of the camera or input device as specified in ISO 12232.',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(107,'Image:MeteringMode',1,'Metering mode','The metering mode',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(108,'Image:WhiteBalance',1,'White balance','Indicates the white balance mode set when the image was shot (auto/manual)',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(109,'Video:Title',1,'Title','Video title',1,0,' ',' ',' ',20,1,0,0,0,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(110,'Video:Author',1,'Author','Video author',1,0,' ',' ',' ',15,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(111,'Video:Height',4,'Height','The height in pixels',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(112,'Video:Width',4,'Width','The width in pixels',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(113,'Video:Duration',4,'Duration','Duration in number of seconds',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(114,'Video:Comments',1,'Comments','General purpose comments',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(115,'Video:FrameRate',5,'Frame rate','Number of frames per seconds',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(116,'Video:Codec',3,'Codec','Codec used for the video',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+INSERT INTO "MetaDataTypes" VALUES(117,'Video:Bitrate',5,'Bitrate','Bitrate in bits/sec',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ');
+CREATE TABLE MetaDataChildren
+(
+	MetaDataID		integer not null,
+	ChildID			integer not null,
+
+	primary key (MetaDataID, ChildID)
+
+);
+INSERT INTO "MetaDataChildren" VALUES(15,19);
+INSERT INTO "MetaDataChildren" VALUES(8,20);
+INSERT INTO "MetaDataChildren" VALUES(8,22);
+INSERT INTO "MetaDataChildren" VALUES(11,25);
+INSERT INTO "MetaDataChildren" VALUES(7,26);
+INSERT INTO "MetaDataChildren" VALUES(12,28);
+INSERT INTO "MetaDataChildren" VALUES(12,29);
+INSERT INTO "MetaDataChildren" VALUES(5,30);
+INSERT INTO "MetaDataChildren" VALUES(5,31);
+INSERT INTO "MetaDataChildren" VALUES(16,33);
+INSERT INTO "MetaDataChildren" VALUES(4,34);
+INSERT INTO "MetaDataChildren" VALUES(17,36);
+INSERT INTO "MetaDataChildren" VALUES(5,38);
+INSERT INTO "MetaDataChildren" VALUES(4,39);
+INSERT INTO "MetaDataChildren" VALUES(2,43);
+INSERT INTO "MetaDataChildren" VALUES(6,48);
+INSERT INTO "MetaDataChildren" VALUES(5,54);
+INSERT INTO "MetaDataChildren" VALUES(8,58);
+INSERT INTO "MetaDataChildren" VALUES(8,59);
+INSERT INTO "MetaDataChildren" VALUES(8,60);
+INSERT INTO "MetaDataChildren" VALUES(8,61);
+INSERT INTO "MetaDataChildren" VALUES(16,70);
+INSERT INTO "MetaDataChildren" VALUES(14,71);
+INSERT INTO "MetaDataChildren" VALUES(4,72);
+INSERT INTO "MetaDataChildren" VALUES(15,73);
+INSERT INTO "MetaDataChildren" VALUES(6,74);
+INSERT INTO "MetaDataChildren" VALUES(5,77);
+INSERT INTO "MetaDataChildren" VALUES(5,81);
+INSERT INTO "MetaDataChildren" VALUES(4,82);
+INSERT INTO "MetaDataChildren" VALUES(14,83);
+INSERT INTO "MetaDataChildren" VALUES(79,84);
+INSERT INTO "MetaDataChildren" VALUES(79,85);
+INSERT INTO "MetaDataChildren" VALUES(16,88);
+INSERT INTO "MetaDataChildren" VALUES(15,89);
+INSERT INTO "MetaDataChildren" VALUES(5,93);
+INSERT INTO "MetaDataChildren" VALUES(4,94);
+INSERT INTO "MetaDataChildren" VALUES(6,95);
+INSERT INTO "MetaDataChildren" VALUES(6,96);
+INSERT INTO "MetaDataChildren" VALUES(16,109);
+INSERT INTO "MetaDataChildren" VALUES(4,110);
+INSERT INTO "MetaDataChildren" VALUES(6,114);
+CREATE TABLE MetaDataGroup
+(
+	MetaDataGroupID		integer not null,
+	ChildID			integer not null,
+
+	primary key (MetaDataGroupID, ChildID)
+
+);
+CREATE TABLE MetadataOptions
+(
+	MetaDataID		Integer not null,
+	OptionName		Text not null,
+	OptionValue		Text default ' ',
+
+	primary key (MetaDataID, OptionName)
+);
+CREATE TABLE ServiceTypes
+(
+	TypeID 			Integer primary key AUTOINCREMENT not null,
+	TypeName		Text COLLATE NOCASE not null,
+
+	TypeCount		Integer default 0,
+
+	DisplayName		Text default ' ',
+	Parent			Text default ' ',
+	Enabled			Integer default 1, 
+	Embedded		Integer default 1, /* service is created by the indexer if embedded. User or app defined services are not embedded */
+	ChildResource		Integer default 0, /* service is a child service */
+	
+	CreateDesktopFile	Integer default 0, /* used by a UI to indicate whether it should create a desktop file for the service if its copied (using the ViewerExec field + uri) */
+
+	/* useful for a UI when determining what actions a hit can have */
+	CanCopy			Integer default 1, 
+	CanDelete		Integer default 1,
+
+	ShowServiceFiles	Integer default 0,
+	ShowServiceDirectories  Integer default 0,
+
+	HasMetadata		Integer default 1,
+	HasFullText		Integer default 1,
+	HasThumbs		Integer default 1,
+	
+	ContentMetadata		Text default ' ', /* the content field is the one most likely to be used for showing a search snippet */ 
+
+	KeyMetadata1		Text default ' ', /* the most commonly requested metadata (especially for tables/grid views) is cached int he services table for extra fast retrieval */
+	KeyMetadata2		Text default ' ',
+	KeyMetadata3		Text default ' ',
+	KeyMetadata4		Text default ' ',
+	KeyMetadata5		Text default ' ',
+	KeyMetadata6		Text default ' ',
+	KeyMetadata7		Text default ' ',
+	KeyMetadata8		Text default ' ',
+	KeyMetadata9		Text default ' ',
+	KeyMetadata10		Text default ' ',
+	KeyMetadata11		Text default ' ',
+
+	UIVisible		Integer default 0,	/* should service appear in a search GUI? */
+	UITitle			Text default ' ',	/* title format as displayed in the metadata tile */
+	UIMetadata1		Text default ' ',	/*UI fields to show in GUI for a hit - if not set then Name,Path,Mime are used */
+	UIMetadata2		Text default ' ',
+	UIMetadata3		Text default ' ',
+	UIView			Text default 'default',
+
+	Description		Text default ' ',
+	Database		integer default 0, /* 0 = DB_FILES, 1 = DB_EMAILS, 2 = DB_MISC, 3 = DB_USER */
+	Icon			Text default ' ',
+
+	IndexerExec		Text default ' ',
+	IndexerOutput		Text default 'stdout',
+	ThumbExec		Text default ' ',
+	ViewerExec		Text default ' ',
+
+	WatchFolders		Text default ' ',
+	IncludeGlob		Text default ' ',
+	ExcludeGlob		Text default ' ',
+
+	FileName		Text default ' ',
+
+	unique (TypeName)
+);
+INSERT INTO "ServiceTypes" VALUES(1,'default',0,' ',' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default',' ',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ');
+INSERT INTO "ServiceTypes" VALUES(2,'Files',0,'All Files',' ',1,1,0,0,1,1,1,1,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',1,' ',' ',' ',' ','icon','All files in the filesystem',0,'system-file-manager',' ','stdout',' ',' ',' ',' ',' ',' ');
+INSERT INTO "ServiceTypes" VALUES(3,'Folders',0,'Folders','Files',1,1,0,0,1,1,1,1,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',1,' ',' ',' ',' ','icon','Folders in the filesystem',0,'folder',' ','stdout',' ',' ',' ',' ',' ',' ');
+INSERT INTO "ServiceTypes" VALUES(4,'Documents',0,'Documents','Files',1,1,0,0,1,1,1,1,1,1,1,'File:Contents','Doc:Title','Doc:Author','Doc:Created',' ',' ',' ',' ',' ',' ',' ',' ',1,' ',' ',' ',' ','default','Office and PDF based files',0,'x-office-document',' ','stdout',' ',' ',' ',' ',' ',' ');
+INSERT INTO "ServiceTypes" VALUES(5,'WebHistory',0,'Web History',' ',1,1,0,0,1,1,0,0,1,1,1,' ','Doc:Title','Doc:URL','Doc:Keywords','User:Keywords',' ',' ',' ',' ',' ',' ',' ',1,' ',' ',' ',' ','default','Web History',0,'x-office-document',' ','stdout',' ',' ',' ',' ',' ',' ');
+INSERT INTO "ServiceTypes" VALUES(6,'Images',0,'Images','Files',1,1,0,0,1,1,1,1,1,0,1,' ','Image:Title','Image:Height','Image:Width','Image:Date','Image:Software','Image:Creator',' ',' ',' ',' ',' ',1,' ',' ',' ',' ','icon','Image based files',0,'image-x-generic',' ','stdout',' ',' ',' ',' ',' ',' ');
+INSERT INTO "ServiceTypes" VALUES(7,'Music',0,'Music','Files',1,1,0,0,1,1,1,1,1,0,0,' ','Audio:Title','Audio:Artist','Audio:Album','Audio:Genre','Audio:Duration','Audio:ReleaseDate','Audio:TrackNo','Audio:Bitrate','Audio:PlayCount','Audio:DateAdded','Audio:LastPlay',1,' ',' ',' ',' ','tabular','Music based files',0,'audio-x-generic',' ','stdout',' ',' ',' ',' ',' ',' ');
+INSERT INTO "ServiceTypes" VALUES(8,'Videos',0,'Videos','Files',1,1,0,0,1,1,1,1,1,0,1,' ','Video:Title','Video:Author','Video:Height','Video:Width','Video:Duration','Audio:Bitrate',' ',' ',' ',' ',' ',1,' ',' ',' ',' ','icon','Video based files',0,'video-x-generic',' ','stdout',' ',' ',' ',' ',' ',' ');
+INSERT INTO "ServiceTypes" VALUES(9,'Text',0,'Text','Files',1,1,0,0,1,1,1,1,0,1,0,'File:Contents',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',1,' ',' ',' ',' ','default','Text based files',0,'text-x-generic',' ','stdout',' ',' ',' ',' ',' ',' ');
+INSERT INTO "ServiceTypes" VALUES(10,'Development',0,'Development','Files',1,1,0,0,1,1,1,1,0,1,0,'File:Contents',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',1,' ',' ',' ',' ','default','Development and source code files',0,'applications-development',' ','stdout',' ',' ',' ',' ',' ',' ');
+INSERT INTO "ServiceTypes" VALUES(11,'Other',0,'Other Files','Files',1,1,0,0,1,1,1,1,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','All other files that do not belong in any other category',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ');
+INSERT INTO "ServiceTypes" VALUES(12,'Emails',0,'Emails',' ',1,1,0,0,1,1,0,0,1,1,1,'Email:Body',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',1,' ','Email:Subject','Email:Sender',' ','default','All Emails',0,'stock_mail-open',' ','stdout',' ',' ',' ',' ',' ',' ');
+INSERT INTO "ServiceTypes" VALUES(13,'EvolutionEmails',0,'Evolution Emails','Emails',1,1,0,0,1,1,0,0,1,1,1,'Email:Body','Email:Subject','Email:Sender','Email:Date',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Evolution based emails',0,' ',' ','stdout',' ','evolution "%1"',' ',' ',' ',' ');
+INSERT INTO "ServiceTypes" VALUES(14,'ModestEmails',0,'Modest Emails','Emails',1,1,0,0,1,1,0,0,1,1,1,'Email:Body','Email:Subject','Email:Sender','Email:Date',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Modest based emails',0,' ',' ','stdout',' ','modest-open "%1"',' ',' ',' ',' ');
+INSERT INTO "ServiceTypes" VALUES(15,'ThunderbirdEmails',0,'Thunderbird Emails','Emails',1,1,0,0,1,1,0,0,1,1,1,'Email:Body','Email:Subject','Email:Sender','Email:Date',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Thunderbird based emails',0,' ',' ','stdout',' ','thunderbird -viewtracker "%1"',' ',' ',' ',' ');
+INSERT INTO "ServiceTypes" VALUES(16,'KMailEmails',0,'KMail Emails','Emails',1,1,0,0,1,1,0,0,1,1,1,'Email:Body','Email:Subject','Email:Sender','Email:Date',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','KMail based emails',0,' ',' ','stdout',' ','kmail "%1"',' ',' ',' ',' ');
+INSERT INTO "ServiceTypes" VALUES(17,'EmailAttachments',0,'Email Attachments',' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',1,' ',' ',' ',' ','default','All files that are attached to an Email',0,'stock_attach',' ','stdout',' ',' ',' ',' ',' ',' ');
+INSERT INTO "ServiceTypes" VALUES(18,'EvolutionAttachments',0,'Evolution Email Attachments','EmailAttachments',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','All files that are attached to an Evolution Email',0,'stock_attach',' ','stdout',' ',' ',' ',' ',' ',' ');
+INSERT INTO "ServiceTypes" VALUES(19,'ModestAttachments',0,'Modest Email Attachments','EmailAttachments',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','All files that are attached to an Modest Email',0,'stock_attach',' ','stdout',' ',' ',' ',' ',' ',' ');
+INSERT INTO "ServiceTypes" VALUES(20,'KMailAttachments',0,'KMail Email Attachments','EmailAttachments',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','All files that are attached to an KMail Email',0,'stock_attach',' ','stdout',' ',' ',' ',' ',' ',' ');
+INSERT INTO "ServiceTypes" VALUES(21,'Conversations',0,'Conversations',' ',1,1,0,0,1,1,1,0,0,1,0,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',1,' ',' ',' ',' ','default','Conversation log files',0,'stock_help-chat',' ','stdout',' ',' ',' ',' ',' ',' ');
+INSERT INTO "ServiceTypes" VALUES(22,'GaimConversations',0,'Gaim Conversations','Conversations',1,1,0,0,1,1,1,0,0,1,0,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','All Gaim Conversation logs',0,'stock_help-chat',' ','stdout',' ',' ',' ',' ',' ',' ');
+INSERT INTO "ServiceTypes" VALUES(23,'Applications',0,'Applications',' ',1,1,0,0,1,1,1,0,0,0,0,' ','App:DisplayName','App:Exec','App:Icon',' ',' ',' ',' ',' ',' ',' ',' ',1,' ',' ',' ',' ','default','Application files',0,'stock_active',' ','stdout',' ',' ',' ',' ',' ',' ');
+CREATE TABLE ServiceTileMetadata
+(
+	ServiceTypeID		Integer not null,
+	MetaName		Text not null,
+
+	primary key (ServiceTypeID, MetaName)
+);
+INSERT INTO "ServiceTileMetadata" VALUES(4,'Doc:Title');
+INSERT INTO "ServiceTileMetadata" VALUES(4,'Doc:Subject');
+INSERT INTO "ServiceTileMetadata" VALUES(4,'Doc:Author');
+INSERT INTO "ServiceTileMetadata" VALUES(4,'Doc:Created');
+INSERT INTO "ServiceTileMetadata" VALUES(4,'Doc:PageCount');
+INSERT INTO "ServiceTileMetadata" VALUES(4,'File:Size');
+INSERT INTO "ServiceTileMetadata" VALUES(5,'Doc:Title');
+INSERT INTO "ServiceTileMetadata" VALUES(5,'Doc:URL');
+INSERT INTO "ServiceTileMetadata" VALUES(5,'Doc:Subject');
+INSERT INTO "ServiceTileMetadata" VALUES(5,'Doc:Author');
+INSERT INTO "ServiceTileMetadata" VALUES(5,'Doc:Created');
+INSERT INTO "ServiceTileMetadata" VALUES(5,'Doc:PageCount');
+INSERT INTO "ServiceTileMetadata" VALUES(5,'File:Size');
+INSERT INTO "ServiceTileMetadata" VALUES(6,'Image:Title');
+INSERT INTO "ServiceTileMetadata" VALUES(6,'Image:Height');
+INSERT INTO "ServiceTileMetadata" VALUES(6,'Image:Width');
+INSERT INTO "ServiceTileMetadata" VALUES(6,'Image:Date');
+INSERT INTO "ServiceTileMetadata" VALUES(6,'Image:Creator');
+INSERT INTO "ServiceTileMetadata" VALUES(6,'Image:Software');
+INSERT INTO "ServiceTileMetadata" VALUES(6,'Image:Comments');
+INSERT INTO "ServiceTileMetadata" VALUES(7,'Audio:Title');
+INSERT INTO "ServiceTileMetadata" VALUES(7,'Audio:Artist');
+INSERT INTO "ServiceTileMetadata" VALUES(7,'Audio:Album');
+INSERT INTO "ServiceTileMetadata" VALUES(7,'Audio:Genre');
+INSERT INTO "ServiceTileMetadata" VALUES(7,'Audio:Duration');
+INSERT INTO "ServiceTileMetadata" VALUES(7,'Audio:ReleaseDate');
+INSERT INTO "ServiceTileMetadata" VALUES(8,'Video:Title');
+INSERT INTO "ServiceTileMetadata" VALUES(8,'Video:Author');
+INSERT INTO "ServiceTileMetadata" VALUES(8,'Video:Height');
+INSERT INTO "ServiceTileMetadata" VALUES(8,'Video:Width');
+INSERT INTO "ServiceTileMetadata" VALUES(8,'Video:Duration');
+INSERT INTO "ServiceTileMetadata" VALUES(8,'Video:Bitrate');
+INSERT INTO "ServiceTileMetadata" VALUES(12,'Email:Sender');
+INSERT INTO "ServiceTileMetadata" VALUES(12,'Email:Subject');
+INSERT INTO "ServiceTileMetadata" VALUES(12,'Email:Date');
+INSERT INTO "ServiceTileMetadata" VALUES(12,'Email:SentTo');
+INSERT INTO "ServiceTileMetadata" VALUES(12,'Email:CC');
+INSERT INTO "ServiceTileMetadata" VALUES(12,'Email:Attachments');
+INSERT INTO "ServiceTileMetadata" VALUES(13,'Email:Sender');
+INSERT INTO "ServiceTileMetadata" VALUES(13,'Email:Subject');
+INSERT INTO "ServiceTileMetadata" VALUES(13,'Email:Date');
+INSERT INTO "ServiceTileMetadata" VALUES(13,'Email:SentTo');
+INSERT INTO "ServiceTileMetadata" VALUES(13,'Email:CC');
+INSERT INTO "ServiceTileMetadata" VALUES(13,'Email:Attachments');
+INSERT INTO "ServiceTileMetadata" VALUES(14,'Email:Sender');
+INSERT INTO "ServiceTileMetadata" VALUES(14,'Email:Subject');
+INSERT INTO "ServiceTileMetadata" VALUES(14,'Email:Date');
+INSERT INTO "ServiceTileMetadata" VALUES(14,'Email:SentTo');
+INSERT INTO "ServiceTileMetadata" VALUES(14,'Email:CC');
+INSERT INTO "ServiceTileMetadata" VALUES(14,'Email:Attachments');
+INSERT INTO "ServiceTileMetadata" VALUES(15,'Email:Sender');
+INSERT INTO "ServiceTileMetadata" VALUES(15,'Email:Subject');
+INSERT INTO "ServiceTileMetadata" VALUES(15,'Email:Date');
+INSERT INTO "ServiceTileMetadata" VALUES(15,'Email:SentTo');
+INSERT INTO "ServiceTileMetadata" VALUES(15,'Email:CC');
+INSERT INTO "ServiceTileMetadata" VALUES(15,'Email:Attachments');
+INSERT INTO "ServiceTileMetadata" VALUES(16,'Email:Sender');
+INSERT INTO "ServiceTileMetadata" VALUES(16,'Email:Subject');
+INSERT INTO "ServiceTileMetadata" VALUES(16,'Email:Date');
+INSERT INTO "ServiceTileMetadata" VALUES(16,'Email:SentTo');
+INSERT INTO "ServiceTileMetadata" VALUES(16,'Email:CC');
+INSERT INTO "ServiceTileMetadata" VALUES(16,'Email:Attachments');
+INSERT INTO "ServiceTileMetadata" VALUES(23,'App:GenericName');
+INSERT INTO "ServiceTileMetadata" VALUES(23,'AppComment');
+INSERT INTO "ServiceTileMetadata" VALUES(23,'App:Categories');
+CREATE TABLE ServiceTabularMetadata
+(
+	ServiceTypeID		Integer not null,
+	MetaName		Text not null,
+
+	primary key (ServiceTypeID, MetaName)
+);
+INSERT INTO "ServiceTabularMetadata" VALUES(4,'File:Name');
+INSERT INTO "ServiceTabularMetadata" VALUES(4,'File:Mime');
+INSERT INTO "ServiceTabularMetadata" VALUES(4,'Doc:Title');
+INSERT INTO "ServiceTabularMetadata" VALUES(4,'Doc:Author');
+INSERT INTO "ServiceTabularMetadata" VALUES(4,'File:Size');
+INSERT INTO "ServiceTabularMetadata" VALUES(4,'File:Modified');
+INSERT INTO "ServiceTabularMetadata" VALUES(4,'Doc:Created');
+INSERT INTO "ServiceTabularMetadata" VALUES(5,'File:Name');
+INSERT INTO "ServiceTabularMetadata" VALUES(5,'File:Mime');
+INSERT INTO "ServiceTabularMetadata" VALUES(5,'Doc:Title');
+INSERT INTO "ServiceTabularMetadata" VALUES(5,'Doc:URL');
+INSERT INTO "ServiceTabularMetadata" VALUES(5,'Doc:Author');
+INSERT INTO "ServiceTabularMetadata" VALUES(5,'File:Size');
+INSERT INTO "ServiceTabularMetadata" VALUES(5,'File:Modified');
+INSERT INTO "ServiceTabularMetadata" VALUES(5,'Doc:Created');
+INSERT INTO "ServiceTabularMetadata" VALUES(6,'File:Name');
+INSERT INTO "ServiceTabularMetadata" VALUES(6,'Image:Height');
+INSERT INTO "ServiceTabularMetadata" VALUES(6,'Image:Width');
+INSERT INTO "ServiceTabularMetadata" VALUES(6,'Image:Date');
+INSERT INTO "ServiceTabularMetadata" VALUES(6,'File:Modified');
+INSERT INTO "ServiceTabularMetadata" VALUES(6,'Image:Creator');
+INSERT INTO "ServiceTabularMetadata" VALUES(6,'Image:Software');
+INSERT INTO "ServiceTabularMetadata" VALUES(7,'Audio:Title');
+INSERT INTO "ServiceTabularMetadata" VALUES(7,'Audio:Artist');
+INSERT INTO "ServiceTabularMetadata" VALUES(7,'Audio:Album');
+INSERT INTO "ServiceTabularMetadata" VALUES(7,'Audio:Genre');
+INSERT INTO "ServiceTabularMetadata" VALUES(7,'Audio:Duration');
+INSERT INTO "ServiceTabularMetadata" VALUES(7,'Audio:ReleaseDate');
+INSERT INTO "ServiceTabularMetadata" VALUES(8,'File:Name');
+INSERT INTO "ServiceTabularMetadata" VALUES(8,'Video:Title');
+INSERT INTO "ServiceTabularMetadata" VALUES(8,'Video:Author');
+INSERT INTO "ServiceTabularMetadata" VALUES(8,'Video:Height');
+INSERT INTO "ServiceTabularMetadata" VALUES(8,'Video:Width');
+INSERT INTO "ServiceTabularMetadata" VALUES(8,'Video:Duration');
+INSERT INTO "ServiceTabularMetadata" VALUES(8,'Video:Bitrate');
+INSERT INTO "ServiceTabularMetadata" VALUES(12,'Email:Sender');
+INSERT INTO "ServiceTabularMetadata" VALUES(12,'Email:Subject');
+INSERT INTO "ServiceTabularMetadata" VALUES(12,'Email:Date');
+INSERT INTO "ServiceTabularMetadata" VALUES(13,'Email:Sender');
+INSERT INTO "ServiceTabularMetadata" VALUES(13,'Email:Subject');
+INSERT INTO "ServiceTabularMetadata" VALUES(13,'Email:Date');
+INSERT INTO "ServiceTabularMetadata" VALUES(14,'Email:Sender');
+INSERT INTO "ServiceTabularMetadata" VALUES(14,'Email:Subject');
+INSERT INTO "ServiceTabularMetadata" VALUES(14,'Email:Date');
+INSERT INTO "ServiceTabularMetadata" VALUES(15,'Email:Sender');
+INSERT INTO "ServiceTabularMetadata" VALUES(15,'Email:Subject');
+INSERT INTO "ServiceTabularMetadata" VALUES(15,'Email:Date');
+INSERT INTO "ServiceTabularMetadata" VALUES(16,'Email:Sender');
+INSERT INTO "ServiceTabularMetadata" VALUES(16,'Email:Subject');
+INSERT INTO "ServiceTabularMetadata" VALUES(16,'Email:Date');
+CREATE TABLE ServiceTypeOptions
+(
+	ServiceTypeID		Integer not null,
+	OptionName		Text not null,
+	OptionValue		Text default ' ',
+
+	primary key (ServiceTypeID, OptionName)
+);
+CREATE TABLE FileMimes
+(
+	Mime			Text primary key not null,
+	ServiceTypeID		Integer default 0,
+	ThumbExec		Text default ' ',
+	MetadataExec		Text default ' ',
+	FullTextExec		Text default ' '
+
+);
+INSERT INTO "FileMimes" VALUES('application/rtf',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('text/richtext',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/msword',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/pdf',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/postscript',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/x-dvi',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/vnd.ms-excel',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('vnd.ms-powerpoint',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/x-abiword',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('text/html',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('text/sgml',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('text/x-tex',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/x-mswrite',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/x-applix-word',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/docbook+xml',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/x-kword',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/x-kword-crypt',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/x-lyx',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/vnd.lotus-1-2-3',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/x-applix-spreadsheet',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/x-gnumeric',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/x-kspread',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/x-kspread-crypt',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/x-quattropro',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/x-sc',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/x-siag',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/x-magicpoint',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/x-kpresenter',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/illustrator',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/vnd.corel-draw',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/vnd.stardivision.draw',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/vnd.oasis.opendocument.graphics',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/x-dia-diagram',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/x-karbon',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/x-killustrator',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/x-kivio',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/x-kontour',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/x-wpg',4,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/vnd.oasis.opendocument.image',6,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/x-krita',6,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/ogg',7,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('text/plain',9,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('text/x-authors',9,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('text/x-changelog',9,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('text/x-copying',9,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('text/x-credits',9,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('text/x-install',9,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('text/x-readme',9,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/x-perl',10,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/x-shellscript',10,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/x-php',10,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/x-java',10,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/x-javascript',10,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/x-glade',10,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/x-csh',10,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/x-class-file',10,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/x-awk',10,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/x-asp',10,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/x-ruby',10,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('application/x-m4',10,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('text/x-m4',10,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('text/x-c++',10,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('text/x-adasrc',10,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('text/x-c',10,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('text/x-c++hdr',10,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('text/x-chdr',10,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('text/x-csharp',10,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('text/x-c++src',10,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('text/x-csrc',10,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('text/x-dcl',10,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('text/x-dsrc',10,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('text/x-emacs-lisp',10,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('text/x-fortran',10,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('text/x-haskell',10,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('text/x-literate-haskell',10,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('text/x-java',10,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('text/x-java-source" ,text/x-makefile',10,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('text/x-objcsrc',10,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('text/x-pascal',10,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('text/x-patch',10,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('text/x-python',10,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('text/x-scheme',10,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('text/x-sql',10,' ',' ',' ');
+INSERT INTO "FileMimes" VALUES('text/x-tcl',10,' ',' ',' ');
+CREATE TABLE FileMimePrefixes
+(
+	MimePrefix		Text primary key not null,
+	ServiceTypeID		Integer default 0,
+	ThumbExec		Text default ' ',
+	MetadataExec		Text default ' ',
+	FullTextExec		Text default ' '
+
+);
+INSERT INTO "FileMimePrefixes" VALUES('application/vnd.oasis.opendocument',4,' ',' ',' ');
+INSERT INTO "FileMimePrefixes" VALUES('application/vnd.sun.xml',4,' ',' ',' ');
+INSERT INTO "FileMimePrefixes" VALUES('application/vnd.stardivision',4,' ',' ',' ');
+INSERT INTO "FileMimePrefixes" VALUES('image/',6,' ',' ',' ');
+INSERT INTO "FileMimePrefixes" VALUES('audio/',7,' ',' ',' ');
+INSERT INTO "FileMimePrefixes" VALUES('video/',8,' ',' ',' ');
+CREATE INDEX BackupMetaDataIndex1 ON BackupMetaData (ServiceID, MetaDataID);
+CREATE INDEX MetaDataTypesIndex1 ON MetaDataTypes (Alias);
+COMMIT;

Added: trunk/tests/scripts/data/email-contents.sql
==============================================================================
--- (empty file)
+++ trunk/tests/scripts/data/email-contents.sql	Fri Sep 26 10:47:33 2008
@@ -0,0 +1 @@
+

Added: trunk/tests/scripts/data/email-meta.sql
==============================================================================
--- (empty file)
+++ trunk/tests/scripts/data/email-meta.sql	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,67 @@
+BEGIN TRANSACTION;
+ANALYZE sqlite_master;
+CREATE TABLE ChildServices
+(
+	ParentID            		Integer not null,
+	ChildID				Integer not null,
+	MetaDataID			Integer not null,
+
+	primary key (ParentID, ChildID, MetaDataID)
+);
+CREATE TABLE ServiceMetaData 
+(
+	ID			Integer primary key AUTOINCREMENT not null,
+	ServiceID		Integer not null,
+	MetaDataID 		Integer  not null,
+	MetaDataValue     	Text,
+	MetaDataDisplay		Text
+
+);
+DELETE FROM sqlite_sequence;
+CREATE TABLE ServiceKeywordMetaData 
+(
+	ID			Integer primary key AUTOINCREMENT not null,
+	ServiceID		Integer not null,
+	MetaDataID 		Integer not null,
+	MetaDataValue		Text COLLATE NOCASE
+);
+CREATE TABLE ServiceNumericMetaData 
+(
+	ID			Integer primary key AUTOINCREMENT not null,
+	ServiceID		Integer not null,
+	MetaDataID 		Integer not null,
+	MetaDataValue		Integer not null
+);
+CREATE TABLE MailSummary
+(
+	ID		Integer primary key AUTOINCREMENT not null,
+	MailApp		Integer not null,
+	MailType	Integer not null,
+	FileName	Text not null,
+	Path		Text not null,
+	UriPrefix	Text,
+	NeedsChecking	Integer default 0,
+	MailCount	Integer,
+	JunkCount	Integer,
+	DeleteCount	Integer,
+	Offset		Integer,
+	LastOffset	Integer,
+	MTime		integer,
+
+	unique (Path)
+);
+CREATE TABLE JunkMail
+(
+	UID			integer not null,
+	SummaryID		Integer not null,
+
+	primary key (UID, SummaryID)
+);
+CREATE INDEX ChildServicesIndex1 ON ChildServices (ChildID);
+CREATE INDEX ServiceMetaDataIndex1 ON ServiceMetaData (ServiceID);
+CREATE INDEX ServiceMetaDataIndex2 ON ServiceMetaData (MetaDataID);
+CREATE INDEX ServiceKeywordMetaDataIndex1 ON ServiceKeywordMetaData (MetaDataID, MetaDataValue);
+CREATE INDEX ServiceKeywordMetaDataIndex2 ON ServiceKeywordMetaData (ServiceID);
+CREATE INDEX ServiceNumericMetaDataIndex1 ON ServiceNumericMetaData (MetaDataID, MetaDataValue);
+CREATE INDEX ServiceNumericMetaDataIndex2 ON ServiceNumericMetaData (ServiceID);
+COMMIT;

Added: trunk/tests/scripts/data/file-contents.sql
==============================================================================

Added: trunk/tests/scripts/data/file-meta.sql
==============================================================================
--- (empty file)
+++ trunk/tests/scripts/data/file-meta.sql	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,42 @@
+BEGIN TRANSACTION;
+ANALYZE sqlite_master;
+CREATE TABLE ChildServices
+(
+	ParentID            		Integer not null,
+	ChildID				Integer not null,
+	MetaDataID			Integer not null,
+
+	primary key (ParentID, ChildID, MetaDataID)
+);
+CREATE TABLE ServiceMetaData 
+(
+	ID			Integer primary key AUTOINCREMENT not null,
+	ServiceID		Integer not null,
+	MetaDataID 		Integer  not null,
+	MetaDataValue     	Text,
+	MetaDataDisplay		Text
+
+);
+DELETE FROM sqlite_sequence;
+CREATE TABLE ServiceKeywordMetaData 
+(
+	ID			Integer primary key AUTOINCREMENT not null,
+	ServiceID		Integer not null,
+	MetaDataID 		Integer not null,
+	MetaDataValue		Text COLLATE NOCASE
+);
+CREATE TABLE ServiceNumericMetaData 
+(
+	ID			Integer primary key AUTOINCREMENT not null,
+	ServiceID		Integer not null,
+	MetaDataID 		Integer not null,
+	MetaDataValue		Integer not null
+);
+CREATE INDEX ChildServicesIndex1 ON ChildServices (ChildID);
+CREATE INDEX ServiceMetaDataIndex1 ON ServiceMetaData (ServiceID);
+CREATE INDEX ServiceMetaDataIndex2 ON ServiceMetaData (MetaDataID);
+CREATE INDEX ServiceKeywordMetaDataIndex1 ON ServiceKeywordMetaData (MetaDataID, MetaDataValue);
+CREATE INDEX ServiceKeywordMetaDataIndex2 ON ServiceKeywordMetaData (ServiceID);
+CREATE INDEX ServiceNumericMetaDataIndex1 ON ServiceNumericMetaData (MetaDataID, MetaDataValue);
+CREATE INDEX ServiceNumericMetaDataIndex2 ON ServiceNumericMetaData (ServiceID);
+COMMIT;

Added: trunk/tests/scripts/data/xesam.sql
==============================================================================
--- (empty file)
+++ trunk/tests/scripts/data/xesam.sql	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,714 @@
+BEGIN TRANSACTION;
+ANALYZE sqlite_master;
+CREATE TABLE XesamMetaDataTypes 
+(
+	ID	 		Integer primary key AUTOINCREMENT not null,
+	MetaName		Text not null  COLLATE NOCASE, 
+	DataTypeID		Integer default 0,
+	DisplayName		text,
+	Description		text default ' ',
+	Enabled			integer default 1, /* used to prevent use of this metadata type */
+	UIVisible		integer default 0, /* should this metadata type be visible in a search criteria UI  */
+	WriteExec		text default ' ', /* used to specify an external program that can write an *embedded* metadata to a file */
+	Alias			text default ' ', /* alternate name for this type (XESAM specs?) */
+	FieldName		text default ' ', /* filedname if present in the services table */
+	Weight			Integer default 1, /* weight of metdata type in ranking */
+	Embedded		Integer default 1, /* 1 if metadata extracted from the file by the indexer and is not updateable by the user. 0 - this metadata can be updated by the user and is external to the file */
+	MultipleValues		Integer default 0, /* 0= type cannot have multiple values per entity, 1= type can have more than 1 value per entity */
+	Delimited		Integer default 0, /* if 1, extra delimiters (hyphen and underscore) are used to break word */
+	Filtered		Integer default 1, /* if 1, words are filtered for numerics (if numeric indexing is disabled), stopwords and min length */
+	Abstract		Integer default 0, /* if 0, can be used for storing metadata - Abstract type classes cannot store metadata and can only be used for searching its decendants */
+	StemMetadata		Integer default 1, /* 1 if metadata should be stemmed */
+	SideCar			Integer default 0, /* should this metadata be backed up in an xmp sidecar file */
+	FileName		Text default ' ',
+
+	Categories		text default ' ',
+	Parents			text default ' ',
+
+	Unique (MetaName)
+);
+INSERT INTO "XesamMetaDataTypes" VALUES(1,'xesam:35mmEquivalent',5,NULL,'Photo metering mode',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Photo',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(2,'xesam:acl',3,NULL,'File access control list',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Filelike',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(3,'xesam:album',3,NULL,'Media album',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Media',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(4,'xesam:albumArtist',3,NULL,'Music album artist',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Audio',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(5,'xesam:albumGain',5,NULL,'Gain adjustment of album',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Audio',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(6,'xesam:albumPeakGain',5,NULL,'Peak gain adjustment of album',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Audio',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(7,'xesam:albumTrackCount',4,NULL,'Album track count',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Audio',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(8,'xesam:aperture',5,NULL,'Photo aperture',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Photo',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(9,'xesam:artist',3,NULL,'Music artist',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Audio',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(10,'xesam:asText',3,NULL,'Content plain-text representation for indexing purposes',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Content',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(11,'xesam:aspectRatio',3,NULL,'Visual content aspect ratio',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Visual',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(12,'xesam:attachmentEncoding',3,NULL,'Email attachment encoding(base64,utf-7, etc)',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:EmailAttachment',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(13,'xesam:audioBPM',4,NULL,'Beats per minute',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Audio',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(14,'xesam:audioBitrate',4,NULL,'Audio Bitrate',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Audio',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(15,'xesam:audioChannels',3,NULL,'Audio channels',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Audio',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(16,'xesam:audioCodec',3,NULL,'Audio codec',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Audio',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(17,'xesam:audioCodecType',3,NULL,'Audio codec type',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Audio',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(18,'xesam:audioSampleBitDepth',4,NULL,'Audio sample data bit depth',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Audio',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(19,'xesam:audioSampleCount',4,NULL,'Audio sample count',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Audio',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(20,'xesam:audioSampleDataType',3,NULL,'Audio sample data type',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Audio',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(21,'xesam:audioSampleRate',5,NULL,'Audio sample rate',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Audio',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(22,'xesam:author',3,NULL,'Content author. Primary contributor.',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Content',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(23,'xesam:autoRating',0,NULL,'Rating of the object provided automatically by software, inferred from user behavior or other indirect indicators.',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Source',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(24,'xesam:baseRevisionID',3,NULL,'RevisionID on which a revision-controlled file is based',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:RevisionControlledFile',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(25,'xesam:bcc',3,NULL,'BCC:',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Email',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(26,'xesam:birthDate',6,NULL,'Contact birthDate',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Person',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(27,'xesam:blogContactURL',3,NULL,'Contact blog URL',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Contact',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(28,'xesam:cameraManufacturer',3,NULL,'Photo camera manufacturer',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Photo',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(29,'xesam:cameraModel',3,NULL,'Photo camera model',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Photo',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(30,'xesam:cc',3,NULL,'CC:',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Email',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(31,'xesam:ccdWidth',5,NULL,'Photo CCD Width',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Photo',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(32,'xesam:cellPhoneNumber',3,NULL,'Contact cell phone number',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Contact',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(33,'xesam:changeCommitTime',6,NULL,'Time of the last change to the base file in the repository(preceding the baseRevisionID?)',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:RevisionControlledFile',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(34,'xesam:changeCommitter',3,NULL,'Who made the last change to the base file in the repository(preceding the baseRevisionID?)',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:RevisionControlledFile',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(35,'xesam:characterCount',4,NULL,'Text character count',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Text',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(36,'xesam:charset',3,NULL,'Content charset encoding',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Content',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(37,'xesam:chatRoom',3,NULL,'Chatroom this message belongs to',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:IMMessage',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(38,'xesam:colorCount',4,NULL,'Visual content color count for palettes',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Visual',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(39,'xesam:colorSpace',3,NULL,'Visual content color space(RGB, CMYK etc)',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Visual',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(40,'xesam:columnCount',4,NULL,'Spreadsheet column count',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Spreadsheet',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(41,'xesam:comment',3,NULL,'Object comment',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:DataObject',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(42,'xesam:commentCharacterCount',4,NULL,'Source code comment character count',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:SourceCode',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(43,'xesam:commitDiff',0,NULL,'The diff of the content and the base file',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:RevisionControlledFile',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(44,'xesam:communicationChannel',3,NULL,'Message communication channel like chatroom name or mailing list',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Message',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(45,'xesam:composer',3,NULL,'Music composer',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Audio',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(46,'xesam:compressionAlgorithm',3,NULL,'Compression algorithm for archivers which support several',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:ArchivedFile',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(47,'xesam:compressionLevel',3,NULL,'Level of compression. How much effort was spent towards achieving maximal compression ratio.',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:ArchivedFile;xesam:Media',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(48,'xesam:conflicts',3,NULL,'Software conflicts with',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:SoftwarePackage',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(49,'xesam:contactMedium',3,NULL,'Generic contact medium',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Contact',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(50,'xesam:contactNick',3,NULL,'Contact nick',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Contact',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(51,'xesam:contactURL',3,NULL,'Contact URL',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Contact',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(52,'xesam:contains',3,NULL,'Containment relation',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Content',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(53,'xesam:contentComment',3,NULL,'Content comment',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Content',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(54,'xesam:contentCreated',6,NULL,'Content creation time',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Content',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(55,'xesam:contentKeyword',3,NULL,'Content keyword/tag',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Content',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(56,'xesam:contentModified',6,NULL,'Content last modification time',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Content',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(57,'xesam:contentType',3,NULL,'Email content mime type/charset',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Email',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(58,'xesam:contributor',3,NULL,'Content contributor. Secondary contributor.',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Content',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(59,'xesam:copyright',3,NULL,'Content copyright',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Content',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(60,'xesam:creator',0,NULL,'Abstract content creator. Use children',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Content',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(61,'xesam:definesClass',3,NULL,'Source code defines class',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:SourceCode',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(62,'xesam:definesFunction',3,NULL,'Source code defines function',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:SourceCode',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(63,'xesam:definesGlobalVariable',3,NULL,'Source code defines global variable',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:SourceCode',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(64,'xesam:deletionTime',6,NULL,'File deletion time',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:DeletedFile',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(65,'xesam:depends',3,NULL,'Dependency relation',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Content',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(66,'xesam:derivedFrom',3,NULL,'Links to the original content from which this content is derived',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Content',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(67,'xesam:description',3,NULL,'Content description. Description of content an order of magnitude more elaborate than Title',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Content',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(68,'xesam:discNumber',4,NULL,'Audio cd number',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Audio',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(69,'xesam:disclaimer',3,NULL,'Content disclaimer',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Content',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(70,'xesam:documentCategory',3,NULL,'Document category: book, article, flyer, pamphlet whatever',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Document',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(71,'xesam:emailAddress',3,NULL,'Contact email address',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Contact',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(72,'xesam:exposureBias',3,NULL,'Photo exposure bias',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Photo',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(73,'xesam:exposureProgram',3,NULL,'Photo exposure program',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Photo',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(74,'xesam:exposureTime',6,NULL,'Photo exposure time',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Photo',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(75,'xesam:familyName',3,NULL,'Person family name',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Person',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(76,'xesam:faxPhoneNumber',3,NULL,'Contact fax phone number',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Contact',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(77,'xesam:fileExtension',3,NULL,'File extension',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Filelike',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(78,'xesam:fileSystemType',3,NULL,'File system type e.g. ext3',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:FileSystem',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(79,'xesam:fingerprint',0,NULL,'Content fingerprint: a small ID calculated from content byte stream, aimed at uniquely identifying the content. Abstract.',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Content',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(80,'xesam:firstUsed',6,NULL,'When the content was used for the first time',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Source',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(81,'xesam:flashUsed',3,NULL,'Photo flash used',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Photo',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(82,'xesam:focalLength',5,NULL,'Photo focal length',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Photo',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(83,'xesam:focusDistance',5,NULL,'Photo focus distance',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Photo',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(84,'xesam:formatSubtype',3,NULL,'Format subtype. Use to indicate format extensions/specifics',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Content',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(85,'xesam:frameCount',4,NULL,'Visual content frame count',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Visual',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(86,'xesam:frameRate',5,NULL,'Visual content frame rate',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Visual',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(87,'xesam:freeSpace',4,NULL,'File system free space',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:FileSystem',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(88,'xesam:gender',3,NULL,'Contact gender',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Person',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(89,'xesam:generator',3,NULL,'Software used to generate the content byte stream',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Content',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(90,'xesam:generatorOptions',3,NULL,'Generator software options',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Content',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(91,'xesam:genre',3,NULL,'Media genre',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Media',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(92,'xesam:givenName',3,NULL,'Person given name',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Person',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(93,'xesam:group',3,NULL,'File group',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Filelike',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(94,'xesam:height',4,NULL,'Visual content height',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Visual',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(95,'xesam:homeEmailAddress',3,NULL,'Contact home email address',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Person',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(96,'xesam:homePhoneNumber',3,NULL,'Contact home phone number',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Person',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(97,'xesam:homePostalAddress',3,NULL,'Contact home address',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Person',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(98,'xesam:homepageContactURL',3,NULL,'Contact homepage URL',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Contact',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(99,'xesam:honorificPrefix',3,NULL,'Person honorific name prefix',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Person',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(100,'xesam:honorificSuffix',3,NULL,'Person honorific name suffix',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Person',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(101,'xesam:horizontalResolution',4,NULL,'Visual content horizontal resolution',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Visual',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(102,'xesam:id',3,NULL,'Content ID',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Content',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(103,'xesam:imContactMedium',3,NULL,'Generic IM contact medium',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Contact',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(104,'xesam:inReplyTo',3,NULL,'In-Reply-To:',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Email',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(105,'xesam:interest',3,NULL,'Contact interests/hobbies',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Contact',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(106,'xesam:interlaceMode',3,NULL,'Visual content interlace mode',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Visual',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(107,'xesam:ircContactMedium',3,NULL,'Contact IRC ID server',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Contact',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(108,'xesam:isContentEncrypted',4,NULL,' ',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Content',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(109,'xesam:isEncrypted',0,NULL,'Is Object or part of it encrypted?',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:DataObject',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(110,'xesam:isImportant',4,NULL,'Is the message important',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:MessageboxMessage',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(111,'xesam:isInProgress',4,NULL,'Is the message in progress',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:MessageboxMessage',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(112,'xesam:isPublicChannel',4,NULL,'Is channel public?',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:CommunicationChannel',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(113,'xesam:isRead',4,NULL,'Is the message read',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:MessageboxMessage',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(114,'xesam:isSourceEncrypted',4,NULL,'Is archived file password-protected?',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:ArchivedFile',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(115,'xesam:isoEquivalent',3,NULL,'Photo ISO equivalent',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Photo',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(116,'xesam:jabberContactMedium',3,NULL,'Contact Jabber ID',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Contact',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(117,'xesam:keyword',3,NULL,'Object keyword/tag',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:DataObject',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(118,'xesam:knows',3,NULL,'FOAF:knows relation. Points to a contact known by this contact.',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Contact',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(119,'xesam:language',3,NULL,'Content language',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Content',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(120,'xesam:lastRefreshed',6,NULL,'Last time the resource info was refreshed',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:RemoteResource',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(121,'xesam:lastUsed',6,NULL,'When the content was last used. Different from last access as this only accounts usage by the user e.g. playing a song as opposed to apps scanning the HD',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Source',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(122,'xesam:legal',3,NULL,'Abstract content legal notice.',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Content',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(123,'xesam:license',3,NULL,'Content license',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Content',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(124,'xesam:licenseType',3,NULL,'Content license type',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Content',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(125,'xesam:lineCount',4,NULL,'Text line count',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Text',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(126,'xesam:links',3,NULL,'Linking/mention relation',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Content',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(127,'xesam:localRevision',3,NULL,'Local revision number. An automatically generated ID that is changed everytime the generator software/revisioning system deems the content has changed.',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Source',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(128,'xesam:lyricist',3,NULL,'Music lyricist',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Audio',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(129,'xesam:mailingList',3,NULL,'Mailing list this message belongs to',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:MailingListEmail',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(130,'xesam:mailingPostalAddress',3,NULL,'Contact mailing address',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Contact',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(131,'xesam:maintainer',3,NULL,'Content maintainer.',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Content',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(132,'xesam:markupCharacterCount',4,NULL,'XML markup character count',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:XML',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(133,'xesam:md5Hash',0,NULL,'MD5 hash',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Content',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(134,'xesam:mediaBitrate',0,NULL,'Media bitrate',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Media',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(135,'xesam:mediaCodec',3,NULL,'Media codec',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Media',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(136,'xesam:mediaCodecType',3,NULL,'Media codec type: lossless, CBR, ABR, VBR',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Media',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(137,'xesam:mediaDuration',0,NULL,'Media duration',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Media',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(138,'xesam:mergeConflict',4,NULL,' ',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:RevisionControlledFile',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(139,'xesam:meteringMode',3,NULL,'Photo metering mode',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Photo',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(140,'xesam:mimeType',3,NULL,'Content mime-type',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Content',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(141,'xesam:mountPoint',3,NULL,'File system mount point',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:FileSystem',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(142,'xesam:name',3,NULL,'Name provided by container',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Source',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(143,'xesam:newsGroup',3,NULL,'News group this message belongs to',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:NewsGroupEmail',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(144,'xesam:occupiedSpace',4,NULL,'File system occupied space',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:FileSystem',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(145,'xesam:orientation',3,NULL,'Photo orientation',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Photo',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(146,'xesam:originURL',0,NULL,'Origin URL, e.g. where the file had been downloaded from',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Source',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(147,'xesam:originalLocation',3,NULL,'Deleted file original location',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:DeletedFile',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(148,'xesam:otherName',3,NULL,'Person other name',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Person',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(149,'xesam:owner',3,NULL,'File owner',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Filelike',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(150,'xesam:pageCount',4,NULL,'Document page count. Slide count for presentations',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Document',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(151,'xesam:paragrapCount',4,NULL,'Document paragraph count',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Document',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(152,'xesam:performer',3,NULL,'Music performer',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Audio',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(153,'xesam:permissions',3,NULL,'File permissions',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Filelike',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(154,'xesam:personPhoto',3,NULL,'Contact photo/avatar',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Person',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(155,'xesam:phoneNumber',3,NULL,'Contact phone number',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Contact',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(156,'xesam:physicalAddress',3,NULL,'Contact postal address',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Contact',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(157,'xesam:pixelDataBitDepth',4,NULL,'Visual content pixel data bit depth',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Visual',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(158,'xesam:pixelDataType',3,NULL,'Visual content pixel data type',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Visual',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(159,'xesam:primaryRecipient',3,NULL,'Message primary recipient',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Message',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(160,'xesam:programmingLanguage',3,NULL,'Source code programming language',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:SourceCode',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(161,'xesam:receptionTime',6,NULL,'Message reception time',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Message',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(162,'xesam:recipient',3,NULL,'Message recipient',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Message',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(163,'xesam:related',3,NULL,'Abstract content relation. Use children',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Content',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(164,'xesam:remotePassword',3,NULL,'Remote resource user password',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:RemoteResource',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(165,'xesam:remotePort',4,NULL,'Server port',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:RemoteResource',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(166,'xesam:remoteServer',3,NULL,'The server hosting the remote resource',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:RemoteResource',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(167,'xesam:remoteUser',3,NULL,'Remote resource user name',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:RemoteResource',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(168,'xesam:replyTo',3,NULL,'ReplyTo:',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Email',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(169,'xesam:rowCount',4,NULL,'Spreadsheet row count',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Spreadsheet',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(170,'xesam:rssFeed',3,NULL,'RSS feed that provided the message',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:RSSMessage',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(171,'xesam:sampleBitDepth',0,NULL,'Media sample data bit depth: 8, 16, 24, 32 etc',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Media',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(172,'xesam:sampleConfiguration',0,NULL,'Media sample configuration/arrangement of components',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Media',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(173,'xesam:sampleDataType',3,NULL,'Media sample data type: int, float',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Media',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(174,'xesam:secondaryRecipient',3,NULL,'Message secondary recipient',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Message',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(175,'xesam:seenAttachedAs',3,NULL,'Name of block device seen to contain the Content when it was online.',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:OfflineMedia',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(176,'xesam:setCount',0,NULL,'Media set count. Sample count for audio(set=one sample per channel), frame count for video',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Media',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(177,'xesam:setRate',0,NULL,'Media set rate. Sample rate for audio(set=one sample per channel), FPS for video',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Media',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(178,'xesam:sha1Hash',0,NULL,'SHA1 hash',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Content',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(179,'xesam:size',4,NULL,'Content/data size in bytes. See also storageSize',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Content',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(180,'xesam:sourceCreated',6,NULL,'Local copy creation time',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Source',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(181,'xesam:sourceModified',6,NULL,'Local copy modification time',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Source',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(182,'xesam:storedSize',4,NULL,'Actual space occupied by the object in the source storage. e.g. compressed file size in archive',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Source',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(183,'xesam:subject',3,NULL,'Content subject. The shortest possible description of content',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Content',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(184,'xesam:summary',3,NULL,'Content summary. Description of content an order of magnitude more elaborate than Description',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Content',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(185,'xesam:supercedes',3,NULL,'Software supercedes',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:SoftwarePackage',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(186,'xesam:targetQuality',3,NULL,'The desired level of quality loss of lossy compression',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Media',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(187,'xesam:title',3,NULL,'Content title. Description of content an order of magnitude more elaborate than Subject',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Content',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(188,'xesam:to',3,NULL,'To:',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Email',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(189,'xesam:totalSpace',4,NULL,'File system total usable space, unlike size, which is the byte size of the entire filesystem including overhead.',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:FileSystem',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(190,'xesam:totalUncompressedSize',4,NULL,'Archive total uncompressed size',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Archive',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(191,'xesam:trackGain',5,NULL,'Gain adjustment of track',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Audio',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(192,'xesam:trackNumber',4,NULL,'Audio track number',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Audio',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(193,'xesam:trackPeakGain',5,NULL,'Peak gain adjustment of track',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Audio',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(194,'xesam:url',3,NULL,'URL to access the content',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Source',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(195,'xesam:useCount',4,NULL,'How many times the content was used. Only usage by the user(not general software access) counts.',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Source',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(196,'xesam:userComment',3,NULL,'User-provided comment',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Source',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(197,'xesam:userKeyword',3,NULL,'User-provided keywords',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Source',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(198,'xesam:userRating',0,NULL,'User-provided rating of the object',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Source',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(199,'xesam:usesNamespace',3,NULL,'Namespace referenced by the XML',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:XML',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(200,'xesam:version',3,NULL,'Content version',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Content',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(201,'xesam:verticalResolution',4,NULL,'Visual content vertical resolution',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Visual',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(202,'xesam:videoBitrate',4,NULL,'Video Bitrate',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Video',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(203,'xesam:videoCodec',3,NULL,'Video codec',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Video',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(204,'xesam:videoCodecType',3,NULL,'Video codec type',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Video',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(205,'xesam:whiteBalance',3,NULL,'Photo white balance',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Photo',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(206,'xesam:width',4,NULL,'Visual content width',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Visual',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(207,'xesam:wordCount',4,NULL,'Text word count',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Text',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(208,'xesam:workEmailAddress',3,NULL,'Contact work email address',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Person',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(209,'xesam:workPhoneNumber',3,NULL,'Contact work phone number',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Person',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(210,'xesam:workPostalAddress',3,NULL,'Contact work address',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Person',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(211,'xesam:actionAccessClassification',3,NULL,'PIM entry access classification',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Event;xesam:Journal;xesam:Task',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(212,'xesam:actionContact',3,NULL,'PIM entry contact',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Event;xesam:FreeBusy;xesam:Journal;xesam:Task',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(213,'xesam:actionDuration',3,NULL,'PIM entry action duration',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Alarm;xesam:Event;xesam:FreeBusy;xesam:Task',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(214,'xesam:actionEnd',3,NULL,'PIM entry action end',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Event;xesam:FreeBusy',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(215,'xesam:actionExceptionDate',3,NULL,'PIM entry exception date',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Event;xesam:Journal;xesam:Task',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(216,'xesam:actionExceptionRule',3,NULL,'PIM entry exception rule',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Event;xesam:Journal;xesam:Task',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(217,'xesam:actionLocation',3,NULL,'PIM entry location',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Event;xesam:Task',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(218,'xesam:actionOrganizer',3,NULL,'PIM entry organizer',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Event;xesam:FreeBusy;xesam:Journal;xesam:Task',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(219,'xesam:actionPriority',3,NULL,'PIM entry priority',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Event;xesam:Task',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(220,'xesam:actionRecurrenceDate',3,NULL,'PIM entry recurrence date',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Event;xesam:Journal;xesam:Task',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(221,'xesam:actionRecurrenceID',3,NULL,'PIM entry recurrence ID',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Event;xesam:Journal;xesam:Task',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(222,'xesam:actionRecurrenceRule',3,NULL,'PIM entry recurrence rule',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Event;xesam:Journal;xesam:Task',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(223,'xesam:actionResources',3,NULL,'PIM activity has alarm',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Event;xesam:Task',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(224,'xesam:actionStart',3,NULL,'PIM entry action start',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Event;xesam:FreeBusy;xesam:Task',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(225,'xesam:actionStatus',3,NULL,'PIM entry status',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Event;xesam:Journal;xesam:Task',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(226,'xesam:actionTrigger',3,NULL,'PIM entry action trigger',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Alarm;xesam:Event;xesam:Task',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(227,'xesam:actionURL',3,NULL,'PIM entry URL',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Event;xesam:FreeBusy;xesam:Journal;xesam:Task',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(228,'xesam:aimContactMedium',3,NULL,'Contact AIM ID',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Contact',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(229,'xesam:alarmAction',3,NULL,'Alarm action',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Alarm',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(230,'xesam:alarmRepeat',3,NULL,'Alarm repeat',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Alarm',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(231,'xesam:applicationDesktopEntryExec',3,NULL,'Command to execute, possibly with arguments',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:ApplicationDesktopEntry',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(232,'xesam:attendee',3,NULL,'PIM entry attendee',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Alarm;xesam:Event;xesam:FreeBusy;xesam:Journal;xesam:Task',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(233,'xesam:desktopEntryIcon',3,NULL,'Desktop entry Icon field value conforming to http://freedesktop.org/wiki/Standards/icon-theme-spec',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:DesktopEntry',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(234,'xesam:desktopMenuCategory',3,NULL,'Category in which the entry should be shown in the desktop menu. http://www.freedesktop.org/Standards/menu-spec',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:DesktopEntry',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(235,'xesam:eventEnd',3,NULL,'Event end time',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Event',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(236,'xesam:eventLocation',3,NULL,'Event location',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Event',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(237,'xesam:eventStart',3,NULL,'Event start time',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Event',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(238,'xesam:eventTransparrent',3,NULL,'Is event transparrent(makes person busy)',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Event',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(239,'xesam:icqContactMedium',3,NULL,'Contact ICQ ID',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Contact',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(240,'xesam:imdbId',0,NULL,'IMDB.com video ID',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Video',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(241,'xesam:isrc',0,NULL,'International Standard Recording Code',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Media',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(242,'xesam:msnContactMedium',3,NULL,'Contact MSN ID',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Contact',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(243,'xesam:musicBrainzAlbumArtistID',3,NULL,'MusicBrainz album artist ID in UUID format',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Audio',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(244,'xesam:musicBrainzAlbumID',3,NULL,'MusicBrainz album ID in UUID format',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Audio',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(245,'xesam:musicBrainzArtistID',3,NULL,'MusicBrainz artist ID in UUID format',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Audio',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(246,'xesam:musicBrainzFingerprint',0,NULL,'MusicBrainz track fingerprint',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Audio',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(247,'xesam:musicBrainzTrackID',3,NULL,'MusicBrainz track ID in UUID format',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Audio',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(248,'xesam:skypeContactMedium',3,NULL,'Contact Skype ID',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Contact',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(249,'xesam:supportedMimeType',3,NULL,'The MIME type supported by this application',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:ApplicationDesktopEntry',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(250,'xesam:taskCompleted',0,NULL,'Is task completed?',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Task',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(251,'xesam:taskDue',0,NULL,'Task due date/time',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Task',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(252,'xesam:taskPercentComplete',0,NULL,'Task completeness',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:Task',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(253,'xesam:yahooContactMedium',3,NULL,'Contact Yahoo ID',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:Contact',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(254,'xesam:contentCategory',3,NULL,'Identifier of content category',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:DataObject',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(255,'xesam:sourceCategory',3,NULL,'Identifier of source category',1,0,' ',' ',' ',1,1,1,0,1,0,1,0,' ','xesam:DataObject',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(256,'xesam:relevancyRating',5,NULL,'Query relevancy rating of the object ',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:DataObject',' ');
+INSERT INTO "XesamMetaDataTypes" VALUES(257,'xesam:snippet',3,NULL,'Data snippet relevant to the search query ',1,0,' ',' ',' ',1,1,0,0,1,0,1,0,' ','xesam:DataObject',' ');
+DELETE FROM sqlite_sequence;
+INSERT INTO "sqlite_sequence" VALUES('XesamMetaDataTypes',257);
+INSERT INTO "sqlite_sequence" VALUES('XesamServiceTypes',69);
+INSERT INTO "sqlite_sequence" VALUES('XesamServiceMapping',12);
+INSERT INTO "sqlite_sequence" VALUES('XesamMetaDataMapping',15);
+INSERT INTO "sqlite_sequence" VALUES('XesamServiceLookup',57);
+INSERT INTO "sqlite_sequence" VALUES('XesamMetaDataLookup',15);
+CREATE TABLE XesamServiceTypes
+(
+	TypeID 			Integer primary key AUTOINCREMENT not null,
+	TypeName		Text COLLATE NOCASE not null,
+
+	TypeCount		Integer default 0,
+
+	DisplayName		Text default ' ',
+	Enabled			Integer default 1, 
+	Embedded		Integer default 1, /* service is created by the indexer if embedded. User or app defined services are not embedded */
+	ChildResource		Integer default 0, /* service is a child service */
+	
+	CreateDesktopFile	Integer default 0, /* used by a UI to indicate whether it should create a desktop file for the service if its copied (using the ViewerExec field + uri) */
+
+	/* useful for a UI when determining what actions a hit can have */
+	CanCopy			Integer default 1, 
+	CanDelete		Integer default 1,
+
+	ShowServiceFiles	Integer default 0,
+	ShowServiceDirectories  Integer default 0,
+
+	HasMetadata		Integer default 1,
+	HasFullText		Integer default 1,
+	HasThumbs		Integer default 1,
+	
+	ContentMetadata		Text default ' ', /* the content field is the one most likely to be used for showing a search snippet */ 
+
+	KeyMetadata1		Text default ' ', /* the most commonly requested metadata (especially for tables/grid views) is cached int he services table for extra fast retrieval */
+	KeyMetadata2		Text default ' ',
+	KeyMetadata3		Text default ' ',
+	KeyMetadata4		Text default ' ',
+	KeyMetadata5		Text default ' ',
+	KeyMetadata6		Text default ' ',
+	KeyMetadata7		Text default ' ',
+	KeyMetadata8		Text default ' ',
+	KeyMetadata9		Text default ' ',
+	KeyMetadata10		Text default ' ',
+	KeyMetadata11		Text default ' ',
+
+	UIVisible		Integer default 0,	/* should service appear in a search GUI? */
+	UITitle			Text default ' ',	/* title format as displayed in the metadata tile */
+	UIMetadata1		Text default ' ',	/*UI fields to show in GUI for a hit - if not set then Name,Path,Mime are used */
+	UIMetadata2		Text default ' ',
+	UIMetadata3		Text default ' ',
+	UIView			Text default 'default',
+
+	Description		Text default ' ',
+	Database		integer default 0, /* 0 = DB_FILES, 1 = DB_EMAILS, 2 = DB_MISC, 3 = DB_USER */
+	Icon			Text default ' ',
+
+	IndexerExec		Text default ' ',
+	IndexerOutput		Text default 'stdout',
+	ThumbExec		Text default ' ',
+	ViewerExec		Text default ' ',
+
+	WatchFolders		Text default ' ',
+	IncludeGlob		Text default ' ',
+	ExcludeGlob		Text default ' ',
+
+	FileName		Text default ' ',
+
+	Parents			text default ' ',
+
+	unique (TypeName)
+);
+INSERT INTO "XesamServiceTypes" VALUES(1,'xesam:Annotation',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Generic annotation. Annotation provides a set of document description properties(like subject, title, description) for a list of objects it links to. It can link to other annotations, however interpretation of this may differ between specific annotation classes..',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(2,'xesam:Archive',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Generic archive',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(3,'xesam:ArchivedFile',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','File stored in an archive',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(4,'xesam:Audio',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Defines audio aspect of content. The content itself may have other aspects.',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(5,'xesam:AudioList',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Generic audio list(playlist). Linking to other content types is forbidden',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(6,'xesam:BlockDevice',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Generic block device. Typically contains partitions/filesystems',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(7,'xesam:Bookmark',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default',' ',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(8,'xesam:CommunicationChannel',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Communication channel',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(9,'xesam:Contact',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Contact',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(10,'xesam:ContactGroup',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','ContactGroup',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(11,'xesam:Content',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Generic content',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(12,'xesam:DataObject',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Generic data object. Unites form and essense aspects represented by Content and Source. Used to aggreate properties that may be extracted from both content and source.',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(13,'xesam:DeletedFile',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','File deleted to trash',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(14,'xesam:Document',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Document is an arrangement of various atomic data types with text being the primary data type.',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(15,'xesam:Documentation',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Documentation is a document containing help, manuals, guides.',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(16,'xesam:Email',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Email message',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(17,'xesam:EmailAttachment',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Generic storage',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(18,'xesam:EmbeddedObject',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Generic embedded/inlined object: attachment, inlined SVG, script etc.',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(19,'xesam:File',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Regular file stored in a filesystem',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(20,'xesam:FileSystem',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Filesystem differs from other containers in that it has total/free/occupied space(though DBs too have similar properties), has volume(content.title), UUID for *ix(content.ID), mount point(if mounted)',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(21,'xesam:Filelike',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','File-like object',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(22,'xesam:Folder',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Generic folder. In general, folders represent a tree-like structure(taxonomy), however on occasion this rule may violated in cases like symlinks.',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(23,'xesam:IMAP4Message',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','IMAP4 mailbox message',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(24,'xesam:IMMessage',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Generic Instant Messaging message',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(25,'xesam:Image',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Visual content',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(26,'xesam:MailingList',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Mailing list',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(27,'xesam:MailingListEmail',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Email message addressed at/received from a mailing list',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(28,'xesam:Media',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Generic raster/sampled media is considered consisting of Sets of Samples being reproduced(played/shown) at once. We describe: sample data type(int/float), data bit depth,configuration(color space for images, channel count for audio); set configuration(pixel dimensions for image); set count and rate.',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(29,'xesam:MediaList',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Generic media content list(playlist). Linking to other content types is forbidden',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(30,'xesam:Message',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Generic message',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(31,'xesam:MessageboxMessage',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Message stored in a message box',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(32,'xesam:Music',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Music content',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(33,'xesam:NewsGroup',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','News group',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(34,'xesam:NewsGroupEmail',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Email message addressed at/received from a news group',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(35,'xesam:OfflineMedia',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Generic offline media. e.g. USB drive not attached at this moment.',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(36,'xesam:Organization',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Organization',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(37,'xesam:POP3Message',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','POP3 mailbox message',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(38,'xesam:Person',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Person',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(39,'xesam:PersonalEmail',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Personal email message(not related to a mailing list or discussion group)',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(40,'xesam:Photo',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','An image with EXIF tags(photo)',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(41,'xesam:Presentation',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Presentation document',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(42,'xesam:Project',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Generic project',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(43,'xesam:RSSFeed',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','RSS feed',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(44,'xesam:RSSMessage',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','RSS message(RSS feed item)',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(45,'xesam:RemoteFile',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Remote file',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(46,'xesam:RemoteMessageboxMessage',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Remote messagebox message',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(47,'xesam:RemoteResource',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Generic remote resource',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(48,'xesam:RevisionControlledFile',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','File managed by a revision control system',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(49,'xesam:RevisionControlledRepository',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Revision-controlled repository. In case of distributed repositories, those must be linked with derivation relations.',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(50,'xesam:SoftwarePackage',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Software distribution package',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(51,'xesam:Source',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Generic source',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(52,'xesam:SourceCode',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Source Code',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(53,'xesam:Spreadsheet',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Spreadsheet document',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(54,'xesam:SystemResource',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Generic system resource like man documentation',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(55,'xesam:Tag',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Tag/keyword. As opposed to folders, there are no limitations on the structure of tags and arbitrary overlaps are possible.',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(56,'xesam:Text',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Defines a textual aspect of content. Properties represent only actual content intended for the user, not markup. Other parts of content like markup should be described using other clsses. Abstract class.',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(57,'xesam:TextDocument',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Text document. For word processing apps.',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(58,'xesam:UncategorizedText',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default',' ',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(59,'xesam:Video',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Video content',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(60,'xesam:Visual',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Visual content',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(61,'xesam:XML',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','XML content',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(62,'xesam:Alarm',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Alarm',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(63,'xesam:ApplicationDesktopEntry',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Application Desktop Entry',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(64,'xesam:DesktopEntry',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Desktop Entry(typically a .desktop file)',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(65,'xesam:Event',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Event',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(66,'xesam:FreeBusy',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','FreeBusy',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(67,'xesam:Journal',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Journal',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(68,'xesam:PIM',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Generic PIM',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+INSERT INTO "XesamServiceTypes" VALUES(69,'xesam:Task',0,' ',1,1,0,0,1,1,0,0,1,1,1,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0,' ',' ',' ',' ','default','Task',0,' ',' ','stdout',' ',' ',' ',' ',' ',' ',' ');
+CREATE TABLE XesamServiceMapping
+(
+	ID			Integer primary key AUTOINCREMENT not null,
+	XesamTypeName		Text,
+	TypeName		Text,
+
+	unique (XesamTypeName, TypeName)
+);
+INSERT INTO "XesamServiceMapping" VALUES(1,'xesam:Audio','Audio');
+INSERT INTO "XesamServiceMapping" VALUES(2,'xesam:Document','Documents');
+INSERT INTO "XesamServiceMapping" VALUES(3,'xesam:Documentation','Documents');
+INSERT INTO "XesamServiceMapping" VALUES(4,'xesam:Email','Email');
+INSERT INTO "XesamServiceMapping" VALUES(5,'xesam:EmailAttachment','EmailAttachment');
+INSERT INTO "XesamServiceMapping" VALUES(6,'xesam:File','File');
+INSERT INTO "XesamServiceMapping" VALUES(7,'xesam:Folder','Folder');
+INSERT INTO "XesamServiceMapping" VALUES(8,'xesam:Image','Image');
+INSERT INTO "XesamServiceMapping" VALUES(9,'xesam:Music','Audio');
+INSERT INTO "XesamServiceMapping" VALUES(10,'xesam:PersonalEmail','Email');
+INSERT INTO "XesamServiceMapping" VALUES(11,'xesam:UncategorizedText','Text');
+INSERT INTO "XesamServiceMapping" VALUES(12,'xesam:Video','Video');
+CREATE TABLE XesamMetaDataMapping
+(
+	ID			Integer primary key AUTOINCREMENT not null,
+	XesamMetaName		Text,
+	MetaName		Text,
+
+	unique (XesamMetaName, MetaName)
+);
+INSERT INTO "XesamMetaDataMapping" VALUES(1,'xesam:album','Audio:Album');
+INSERT INTO "XesamMetaDataMapping" VALUES(2,'xesam:albumArtist','Audio:Artist');
+INSERT INTO "XesamMetaDataMapping" VALUES(3,'xesam:artist','Audio:Artist');
+INSERT INTO "XesamMetaDataMapping" VALUES(4,'xesam:author','Email:Sender');
+INSERT INTO "XesamMetaDataMapping" VALUES(5,'xesam:cc','Email:CC');
+INSERT INTO "XesamMetaDataMapping" VALUES(6,'xesam:genre','Audio:Genre');
+INSERT INTO "XesamMetaDataMapping" VALUES(7,'xesam:primaryRecipient','Email:Recipient');
+INSERT INTO "XesamMetaDataMapping" VALUES(8,'xesam:recipient','Email:Recipient');
+INSERT INTO "XesamMetaDataMapping" VALUES(9,'xesam:secondaryRecipient','Email:Recipient');
+INSERT INTO "XesamMetaDataMapping" VALUES(10,'xesam:title','Audio:Title');
+INSERT INTO "XesamMetaDataMapping" VALUES(11,'xesam:title','Doc:Title');
+INSERT INTO "XesamMetaDataMapping" VALUES(12,'xesam:title','Video:Title');
+INSERT INTO "XesamMetaDataMapping" VALUES(13,'xesam:to','Email:Recipient');
+INSERT INTO "XesamMetaDataMapping" VALUES(14,'xesam:trackNumber','Audio:TrackNo');
+INSERT INTO "XesamMetaDataMapping" VALUES(15,'xesam:url','File:Name');
+CREATE TABLE XesamServiceChildren
+(
+	Parent			Text,
+	Child			Text,
+
+	unique (Parent, Child)
+);
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Content','xesam:Annotation');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Content','xesam:Archive');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Filelike','xesam:ArchivedFile');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Media','xesam:Audio');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:MediaList','xesam:AudioList');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Source','xesam:BlockDevice');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Annotation','xesam:Bookmark');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Content','xesam:CommunicationChannel');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Content','xesam:Contact');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Content','xesam:ContactGroup');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:DataObject','xesam:Content');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Filelike','xesam:DeletedFile');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Text','xesam:Document');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Document','xesam:Documentation');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Message','xesam:Email');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:EmbeddedObject','xesam:EmailAttachment');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Source','xesam:EmbeddedObject');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Filelike','xesam:File');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Content','xesam:FileSystem');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Source','xesam:Filelike');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Annotation','xesam:Folder');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:RemoteMessageboxMessage','xesam:IMAP4Message');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Message','xesam:IMMessage');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Visual','xesam:Image');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:ContactGroup','xesam:MailingList');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:CommunicationChannel','xesam:MailingList');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Email','xesam:MailingListEmail');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Content','xesam:Media');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Annotation','xesam:MediaList');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Content','xesam:Message');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Source','xesam:MessageboxMessage');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Audio','xesam:Music');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:CommunicationChannel','xesam:NewsGroup');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Email','xesam:NewsGroupEmail');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Source','xesam:OfflineMedia');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Contact','xesam:Organization');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:RemoteMessageboxMessage','xesam:POP3Message');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Contact','xesam:Person');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Email','xesam:PersonalEmail');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Image','xesam:Photo');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Document','xesam:Presentation');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Annotation','xesam:Project');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:CommunicationChannel','xesam:RSSFeed');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Message','xesam:RSSMessage');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:RemoteResource','xesam:RemoteFile');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Filelike','xesam:RemoteFile');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:RemoteResource','xesam:RemoteMessageboxMessage');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:MessageboxMessage','xesam:RemoteMessageboxMessage');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Source','xesam:RemoteResource');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:File','xesam:RevisionControlledFile');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Content','xesam:RevisionControlledRepository');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Content','xesam:SoftwarePackage');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:DataObject','xesam:Source');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Text','xesam:SourceCode');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Document','xesam:Spreadsheet');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Source','xesam:SystemResource');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Annotation','xesam:Tag');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Content','xesam:Text');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Document','xesam:TextDocument');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Text','xesam:UncategorizedText');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Audio','xesam:Video');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Visual','xesam:Video');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Media','xesam:Visual');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Text','xesam:XML');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:PIM','xesam:Alarm');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:DesktopEntry','xesam:ApplicationDesktopEntry');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Content','xesam:DesktopEntry');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:PIM','xesam:Event');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:PIM','xesam:FreeBusy');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:PIM','xesam:Journal');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:Content','xesam:PIM');
+INSERT INTO "XesamServiceChildren" VALUES('xesam:PIM','xesam:Task');
+CREATE TABLE XesamMetaDataChildren
+(
+	Parent			Text,
+	Child			Text,
+
+	unique (Parent, Child)
+);
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:author','xesam:albumArtist');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:author','xesam:artist');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:mediaBitrate','xesam:audioBitrate');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:sampleConfiguration','xesam:audioChannels');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:mediaCodec','xesam:audioCodec');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:mediaCodecType','xesam:audioCodecType');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:sampleBitDepth','xesam:audioSampleBitDepth');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:setCount','xesam:audioSampleCount');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:sampleDataType','xesam:audioSampleDataType');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:setRate','xesam:audioSampleRate');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:creator','xesam:author');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:secondaryRecipient','xesam:bcc');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:contactURL','xesam:blogContactURL');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:secondaryRecipient','xesam:cc');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:phoneNumber','xesam:cellPhoneNumber');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:communicationChannel','xesam:chatRoom');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:sampleConfiguration','xesam:colorSpace');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:author','xesam:composer');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:related','xesam:conflicts');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:contactMedium','xesam:contactURL');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:depends','xesam:contains');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:comment','xesam:contentComment');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:keyword','xesam:contentKeyword');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:creator','xesam:contributor');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:legal','xesam:copyright');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:related','xesam:depends');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:links','xesam:derivedFrom');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:legal','xesam:disclaimer');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:contactMedium','xesam:emailAddress');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:phoneNumber','xesam:faxPhoneNumber');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:setCount','xesam:frameCount');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:setRate','xesam:frameRate');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:emailAddress','xesam:homeEmailAddress');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:phoneNumber','xesam:homePhoneNumber');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:physicalAddress','xesam:homePostalAddress');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:contactURL','xesam:homepageContactURL');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:contactMedium','xesam:imContactMedium');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:derivedFrom','xesam:inReplyTo');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:imContactMedium','xesam:ircContactMedium');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:isEncrypted','xesam:isSourceEncrypted');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:imContactMedium','xesam:jabberContactMedium');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:related','xesam:knows');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:legal','xesam:license');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:legal','xesam:licenseType');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:related','xesam:links');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:author','xesam:lyricist');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:communicationChannel','xesam:mailingList');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:physicalAddress','xesam:mailingPostalAddress');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:creator','xesam:maintainer');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:fingerprint','xesam:md5Hash');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:communicationChannel','xesam:newsGroup');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:author','xesam:performer');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:contactMedium','xesam:phoneNumber');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:contactMedium','xesam:physicalAddress');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:sampleBitDepth','xesam:pixelDataBitDepth');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:sampleDataType','xesam:pixelDataType');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:recipient','xesam:primaryRecipient');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:lineCount','xesam:rowCount');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:communicationChannel','xesam:rssFeed');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:recipient','xesam:secondaryRecipient');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:name','xesam:seenAttachedAs');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:fingerprint','xesam:sha1Hash');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:related','xesam:supercedes');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:primaryRecipient','xesam:to');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:comment','xesam:userComment');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:keyword','xesam:userKeyword');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:mediaBitrate','xesam:videoBitrate');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:mediaCodec','xesam:videoCodec');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:mediaCodecType','xesam:videoCodecType');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:emailAddress','xesam:workEmailAddress');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:phoneNumber','xesam:workPhoneNumber');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:physicalAddress','xesam:workPostalAddress');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:imContactMedium','xesam:aimContactMedium');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:imContactMedium','xesam:icqContactMedium');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:id','xesam:imdbId');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:id','xesam:isrc');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:imContactMedium','xesam:msnContactMedium');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:id','xesam:musicBrainzAlbumArtistID');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:id','xesam:musicBrainzAlbumID');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:id','xesam:musicBrainzArtistID');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:fingerprint','xesam:musicBrainzFingerprint');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:id','xesam:musicBrainzTrackID');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:imContactMedium','xesam:skypeContactMedium');
+INSERT INTO "XesamMetaDataChildren" VALUES('xesam:imContactMedium','xesam:yahooContactMedium');
+CREATE TABLE XesamServiceLookup
+(	
+	ID			Integer primary key AUTOINCREMENT not null,
+	XesamTypeName		Text,
+	TypeName		Text,
+
+	unique (XesamTypeName, TypeName)
+);
+INSERT INTO "XesamServiceLookup" VALUES(1,'xesam:Annotation','Folder');
+INSERT INTO "XesamServiceLookup" VALUES(3,'xesam:Audio','Audio');
+INSERT INTO "XesamServiceLookup" VALUES(4,'xesam:Audio','Video');
+INSERT INTO "XesamServiceLookup" VALUES(8,'xesam:Content','Video');
+INSERT INTO "XesamServiceLookup" VALUES(10,'xesam:Content','Folder');
+INSERT INTO "XesamServiceLookup" VALUES(11,'xesam:Content','Audio');
+INSERT INTO "XesamServiceLookup" VALUES(12,'xesam:Content','Image');
+INSERT INTO "XesamServiceLookup" VALUES(13,'xesam:Content','Email');
+INSERT INTO "XesamServiceLookup" VALUES(14,'xesam:Content','Documents');
+INSERT INTO "XesamServiceLookup" VALUES(15,'xesam:Content','Text');
+INSERT INTO "XesamServiceLookup" VALUES(16,'xesam:DataObject','EmailAttachment');
+INSERT INTO "XesamServiceLookup" VALUES(20,'xesam:DataObject','Video');
+INSERT INTO "XesamServiceLookup" VALUES(22,'xesam:DataObject','Folder');
+INSERT INTO "XesamServiceLookup" VALUES(23,'xesam:DataObject','Audio');
+INSERT INTO "XesamServiceLookup" VALUES(24,'xesam:DataObject','Image');
+INSERT INTO "XesamServiceLookup" VALUES(25,'xesam:DataObject','Email');
+INSERT INTO "XesamServiceLookup" VALUES(26,'xesam:DataObject','Documents');
+INSERT INTO "XesamServiceLookup" VALUES(27,'xesam:DataObject','Text');
+INSERT INTO "XesamServiceLookup" VALUES(28,'xesam:DataObject','File');
+INSERT INTO "XesamServiceLookup" VALUES(30,'xesam:Document','Documents');
+INSERT INTO "XesamServiceLookup" VALUES(31,'xesam:Documentation','Documents');
+INSERT INTO "XesamServiceLookup" VALUES(33,'xesam:Email','Email');
+INSERT INTO "XesamServiceLookup" VALUES(34,'xesam:EmailAttachment','EmailAttachment');
+INSERT INTO "XesamServiceLookup" VALUES(35,'xesam:EmbeddedObject','EmailAttachment');
+INSERT INTO "XesamServiceLookup" VALUES(36,'xesam:File','File');
+INSERT INTO "XesamServiceLookup" VALUES(37,'xesam:Filelike','File');
+INSERT INTO "XesamServiceLookup" VALUES(38,'xesam:Folder','Folder');
+INSERT INTO "XesamServiceLookup" VALUES(39,'xesam:Image','Image');
+INSERT INTO "XesamServiceLookup" VALUES(40,'xesam:Media','Image');
+INSERT INTO "XesamServiceLookup" VALUES(42,'xesam:Media','Audio');
+INSERT INTO "XesamServiceLookup" VALUES(44,'xesam:Media','Video');
+INSERT INTO "XesamServiceLookup" VALUES(46,'xesam:Message','Email');
+INSERT INTO "XesamServiceLookup" VALUES(47,'xesam:Music','Audio');
+INSERT INTO "XesamServiceLookup" VALUES(48,'xesam:PersonalEmail','Email');
+INSERT INTO "XesamServiceLookup" VALUES(49,'xesam:Source','EmailAttachment');
+INSERT INTO "XesamServiceLookup" VALUES(50,'xesam:Source','File');
+INSERT INTO "XesamServiceLookup" VALUES(52,'xesam:Text','Documents');
+INSERT INTO "XesamServiceLookup" VALUES(53,'xesam:Text','Text');
+INSERT INTO "XesamServiceLookup" VALUES(54,'xesam:UncategorizedText','Text');
+INSERT INTO "XesamServiceLookup" VALUES(55,'xesam:Video','Video');
+INSERT INTO "XesamServiceLookup" VALUES(56,'xesam:Visual','Image');
+INSERT INTO "XesamServiceLookup" VALUES(57,'xesam:Visual','Video');
+CREATE TABLE XesamMetaDataLookup
+(	
+	ID			Integer primary key AUTOINCREMENT not null,
+	XesamMetaName		Text,
+	MetaName		Text,
+
+	unique (XesamMetaName, MetaName)
+);
+INSERT INTO "XesamMetaDataLookup" VALUES(1,'xesam:album','Audio:Album');
+INSERT INTO "XesamMetaDataLookup" VALUES(2,'xesam:albumArtist','Audio:Artist');
+INSERT INTO "XesamMetaDataLookup" VALUES(3,'xesam:artist','Audio:Artist');
+INSERT INTO "XesamMetaDataLookup" VALUES(4,'xesam:author','Email:Sender');
+INSERT INTO "XesamMetaDataLookup" VALUES(5,'xesam:cc','Email:CC');
+INSERT INTO "XesamMetaDataLookup" VALUES(6,'xesam:genre','Audio:Genre');
+INSERT INTO "XesamMetaDataLookup" VALUES(7,'xesam:primaryRecipient','Email:Recipient');
+INSERT INTO "XesamMetaDataLookup" VALUES(8,'xesam:recipient','Email:Recipient');
+INSERT INTO "XesamMetaDataLookup" VALUES(9,'xesam:secondaryRecipient','Email:Recipient');
+INSERT INTO "XesamMetaDataLookup" VALUES(10,'xesam:title','Audio:Title');
+INSERT INTO "XesamMetaDataLookup" VALUES(11,'xesam:title','Doc:Title');
+INSERT INTO "XesamMetaDataLookup" VALUES(12,'xesam:title','Video:Title');
+INSERT INTO "XesamMetaDataLookup" VALUES(13,'xesam:to','Email:Recipient');
+INSERT INTO "XesamMetaDataLookup" VALUES(14,'xesam:trackNumber','Audio:TrackNo');
+INSERT INTO "XesamMetaDataLookup" VALUES(15,'xesam:url','File:Name');
+COMMIT;

Added: trunk/tests/scripts/dummy_data_start.sh.in
==============================================================================
--- (empty file)
+++ trunk/tests/scripts/dummy_data_start.sh.in	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,71 @@
+#! /bin/sh
+
+SCRIPTS_DIR= top_srcdir@/tests/scripts
+SQLITE_EXEC= sqlite_exec@
+
+. $SCRIPTS_DIR/xdg_dirs.source
+
+mkdir -p $XDG_CACHE_HOME/tracker
+
+echo -n Creating   
+
+#
+## file-metadata.db
+#
+
+echo -n file-metadata.db, 
+
+$SQLITE_EXEC $XDG_CACHE_HOME/tracker/file-meta.db < $SCRIPTS_DIR/data/file-meta.sql
+
+#
+## file-contents.db
+#
+
+echo -n file-contents.db, 
+
+$SQLITE_EXEC $XDG_CACHE_HOME/tracker/file-contents.db < $SCRIPTS_DIR/data/file-contents.sql
+
+#
+## email-meta.db
+#
+
+echo -n email-meta.db, 
+
+$SQLITE_EXEC $XDG_CACHE_HOME/tracker/email-meta.db < $SCRIPTS_DIR/data/email-meta.sql
+
+#
+## email-contents.db
+#
+
+echo -n email-contents.db, 
+
+$SQLITE_EXEC $XDG_CACHE_HOME/tracker/email-contents.db < $SCRIPTS_DIR/data/email-contents.sql
+
+#
+## xesam.db
+#
+
+echo -n xesam.db, 
+
+$SQLITE_EXEC $XDG_CACHE_HOME/tracker/xesam.db < $SCRIPTS_DIR/data/xesam.sql
+
+
+mkdir -p $XDG_DATA_HOME/tracker/data
+
+#
+## common.db
+#
+
+echo -n common.db, 
+
+$SQLITE_EXEC $XDG_DATA_HOME/tracker/data/common.db < $SCRIPTS_DIR/data/common.sql
+
+
+#
+## cache.db
+#
+
+echo cache.db .. done
+
+# Done by trackerd or tracker-indexer
+

Added: trunk/tests/scripts/dummy_data_stop.sh.in
==============================================================================
--- (empty file)
+++ trunk/tests/scripts/dummy_data_stop.sh.in	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,11 @@
+#! /bin/sh
+
+SCRIPTS_DIR= top_srcdir@/tests/scripts
+
+. $SCRIPTS_DIR/xdg_dirs.source
+
+rm -rf $XDG_CACHE_HOME/tracker
+rm -rf $XDG_DATA_HOME/tracker
+
+. $SCRIPTS_DIR/xdg_dirs.unsource
+

Added: trunk/tests/scripts/testing.txt
==============================================================================
--- (empty file)
+++ trunk/tests/scripts/testing.txt	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,19 @@
+./dummy_data_stop.sh
+./dummy_data_start
+source xdg_dirs.source
+
+run trackerd
+run cd /tmp/Tracker-test/.cache/
+run sqlite3 xesam.db
+run pidof trackerd
+$PID appears
+
+sqlite3> prompt appears
+ATTACH './email-meta.db' as 'email-meta';
+ATTACH './file-meta.db' as 'file-meta';
+ATTACH '../../.local/share/tracker/data/common.db' as 'common';
+ATTACH '/tmp/Tracker-pvanhoof.$PID/cache.db' as 'cache';
+
+./dummy_data_stop.sh
+
+

Added: trunk/tests/scripts/xdg_dirs.source
==============================================================================
--- (empty file)
+++ trunk/tests/scripts/xdg_dirs.source	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,2 @@
+export XDG_CACHE_HOME=/tmp/Tracker-test/.cache
+export XDG_DATA_HOME=/tmp/Tracker-test/.local/share

Added: trunk/tests/scripts/xdg_dirs.unsource
==============================================================================
--- (empty file)
+++ trunk/tests/scripts/xdg_dirs.unsource	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,2 @@
+export XDG_CACHE_HOME=
+export XDG_DATA_HOME=

Added: trunk/tests/tracker-fts/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/tests/tracker-fts/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,26 @@
+include $(top_srcdir)/Makefile.decl
+
+noinst_PROGRAMS = $(TEST_PROGS)
+
+TEST_PROGS += tracker-fts-test
+
+INCLUDES = 								\
+	-DG_LOG_DOMAIN=\"Tracker\"					\
+	-I$(top_srcdir)/src						\
+	$(SQLITE3_CFLAGS)						\
+	$(GTHREAD_CFLAGS)						\
+	$(PANGO_CFLAGS)							\
+	$(GLIB2_CFLAGS)
+
+
+tracker_fts_test_SOURCES =						\
+	tracker-fts-test.c
+
+tracker_fts_test_LDADD =						\
+	$(top_builddir)/src/libtracker-common/libtracker-common.la 	\
+	$(SQLITE3_LIBS)							\
+	$(GTHREAD_LIBS)							\
+	$(PANGO_LIBS)							\
+	$(GLIB2_LIBS)							\
+	$(top_builddir)/src/libstemmer/libstemmer.la	 		
+	

Added: trunk/tests/tracker-fts/tracker-fts-test.c
==============================================================================
--- (empty file)
+++ trunk/tests/tracker-fts/tracker-fts-test.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,122 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <sqlite3.h>
+
+#include <glib.h>
+#include <glib-object.h>
+
+static gint
+callback (void	 *NotUsed,
+	  gint	  argc,
+	  gchar **argv,
+	  gchar **azColName)
+{
+	gint i;
+
+	for (i = 0; i < argc; i++) {
+		printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
+	}
+
+	printf("\n");
+
+	return 0;
+}
+
+static void
+exec_sql (sqlite3     *db,
+	  const gchar *sql)
+{
+	gchar *zErrMsg;
+	gint   rc;
+
+	rc = sqlite3_exec (db, sql , callback, 0, &zErrMsg);
+
+	if (rc != SQLITE_OK) {
+		g_printerr ("SQL error: %s\n", zErrMsg);
+		sqlite3_free (zErrMsg);
+	}
+}
+
+int
+main (int argc, char **argv)
+{
+	sqlite3  *db;
+	gint	  rc;
+	gboolean  db_exists = FALSE;
+	gchar	 *st = NULL;
+	gchar	 *sql;
+
+	g_type_init ();
+	g_thread_init (NULL);
+
+	/* FOR NOW! Return EXIT_SUCCESS (martyn)
+	 *
+	 * This has to work with no parameters. These tests are for
+	 * the unit tests, for any tests which are not for the Glib
+	 * unit test frame work, we should be adding those binaries to
+	 * the utils/ directory.
+	 */
+	return EXIT_SUCCESS;
+
+	if (argc != 2) {
+		g_printerr ("Usage: %s MATCH_TERM\n", argv[0]);
+		g_printerr ("EG: %s stew\n", argv[0]);
+		return EXIT_FAILURE;
+	}
+
+	db_exists = g_file_test ("/tmp/test.db", G_FILE_TEST_EXISTS);
+
+	rc = sqlite3_open ("/tmp/test.db", &db);
+	if (rc) {
+		g_printerr ("Can't open database: %s\n", sqlite3_errmsg(db));
+		sqlite3_close(db);
+		return EXIT_FAILURE;
+	}
+
+	sqlite3_enable_load_extension (db, 1);
+	sqlite3_load_extension (db, "tracker-fts.so", NULL, &st);
+
+	if (st) {
+		fprintf(stderr, "SQL error: %s\n", st);
+		sqlite3_free(st);
+	}
+
+	if (!db_exists) {
+		exec_sql (db, "create virtual table recipe using trackerfts (name, ingredients)");
+		exec_sql (db, "insert into recipe (name, ingredients) values ('broccoli stew', 'broccoli,peppers,cheese and tomatoes')");
+		exec_sql (db, "insert into recipe (name, ingredients) values ('pumpkin stew', 'pumpkin,onions,garlic and celery')");
+		exec_sql (db, "insert into recipe (name, ingredients) values ('broccoli pie', 'broccoli,cheese,onions and flour.')");
+		exec_sql (db, "insert into recipe (name, ingredients) values ('pumpkin pie', 'pumpkin,sugar,flour and butter.')");
+	}
+
+	sql = g_strdup_printf ("select rowid, name, ingredients, snippet(recipe), offsets(recipe) from recipe where recipe match '%s'", argv[1]);
+	exec_sql (db, sql);
+	g_free (sql);
+
+	sqlite3_close(db);
+
+	return EXIT_SUCCESS;
+}
+

Added: trunk/tests/tracker-indexer/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/tests/tracker-indexer/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,67 @@
+include $(top_srcdir)/Makefile.decl
+
+noinst_PROGRAMS = $(TEST_SETUP) $(TEST_PROGS)
+
+TEST_SETUP  = $(generated_files)
+TEST_PROGS += tracker-metadata-utils
+
+INCLUDES = 								\
+	-g -DTEST							\
+	-DG_LOG_DOMAIN=\"Tracker\"					\
+	-DINDEXER_MODULES_DIR=\""$(libdir)/tracker/indexer-modules"\"	\
+	-DLIBDIR=\""$(libdir)"\"					\
+	-DLIBEXEC_PATH=\""$(libexecdir)"\"				\
+	-I$(top_srcdir)/src						\
+	-I$(top_srcdir)/tests/common					\
+	$(DBUS_CFLAGS)							\
+	$(PANGO_CFLAGS)							\
+	$(GMODULE_CFLAGS)						\
+	$(GTHREAD_CFLAGS)						\
+	$(GLIB2_CFLAGS)			
+
+tracker_metadata_utils_SOURCES = 					\
+	tracker-dbus.c							\
+	tracker-dbus.h							\
+	tracker-indexer.c						\
+	tracker-indexer.h						\
+	tracker-indexer-db.c						\
+	tracker-indexer-db.h						\
+	tracker-indexer-module.c					\
+	tracker-indexer-module.h					\
+	tracker-marshal-main.c						\
+	tracker-metadata.c						\
+	tracker-metadata.h						\
+	tracker-metadata-utils.c 					\
+	tracker-metadata-utils.h 					\
+	tracker-metadata-utils-test.c 					\
+	tracker-module.h
+
+tracker_metadata_utils_LDADD =	                                        \
+	$(top_builddir)/src/libtracker-db/libtracker-db.la 		\
+	$(top_builddir)/src/libtracker-common/libtracker-common.la 	\
+	$(top_builddir)/tests/common/libtracker-testcommon.la           \
+	$(DBUS_LIBS)							\
+	$(PANGO_LIBS)							\
+	$(GMODULE_LIBS)							\
+	$(GTHREAD_LIBS)							\
+	$(GIO_LIBS)							\
+	$(GLIB2_LIBS)							
+
+#
+# We don't add these to CLEANFILES because they are created from
+# src/tracker-indexer and will be overwritten when we rebuild from
+# there. 
+#
+marshal_sources =                                         		\
+        tracker-marshal.h                             			\
+        tracker-marshal.c
+
+dbus_sources = 								\
+	tracker-indexer-glue.h
+
+BUILT_SOURCES = 							\
+	$(dbus_sources)							\
+	$(marshal_sources)
+
+EXTRA_DIST = 								\
+	$(BUILT_SOURCES)

Added: trunk/tests/tracker-indexer/tracker-metadata-utils-test.c
==============================================================================
--- (empty file)
+++ trunk/tests/tracker-indexer/tracker-metadata-utils-test.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,6 @@
+int
+main (int    argc,
+      char **argv)
+{
+	return 0;
+}

Added: trunk/tests/trackerd/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/tests/trackerd/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,44 @@
+include $(top_srcdir)/Makefile.decl
+
+SUBDIRS = xesam .
+
+noinst_PROGRAMS = $(TEST_PROGS)
+
+INCLUDES = \
+	-DG_LOG_DOMAIN=\"Tracker\" \
+	-I$(top_srcdir)/src \
+	-I$(top_srcdir)/src/trackerd \
+	-I$(top_srcdir)/tests/common	\
+	$(GMODULE_CFLAGS) \
+	$(GTHREAD_CFLAGS) \
+	$(GLIB2_CFLAGS) \
+	$(QDBM_CFLAGS) \
+	$(GIO_GFLAGS) \
+	$(DBUS_CFLAGS)
+
+if HAVE_XESAM_GLIB
+
+TEST_PROGS += tracker-xesam 
+
+INCLUDES += $(XESAM_GLIB_CFLAGS)
+
+tracker_xesam_SOURCES = \
+	tracker-xesam-test.c \
+	tracker-xesam-test.h \
+	tracker-xesam-session-test.c \
+	tracker-xesam-session-test.h \
+	tracker-xesam-hit-test.c \
+	tracker-xesam-hit-test.h \
+	tracker-xesam-hits-test.c \
+	tracker-xesam-hits-test.h
+
+tracker_xesam_LDADD = \
+	$(top_builddir)/tests/trackerd/xesam/libxesamhelper.la \
+	$(XESAM_GLIB_LIBS) \
+	$(GMODULE_LIBS) \
+	$(GTHREAD_LIBS) \
+	$(GLIB2_LIBS) \
+	$(DBUS_LIBS)
+
+endif
+

Added: trunk/tests/trackerd/README
==============================================================================
--- (empty file)
+++ trunk/tests/trackerd/README	Fri Sep 26 10:47:33 2008
@@ -0,0 +1 @@
+To run these tests you must first run trackerd and have data loaded

Added: trunk/tests/trackerd/tracker-xesam-hit-test.c
==============================================================================
--- (empty file)
+++ trunk/tests/trackerd/tracker-xesam-hit-test.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,247 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with main.c; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301,  USA
+ */
+
+#include <glib.h>
+#include <glib/gtestutils.h>
+#include <glib-object.h>
+
+#include <xesam-glib/xesam-glib.h>
+#include "xesam/xesam-g-utils.h"
+#include <xesam-glib/xesam-g-searcher.h>
+#include <xesam-glib/xesam-g-dbussearcher.h>
+#include <xesam-glib/xesam-g-session.h>
+#include <xesam-glib/xesam-g-search.h>
+#include "xesam/xesam-g-globals-private.h"
+#include "xesam/xesam-g-debug-private.h"
+#include "xesam/xesam-g-testsearcher.h"
+#include "xesam/gtestextensions.h"
+
+#include "tracker-xesam-test.h"
+
+/* Shortcut to defining a test */
+#define TEST(func) g_test_add ("/tracker/xesam/hit/"#func, Fixture, NULL, (void (*) (Fixture*, gconstpointer))setup, test_##func, teardown);
+
+#define NUM_FIELDS _get_num_fields()
+
+/* Fields set in the session's hit-fields property */
+const gchar *TEST_FIELDS[2] =
+{
+//	"xesam:url",
+//	"xesam:title",
+//	"xesam:subject",
+	"xesam:album",
+	NULL
+};
+
+/* Test fixture passed to all tests */
+typedef struct {
+	GMainLoop		*mainloop;
+	GError			*error;
+	XesamGSearcher		*searcher;
+	XesamGSession		*session;
+	XesamGSearch		*search;
+	XesamGHits		*hits;
+} Fixture;
+
+/* HELPER METHODS BEGIN */
+
+static guint
+_get_num_fields (void)
+{
+	int	i;
+	static	guint num_fields = 0;
+
+	if (num_fields != 0)
+		return num_fields;
+
+	for (i = 0; ; i++) {
+		if (TEST_FIELDS[i] == NULL) {
+			num_fields = i;
+			return i;
+		}
+	}
+}
+
+/* HELPER METHODS END */
+
+static void
+setup (Fixture		 *fix,
+	   GType *searcher_type)
+{
+	fix->mainloop = g_main_loop_new (NULL, FALSE);
+	fix->searcher = XESAM_G_SEARCHER(xesam_g_dbus_searcher_new_default ());
+	fix->session = xesam_g_session_new (fix->searcher);
+
+	g_object_set (fix->session, "hit-fields", TEST_FIELDS, NULL);
+
+	fix->search = xesam_g_session_new_search (fix->session, xesam_g_query_new_from_xml(TEST_XML));
+
+	g_assert (XESAM_IS_G_SEARCHER (fix->searcher));
+	g_assert (XESAM_IS_G_SESSION (fix->session));
+	g_assert (XESAM_IS_G_SEARCH (fix->search));
+
+	xesam_g_search_start (fix->search);
+	if (gtx_wait_for_signal(G_OBJECT(fix->search), TIMEOUT, "hits-added",
+				&fix->hits))
+		g_critical ("Test search '%s' did not spawn any hits",
+			    TEST_XML);
+
+	g_assert (XESAM_IS_G_HITS (fix->hits));
+	g_assert_cmpint (xesam_g_hits_get_count (fix->hits), >, 0);
+}
+
+static void
+teardown (Fixture		*fix,
+		  gconstpointer test_data)
+{
+	/* Make sure we did not screw up the searcher and session in the test */
+	g_assert (XESAM_IS_G_SEARCHER(fix->searcher));
+	g_assert (XESAM_IS_G_SESSION(fix->session));
+	g_assert (XESAM_IS_G_SEARCH(fix->search));
+	g_assert (XESAM_IS_G_HITS(fix->hits));
+
+	/* Allow to process any dangling async calls. This is needed
+	 * to make the leak detecction below work. */
+	gtx_yield_main_loop (TIMEOUT);
+
+	g_main_loop_unref (fix->mainloop);
+	gtx_assert_last_unref (fix->hits);
+	gtx_assert_last_unref (fix->search);
+	gtx_assert_last_unref (fix->session);
+
+	/* Allow to process any dangling async calls. This is needed
+	 * to make the leak detecction below work. Yes, we need it two times */
+	gtx_yield_main_loop (TIMEOUT);
+
+	gtx_assert_last_unref (fix->searcher);
+
+	if (fix->error)
+		g_error_free (fix->error);
+
+}
+
+static void
+test_field_data (Fixture	*fix,
+	  gconstpointer	data)
+{
+	guint		hit_count, i, j;
+	XesamGHit	*hit;
+	const GPtrArray	*raw;
+
+	hit_count = xesam_g_hits_get_count (fix->hits);
+	g_assert_cmpint (hit_count, >, 0);
+
+	for (i = 0; i < hit_count; i++) {
+		hit = xesam_g_hits_get (fix->hits, i);
+		g_assert (XESAM_IS_G_HIT (hit));
+
+		raw = xesam_g_hit_get_data (hit);
+		g_assert (raw != NULL);
+		g_assert_cmpint (raw->len, ==, NUM_FIELDS);
+
+		/* Assert that all field data members are valid GValues */
+		for (j = 0; j < raw->len; j++) {
+			g_assert (G_IS_VALUE(g_ptr_array_index(raw, j)));
+		}
+	}
+}
+
+static void
+test_get_field (Fixture		*fix,
+		gconstpointer	data)
+{
+	XesamGHit	*hit;
+	const GValue	*field_value;
+	guint		i;
+
+	/* We just check the first Hit in the Hits object */
+	hit = xesam_g_hits_get (fix->hits, 0);
+
+	for (i = 0; i < NUM_FIELDS; i++) {
+		field_value = xesam_g_hit_get_field (hit, TEST_FIELDS[i]);
+		g_assert (G_IS_VALUE (field_value));
+	}
+}
+
+static void
+test_get_field_by_id (Fixture		*fix,
+		      gconstpointer	data)
+{
+	XesamGHit	*hit;
+	const GValue	*field_value_by_key, *field_value_by_id;
+	guint		i;
+
+	/* We just check the first Hit in the Hits object */
+	hit = xesam_g_hits_get (fix->hits, 0);
+
+	for (i = 0; i < NUM_FIELDS; i++) {
+		field_value_by_key = xesam_g_hit_get_field (hit, TEST_FIELDS[i]);
+		field_value_by_id = xesam_g_hit_get_field_by_id (hit, i);
+
+		g_assert (field_value_by_key == field_value_by_id);
+		g_assert (G_IS_VALUE (field_value_by_key));
+		g_assert (G_IS_VALUE (field_value_by_id));
+	}
+}
+
+static void
+test_get_id (Fixture		*fix,
+	     gconstpointer	data)
+{
+	XesamGHit	*hit;
+	guint		i;
+	guint count = xesam_g_hits_get_count (fix->hits);
+
+	for (i = 0; i < count; i++) {
+		hit = xesam_g_hits_get (fix->hits, i);
+
+		/* This assumes that hit is the first XesamGHit to be delievered */
+		g_assert_cmpint (i, ==, xesam_g_hit_get_id (hit));
+	}
+}
+
+static void
+test_get_field_names (Fixture		*fix,
+		      gconstpointer	data)
+{
+	XesamGHit	*hit;
+	GStrv		field_names;
+	guint		i;
+
+	hit = xesam_g_hits_get (fix->hits, 0);
+	field_names = xesam_g_hit_get_field_names (hit);
+
+	/* This also tests that both arrays ends with NULL */
+	for (i = 0; i <= NUM_FIELDS; i++) {
+		g_assert_cmpstr (TEST_FIELDS[i], ==, field_names[i]);
+	}
+
+	g_strfreev (field_names);
+}
+
+
+void
+g_test_add_hit_tests (void)
+{
+
+	TEST (field_data);
+	TEST (get_field);
+	TEST (get_field_by_id);
+	TEST (get_id);
+	TEST (get_field_names);
+
+	return;
+}

Added: trunk/tests/trackerd/tracker-xesam-hit-test.h
==============================================================================
--- (empty file)
+++ trunk/tests/trackerd/tracker-xesam-hit-test.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,25 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+#ifndef _TRACKER_XESAM_HIT_TEST_H_
+#define _TRACKER_XESAM_HIT_TEST_H_
+
+void  g_test_add_hit_tests (void);
+
+#endif

Added: trunk/tests/trackerd/tracker-xesam-hits-test.c
==============================================================================
--- (empty file)
+++ trunk/tests/trackerd/tracker-xesam-hits-test.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,163 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with main.c; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301,  USA
+ */
+
+#include <glib.h>
+#include <glib/gtestutils.h>
+#include <glib-object.h>
+
+#include <xesam-glib/xesam-glib.h>
+#include "xesam/xesam-g-utils.h"
+#include <xesam-glib/xesam-g-searcher.h>
+#include <xesam-glib/xesam-g-dbussearcher.h>
+#include <xesam-glib/xesam-g-session.h>
+#include <xesam-glib/xesam-g-search.h>
+#include "xesam/xesam-g-globals-private.h"
+#include "xesam/xesam-g-debug-private.h"
+#include "xesam/xesam-g-testsearcher.h"
+#include "xesam/gtestextensions.h"
+
+#include "tracker-xesam-test.h"
+
+/* Shortcut to defining a test */
+#define TEST(func) g_test_add ("/tracker/xesam/hits/"#func, Fixture, NULL, (void (*) (Fixture*, gconstpointer))setup, test_##func, teardown);
+
+
+/* Test fixture passed to all tests */
+typedef struct {
+	GMainLoop		*mainloop;
+	GError			*error;
+	XesamGSearcher		*searcher;
+	XesamGSession		*session;
+	XesamGSearch		*search;
+	XesamGHits		*hits;
+	GList			*hit_list;
+} Fixture;
+
+/* Fields set in the session's hit-fields property */
+const gchar *HITS_TEST_FIELDS[2] =
+{
+//	"xesam:url",
+//	"xesam:title",
+//	"xesam:subject",
+	"xesam:album",
+	NULL
+};
+
+
+/* HELPER METHODS BEGIN */
+
+
+
+/* HELPER METHODS END */
+
+static void
+setup (Fixture		 *fix,
+	   GType *searcher_type)
+{
+	fix->mainloop = g_main_loop_new (NULL, FALSE);
+	fix->searcher = XESAM_G_SEARCHER(xesam_g_dbus_searcher_new_default ());
+	fix->session = xesam_g_session_new (fix->searcher);
+
+	g_object_set (fix->session, "hit-fields", HITS_TEST_FIELDS, NULL);
+
+	fix->search = xesam_g_session_new_search (fix->session, xesam_g_query_new_from_xml(TEST_XML));
+
+	g_assert (XESAM_IS_G_SEARCHER (fix->searcher));
+	g_assert (XESAM_IS_G_SESSION (fix->session));
+	g_assert (XESAM_IS_G_SEARCH (fix->search));
+
+	xesam_g_search_start (fix->search);
+	if (gtx_wait_for_signal(G_OBJECT(fix->search), TIMEOUT, "hits-added",
+				&fix->hits))
+		g_critical ("Test search '%s' did not spawn any hits",
+			    TEST_XML);
+
+	g_assert (XESAM_IS_G_HITS (fix->hits));
+	g_assert_cmpint (xesam_g_hits_get_count (fix->hits), >, 0);
+}
+
+static void
+teardown (Fixture		*fix,
+		  gconstpointer test_data)
+{
+	if (fix->hit_list)
+		g_list_foreach (fix->hit_list, (GFunc) g_object_unref, NULL);
+
+	/* Make sure we did not screw up the searcher and session in the test */
+	g_assert (XESAM_IS_G_SEARCHER(fix->searcher));
+	g_assert (XESAM_IS_G_SESSION(fix->session));
+	g_assert (XESAM_IS_G_SEARCH(fix->search));
+	g_assert (XESAM_IS_G_HITS(fix->hits));
+
+	/* Allow to process any dangling async calls. This is needed
+	 * to make the leak detecction below work. */
+	gtx_yield_main_loop (TIMEOUT);
+
+	g_main_loop_unref (fix->mainloop);
+	gtx_assert_last_unref (fix->hits);
+	gtx_assert_last_unref (fix->search);
+	gtx_assert_last_unref (fix->session);
+
+	/* Allow to process any dangling async calls. This is needed
+	 * to make the leak detecction below work. Yes, we need it two times */
+	gtx_yield_main_loop (TIMEOUT);
+
+	gtx_assert_last_unref (fix->searcher);
+
+	if (fix->error)
+		g_error_free (fix->error);
+}
+
+static void
+test_count (Fixture	    *fix,
+	     gconstpointer  data)
+{
+	guint hit_count;
+
+	hit_count = xesam_g_hits_get_count (fix->hits);
+
+	g_assert_cmpint (hit_count, >, 0);
+}
+
+static void
+test_get (Fixture	*fix,
+	  gconstpointer	data)
+{
+	guint		hit_count, i;
+	XesamGHit	*hit;
+
+	hit_count = xesam_g_hits_get_count (fix->hits);
+	g_assert_cmpint (hit_count, >, 0);
+
+	for (i = 0; i < hit_count; i++) {
+		hit = xesam_g_hits_get (fix->hits, i);
+		g_assert (XESAM_IS_G_HIT (hit));
+
+		/* Build a list of hit objects with refs. We use this
+		 * to assert that they are properly freed later */
+		fix->hit_list = g_list_prepend (fix->hit_list, hit);
+		g_object_ref (hit);
+	}
+}
+
+void
+g_test_add_hits_tests (void)
+{
+	TEST (count);
+	TEST (get);
+
+	return;
+}

Added: trunk/tests/trackerd/tracker-xesam-hits-test.h
==============================================================================
--- (empty file)
+++ trunk/tests/trackerd/tracker-xesam-hits-test.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,25 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+#ifndef _TRACKER_XESAM_HITS_TEST_H_
+#define _TRACKER_XESAM_HITS_TEST_H_
+
+void  g_test_add_hits_tests (void);
+
+#endif

Added: trunk/tests/trackerd/tracker-xesam-session-test.c
==============================================================================
--- (empty file)
+++ trunk/tests/trackerd/tracker-xesam-session-test.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,315 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with main.c; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301,  USA
+ */
+
+#include <glib.h>
+#include <glib/gtestutils.h>
+#include <glib-object.h>
+
+#include <xesam-glib/xesam-glib.h>
+#include <xesam-glib/xesam-g-searcher.h>
+#include <xesam-glib/xesam-g-dbussearcher.h>
+#include <xesam-glib/xesam-g-session.h>
+
+#include "xesam/xesam-g-utils.h"
+#include "xesam/xesam-g-testsearcher.h"
+#include "xesam/gtestextensions.h"
+
+#include "tracker-xesam-test.h"
+
+/* Shortcut to defining a test */
+#define TEST(func) g_test_add ("/tracker/xesam/session/"#func, Fixture, NULL, (void (*) (Fixture*, gconstpointer))setup, test_##func, teardown);
+
+
+/* Test fixture passed to all tests */
+typedef struct {
+	GMainLoop			*mainloop;
+	GError				*error;
+	XesamGSearcher		*searcher;
+	XesamGSession		*session;
+} Fixture;
+
+/* HELPER METHODS BEGIN */
+
+/**
+ * check_property
+ * @session:
+ * @property:
+ *
+ * Returns: %TRUE iff the property is available after atmost %TIMEOUT.
+ */
+static gboolean
+check_property (XesamGSession *session, gchar *property)
+{
+	gchar *notify_signal;
+	GParamSpec	  *tmp_pspec;
+
+	notify_signal = g_strconcat ("notify::", property, NULL);
+
+	if (xesam_g_session_test_property (session, property)) {
+		g_free (notify_signal);
+		return TRUE;
+	}
+
+	if (gtx_wait_for_signal (G_OBJECT(session), TIMEOUT,
+							 notify_signal, &tmp_pspec)) {
+		g_param_spec_unref (tmp_pspec);
+		if (xesam_g_session_test_property (session, property)) {
+			g_free (notify_signal);
+			return TRUE;
+		}
+		g_free (notify_signal);
+		return FALSE;
+
+	}
+
+	if (xesam_g_session_test_property (session, property)) {
+		g_free (notify_signal);
+		return TRUE;
+	}
+
+	g_critical ("'%s' emitted, but test_property() returns FALSE", notify_signal);
+	g_free (notify_signal);
+	return FALSE;
+}
+
+/* HELPER METHODS END */
+
+static void
+setup (Fixture		 *fix,
+	   GType *searcher_type)
+{
+	fix->mainloop = g_main_loop_new (NULL, FALSE);
+	fix->searcher = XESAM_G_SEARCHER(xesam_g_dbus_searcher_new_default ());
+	fix->session = xesam_g_session_new (fix->searcher);
+
+	g_assert (XESAM_IS_G_SEARCHER(fix->searcher));
+	g_assert (XESAM_IS_G_SESSION(fix->session));
+}
+
+static void
+teardown (Fixture		*fix,
+		  gconstpointer test_data)
+{
+	/* Make sure we did not screw up the searcher and session in the test */
+	g_assert (XESAM_IS_G_SEARCHER(fix->searcher));
+	g_assert (XESAM_IS_G_SESSION(fix->session));
+
+	g_main_loop_unref (fix->mainloop);
+	g_object_unref (fix->searcher);
+	g_object_unref (fix->session);
+
+	if (fix->error)
+		g_error_free (fix->error);
+}
+
+/* Test that a session becomes ready within a reasonable timeframe */
+static void
+test_ready (Fixture *fix,
+			  gconstpointer	test_data)
+{
+	if (gtx_wait_for_signal (G_OBJECT(fix->session), TIMEOUT, "ready"))
+		g_critical ("'ready' signal never emitted");
+
+	if (!gtx_wait_for_signal (G_OBJECT(fix->session), TIMEOUT, "ready"))
+		g_critical ("'ready' signal emitted twice");
+
+	g_assert (xesam_g_session_is_ready (fix->session));
+}
+
+/* Test that we can call close immediately on a session */
+static void
+test_immediate_close (Fixture		*fix,
+					  gconstpointer	test_data)
+{
+	xesam_g_session_close (fix->session);
+
+	if (gtx_wait_for_signal (G_OBJECT(fix->session), TIMEOUT, "closed"))
+		g_critical ("'closed' signal never emitted");
+
+	g_assert (xesam_g_session_is_closed (fix->session));
+}
+
+/* Test that we can call close multiple times without problems */
+static void
+test_multiple_close (Fixture		*fix,
+					 gconstpointer	test_data)
+{
+	/* The first close() should go well */
+	xesam_g_session_close (fix->session);
+	if (gtx_wait_for_signal (G_OBJECT(fix->session), TIMEOUT, "closed"))
+		g_critical ("'closed' signal never emitted");
+	g_assert (xesam_g_session_is_closed (fix->session));
+
+	/* Next close should not emit 'closed' */
+	xesam_g_session_close (fix->session);
+	if (!gtx_wait_for_signal (G_OBJECT(fix->session), TIMEOUT, "closed"))
+		g_critical ("'closed' signal emitted, but session already closed");
+	g_assert (xesam_g_session_is_closed (fix->session));
+
+	/* Even more closing should not emit 'closed' */
+	xesam_g_session_close (fix->session);
+	if (!gtx_wait_for_signal (G_OBJECT(fix->session), TIMEOUT, "closed"))
+		g_critical ("'closed' signal emitted, but session already closed");
+	g_assert (xesam_g_session_is_closed (fix->session));
+}
+
+/* Test that we can call sync on all props */
+static void
+test_sync_properties (Fixture *fix,
+					 gconstpointer test_data)
+{
+	XesamGSession *session;
+
+	session = fix->session;
+
+	xesam_g_session_sync_property (session, "search-live");
+	if (!check_property (session, "search-live"))
+		g_critical ("search-live not synced");
+
+	xesam_g_session_sync_property (session, "hit-fields");
+	if (!check_property(session, "hit-fields"))
+		g_critical ("hit-fields not synced");
+
+	xesam_g_session_sync_property (session, "hit-fields-extended");
+	if (!check_property(session, "hit-fields-extended"))
+		g_critical ("hit-fields-extended not synced");
+
+	xesam_g_session_sync_property (session, "hit-snippet-length");
+	if (!check_property(session, "hit-snippet-length"))
+		g_critical ("hit-snippet-length not synced");
+
+	xesam_g_session_sync_property (session, "sort-primary");
+	if (!check_property(session, "sort-primary"))
+		g_critical ("sort-primary not synced");
+
+	xesam_g_session_sync_property (session, "sort-secondary");
+	if (!check_property(session, "sort-secondary"))
+		g_critical ("sort-secondary not synced");
+
+	xesam_g_session_sync_property (session, "sort-order");
+	if (!check_property(session, "sort-order"))
+		g_critical ("sort-order not synced");
+
+	xesam_g_session_sync_property (session, "vendor-id");
+	if (!check_property(session, "vendor-id"))
+		g_critical ("vendor-id not synced");
+
+	xesam_g_session_sync_property (session, "vendor-version");
+	if (!check_property(session, "vendor-version"))
+		g_critical ("vendor-version not synced");
+
+	xesam_g_session_sync_property (session, "vendor-display");
+	if (!check_property(session, "vendor-display"))
+		g_critical ("vendor-display not synced");
+
+	xesam_g_session_sync_property (session, "vendor-xesam");
+	if (!check_property(session, "vendor-xesam"))
+		g_critical ("vendor-xesam not synced");
+
+	xesam_g_session_sync_property (session, "vendor-ontology-fields");
+	if (!check_property(session, "vendor-ontology-fields"))
+		g_critical ("vendor-ontology-fields not synced");
+
+	xesam_g_session_sync_property (session, "vendor-ontology-contents");
+	if (!check_property(session, "vendor-ontology-contents"))
+		g_critical ("vendor-ontology-ceontents not synced");
+
+	xesam_g_session_sync_property (session, "vendor-ontology-sources");
+	if (!check_property(session, "vendor-ontology-sources"))
+		g_critical ("vendor-ontology-sources not synced");
+
+	xesam_g_session_sync_property (session, "vendor-extensions");
+	if (!check_property(session, "vendor-extensions"))
+		g_critical ("vendor-extensions not synced");
+
+	xesam_g_session_sync_property (session, "vendor-ontologies");
+	if (!check_property(session, "vendor-ontologies"))
+		g_critical ("vendor-ontologies not synced");
+
+	xesam_g_session_sync_property (session, "vendor-maxhits");
+	if (!check_property(session, "vendor-maxhits"))
+		g_critical ("vendor-maxhits not synced");
+
+}
+
+/* Test that we can create a new Search object. Testing the functionality
+ * of the spawned search is done in the test-search.c test suite. */
+static void
+test_immediate_new_search (Fixture *fix,
+						   gconstpointer test_data)
+{
+	XesamGSearch	*search;
+
+	/* Don't bother to wait for 'ready'. This should work regardless. */
+	search = xesam_g_session_new_search_from_text (fix->session,
+												   "hello world");
+
+	g_assert (XESAM_IS_G_SEARCH (search));
+	g_assert (xesam_g_search_get_session (search) == fix->session);
+
+	g_object_unref (search);
+
+	/* search will not be finalized before it has received a handle */
+	gtx_yield_main_loop (TIMEOUT);
+}
+
+
+static void
+test_field_map (Fixture	*fix,
+		gconstpointer	test_data)
+{
+	GHashTable  *map;
+	GParamSpec  *pspec;
+
+	if (gtx_wait_for_signal (G_OBJECT(fix->session),TIMEOUT, "ready"))
+		g_critical ("'ready' never emitted on session");
+
+	map = xesam_g_session_get_field_map (fix->session);
+	g_assert (map != NULL);
+	g_assert_cmpint (g_hash_table_size (map), == , 1);
+	g_assert_cmpint (*((int*)g_hash_table_lookup (map, "xesam:url")), ==, 0);
+
+	/* Update hit-fields */
+	gchar *hit_fields[4] = {"xesam:url", "xesam:title", "xesam:subject", NULL};
+	g_object_set (fix->session, "hit-fields", hit_fields, NULL);
+
+	/* Wait for propoerty change to be registered remotely */
+	if (gtx_wait_for_signal (G_OBJECT(fix->session),TIMEOUT,
+				 "notify::hit-fields", &pspec))
+		g_critical ("'notify::hit-fields' never emitted on session");
+
+	g_param_spec_unref (pspec);
+
+	map = xesam_g_session_get_field_map (fix->session);
+	g_assert_cmpint (g_hash_table_size (map), == , 3);
+	g_assert_cmpint (*((int*)g_hash_table_lookup (map, "xesam:url")), ==, 0);
+	g_assert_cmpint (*((int*)g_hash_table_lookup (map, "xesam:title")), ==, 1);
+	g_assert_cmpint (*((int*)g_hash_table_lookup (map, "xesam:subject")), ==, 2);
+}
+
+
+void
+g_test_add_session_tests (void)
+{
+	TEST (ready);
+	TEST (immediate_close);
+	TEST (multiple_close);
+	TEST (sync_properties);
+	TEST (immediate_new_search);
+	TEST (field_map);
+
+	return;
+}

Added: trunk/tests/trackerd/tracker-xesam-session-test.h
==============================================================================
--- (empty file)
+++ trunk/tests/trackerd/tracker-xesam-session-test.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,25 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+#ifndef _TRACKER_XESAM_SESSION_TEST_H_
+#define _TRACKER_XESAM_SESSION_TEST_H_
+
+void g_test_add_session_tests (void);
+
+#endif

Added: trunk/tests/trackerd/tracker-xesam-test.c
==============================================================================
--- (empty file)
+++ trunk/tests/trackerd/tracker-xesam-test.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,64 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+#include <glib.h>
+#include <glib/gtestutils.h>
+
+#include <dbus/dbus-glib-bindings.h>
+
+#include "tracker-xesam-session-test.h"
+#include "tracker-xesam-hits-test.h"
+#include "tracker-xesam-hit-test.h"
+
+#include "tracker-xesam-test.h"
+
+/*
+ * This is a hack to initialize the dbus glib specialized types.
+ * See bug https://bugs.freedesktop.org/show_bug.cgi?id=13908
+ */
+static void
+init_dbus_glib_types (void)
+{
+	DBusGConnection *connection;
+	GError			*error;
+	error = NULL;
+	connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
+	dbus_g_connection_unref (connection);
+}
+
+int
+main (int argc, char **argv) {
+
+	int result;
+
+	g_type_init ();
+	g_test_init (&argc, &argv, NULL);
+
+	init_dbus_glib_types();
+
+	g_test_bug_base ("http://bugzilla.gnome.org/";);
+
+	g_test_add_session_tests ();
+	g_test_add_hit_tests ();
+	g_test_add_hits_tests ();
+
+	result = g_test_run ();
+
+	return result;
+}

Added: trunk/tests/trackerd/tracker-xesam-test.h
==============================================================================
--- (empty file)
+++ trunk/tests/trackerd/tracker-xesam-test.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,26 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+#ifndef _TRACKER_XESAM_TEST_H_
+#define _TRACKER_XESAM_TEST_H_
+
+#define TEST_XML "<request><query><contains><field name=\"xesam:album\" /><string>li</string></contains></query></request>"
+#define TIMEOUT 1500
+
+#endif

Added: trunk/tests/trackerd/xesam/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/tests/trackerd/xesam/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,26 @@
+include $(top_srcdir)/Makefile.decl
+
+if HAVE_XESAM_GLIB
+
+noinst_LTLIBRARIES = libxesamhelper.la
+
+INCLUDES = \
+        -DG_LOG_DOMAIN=\"Tracker\" \
+        -I$(top_srcdir)/src \
+        $(GMODULE_CFLAGS) \
+        $(GTHREAD_CFLAGS) \
+        $(GLIB2_CFLAGS) \
+	$(XESAM_GLIB_CFLAGS)
+
+libxesamhelper_la_SOURCES = \
+	xesam-g-debug-private.h \
+	xesam-g-utils.h \
+	xesam-g-globals-private.h \
+	gtestextensions.c \
+	gtestextensions.h \
+	xesam-g-test-query-builder.c \
+	xesam-g-test-query-builder.h \
+	xesam-g-testsearcher.c \
+	xesam-g-testsearcher.h
+
+endif

Added: trunk/tests/trackerd/xesam/README
==============================================================================
--- (empty file)
+++ trunk/tests/trackerd/xesam/README	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,5 @@
+These file came from xesam-glib and are needed to build mock objects and 
+infrastructure needed for xesam-glib.
+
+Perhaps we should discuss with Kamstrup (author of xesam-glib) how and if
+we can share this in a more decent way?

Added: trunk/tests/trackerd/xesam/gtestextensions.c
==============================================================================
--- (empty file)
+++ trunk/tests/trackerd/xesam/gtestextensions.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,382 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * gtestextensions
+ * Copyright (C) Mikkel Kamstrup Erlandsen 2008 <mikkel kamstrup gmail com>
+ *		 Scott Asofyet 2008 (wait_for_signal code)
+ *
+ * gtestextensions is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * gtestextensions 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 xesam-glib.  If not, write to:
+ *	The Free Software Foundation, Inc.,
+ *	51 Franklin Street, Fifth Floor
+ *	Boston, MA  02110-1301, USA.
+ */
+
+#include <gobject/gvaluecollector.h>
+#include <string.h> /* memset(), from G_VALUE_COLLECT() */
+#include "gtestextensions.h"
+
+typedef struct {
+    GClosure closure;
+    GValue * return_value;
+    GValueArray * param_values;
+    GMainLoop * loop;
+} WaitForSignalClosure;
+
+
+gboolean
+gtx_dispatch_test_case (GtxTestContext *ctx)
+{
+	g_assert (ctx != NULL);
+	ctx->test_func (ctx->fixture, ctx->test_data);
+	return FALSE;
+}
+
+void
+gtx_flush_sources (gboolean may_block)
+{
+	GMainContext *ctx;
+
+	ctx = g_main_context_default ();
+
+	while (g_main_context_pending (ctx))
+		g_main_context_iteration (ctx, may_block);
+}
+
+/**
+ * gtx_quit_main_loop
+ * @loop: The main loop to quit
+ *
+ * This is a convenience to close a #GMainLoop via a #GSourceFunc.
+ *
+ * Returns: Always %FALSE
+ */
+gboolean
+gtx_quit_main_loop (GMainLoop *loop)
+{
+	g_assert (loop != NULL);
+
+	if (g_main_loop_is_running (loop))
+		g_main_loop_quit (loop);
+	else
+		g_warning ("Tried to quit non-running GMainLoop %p", loop);
+
+	return FALSE;
+}
+
+/**
+ * gtx_yield_main_loop
+ * @millis: Number of milli seconds to return control to the main loop
+ *
+ * Return control to the main loop for a specified amount of time. This
+ * is to allow async test to process events in a blocking fashion.
+ */
+void
+gtx_yield_main_loop (guint millis)
+{
+	GMainLoop *recursive_main;
+
+	recursive_main = g_main_loop_new (NULL, FALSE);
+
+	g_timeout_add (millis, (GSourceFunc)gtx_quit_main_loop, recursive_main);
+	g_main_loop_run (recursive_main);
+
+	gtx_flush_sources (FALSE);
+
+	g_main_loop_unref (recursive_main);
+}
+
+static void
+wait_for_signal_closure_marshal (GClosure *closure,
+				 GValue *return_value,
+				 guint n_param_values,
+				 const GValue *param_values,
+				 gpointer invocation_hint,
+				 gpointer marshal_data)
+{
+    WaitForSignalClosure * wfsclosure = (WaitForSignalClosure *) closure;
+    guint i;
+
+    wfsclosure->param_values = g_value_array_new (n_param_values);
+    for (i = 0 ; i < n_param_values ; i++)
+	g_value_array_append (wfsclosure->param_values, param_values + i);
+
+    if (return_value)
+	g_value_copy (wfsclosure->return_value, return_value);
+
+    gtx_quit_main_loop (wfsclosure->loop);
+
+    (void) invocation_hint;
+    (void) marshal_data;
+}
+
+static gboolean
+wait_for_signal_values (GObject * object,
+			gint max_wait_ms,
+			guint signal_id,
+			GQuark detail,
+			GValue * return_value,
+			GValueArray ** param_values)
+{
+    WaitForSignalClosure * wfs;
+    guint handler_id;
+    guint max_timeout_id;
+    gboolean timed_out = FALSE;
+
+    wfs = (WaitForSignalClosure *)
+	g_closure_new_simple (sizeof (WaitForSignalClosure), NULL);
+    wfs->loop = g_main_loop_new (NULL, FALSE);
+    wfs->return_value = return_value;
+    wfs->param_values = NULL;
+
+    g_closure_set_marshal (&wfs->closure, wait_for_signal_closure_marshal);
+
+    handler_id = g_signal_connect_closure_by_id (object, signal_id, detail,
+						 &wfs->closure, FALSE);
+
+    max_timeout_id = g_timeout_add (max_wait_ms, (GSourceFunc)gtx_quit_main_loop,
+									wfs->loop);
+
+    g_main_loop_run (wfs->loop);
+
+    g_closure_invalidate (&wfs->closure);
+
+    if (wfs->param_values) {
+	if (param_values)
+	    *param_values = wfs->param_values;
+	else
+	    g_value_array_free (wfs->param_values);
+
+	wfs->param_values = NULL;
+
+    } else {
+	timed_out = TRUE;
+    }
+
+    g_main_loop_unref (wfs->loop);
+    wfs->loop = NULL;
+
+    /* Closure will be destroyed here */
+    g_signal_handler_disconnect (object, handler_id);
+	g_source_remove (max_timeout_id);
+
+    return timed_out;
+}
+
+
+/**
+ * gtx_wait_for_signal
+ * @object:  The object
+ * @max_wait_ms:  Maximum number of milliseconds to wait before giving up
+ *		  and failing; passed directly to g_timeout_add().
+ * @detailed_signal:  Detailed signal name for which to wait.
+ * @...:  If @detailed_signal has a return value, the first vararg should
+ *	  be the value to return from the signal handler.  The rest of the
+ *	  varargs should be pointers to variables in which to store the
+ *	  parameters passed to the signal.  Pass NULL for any param in
+ *	  which you are not interested.  The caller is responsible for
+ *	  freeing or unreffing any strings or objects returned here.
+ *
+ * returns: True if the signal was received, false if the timeout was
+ *	    reached without the signal firing.
+ */
+gboolean
+gtx_wait_for_signal (GObject * object,
+		 gint max_wait_ms,
+		 const gchar * detailed_signal,
+		 ...)
+{
+    GSignalQuery query;
+    GValue * return_value = NULL, the_return_value = { 0, };
+    GValueArray * param_values = NULL;
+    guint signal_id;
+    GQuark detail;
+    gboolean ret;
+    va_list ap;
+
+    if (! g_signal_parse_name (detailed_signal, G_OBJECT_TYPE (object),
+			       &signal_id, &detail, FALSE))
+	g_error ("Signal %s is invalid for object type %s",
+		 detailed_signal, g_type_name (G_OBJECT_TYPE (object)));
+
+    g_signal_query (signal_id, &query);
+
+    va_start (ap, detailed_signal);
+
+    if (query.return_type != G_TYPE_NONE) {
+	GType t = query.return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE;
+	gboolean static_scope = t & G_SIGNAL_TYPE_STATIC_SCOPE;
+	gchar * error;
+	g_value_init (&the_return_value, t);
+	return_value = &the_return_value;
+	G_VALUE_COLLECT (return_value, ap,
+			 static_scope ? G_VALUE_NOCOPY_CONTENTS : 0,
+			 &error);
+	if (error)
+	    g_error ("%s: %s", G_STRLOC, error);
+    }
+
+    ret = wait_for_signal_values (object,
+				  max_wait_ms,
+				  signal_id,
+				  detail,
+				  return_value,
+				  &param_values);
+
+    if (param_values) {
+	guint i;
+
+	/* Skip the instance */
+	for (i = 1 ; i < param_values->n_values ; i++) {
+	    GValue * v = g_value_array_get_nth (param_values, i);
+	    switch (G_TYPE_FUNDAMENTAL (G_VALUE_TYPE (v))) {
+		case G_TYPE_INVALID:
+		case G_TYPE_NONE:
+		case G_TYPE_INTERFACE:
+		    break;
+
+		case G_TYPE_CHAR:
+		    {
+			gchar * p = va_arg (ap, gchar *);
+			if (p)
+			    *p = g_value_get_char (v);
+		    }
+		    break;
+		case G_TYPE_UCHAR:
+		    {
+			guchar * p = va_arg (ap, guchar *);
+			if (p)
+			    *p = g_value_get_uchar (v);
+		    }
+		    break;
+		case G_TYPE_BOOLEAN:
+		    {
+			gboolean * p = va_arg (ap, gboolean *);
+			if (p)
+			    *p = g_value_get_boolean (v);
+		    }
+		    break;
+		case G_TYPE_INT:
+		    {
+			gint * p = va_arg (ap, gint *);
+			if (p)
+			    *p = g_value_get_int (v);
+		    }
+		    break;
+		case G_TYPE_UINT:
+		    {
+			guint * p = va_arg (ap, guint *);
+			if (p)
+			    *p = g_value_get_uint (v);
+		    }
+		    break;
+		case G_TYPE_LONG:
+		    {
+			glong * p = va_arg (ap, glong *);
+			if (p)
+			    *p = g_value_get_long (v);
+		    }
+		    break;
+		case G_TYPE_ULONG:
+		    {
+			gulong * p = va_arg (ap, gulong *);
+			if (p)
+			    *p = g_value_get_ulong (v);
+		    }
+		    break;
+		case G_TYPE_INT64:
+		    {
+			gint64 * p = va_arg (ap, gint64 *);
+			if (p)
+			    *p = g_value_get_int64 (v);
+		    }
+		    break;
+		case G_TYPE_UINT64:
+		    {
+			guint64 * p = va_arg (ap, guint64 *);
+			if (p)
+			    *p = g_value_get_uint64 (v);
+		    }
+		    break;
+		case G_TYPE_ENUM:
+		    {
+			gint * p = va_arg (ap, gint *);
+			if (p)
+			    *p = g_value_get_int (v);
+		    }
+		    break;
+		case G_TYPE_FLAGS:
+		    {
+			guint * p = va_arg (ap, guint *);
+			if (p)
+			    *p = g_value_get_uint (v);
+		    }
+		    break;
+		case G_TYPE_FLOAT:
+		    {
+			gfloat * p = va_arg (ap, gfloat *);
+			if (p)
+			    *p = g_value_get_float (v);
+		    }
+		    break;
+		case G_TYPE_DOUBLE:
+		    {
+			gdouble * p = va_arg (ap, gdouble *);
+			if (p)
+			    *p = g_value_get_double (v);
+		    }
+		    break;
+		case G_TYPE_STRING:
+		    {
+			gchar ** p = va_arg (ap, gchar **);
+			if (p)
+			    *p = g_value_dup_string (v);
+		    }
+		    break;
+		case G_TYPE_POINTER:
+		    {
+			gpointer * p = va_arg (ap, gpointer *);
+			if (p)
+			    *p = g_value_get_pointer (v);
+		    }
+		    break;
+		case G_TYPE_BOXED:
+		    {
+			gpointer * p = va_arg (ap, gpointer *);
+			if (p)
+			    *p = g_value_dup_boxed (v);
+		    }
+		    break;
+		case G_TYPE_PARAM:
+		    {
+			GParamSpec ** p = va_arg (ap, GParamSpec **);
+			if (p)
+			    *p = g_value_dup_param (v);
+		    }
+		    break;
+		case G_TYPE_OBJECT:
+		    {
+			GObject ** p = va_arg (ap, GObject **);
+			if (p)
+			    *p = g_value_dup_object (v);
+		    }
+		    break;
+	    }
+	}
+
+	g_value_array_free (param_values);
+    }
+
+    va_end (ap);
+
+    return ret;
+}

Added: trunk/tests/trackerd/xesam/gtestextensions.h
==============================================================================
--- (empty file)
+++ trunk/tests/trackerd/xesam/gtestextensions.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,115 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * gtestextensions
+ * Copyright (C) Mikkel Kamstrup Erlandsen 2008 <mikkel kamstrup gmail com>
+ *
+ * gtestextensions is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * gtestextensions 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 xesam-glib.  If not, write to:
+ *	The Free Software Foundation, Inc.,
+ *	51 Franklin Street, Fifth Floor
+ *	Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __G_TEST_EXTENSIONS_H__
+#define __G_TEST_EXTENSIONS_H__
+
+#include <glib.h>
+#include <glib/gtestutils.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef void (*GtxTestFunc) (gpointer fixture, gconstpointer test_data);
+
+typedef struct {
+	gpointer		fixture;
+	gconstpointer	test_data;
+	GtxTestFunc	test_func;
+	GMainLoop		*loop;
+} GtxTestContext;
+
+gboolean	gtx_wait_for_signal				(GObject		*object,
+											 gint			max_wait_ms,
+											 const gchar	*detailed_signal,
+											 ...);
+
+gboolean	gtx_dispatch_test_case			(GtxTestContext	*ctx);
+
+gboolean	gtx_quit_main_loop				(GMainLoop		*loop);
+
+void		gtx_yield_main_loop				(guint			millis);
+
+void		gtx_flush_sources				(gboolean may_block);
+
+/**
+ * GTX_DEFINE_LOOPED
+ * @func: A function to be called as an idle handler in a #GMainLoop
+ *
+ * Declare that a test function is runnable in a #GMainLoop idle handler.
+ * Use GTX_LOOPED() to get a reference to a version of @func being run
+ * in a main loop.
+ */
+#define GTX_DEFINE_LOOPED(func) \
+							void \
+							func##__gtx_looped (gpointer fixture,\
+												gconstpointer test_data)\
+							{\
+								GMainLoop   *loop;\
+								GtxTestContext *ctx;\
+								\
+								loop = g_main_loop_new (NULL, FALSE);\
+								ctx = g_new0 (GtxTestContext, 1);\
+								\
+								ctx->fixture = fixture;\
+								ctx->test_data = test_data;\
+								ctx->test_func = func;\
+								ctx->loop = loop;\
+								\
+								g_idle_add ((GSourceFunc)gtx_dispatch_test_case,\
+											ctx);\
+								g_idle_add ((GSourceFunc)gtx_quit_main_loop,\
+											loop);\
+								\
+								g_main_loop_run (loop);\
+								\
+								g_free (ctx);\
+							}
+
+/**
+ * GTX_LOOPED
+ * @func: A function on which GTX_DEFINE_LOOPED has been set
+ *
+ * Get a reference to the version of @func being run inside a main loop.
+ */
+#define GTX_LOOPED(func) func##__gtx_looped
+
+/**
+ * gtx_assert_last_unref
+ * @o: A #GObject
+ *
+ * Calls g_object_unref on @o and raises a critical error if the
+ * #GObject is not finalized after this call.
+ */
+/* This macro is based on code by Benjamin Otte, April, 2008 */
+#define gtx_assert_last_unref(o) G_STMT_START { \
+	gpointer _tmp = o; \
+	g_object_add_weak_pointer (G_OBJECT(o), &_tmp); \
+	g_object_unref (G_OBJECT(o)); \
+	if (_tmp != NULL) \
+		g_critical ("Leak detected. Object %s %p is not unreferenced",\
+					g_type_name(G_OBJECT_TYPE(o)), o);\
+} G_STMT_END
+
+G_END_DECLS
+
+#endif /* __G_TEST_EXTENSIONS_H__ */

Added: trunk/tests/trackerd/xesam/xesam-g-debug-private.h
==============================================================================
--- (empty file)
+++ trunk/tests/trackerd/xesam/xesam-g-debug-private.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,90 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with main.c; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301,  USA
+ */
+
+/*
+ * This file contains special tools for debugging, used in xesam-glib
+ */
+
+#ifndef _XESAM_G_DEBUG_PRIVATE_H_
+#define _XESAM_G_DEBUG_PRIVATE_H_
+
+#include "config.h"
+
+G_BEGIN_DECLS
+
+#ifndef XESAM_G_LOG_DOMAIN
+#define XESAM_G_LOG_DOMAIN    "XesamGLib"
+#endif	/* XESAM_G_LOG_DOMAIN */
+
+/*
+ * Make xesam_g_debug a noop if ENABLE_DEBUG is not defined
+ */
+#ifdef ENABLE_DEBUG
+
+#   ifdef G_HAVE_ISO_VARARGS
+#	   define xesam_g_debug(...)	g_log (XESAM_G_LOG_DOMAIN, \
+										   G_LOG_LEVEL_DEBUG,  \
+										   __VA_ARGS__)
+#	   define xesam_g_debug_object(object, ...)    xesam_g_debug_object_real (object, __VA_ARGS__)
+
+#   elif defined(G_HAVE_GNUC_VARARGS)
+#	   define xesam_g_debug(format...)  g_log (XESAM_G_LOG_DOMAIN,	 \
+											   G_LOG_LEVEL_DEBUG,	\
+											   format)
+#	   define xesam_g_debug_object(object, format...)    xesam_g_debug_object_real (object, format)
+#   else   /* no varargs macros */
+static void
+xesam_g_debug (const gchar *format,
+			   ...)
+{
+	va_list args;
+	va_start (args, format);
+	g_logv (XESAM_G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, format, args);
+	va_end (args);
+}
+
+static void
+xesam_g_debug_object (GObject *obj,
+					  const gchar *format,
+					  ...)
+{
+	va_list args;
+	va_start (args, format);
+	xesam_g_debug_object_va (obj, format, args);
+	va_end (args);
+}
+#   endif  /* !__GNUC__ */
+
+#else /* NO DEBUGGING OUTPUT */
+
+#   ifdef G_HAVE_ISO_VARARGS
+#	   define xesam_g_debug(...)	G_STMT_START{ (void)0; }G_STMT_END
+#	   define xesam_g_debug_object(object, ...)    G_STMT_START{ (void)0; }G_STMT_END
+#   elif defined(G_HAVE_GNUC_VARARGS)
+#	   define xesam_g_debug(format...)     G_STMT_START{ (void)0; }G_STMT_END
+#	   define xesam_g_debug_object(object, format...)     G_STMT_START{ (void)0; }G_STMT_END
+#   else   /* no varargs macros */
+
+static void xesam_g_debug (const gchar *format, ...) { ; }
+static void xesam_g_debug_object (GObject *obj, const gchar *format, ...) { ; }
+
+#   endif /* !__GNUC__ */
+
+#endif /* ENABLE_DEBUG */
+
+G_END_DECLS
+
+#endif /* _XESAM_G_DEBUG_PRIVATE_H_ */

Added: trunk/tests/trackerd/xesam/xesam-g-globals-private.h
==============================================================================
--- (empty file)
+++ trunk/tests/trackerd/xesam/xesam-g-globals-private.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,55 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with main.c; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301,  USA
+ */
+
+/*
+ * This file contains declarations that are considered to be "package private"
+ * in Java terminology. Ie methods and constants for shared use inside
+ * xesam-glib only.
+ */
+
+#ifndef _XESAM_G_GLOBALS_PRIVATE_H_
+#define _XESAM_G_GLOBALS_PRIVATE_H_
+
+#include <xesam-glib/xesam-g-search.h>
+#include <xesam-glib/xesam-g-hit.h>
+#include <xesam-glib/xesam-g-hits.h>
+
+G_BEGIN_DECLS
+
+XesamGSearch* xesam_g_search_new		    (XesamGSession	*session,
+													 XesamGQuery	*query);
+
+XesamGSearch* xesam_g_search_new_from_text	    (XesamGSession	*session,
+													 gchar			*search_text);
+
+XesamGHit*    xesam_g_hit_new			    (guint	    id,
+						     GHashTable     *field_map,
+						     GPtrArray	    *field_data);
+
+XesamGHits*   xesam_g_hits_new			    (XesamGSearch   *search,
+						     guint	    batch_offset,
+						     guint	    count,
+						     GPtrArray	    *hits_data);
+
+GHashTable*   xesam_g_session_get_field_map	    (XesamGSession  *session);
+
+GHashTable*   xesam_g_search_get_field_map	    (XesamGSearch  *search);
+
+
+
+G_END_DECLS
+
+#endif /* _XESAM_G_GLOBALS_PRIVATE_H_ */

Added: trunk/tests/trackerd/xesam/xesam-g-test-query-builder.c
==============================================================================
--- (empty file)
+++ trunk/tests/trackerd/xesam/xesam-g-test-query-builder.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,448 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * xesam-glib
+ * Copyright (C) Mikkel Kamstrup Erlandsen 2008 <mikkel kamstrup gmail com>
+ *
+ * xesam-glib is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * xesam-glib 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 xesam-glib.  If not, write to:
+ *	The Free Software Foundation, Inc.,
+ *	51 Franklin Street, Fifth Floor
+ *	Boston, MA  02110-1301, USA.
+ */
+
+#include "xesam-g-test-query-builder.h"
+#include <xesam-glib/xesam-g-query-builder.h>
+#include <xesam-glib/xesam-g-query-token.h>
+#include <xesam-glib/xesam-glib-globals.h>
+#include "xesam-g-utils.h"
+#include "xesam-g-debug-private.h"
+
+enum  {
+	XESAM_G_TEST_QUERY_BUILDER_DUMMY_PROPERTY
+};
+
+typedef struct {
+	gchar				*attr1;
+	gchar				*attr2;
+	gboolean			*bool_attr;
+	gchar				*text;
+	XesamGQueryToken	token;
+} ExpectedElementData;
+
+struct _XesamGTestQueryBuilderPrivate {
+	GSList	*expected_data;
+};
+
+static gboolean xesam_g_test_query_builder_real_start_query (XesamGQueryBuilder		*self,
+															 XesamGQueryToken		token,
+															 const gchar			*content_cat,
+															 const gchar			*source_cat,
+															 GError					**error);
+
+static gboolean xesam_g_test_query_builder_real_add_clause (XesamGQueryBuilder		*self,
+															XesamGQueryToken		token,
+															const gchar				*boost,
+															const gboolean			*negate,
+															const gchar				**attr_names,
+															const gchar				**attr_vals,
+															GError					**error);
+
+static gboolean xesam_g_test_query_builder_real_add_field (XesamGQueryBuilder		*self,
+														   XesamGQueryToken			token,
+														   const char				*name,
+														   GError					**error);
+
+static gboolean xesam_g_test_query_builder_real_add_value (XesamGQueryBuilder		*self,
+														   XesamGQueryToken			token,
+														   const gchar				*value,
+														   const gchar				**attr_names,
+														   const gchar				**attr_vals,
+														   GError					**error);
+
+static gboolean xesam_g_test_query_builder_real_close_clause
+														  (XesamGQueryBuilder		*self,
+														   GError					**error);
+
+static gboolean xesam_g_test_query_builder_real_close_query
+														  (XesamGQueryBuilder		*self,
+														   GError					**error);
+
+static ExpectedElementData*
+			expected_element_data_new					(guint					element_detail,
+														 const gchar			*attr1,
+														 const gchar			*attr2,
+														 const gboolean			*bool_attr,
+														 const gchar			*text);
+
+static void expected_element_data_destroy				(ExpectedElementData	*exp_data);
+
+static void check_expected_data							(XesamGTestQueryBuilder	*self,
+														 XesamGQueryToken		token,
+														 const gchar			*attr1,
+														 const gchar			*attr2,
+														 const gboolean			*bool_attr,
+														 const gchar			*text);
+
+static gpointer xesam_g_test_query_builder_parent_class = NULL;
+static XesamGQueryBuilderIface* xesam_g_test_query_builder_parent_iface = NULL;
+
+
+static gboolean
+xesam_g_test_query_builder_real_start_query (XesamGQueryBuilder		*base,
+											 XesamGQueryToken		token,
+											 const gchar			*content_cat,
+											 const gchar			*source_cat,
+											 GError					**error)
+{
+	XesamGTestQueryBuilder	*self;
+
+	g_return_val_if_fail (XESAM_IS_G_TEST_QUERY_BUILDER (base), FALSE);
+
+	self = XESAM_G_TEST_QUERY_BUILDER (base);
+
+	xesam_g_debug_object (self, "start_query (%s, content='%s', source='%s')",
+						  xesam_g_query_token_get_name (token),
+						  content_cat, source_cat);
+
+	check_expected_data (self, token, content_cat, source_cat, NULL, NULL);
+
+	return TRUE;
+}
+
+static gboolean
+xesam_g_test_query_builder_real_add_clause (XesamGQueryBuilder		*base,
+											XesamGQueryToken		token,
+											const gchar				*boost,
+											const gboolean			*negate,
+											const gchar				**attr_names,
+											const gchar				**attr_vals,
+											GError					**error)
+{
+	XesamGTestQueryBuilder	*self;
+
+	g_return_val_if_fail (XESAM_IS_G_TEST_QUERY_BUILDER (base), FALSE);
+
+	self = XESAM_G_TEST_QUERY_BUILDER (base);
+
+	xesam_g_debug_object (self, "add_clause (%s, negate='%s' boost='%s')",
+						  xesam_g_query_token_get_name (token),
+						  negate ? (*negate ? "True" : "False") : "NULL",
+						  boost);
+
+	check_expected_data (self, token, boost, NULL, negate, NULL);
+
+	return TRUE;
+}
+
+static gboolean
+xesam_g_test_query_builder_real_add_field (XesamGQueryBuilder		*base,
+										   XesamGQueryToken			token,
+										   const char				*name,
+										   GError					**error)
+{
+	XesamGTestQueryBuilder	*self;
+
+	g_return_val_if_fail (XESAM_IS_G_TEST_QUERY_BUILDER (base), FALSE);
+
+	self = XESAM_G_TEST_QUERY_BUILDER (base);
+
+	xesam_g_debug_object (self, "add_field (%s, name='%s')",
+						  xesam_g_query_token_get_name (token), name);
+
+	check_expected_data (self, token, name, NULL, NULL, NULL);
+
+	return TRUE;
+}
+
+static gboolean
+xesam_g_test_query_builder_real_add_value (XesamGQueryBuilder		*base,
+										   XesamGQueryToken			token,
+										   const gchar				*value,
+										   const gchar				**attr_names,
+										   const gchar				**attr_vals,
+										   GError					**error)
+{
+	XesamGTestQueryBuilder	*self;
+	const gchar				*attr1 = NULL, *attr2 = NULL;
+
+	g_return_val_if_fail (XESAM_IS_G_TEST_QUERY_BUILDER (base), FALSE);
+
+	self = XESAM_G_TEST_QUERY_BUILDER (base);
+
+	if (attr_names[0]) {
+		attr1 = attr_names[0];
+		if (attr_names[1])
+			attr2 = attr_names[1];
+	}
+
+	xesam_g_debug_object (self, "add_value (%s='%s', attr1='%s', attr2='%s')",
+						  xesam_g_query_token_get_name (token),
+						  value,
+						  attr1,
+						  attr2);
+
+	check_expected_data (self, token, attr1, attr2, NULL, value);
+
+	return TRUE;
+}
+
+static gboolean
+xesam_g_test_query_builder_real_close_clause (XesamGQueryBuilder	*base,
+											  GError				**error)
+{
+	XesamGTestQueryBuilder	*self;
+
+	g_return_val_if_fail (XESAM_IS_G_TEST_QUERY_BUILDER (base), FALSE);
+
+	self = XESAM_G_TEST_QUERY_BUILDER (base);
+
+	xesam_g_debug_object (self, "close_clause ()");
+
+	check_expected_data (self, XESAM_G_QUERY_TOKEN_NONE, NULL, NULL, NULL, NULL);
+
+	return TRUE;
+}
+
+static gboolean
+xesam_g_test_query_builder_real_close_query (XesamGQueryBuilder	*base,
+											 GError				**error)
+{
+	XesamGTestQueryBuilder	*self;
+
+	g_return_val_if_fail (XESAM_IS_G_TEST_QUERY_BUILDER (base), FALSE);
+
+	self = XESAM_G_TEST_QUERY_BUILDER (base);
+
+	xesam_g_debug_object (self, "close_query ()");
+
+	check_expected_data (self, XESAM_G_QUERY_TOKEN_NONE, NULL, NULL, NULL, NULL);
+
+	return TRUE;
+}
+
+
+static void
+xesam_g_test_query_builder_finalize (GObject *obj)
+{
+	GSList							*iter;
+	XesamGTestQueryBuilderPrivate	*priv;
+	ExpectedElementData				*exp_data;
+
+	xesam_g_debug_object (obj, "Finalizing");
+
+	priv = XESAM_G_TEST_QUERY_BUILDER(obj)->priv;
+
+	for (iter = priv->expected_data; iter; iter = iter->next) {
+		exp_data = (ExpectedElementData*)iter->data;
+		expected_element_data_destroy (exp_data);
+	}
+
+	if (priv->expected_data) {
+		g_slist_free (priv->expected_data);
+		g_critical ("TestQueryBuilder still contains expected data");
+	}
+
+}
+
+static void
+xesam_g_test_query_builder_class_init (XesamGTestQueryBuilderClass * klass)
+{
+	GObjectClass* object_class = G_OBJECT_CLASS (klass);
+
+	xesam_g_test_query_builder_parent_class = g_type_class_peek_parent (klass);
+
+	object_class->finalize = xesam_g_test_query_builder_finalize;
+}
+
+
+static void
+xesam_g_test_query_builder_interface_init (XesamGQueryBuilderIface * iface)
+{
+	xesam_g_test_query_builder_parent_iface = g_type_interface_peek_parent (iface);
+
+	iface->start_query = xesam_g_test_query_builder_real_start_query;
+	iface->add_clause = xesam_g_test_query_builder_real_add_clause;
+	iface->add_field = xesam_g_test_query_builder_real_add_field;
+	iface->add_value = xesam_g_test_query_builder_real_add_value;
+	iface->close_clause = xesam_g_test_query_builder_real_close_clause;
+	iface->close_query = xesam_g_test_query_builder_real_close_query;
+}
+
+
+static void
+xesam_g_test_query_builder_init (XesamGTestQueryBuilder *self)
+{
+	self->priv = g_new0 (XesamGTestQueryBuilderPrivate, 1);
+
+}
+
+
+GType
+xesam_g_test_query_builder_get_type (void)
+{
+	static GType xesam_g_test_query_builder_type_id = 0;
+
+	if (G_UNLIKELY (xesam_g_test_query_builder_type_id == 0)) {
+
+		static const GTypeInfo g_define_type_info = {
+			sizeof (XesamGTestQueryBuilderClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) xesam_g_test_query_builder_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,
+			sizeof (XesamGTestQueryBuilder),
+			0,
+			(GInstanceInitFunc) xesam_g_test_query_builder_init
+		};
+
+		static const GInterfaceInfo xesam_g_query_builder_info = {
+			(GInterfaceInitFunc) xesam_g_test_query_builder_interface_init,
+			(GInterfaceFinalizeFunc) NULL,
+			NULL
+		};
+
+		xesam_g_test_query_builder_type_id = g_type_register_static (G_TYPE_OBJECT,
+																	 "XesamGTestQueryBuilder",
+																	 &g_define_type_info,
+																	 0);
+
+		g_type_add_interface_static (xesam_g_test_query_builder_type_id,
+									 XESAM_TYPE_G_QUERY_BUILDER,
+									 &xesam_g_query_builder_info);
+	}
+
+	return xesam_g_test_query_builder_type_id;
+}
+
+XesamGTestQueryBuilder*
+xesam_g_test_query_builder_new (void)
+{
+	XesamGTestQueryBuilder	*self;
+
+	self = g_object_new (XESAM_TYPE_G_TEST_QUERY_BUILDER, NULL);
+
+	return self;
+}
+
+void
+xesam_g_test_query_builder_expect_data (XesamGTestQueryBuilder	*self,
+										XesamGQueryToken		token,
+										const gchar				*attr1,
+										const gchar				*attr2,
+										const gboolean			*bool_attr,
+										const gchar				*text)
+{
+	ExpectedElementData	*exp_data;
+
+	g_return_if_fail (XESAM_IS_G_TEST_QUERY_BUILDER(self));
+
+	exp_data = expected_element_data_new (token, attr1, attr2, bool_attr, text);
+
+	/* Yes, appending data to a GSList is silly, but we don't care the slightest
+	 * about performance here */
+	self->priv->expected_data = g_slist_append (self->priv->expected_data, exp_data);
+
+}
+
+void
+xesam_g_test_query_builder_expect_close_clause (XesamGTestQueryBuilder	*self)
+{
+	g_return_if_fail (XESAM_IS_G_TEST_QUERY_BUILDER (self));
+
+	xesam_g_test_query_builder_expect_data (self,
+											XESAM_G_QUERY_TOKEN_NONE,
+											NULL, NULL, NULL, NULL);
+}
+
+void
+xesam_g_test_query_builder_expect_close_query (XesamGTestQueryBuilder	*self)
+{
+	g_return_if_fail (XESAM_IS_G_TEST_QUERY_BUILDER (self));
+
+	xesam_g_test_query_builder_expect_data (self,
+											XESAM_G_QUERY_TOKEN_NONE,
+											NULL, NULL, NULL, NULL);
+}
+
+static ExpectedElementData*
+expected_element_data_new (XesamGQueryToken			token,
+						   const gchar				*attr1,
+						   const gchar				*attr2,
+						   const gboolean			*bool_attr,
+						   const gchar				*text)
+{
+	ExpectedElementData	*exp_data;
+
+	exp_data = g_slice_new (ExpectedElementData);
+	exp_data->token = token;
+	exp_data->attr1 = g_strdup (attr1);
+	exp_data->attr2 = g_strdup (attr2);
+	exp_data->bool_attr = g_memdup (bool_attr, sizeof(gboolean));
+	exp_data->text = g_strdup (text);
+
+	return exp_data;
+}
+
+static void
+expected_element_data_destroy (ExpectedElementData *exp_data)
+{
+	if (exp_data->attr1) g_free (exp_data->attr1);
+	if (exp_data->attr2) g_free (exp_data->attr2);
+	if (exp_data->bool_attr != NULL) g_free (exp_data->bool_attr);
+	if (exp_data->text) g_free (exp_data->text);
+
+	g_slice_free (ExpectedElementData, exp_data);
+}
+
+static void
+check_expected_data (XesamGTestQueryBuilder *self,
+					 XesamGQueryToken		token,
+					 const gchar			*attr1,
+					 const gchar			*attr2,
+					 const gboolean			*bool_attr,
+					 const gchar			*text)
+{
+	ExpectedElementData	*exp_data;
+
+	g_return_if_fail (XESAM_IS_G_TEST_QUERY_BUILDER(self));
+
+	if (!self->priv->expected_data) {
+		/* No data to check */
+		return;
+	}
+
+	exp_data = (ExpectedElementData*) self->priv->expected_data->data;
+
+	g_assert_cmpstr (xesam_g_query_token_get_name (token), ==,
+					 xesam_g_query_token_get_name (exp_data->token));
+
+	g_assert_cmpstr (attr1, ==, exp_data->attr1);
+	g_assert_cmpstr (attr2, ==, exp_data->attr2);
+	g_assert_cmpstr (text, ==, exp_data->text);
+
+	if (bool_attr != NULL && exp_data->bool_attr != NULL)
+		g_assert_cmpuint (*bool_attr, ==, *exp_data->bool_attr);
+	else if (bool_attr != NULL && !*bool_attr && exp_data->bool_attr == NULL)
+		/* bool_attr is False and we expect it to be unset. This is ok. */;
+	else if (!(bool_attr == NULL && exp_data->bool_attr == NULL))
+		g_critical ("bool_attr and exp_data->bool_attr differ. %p != %p",
+					bool_attr, exp_data->bool_attr);
+	/* else : both bool_attr are NULL */
+
+
+	self->priv->expected_data = g_slist_delete_link (self->priv->expected_data,
+													 self->priv->expected_data);
+
+	expected_element_data_destroy (exp_data);
+}

Added: trunk/tests/trackerd/xesam/xesam-g-test-query-builder.h
==============================================================================
--- (empty file)
+++ trunk/tests/trackerd/xesam/xesam-g-test-query-builder.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,72 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * xesam-glib
+ * Copyright (C) Mikkel Kamstrup Erlandsen 2008 <mikkel kamstrup gmail com>
+ *
+ * xesam-glib is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * xesam-glib 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 xesam-glib.  If not, write to:
+ *	The Free Software Foundation, Inc.,
+ *	51 Franklin Street, Fifth Floor
+ *	Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __XESAM_G_TEST_QUERY_BUILDER_H__
+#define __XESAM_G_TEST_QUERY_BUILDER_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include "xesam-glib/xesam-g-query-builder.h"
+#include "xesam-glib/xesam-g-query-token.h"
+
+G_BEGIN_DECLS
+
+
+#define XESAM_TYPE_G_TEST_QUERY_BUILDER (xesam_g_test_query_builder_get_type ())
+#define XESAM_G_TEST_QUERY_BUILDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), XESAM_TYPE_G_TEST_QUERY_BUILDER, XesamGTestQueryBuilder))
+#define XESAM_G_TEST_QUERY_BUILDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), XESAM_TYPE_G_TEST_QUERY_BUILDER, XesamGTestQueryBuilderClass))
+#define XESAM_IS_G_TEST_QUERY_BUILDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), XESAM_TYPE_G_TEST_QUERY_BUILDER))
+#define XESAM_IS_G_TEST_QUERY_BUILDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), XESAM_TYPE_G_TEST_QUERY_BUILDER))
+#define XESAM_G_TEST_QUERY_BUILDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), XESAM_TYPE_G_TEST_QUERY_BUILDER, XesamGTestQueryBuilderClass))
+
+typedef struct _XesamGTestQueryBuilder XesamGTestQueryBuilder;
+typedef struct _XesamGTestQueryBuilderClass XesamGTestQueryBuilderClass;
+typedef struct _XesamGTestQueryBuilderPrivate XesamGTestQueryBuilderPrivate;
+
+struct _XesamGTestQueryBuilder {
+	GObject parent;
+	XesamGTestQueryBuilderPrivate *priv;
+};
+struct _XesamGTestQueryBuilderClass {
+	GObjectClass parent;
+};
+
+XesamGTestQueryBuilder*		xesam_g_test_query_builder_new			(void);
+
+GType						xesam_g_test_query_builder_get_type		(void);
+
+void						xesam_g_test_query_builder_expect_data	(XesamGTestQueryBuilder	*self,
+																	 XesamGQueryToken		token,
+																	 const gchar			*attr1,
+																	 const gchar			*attr2,
+																	 const gboolean			*bool_attr,
+																	 const gchar			*value);
+
+void						xesam_g_test_query_builder_expect_close_clause
+																	(XesamGTestQueryBuilder	*self);
+
+void						xesam_g_test_query_builder_expect_close_query
+																	(XesamGTestQueryBuilder	*self);
+
+G_END_DECLS
+
+#endif

Added: trunk/tests/trackerd/xesam/xesam-g-testsearcher.c
==============================================================================
--- (empty file)
+++ trunk/tests/trackerd/xesam/xesam-g-testsearcher.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,449 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * xesam-glib
+ * Copyright (C) Mikkel Kamstrup Erlandsen 2007 <mikkel kamstrup gmail com>
+ *
+ * xesam-glib is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * xesam-glib 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 xesam-glib.  If not, write to:
+ *	The Free Software Foundation, Inc.,
+ *	51 Franklin Street, Fifth Floor
+ *	Boston, MA  02110-1301, USA.
+ */
+
+#include "xesam-g-testsearcher.h"
+#include <xesam-glib/xesam-g-searcher.h>
+#include <xesam-glib/xesam-glib-globals.h>
+#include "xesam-g-utils.h"
+
+enum  {
+	XESAM_G_TEST_SEARCHER_DUMMY_PROPERTY
+};
+
+typedef struct {
+	GValue	live;					// Boolean
+	GValue	hit_fields;			// GStrv
+	GValue	hit_fields_extended;	// GStrv
+	GValue	hit_snippet_length;	// UInt
+	GValue	sort_primary;			// String
+	GValue	sort_secondary;		// String
+	GValue	sort_order;			// String
+	GValue	vendor_id;				// String
+	GValue	vendor_version;		// UInt
+	GValue	vendor_display;		// String
+	GValue	vendor_xesam;			// UInt
+	GValue	vendor_ontology_fields;  // GStrv
+	GValue	vendor_ontology_contents;// GStrv
+	GValue	vendor_ontology_sources; // GStrv
+	GValue	vendor_extensions;		// GStrv
+	GValue	vendor_ontologies;		// GPtrArray(GStrv)
+	GValue	vendor_maxhits;		// UInt
+} Properties;
+
+struct _XesamGTestSearcherPrivate {
+	Properties		*props;
+};
+
+static void xesam_g_test_searcher_real_new_session		(XesamGSearcher		*base,
+														 XesamGSearcherGotHandle callback,
+														 gpointer			user_data);
+
+static void xesam_g_test_searcher_real_close_session	(XesamGSearcher		*base,
+														 const gchar			*session_handle,
+														 XesamGSearcherVoidResponse callback,
+														 gpointer			user_data);
+
+static void xesam_g_test_searcher_real_get_property	(XesamGSearcher		*base,
+														 const gchar			*session_handle,
+														 const gchar			*prop_name,
+														 XesamGSearcherGotProperty callback,
+														 gpointer			user_data);
+
+static void xesam_g_test_searcher_real_set_property	(XesamGSearcher		*base,
+														 const gchar		*session_handle,
+														 const gchar		*prop_name,
+														 const GValue		*value,
+														 XesamGSearcherGotProperty callback,
+														 gpointer			user_data);
+
+static void xesam_g_test_searcher_real_new_search		(XesamGSearcher		*base,
+														 const gchar			*session_handle,
+														 const gchar			*query,
+														 XesamGSearcherGotHandle callback,
+														 gpointer			user_data);
+
+static void xesam_g_test_searcher_real_start_search	(XesamGSearcher		*base,
+														 const gchar			*search_handle,
+														 XesamGSearcherVoidResponse callback,
+														 gpointer			user_data);
+
+static void xesam_g_test_searcher_real_close_search	(XesamGSearcher		*base,
+														 const gchar			*search_handle,
+														 XesamGSearcherVoidResponse callback,
+														 gpointer			user_data);
+
+static void xesam_g_test_searcher_real_get_hits			(XesamGSearcher		*base,
+														 const gchar			*search_handle,
+														 guint				count,
+														 XesamGSearcherGotHits callback,
+														 gpointer			user_data);
+
+static void xesam_g_test_searcher_real_get_hit_data		(XesamGSearcher		*base,
+														 const gchar		*search_handle,
+														 GArray				*hit_ids,
+														 GStrv				field_names,
+														 XesamGSearcherGotHits callback,
+														 gpointer user_data);
+
+static void xesam_g_test_searcher_real_get_hit_count	(XesamGSearcher		*base,
+														 const gchar			*search_handle,
+														 XesamGSearcherGotHitCount callback,
+														 gpointer user_data);
+
+static void xesam_g_test_searcher_real_get_state		(XesamGSearcher		*base,
+														 XesamGSearcherGotState callback,
+														 gpointer			user_data);
+
+
+static gpointer xesam_g_test_searcher_parent_class = NULL;
+static XesamGSearcherIface* xesam_g_test_searcher_xesam_g_searcher_parent_iface = NULL;
+
+
+static void
+xesam_g_test_searcher_real_new_session (XesamGSearcher	*base,
+										XesamGSearcherGotHandle callback,
+										gpointer		user_data)
+{
+	static int session_num = 0;
+
+	XesamGTestSearcher * self;
+	GString *session_handle_s;
+
+	self = XESAM_G_TEST_SEARCHER (base);
+	session_handle_s = g_string_new (NULL);
+	g_string_printf (session_handle_s, "dummy-session-%d", session_num);
+
+	callback (base, g_string_free (session_handle_s, FALSE), user_data, NULL);
+}
+
+
+static void
+xesam_g_test_searcher_real_close_session (XesamGSearcher	*base,
+										  const gchar		*session_handle,
+										  XesamGSearcherVoidResponse callback,
+										  gpointer			user_data)
+{
+	XesamGTestSearcher * self;
+	self = XESAM_G_TEST_SEARCHER (base);
+	g_return_if_fail (session_handle != NULL);
+
+	callback (base, user_data, NULL);
+}
+
+
+static void
+xesam_g_test_searcher_real_get_property (XesamGSearcher		*base,
+										 const gchar		*session_handle,
+										 const gchar		*prop_name,
+										 XesamGSearcherGotProperty callback,
+										 gpointer			user_data)
+{
+	XesamGTestSearcher  *self;
+	GValue				*target_val;
+
+	self = XESAM_G_TEST_SEARCHER (base);
+	g_return_if_fail (session_handle != NULL);
+	g_return_if_fail (prop_name != NULL);
+
+	if (g_str_equal (prop_name, "search.live"))
+		target_val = &self->priv->props->live;
+	else if (g_str_equal (prop_name, "hit.fields"))
+		target_val = &self->priv->props->hit_fields;
+	else if (g_str_equal (prop_name, "hit.fields.extended"))
+		target_val = &self->priv->props->hit_fields_extended;
+	else
+		g_critical ("GetProperty '%s' not supported by test searcher", prop_name);
+
+	callback (base, g_strdup (prop_name),
+			  xesam_g_clone_value(target_val), user_data, NULL);
+}
+
+
+static void
+xesam_g_test_searcher_real_set_property (XesamGSearcher	*base,
+										 const gchar	*session_handle,
+										 const gchar	*prop_name,
+										 const GValue	*value,
+										 XesamGSearcherGotProperty callback,
+										 gpointer		user_data)
+{
+	XesamGTestSearcher  *self;
+	GValue				*target_val;
+
+	self = XESAM_G_TEST_SEARCHER (base);
+	g_return_if_fail (session_handle != NULL);
+	g_return_if_fail (prop_name != NULL);
+	g_return_if_fail (G_IS_VALUE(value));
+
+	if (g_str_equal (prop_name, "search.live")) {
+		target_val = &self->priv->props->live;
+	} else if (g_str_equal (prop_name, "hit.fields")) {
+		target_val =  &self->priv->props->hit_fields;
+	} else if (g_str_equal (prop_name, "hit.fields.extended")) {
+		target_val =  &self->priv->props->hit_fields_extended;
+	} else
+		g_critical ("SetProperty '%s' not supported by test searcher", prop_name);
+
+	callback (base, g_strdup(prop_name),
+			  xesam_g_clone_value(target_val), user_data, NULL);
+}
+
+
+static void
+xesam_g_test_searcher_real_new_search (XesamGSearcher	*base,
+									   const gchar		*session_handle,
+									   const gchar		*query,
+									   XesamGSearcherGotHandle callback,
+									   gpointer user_data)
+{
+	XesamGTestSearcher * self;
+	self = XESAM_G_TEST_SEARCHER (base);
+	g_return_if_fail (session_handle != NULL);
+	g_return_if_fail (query != NULL);
+}
+
+
+static void
+xesam_g_test_searcher_real_start_search (XesamGSearcher	*base,
+										 const gchar		*search_handle,
+										 XesamGSearcherVoidResponse callback,
+										 gpointer		user_data)
+{
+	XesamGTestSearcher * self;
+	self = XESAM_G_TEST_SEARCHER (base);
+	g_return_if_fail (search_handle != NULL);
+}
+
+
+static void
+xesam_g_test_searcher_real_close_search (XesamGSearcher	*base,
+										 const gchar		*search_handle,
+										 XesamGSearcherVoidResponse callback,
+										 gpointer user_data)
+{
+	XesamGTestSearcher * self;
+	self = XESAM_G_TEST_SEARCHER (base);
+	g_return_if_fail (search_handle != NULL);
+}
+
+
+static void
+xesam_g_test_searcher_real_get_hits (XesamGSearcher	*base,
+									 const gchar		*search_handle,
+									 guint			count,
+									 XesamGSearcherGotHits callback,
+									 gpointer user_data)
+{
+	XesamGTestSearcher * self;
+	self = XESAM_G_TEST_SEARCHER (base);
+	g_return_if_fail (search_handle != NULL);
+}
+
+
+static void
+xesam_g_test_searcher_real_get_hit_data (XesamGSearcher	*base,
+										 const gchar	*search_handle,
+										 GArray			*hit_ids,
+										 GStrv			field_names,
+										 XesamGSearcherGotHits callback,
+										 gpointer user_data)
+{
+	XesamGTestSearcher * self;
+	self = XESAM_G_TEST_SEARCHER (base);
+	g_return_if_fail (search_handle != NULL);
+	g_return_if_fail (field_names != NULL);
+	g_return_if_fail (hit_ids != NULL);
+}
+
+
+static void
+xesam_g_test_searcher_real_get_hit_count (XesamGSearcher	*base,
+										  const gchar		*search_handle,
+										  XesamGSearcherGotHitCount callback,
+										  gpointer			user_data)
+{
+	XesamGTestSearcher * self;
+	self = XESAM_G_TEST_SEARCHER (base);
+	g_return_if_fail (search_handle != NULL);
+}
+
+
+static void
+xesam_g_test_searcher_real_get_state (XesamGSearcher	*base,
+									  XesamGSearcherGotState callback,
+									  gpointer			user_data)
+{
+	XesamGTestSearcher * self;
+	self = XESAM_G_TEST_SEARCHER (base);
+}
+
+
+XesamGTestSearcher*
+xesam_g_test_searcher_new (void)
+{
+	XesamGTestSearcher * self;
+	self = g_object_newv (XESAM_TYPE_G_TEST_SEARCHER, 0, NULL);
+	return self;
+}
+
+
+static void
+xesam_g_test_searcher_finalize (GObject *obj)
+{
+	Properties					*props;
+	XesamGTestSearcher			*self;
+	XesamGTestSearcherPrivate	*priv;
+
+	self = XESAM_G_TEST_SEARCHER (obj);
+	priv = self->priv;
+	props = priv->props;
+
+	/* FIXME: Free prop GValue contents */
+
+	g_free (props);
+	g_free (priv);
+
+	g_debug ("Finalized test searcher\n");
+}
+
+static void
+xesam_g_test_searcher_class_init (XesamGTestSearcherClass * klass)
+{
+	GObjectClass* object_class = G_OBJECT_CLASS (klass);
+
+	xesam_g_test_searcher_parent_class = g_type_class_peek_parent (klass);
+
+	object_class->finalize = xesam_g_test_searcher_finalize;
+}
+
+
+static void
+xesam_g_test_searcher_xesam_g_searcher_interface_init (XesamGSearcherIface * iface)
+{
+	xesam_g_test_searcher_xesam_g_searcher_parent_iface = g_type_interface_peek_parent (iface);
+	iface->new_session = xesam_g_test_searcher_real_new_session;
+	iface->close_session = xesam_g_test_searcher_real_close_session;
+	iface->get_property = xesam_g_test_searcher_real_get_property;
+	iface->set_property = xesam_g_test_searcher_real_set_property;
+	iface->new_search = xesam_g_test_searcher_real_new_search;
+	iface->start_search = xesam_g_test_searcher_real_start_search;
+	iface->close_search = xesam_g_test_searcher_real_close_search;
+	iface->get_hits = xesam_g_test_searcher_real_get_hits;
+	iface->get_hit_data = xesam_g_test_searcher_real_get_hit_data;
+	iface->get_hit_count = xesam_g_test_searcher_real_get_hit_count;
+	iface->get_state = xesam_g_test_searcher_real_get_state;
+}
+
+
+static void
+xesam_g_test_searcher_init (XesamGTestSearcher *self)
+{
+	Properties *props;
+
+	self->priv = g_new0 (XesamGTestSearcherPrivate, 1);
+	props = g_new0 (Properties, 1);
+	self->priv->props = props;
+
+	g_value_init(&props->live, G_TYPE_BOOLEAN);
+	g_value_init(&props->hit_fields, G_TYPE_STRV);
+	g_value_init(&props->hit_fields_extended, G_TYPE_STRV);
+	g_value_init(&props->hit_snippet_length, G_TYPE_UINT);
+	g_value_init(&props->sort_primary, G_TYPE_STRING);
+	g_value_init(&props->sort_secondary, G_TYPE_STRING);
+	g_value_init(&props->sort_order, G_TYPE_STRING);
+	g_value_init(&props->vendor_id, G_TYPE_STRING);
+	g_value_init(&props->vendor_version, G_TYPE_UINT);
+	g_value_init(&props->vendor_display, G_TYPE_STRING);
+	g_value_init(&props->vendor_xesam, G_TYPE_UINT);
+	g_value_init(&props->vendor_ontology_fields, G_TYPE_STRV);
+	g_value_init(&props->vendor_ontology_contents, G_TYPE_STRV);
+	g_value_init(&props->vendor_ontology_sources, G_TYPE_STRV);
+	g_value_init(&props->vendor_extensions, G_TYPE_STRV);
+	g_value_init(&props->vendor_ontologies, XESAM_TYPE_STRV_ARRAY);
+	g_value_init(&props->vendor_maxhits, G_TYPE_UINT);
+
+	const gchar *hit_fields[2] = {"xesam:url", NULL};
+	const gchar *hit_fields_extended[1] = {NULL};
+	const gchar *fields[3] = {"xesam:url", "xesam:relevancyRating", NULL};
+	const gchar *contents[2] = {"xesam:Content", NULL};
+	const gchar *sources[2] = {"xesam:Source", NULL};
+	const gchar *exts[1] = {NULL};
+	const gchar *dummy_onto[4] = {"dummy-onto","0.1","/usr/share/xesam/ontologies/dummy-onto-0.1", NULL};
+	GPtrArray *ontos = g_ptr_array_new ();
+	g_ptr_array_add (ontos, dummy_onto);
+
+	g_value_set_boolean (&props->live, FALSE);
+	g_value_set_boxed (&props->hit_fields, hit_fields);
+	g_value_set_boxed (&props->hit_fields_extended, hit_fields_extended);
+	g_value_set_uint (&props->hit_snippet_length, 200);
+	g_value_set_string (&props->sort_primary, "xesam:relevancyRating");
+	g_value_set_string (&props->sort_secondary, "");
+	g_value_set_string (&props->sort_order, "descending");
+	g_value_set_string (&props->vendor_id, "XesamGLibTestSearcher");
+	g_value_set_uint (&props->vendor_version, 90);
+	g_value_set_string (&props->vendor_display, "Test Searcher for Xesam-GLib");
+	g_value_set_uint (&props->vendor_xesam, 90);
+	g_value_set_boxed (&props->vendor_ontology_fields, fields);
+	g_value_set_boxed (&props->vendor_ontology_contents, contents);
+	g_value_set_boxed (&props->vendor_ontology_sources, sources);
+	g_value_set_boxed (&props->vendor_extensions, exts);
+	g_value_set_boxed(&props->vendor_ontologies, ontos);
+	g_value_set_uint (&props->vendor_maxhits, 50);
+}
+
+
+GType
+xesam_g_test_searcher_get_type (void)
+{
+	static GType xesam_g_test_searcher_type_id = 0;
+
+	if (G_UNLIKELY (xesam_g_test_searcher_type_id == 0)) {
+
+		static const GTypeInfo g_define_type_info = {
+			sizeof (XesamGTestSearcherClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) xesam_g_test_searcher_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,
+			sizeof (XesamGTestSearcher),
+			0,
+			(GInstanceInitFunc) xesam_g_test_searcher_init
+		};
+
+		static const GInterfaceInfo xesam_g_searcher_info = {
+			(GInterfaceInitFunc) xesam_g_test_searcher_xesam_g_searcher_interface_init,
+			(GInterfaceFinalizeFunc) NULL,
+			NULL};
+
+		xesam_g_test_searcher_type_id = g_type_register_static (G_TYPE_OBJECT,
+																"XesamGTestSearcher",
+																&g_define_type_info,
+																0);
+
+		g_type_add_interface_static (xesam_g_test_searcher_type_id,
+									 XESAM_TYPE_G_SEARCHER,
+									 &xesam_g_searcher_info);
+	}
+
+	return xesam_g_test_searcher_type_id;
+}

Added: trunk/tests/trackerd/xesam/xesam-g-testsearcher.h
==============================================================================
--- (empty file)
+++ trunk/tests/trackerd/xesam/xesam-g-testsearcher.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,59 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * xesam-glib
+ * Copyright (C) Mikkel Kamstrup Erlandsen 2007 <mikkel kamstrup gmail com>
+ *
+ * xesam-glib is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * xesam-glib 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 xesam-glib.  If not, write to:
+ *	The Free Software Foundation, Inc.,
+ *	51 Franklin Street, Fifth Floor
+ *	Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __XESAM_G_TESTSEARCHER_H__
+#define __XESAM_G_TESTSEARCHER_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <stdlib.h>
+#include <string.h>
+#include "xesam-glib/xesam-g-searcher.h"
+
+G_BEGIN_DECLS
+
+
+#define XESAM_TYPE_G_TEST_SEARCHER (xesam_g_test_searcher_get_type ())
+#define XESAM_G_TEST_SEARCHER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), XESAM_TYPE_G_TEST_SEARCHER, XesamGTestSearcher))
+#define XESAM_G_TEST_SEARCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), XESAM_TYPE_G_TEST_SEARCHER, XesamGTestSearcherClass))
+#define XESAM_IS_G_TEST_SEARCHER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), XESAM_TYPE_G_TEST_SEARCHER))
+#define XESAM_IS_G_TEST_SEARCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), XESAM_TYPE_G_TEST_SEARCHER))
+#define XESAM_G_TEST_SEARCHER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), XESAM_TYPE_G_TEST_SEARCHER, XesamGTestSearcherClass))
+
+typedef struct _XesamGTestSearcher XesamGTestSearcher;
+typedef struct _XesamGTestSearcherClass XesamGTestSearcherClass;
+typedef struct _XesamGTestSearcherPrivate XesamGTestSearcherPrivate;
+
+struct _XesamGTestSearcher {
+	GObject parent;
+	XesamGTestSearcherPrivate *priv;
+};
+struct _XesamGTestSearcherClass {
+	GObjectClass parent;
+};
+
+XesamGTestSearcher* xesam_g_test_searcher_new (void);
+GType xesam_g_test_searcher_get_type (void);
+
+G_END_DECLS
+
+#endif

Added: trunk/tests/trackerd/xesam/xesam-g-utils.h
==============================================================================
--- (empty file)
+++ trunk/tests/trackerd/xesam/xesam-g-utils.h	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,48 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * xesam-glib
+ * Copyright (C) Mikkel Kamstrup Erlandsen 2007 <mikkel kamstrup gmail com>
+ *
+ * xesam-glib is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * xesam-glib 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 xesam-glib.  If not, write to:
+ *	The Free Software Foundation, Inc.,
+ *	51 Franklin Street, Fifth Floor
+ *	Boston, MA  02110-1301, USA.
+ */
+
+/*
+ * xesam-g-utils.h defines commonly used *private* functionality
+ * of the xesam-glib.
+ */
+
+#ifndef _XESAM_G_UTILS_H_
+#define _XESAM_G_UTILS_H_
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+GValue*			init_value_if_null				(GValue **value,
+												 GType value_type);
+
+gchar*			g_property_to_xesam_property	(gchar	*g_prop_name);
+
+void			free_ptr_array_of_values		(GPtrArray *array);
+
+GValue*			xesam_g_clone_value				(const GValue *orig);
+
+const gchar*	digit_as_const_char				(guint			digit);
+
+G_END_DECLS
+
+#endif /* _XESAM_G_UTILS_H_ */

Added: trunk/thumbnailers/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/thumbnailers/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,3 @@
+include $(top_srcdir)/Makefile.decl
+
+SUBDIRS = application image

Added: trunk/thumbnailers/application/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/thumbnailers/application/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,11 @@
+include $(top_srcdir)/Makefile.decl
+
+thumbappbindir = $(libdir)/tracker/thumbnailers/application
+
+thumbappbin_SCRIPTS = pdf_thumbnailer \
+			vnd.oasis.opendocument.graphics_thumbnailer \
+			vnd.oasis.opendocument.presentation_thumbnailer \
+			vnd.oasis.opendocument.spreadsheet_thumbnailer \
+			vnd.oasis.opendocument.text_thumbnailer
+
+EXTRA_DIST = $(thumbappbin_SCRIPTS)

Added: trunk/thumbnailers/application/pdf_thumbnailer
==============================================================================
--- (empty file)
+++ trunk/thumbnailers/application/pdf_thumbnailer	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+nice -n19  evince-thumbnailer  -s $3 "$1" $2
+

Added: trunk/thumbnailers/application/vnd.oasis.opendocument.graphics_thumbnailer
==============================================================================
--- (empty file)
+++ trunk/thumbnailers/application/vnd.oasis.opendocument.graphics_thumbnailer	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+nice -n19 unzip -p "$1" Thumbnails/thumbnail.png | convert - -resize $3x$3 "$2"

Added: trunk/thumbnailers/application/vnd.oasis.opendocument.presentation_thumbnailer
==============================================================================
--- (empty file)
+++ trunk/thumbnailers/application/vnd.oasis.opendocument.presentation_thumbnailer	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+nice -n19 unzip -p "$1" Thumbnails/thumbnail.png | convert - -resize $3x$3 "$2"

Added: trunk/thumbnailers/application/vnd.oasis.opendocument.spreadsheet_thumbnailer
==============================================================================
--- (empty file)
+++ trunk/thumbnailers/application/vnd.oasis.opendocument.spreadsheet_thumbnailer	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+nice -n19 unzip -p "$1" Thumbnails/thumbnail.png | convert - -resize $3x$3 "$2"

Added: trunk/thumbnailers/application/vnd.oasis.opendocument.text_thumbnailer
==============================================================================
--- (empty file)
+++ trunk/thumbnailers/application/vnd.oasis.opendocument.text_thumbnailer	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+nice -n19 unzip -p "$1" Thumbnails/thumbnail.png | convert - -resize $3x$3 "$2"

Added: trunk/thumbnailers/image/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/thumbnailers/image/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,21 @@
+include $(top_srcdir)/Makefile.decl
+
+thumbappbindir = $(libdir)/tracker/thumbnailers/image
+
+if HAVE_IMAGEMAGICK
+thumbappbin_SCRIPTS = 		\
+	png_thumbnailer		\
+	jpeg_thumbnailer 	\
+	gif_thumbnailer		\
+	tiff_thumbnailer
+else
+if HAVE_HILDON_THUMBNAIL
+thumbappbin_SCRIPTS = 		\
+	hildon/png_thumbnailer 	\
+	hildon/jpeg_thumbnailer \
+	hildon/gif_thumbnailer	\
+	hildon/tiff_thumbnailer
+endif
+endif
+
+EXTRA_DIST = $(thumbappbin_SCRIPTS)

Added: trunk/thumbnailers/image/gif_thumbnailer
==============================================================================
--- (empty file)
+++ trunk/thumbnailers/image/gif_thumbnailer	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+nice -n19 convert "$1" -thumbnail $3x$3 $2

Added: trunk/thumbnailers/image/hildon/gif_thumbnailer
==============================================================================
--- (empty file)
+++ trunk/thumbnailers/image/hildon/gif_thumbnailer	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+/usr/bin/nice -n19 /usr/bin/hildon-thumb-gdk-pixbuf file:"$1" image/gif $2 0 $3 $3
+

Added: trunk/thumbnailers/image/hildon/jpeg_thumbnailer
==============================================================================
--- (empty file)
+++ trunk/thumbnailers/image/hildon/jpeg_thumbnailer	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+/usr/bin/nice -n19 /usr/bin/hildon-thumb-gdk-pixbuf file:"$1" image/jpeg $2 0 $3 $3
+

Added: trunk/thumbnailers/image/hildon/png_thumbnailer
==============================================================================
--- (empty file)
+++ trunk/thumbnailers/image/hildon/png_thumbnailer	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+/usr/bin/nice -n19 /usr/bin/hildon-thumb-gdk-pixbuf file:"$1" image/png $2 0 $3 $3
+

Added: trunk/thumbnailers/image/hildon/tiff_thumbnailer
==============================================================================
--- (empty file)
+++ trunk/thumbnailers/image/hildon/tiff_thumbnailer	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+/usr/bin/nice -n19 /usr/bin/hildon-thumb-gdk-pixbuf file:"$1" image/tiff $2 0 $3 $3
+

Added: trunk/thumbnailers/image/jpeg_thumbnailer
==============================================================================
--- (empty file)
+++ trunk/thumbnailers/image/jpeg_thumbnailer	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+nice -n19 convert "$1" -thumbnail $3x$3 $2

Added: trunk/thumbnailers/image/png_thumbnailer
==============================================================================
--- (empty file)
+++ trunk/thumbnailers/image/png_thumbnailer	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+nice -n19 convert "$1" -thumbnail $3x$3 $2

Added: trunk/thumbnailers/image/tiff_thumbnailer
==============================================================================
--- (empty file)
+++ trunk/thumbnailers/image/tiff_thumbnailer	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+nice -n19 convert "$1" -thumbnail $3x$3 $2

Added: trunk/tracker.spec
==============================================================================
--- (empty file)
+++ trunk/tracker.spec	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,121 @@
+Summary: An object database, tag/metadata database, search tool and indexer
+Name: tracker
+Version: 0.5.1
+Release: 1%{?dist}
+License: GPL
+Group: Applications/System
+URL: http://www.gnome.org/~jamiemcc/tracker/
+Source0: http://www.gnome.org/~jamiemcc/tracker/tracker-%{version}.tar.gz
+Source1: trackerd.desktop
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+BuildRequires: gmime-devel, poppler-devel, gettext
+BuildRequires: gnome-desktop-devel, gamin-devel
+BuildRequires: libexif-devel, libgsf-devel, gstreamer-devel
+BuildRequires: desktop-file-utils, intltool
+%if "%fedora" >= "6"
+BuildRequires: sqlite-devel
+%else
+BuildRequires: dbus-devel, dbus-glib
+%endif
+
+%description
+Tracker is a powerful desktop-neutral first class object database,
+tag/metadata database, search tool and indexer.
+
+It consists of a common object database that allows entities to have an
+almost infinte number of properties, metadata (both embedded/harvested as
+well as user definable), a comprehensive database of keywords/tags and
+links to other entities.
+
+It provides additional features for file based objects including context
+linking and audit trails for a file object.
+
+It has the ability to index, store, harvest metadata. retrieve and search
+all types of files and other first class objects
+
+%package devel
+Summary: Headers for developing programs that will use %{name}
+Group: Development/Libraries
+Requires: %{name} = %{version}-%{release}
+Requires: pkgconfig
+
+%description devel
+This package contains the static libraries and header files needed for
+developing with tracker
+
+%prep
+%setup -q
+
+%build
+%if "%fedora" >= "6"
+%configure --disable-static --enable-external-sqlite
+%else
+%configure --disable-static
+%endif
+# make %{?_smp_mflags} fails
+make
+
+
+%install
+rm -rf %{buildroot}
+make DESTDIR=%{buildroot} install
+
+# Add an autostart for trackerd
+mkdir -p %{buildroot}%{_sysconfdir}/xdg/autostart
+cp %{SOURCE1} %{buildroot}%{_sysconfdir}/xdg/autostart/
+
+rm -rf %{buildroot}%{_libdir}/*.la
+
+%clean
+rm -rf %{buildroot}
+
+%post -p /sbin/ldconfig
+
+%postun -p /sbin/ldconfig
+
+%files
+%defattr(-, root, root, -)
+%doc AUTHORS ChangeLog COPYING NEWS README
+%{_bindir}/htmless
+%{_bindir}/o3totxt
+%{_bindir}/tracker*
+%{_datadir}/tracker/
+%{_datadir}/pixmaps/tracker/
+%{_datadir}/dbus-1/services/tracker.service
+%{_libdir}/*.so.*
+%{_mandir}/man1/tracker*.1.gz
+%{_sysconfdir}/xdg/autostart/trackerd.desktop
+
+%files devel
+%defattr(-, root, root, -)
+%{_includedir}/tracker*
+%{_libdir}/*.so
+%{_libdir}/pkgconfig/*.pc
+
+%changelog
+* Mon Nov 06 2006 Deji Akingunola <dakingun gmail com> - 0.5.1-1
+- Update to new version
+
+* Mon Nov 06 2006 Deji Akingunola <dakingun gmail com> - 0.5.0-7
+- Have the devel subpackage require pkgconfig
+- Make the description field not have more than 76 characters on a line
+- Fix up the RPM group
+
+* Mon Nov 06 2006 Deji Akingunola <dakingun gmail com> - 0.5.0-6
+- Explicitly require dbus-devel and dbus-glib (needed for FC < 6)
+
+* Sun Nov 05 2006 Deji Akingunola <dakingun gmail com> - 0.5.0-5
+- Remove unneeded BRs (gnome-utils-devel and openssl-devel)
+
+* Sun Nov 05 2006 Deji Akingunola <dakingun gmail com> - 0.5.0-4
+- Add autostart desktop file.
+- Edit the package description as suggested in review
+
+* Sat Nov 04 2006 Deji Akingunola <dakingun gmail com> - 0.5.0-3
+- More cleaups to the spec file.
+
+* Sat Nov 04 2006 Deji Akingunola <dakingun gmail com> - 0.5.0-2
+- Add needed BRs
+
+* Sat Nov 04 2006 Deji Akingunola <dakingun gmail com> - 0.5.0-1
+- Initial packaging for Fedora Extras

Added: trunk/utils/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/utils/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,3 @@
+include $(top_srcdir)/Makefile.decl
+
+SUBDIRS = qdbm
\ No newline at end of file

Added: trunk/utils/qdbm/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/utils/qdbm/Makefile.am	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,28 @@
+include $(top_srcdir)/Makefile.decl
+
+noinst_PROGRAMS = print search
+
+INCLUDES = 								\
+	-DG_LOG_DOMAIN=\"Tracker\"					\
+	-I$(top_srcdir)/src						\
+	$(QDBM_CFLAGS) 							\
+	$(GLIB2_CFLAGS)
+
+print_SOURCES = 							\
+	print-words.c
+
+print_LDADD = 								\
+	$(top_builddir)/src/libtracker-common/libtracker-common.la 	\
+	$(top_builddir)/src/libtracker-db/libtracker-db.la	 	\
+	$(QDBM_LIBS) 							\
+	$(GLIB2_LIBS)							
+
+search_SOURCES = 							\
+	search-word.c
+
+search_LDADD = 								\
+	$(top_builddir)/src/libtracker-common/libtracker-common.la 	\
+	$(top_builddir)/src/libtracker-db/libtracker-db.la	 	\
+	$(QDBM_LIBS) 							\
+	$(GLIB2_LIBS)							
+

Added: trunk/utils/qdbm/print-words.c
==============================================================================
--- (empty file)
+++ trunk/utils/qdbm/print-words.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,161 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia
+
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <depot.h>
+
+#include <glib.h>
+
+#include <libtracker-db/tracker-db-index-item.h>
+
+#define USAGE "Usage: print -f qdbm-file\n"
+
+static gchar	    *filename;
+static gboolean      print_services;
+
+static GOptionEntry  entries[] = {
+	{ "index-file", 'f', 0,
+	  G_OPTION_ARG_STRING, &filename,
+	  "QDBM index file",
+	  NULL },
+	{ "print-services", 's', 0,
+	  G_OPTION_ARG_NONE, &print_services,
+	  "Print service ID and service type ID for each word",
+	  NULL },
+	{ NULL }
+};
+
+static TrackerDBIndexItem *
+get_word_hits (DEPOT	   *index,
+	       const gchar *word,
+	       guint	   *count)
+{
+	TrackerDBIndexItem *details;
+	gint		    tsiz;
+	gchar		   *tmp;
+
+	g_return_val_if_fail (word != NULL, NULL);
+
+	details = NULL;
+
+	if (count) {
+		*count = 0;
+	}
+
+	if ((tmp = dpget (index, word, -1, 0, 100, &tsiz)) != NULL) {
+		if (tsiz >= (gint) sizeof (TrackerDBIndexItem)) {
+			details = (TrackerDBIndexItem *) tmp;
+
+			if (count) {
+				*count = tsiz / sizeof (TrackerDBIndexItem);
+			}
+		}
+	}
+
+	return details;
+}
+
+static void
+load_terms_from_index (gchar *filename)
+{
+    DEPOT	       *depot;
+    gchar	       *key;
+    guint		hits, i;
+    TrackerDBIndexItem *results;
+
+    depot = dpopen (filename, DP_OREADER | DP_ONOLCK, -1);
+
+    if (depot == NULL) {
+	   g_print ("Unable to open file: %s "
+		    "(Could be a lock problem: is tracker running?)\n",
+		    filename);
+	   g_print ("Using version %s of qdbm\n",
+		    dpversion);
+	   return;
+    }
+
+    dpiterinit (depot);
+
+    key = dpiternext (depot, NULL);
+
+    while (key != NULL) {
+	    g_print (" - %s ", key);
+
+	    if (print_services) {
+		    results = get_word_hits (depot, key, &hits);
+		    for (i = 0; i < hits; i++) {
+			    g_print (" (id:%d  t:%d s:%d) ",
+				     tracker_db_index_item_get_id (&results[i]),
+				     tracker_db_index_item_get_service_type (&results[i]),
+				     tracker_db_index_item_get_score (&results[i]));
+		    }
+	    }
+
+	    g_print ("\n");
+	    g_free (key);
+	    key = dpiternext (depot, NULL);
+    }
+
+    g_print ("Total: %d terms.\n", dprnum (depot));
+    dpclose (depot);
+}
+
+int
+main (gint argc, gchar** argv)
+{
+	GOptionContext *context;
+	GError	       *error = NULL;
+
+	context = g_option_context_new ("- QDBM index printer");
+	g_option_context_add_main_entries (context, entries, NULL);
+	g_option_context_parse (context, &argc, &argv, &error);
+
+	if (error) {
+		gchar *help;
+
+		g_printerr ("Invalid arguments, %s\n", error->message);
+
+		help = g_option_context_get_help (context, TRUE, NULL);
+		g_printerr (help);
+
+		g_free (help);
+		g_clear_error (&error);
+		g_option_context_free (context);
+
+		return EXIT_FAILURE;
+	}
+
+	if (!filename) {
+		gchar *help;
+
+		help = g_option_context_get_help (context, TRUE, NULL);
+		g_printerr (help);
+
+		g_free (help);
+		g_option_context_free (context);
+
+		return EXIT_FAILURE;
+	}
+
+	g_option_context_free (context);
+
+	load_terms_from_index (filename);
+
+	return EXIT_SUCCESS;
+}

Added: trunk/utils/qdbm/search-word.c
==============================================================================
--- (empty file)
+++ trunk/utils/qdbm/search-word.c	Fri Sep 26 10:47:33 2008
@@ -0,0 +1,156 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia
+
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <glib.h>
+
+#include <depot.h>
+
+#include <libtracker-db/tracker-db-index-item.h>
+
+static gchar	    *filename;
+static gchar	    *word;
+
+static GOptionEntry  entries[] = {
+	{ "index-file", 'f', 0,
+	  G_OPTION_ARG_STRING, &filename,
+	  "QDBM index file",
+	  NULL },
+	{ "word", 'w', 0,
+	  G_OPTION_ARG_STRING, &word,
+	  "Print service ID and service type ID of every hit for this word",
+	  NULL },
+	{ NULL }
+};
+
+static TrackerDBIndexItem *
+get_word_hits (DEPOT	   *index,
+	       const gchar *word,
+	       guint	   *count)
+{
+	TrackerDBIndexItem *items;
+	gchar		   *tmp;
+	gint		    tsiz;
+
+	g_return_val_if_fail (word != NULL, NULL);
+
+	items = NULL;
+
+	if (count) {
+		*count = 0;
+	}
+
+	if ((tmp = dpget (index, word, -1, 0, 1000000, &tsiz)) != NULL) {
+		if (tsiz >= (gint) sizeof (TrackerDBIndexItem)) {
+			items = (TrackerDBIndexItem *) tmp;
+
+			if (count) {
+				*count = tsiz / sizeof (TrackerDBIndexItem);
+			}
+		}
+	}
+
+	return items;
+}
+
+static void
+show_term_in_index (const gchar *filename,
+		    const gchar *word)
+{
+    TrackerDBIndexItem *items;
+    DEPOT	       *depot;
+    guint		hits, i;
+
+    hits = 0;
+
+    depot = dpopen (filename, DP_OREADER, -1);
+
+    if (depot == NULL) {
+	   g_print ("Unable to open file: %s "
+		    "(Could be a lock problem: is tracker running?)\n",
+		    filename);
+	   g_print ("Using version %s of qdbm\n",
+		    dpversion);
+	   return;
+    }
+
+    items = get_word_hits (depot, word, &hits);
+
+    if (hits < 1 ) {
+	    g_print ("No results for %s\n", word);
+	    return;
+    }
+
+    g_print (" - %s ", word);
+
+    for (i = 0; i < hits; i++) {
+	    g_print (" (id:%d  t:%d) ",
+		     items[i].id,
+		     tracker_db_index_item_get_service_type (&items[i]));
+    }
+
+    g_print ("\n");
+
+    g_print ("Total: %d terms.\n", dprnum (depot));
+    dpclose (depot);
+}
+
+int
+main (gint argc, gchar** argv)
+{
+	GOptionContext *context;
+	GError	       *error = NULL;
+
+	context = g_option_context_new ("- QDBM index searcher");
+	g_option_context_add_main_entries (context, entries, NULL);
+	g_option_context_parse (context, &argc, &argv, &error);
+
+	if (error) {
+		gchar *help;
+
+		g_printerr ("Invalid arguments, %s\n", error->message);
+
+		help = g_option_context_get_help (context, TRUE, NULL);
+		g_printerr (help);
+
+		g_free (help);
+		g_clear_error (&error);
+		g_option_context_free (context);
+
+		return EXIT_FAILURE;
+	}
+
+	if (!filename || !word) {
+		gchar *help;
+
+		help = g_option_context_get_help (context, TRUE, NULL);
+		g_printerr (help);
+
+		g_free (help);
+		g_option_context_free (context);
+
+		return EXIT_FAILURE;
+	}
+
+	g_option_context_free (context);
+
+	show_term_in_index (filename, word);
+
+	return 0;
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]