]> arthur.barton.de Git - netatalk.git/commitdiff
Merge remote-tracking branch 'origin/branch-netatalk-3-0' into develop
authorRalph Boehme <sloowfranklin@gmail.com>
Tue, 22 Oct 2013 18:02:59 +0000 (20:02 +0200)
committerRalph Boehme <sloowfranklin@gmail.com>
Tue, 22 Oct 2013 18:02:59 +0000 (20:02 +0200)
Conflicts:
configure.ac
libatalk/Makefile.am

76 files changed:
.gitignore
NEWS
VERSION
bin/ad/ad.h
bin/ad/ad_cp.c
bin/ad/ad_mv.c
bin/ad/ad_rm.c
bin/ad/ad_util.c
bin/uniconv/iso8859_1_adapted.c
config/.gitignore
config/Makefile.am
config/dbus-session.conf.tmpl [new file with mode: 0644]
configure.ac
doc/Makefile.am
doc/manpages/man5/afp.conf.5.xml
doc/manual/Makefile.am
doc/manual/configuration.xml
doc/manual/install.xml
doc/manual/manual.xml.in
doc/manual/netatalk.html
doc/www/ReleaseNotes
etc/Makefile.am
etc/afpd/.gitignore
etc/afpd/Makefile.am
etc/afpd/afp_dsi.c
etc/afpd/auth.c
etc/afpd/file.c
etc/afpd/main.c
etc/afpd/spotlight-packet.bin [new file with mode: 0644]
etc/afpd/spotlight-packet2.bin [new file with mode: 0644]
etc/afpd/spotlight.c [new file with mode: 0644]
etc/afpd/spotlight_marshalling.c [new file with mode: 0644]
etc/netatalk/Makefile.am
etc/netatalk/netatalk.c
etc/spotlight/.gitignore [new file with mode: 0644]
etc/spotlight/Makefile.am [new file with mode: 0644]
etc/spotlight/slmod_sparql.c [new file with mode: 0644]
etc/spotlight/slmod_sparql_map.c [new file with mode: 0644]
etc/spotlight/slmod_sparql_map.h [new file with mode: 0644]
etc/spotlight/slmod_sparql_parser.c [new file with mode: 0644]
etc/spotlight/slmod_sparql_parser.h [new file with mode: 0644]
etc/spotlight/slmod_sparql_parser.y [new file with mode: 0644]
etc/spotlight/spotlight_rawquery_lexer.c [new file with mode: 0644]
etc/spotlight/spotlight_rawquery_lexer.l [new file with mode: 0644]
etc/uams/uams_dhx2_pam.c
include/atalk/Makefile.am
include/atalk/byteorder.h [new file with mode: 0644]
include/atalk/dalloc.h [new file with mode: 0644]
include/atalk/dsi.h
include/atalk/errchk.h
include/atalk/globals.h
include/atalk/logger.h
include/atalk/spotlight.h [new file with mode: 0644]
include/atalk/talloc.h [new file with mode: 0644]
include/atalk/util.h
include/atalk/volume.h
libatalk/Makefile.am
libatalk/talloc/Makefile.am [new file with mode: 0644]
libatalk/talloc/dalloc.c [new file with mode: 0644]
libatalk/talloc/talloc.c [new file with mode: 0644]
libatalk/unicode/Makefile.am
libatalk/unicode/byteorder.h [deleted file]
libatalk/unicode/charcnv.c
libatalk/unicode/charsets/generic_cjk.h
libatalk/unicode/charsets/generic_mb.c
libatalk/unicode/charsets/mac_hebrew.c
libatalk/unicode/iconv.c
libatalk/unicode/utf8.c
libatalk/unicode/util_unistr.c
libatalk/util/cnid.c
libatalk/util/logger.c
libatalk/util/netatalk_conf.c
macros/netatalk.m4
macros/summary.m4
man/man5/afp.conf.5.in
test/afpd/Makefile.am

index f9ba2b20ff909c898baa04bd4f33e589c40fde61..cecdf4e2390733122fdb355cb0ce100182fd9fe0 100644 (file)
@@ -19,6 +19,7 @@ ltconfig
 ltmain.sh
 autom4te.cache
 autoscan.log
+ylwrap
 *.rpm
 *.deb
 *.dsc
diff --git a/NEWS b/NEWS
index f9d88e2789eaa6878f86294d87f673c182adeef3..2188fcf1a5aa165107955d3633b1943d84ac23e2 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,14 @@
+Changes in 3.1.0
+================
+* NEW: AFP Spotlight support with Gnome Tracker
+* NEW: New option "spotlight" (G/V)
+* NEW: Configure option --with-tracker-pkgconfig-version
+* NEW: Configure option --with-tracker-prefix
+* NEW: If Spotlight is enabled, launch our own dbus instance
+* NEW: New option "dbus daemon" (G)
+* UPD: Add configure option --with-afpstats for overriding the
+       result of autodetecting dbus-glib presence
+
 Changes in 3.0.6
 ================
 * FIX: charset conversion failed when copying from Mac OS 9. Bug #523.
diff --git a/VERSION b/VERSION
index 80c9e7f72eeb670fc0d96ccbe58b1a7935d12859..327e66a7a1c19664ffa9a879074a946fe02941a1 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-3.0.6dev
\ No newline at end of file
+3.1-beta2
\ No newline at end of file
index 3195641f7cb291c78e66d580a4d3a047f9d53997..9155900c990f3f9f02773a11ffc7b6f1ea3fcbb6 100644 (file)
@@ -69,7 +69,6 @@ extern int ad_find(int argc, char **argv, AFPObj *obj);
 /* ad_util.c */
 extern int openvol(AFPObj *obj, const char *path, afpvol_t *vol);
 extern void closevol(afpvol_t *vol);
-extern cnid_t cnid_for_path(const afpvol_t *vol, const char *path, cnid_t *did);
 extern cnid_t cnid_for_paths_parent(const afpvol_t *vol, const char *path, cnid_t *did);
 extern char *utompath(const struct vol *, const char *);
 extern int convert_dots_encoding(const afpvol_t *svol, const afpvol_t *dvol, char *path, size_t buflen);
index 239936dc06940cb788a62fd198e60f6c0051878b..5cefe9648a30484cb007d99fd834eabd8bacd4bc 100644 (file)
@@ -509,7 +509,7 @@ static int copy(const char *path,
 
             /* Get CNID of Parent and add new childir to CNID database */
             ppdid = pdid;
-            if ((did = cnid_for_path(&dvolume, to.p_path, &pdid)) == CNID_INVALID) {
+            if ((did = cnid_for_path(dvolume.vol->v_cdb, dvolume.vol->v_path, to.p_path, &pdid)) == CNID_INVALID) {
                 SLOG("Error resolving CNID for %s", to.p_path);
                 badcp = rval = 1;
                 return -1;
@@ -577,7 +577,7 @@ static int copy(const char *path,
             /* Get CNID of Parent and add new childir to CNID database */
             pdid = did;
             cnid_t cnid;
-            if ((cnid = cnid_for_path(&dvolume, to.p_path, &did)) == CNID_INVALID) {
+            if ((cnid = cnid_for_path(dvolume.vol->v_cdb, dvolume.vol->v_path, to.p_path, &did)) == CNID_INVALID) {
                 SLOG("Error resolving CNID for %s", to.p_path);
                 badcp = rval = 1;
                 return -1;
index 72698f73e9ad363e2ee5495d0596c77d19144fc2..3fdf1ea97ed052418913759c26b19168ad899be2 100644 (file)
@@ -278,7 +278,7 @@ static int do_move(const char *from, const char *to)
     
     cnid_t cnid = 0;
     if (!mustcopy) {
-        if ((cnid = cnid_for_path(&svolume, from, &did)) == CNID_INVALID) {
+        if ((cnid = cnid_for_path(svolume.vol->v_cdb, svolume.vol->v_path, from, &did)) == CNID_INVALID) {
             SLOG("Couldn't resolve CNID for %s", from);
             return -1;
         }
index 195fee06979301eeef8e9e80d1c66c7d34f00904..b32e659fad5ff73a833a0c6de20575adcde66fb5 100644 (file)
@@ -211,7 +211,7 @@ static int rm(const char *path,
 
             /* Get CNID of Parent and add new childir to CNID database */
             pdid = did;
-            if ((cnid = cnid_for_path(&volume, path, &did)) == CNID_INVALID) {
+            if ((cnid = cnid_for_path(volume.vol->v_cdb, volume.vol->v_path, path, &did)) == CNID_INVALID) {
                 SLOG("Error resolving CNID for %s", path);
                 return -1;
             }
@@ -247,7 +247,7 @@ static int rm(const char *path,
             }
 
             /* Get CNID of Parent and add new childir to CNID database */
-            if ((did = cnid_for_path(&volume, path, &pdid)) == CNID_INVALID) {
+            if ((did = cnid_for_path(volume.vol->v_cdb, volume.vol->v_path, path, &pdid)) == CNID_INVALID) {
                 SLOG("Error resolving CNID for %s", path);
                 return -1;
             }
@@ -293,7 +293,7 @@ static int rm(const char *path,
 
             /* Get CNID of Parent and add new childir to CNID database */
             pdid = did;
-            if ((cnid = cnid_for_path(&volume, path, &did)) == CNID_INVALID) {
+            if ((cnid = cnid_for_path(volume.vol->v_cdb, volume.vol->v_path, path, &did)) == CNID_INVALID) {
                 SLOG("Error resolving CNID for %s", path);
                 return -1;
             }
index 340dc5685e66426f80244b87bc3d3976fd1b6f18..7d4d2fb9e832fbb009fccb9a5b17588a73360076 100644 (file)
@@ -236,73 +236,6 @@ int convert_dots_encoding(const afpvol_t *svol, const afpvol_t *dvol, char *path
     return 0;
 }
 
-/*!
- * ResolvesCNID of a given paths
- *
- * path might be:
- * (a) relative:
- *     "dir/subdir" with cwd: "/afp_volume/topdir"
- * (b) absolute:
- *     "/afp_volume/dir/subdir"
- *
- * path MUST be pointing inside vol, this is usually the case as vol has been build from
- * path using loadvolinfo and friends.
- *
- * @param vol  (r) pointer to afpvol_t
- * @param path (r) path, see above
- * @param did  (rw) parent CNID of returned CNID
- *
- * @returns CNID of path
- */
-cnid_t cnid_for_path(const afpvol_t *vol,
-                     const char *path,
-                     cnid_t *did)
-{
-    EC_INIT;
-
-    cnid_t cnid;
-    bstring rpath = NULL;
-    bstring statpath = NULL;
-    struct bstrList *l = NULL;
-    struct stat st;
-
-    cnid = htonl(2);
-
-    EC_NULL(rpath = rel_path_in_vol(path, vol->vol->v_path));
-    EC_NULL(statpath = bfromcstr(vol->vol->v_path));
-    EC_ZERO(bcatcstr(statpath, "/"));
-
-    l = bsplit(rpath, '/');
-    for (int i = 0; i < l->qty ; i++) {
-        *did = cnid;
-
-        EC_ZERO(bconcat(statpath, l->entry[i]));
-        EC_ZERO_LOGSTR(lstat(cfrombstr(statpath), &st),
-                       "lstat(rpath: %s, elem: %s): %s: %s",
-                       cfrombstr(rpath), cfrombstr(l->entry[i]),
-                       cfrombstr(statpath), strerror(errno));
-
-        if ((cnid = cnid_add(vol->vol->v_cdb,
-                             &st,
-                             *did,
-                             cfrombstr(l->entry[i]),
-                             blength(l->entry[i]),
-                             0)) == CNID_INVALID) {
-            EC_FAIL;
-        }
-        EC_ZERO(bcatcstr(statpath, "/"));
-    }
-
-EC_CLEANUP:
-    bdestroy(rpath);
-    bstrListDestroy(l);
-    bdestroy(statpath);
-    if (ret != 0)
-        return CNID_INVALID;
-
-    return cnid;
-}
-
 /*!
  * Resolves CNID of a given paths parent directory
  *
index 7cdd43c3995d31ee455910d872f9071d589085b6..96cfcf5840eb5e3137178abe2345c6233ccaa0e7 100644 (file)
@@ -27,8 +27,7 @@
 
 #include <atalk/unicode.h>
 #include <atalk/logger.h>
-
-#include "../../libatalk/unicode/byteorder.h"
+#include <atalk/byteorder.h>
 
 static size_t   iso8859_adapted_pull(void *,char **, size_t *, char **, size_t *);
 static size_t   iso8859_adapted_push(void *,char **, size_t *, char **, size_t *);
index ec0b4097416de6cd166138540731526921984658..f6ebd89c41505fa9e57e357cdf4895758e34414d 100644 (file)
@@ -2,3 +2,4 @@ Makefile
 Makefile.in
 *.o
 afp.conf
+dbus-session.conf
index e72c38f39f3044065f772f7e4c79e7562b1b0cf4..5003de1b8b9fab7623558883244091a74f463073 100644 (file)
@@ -3,10 +3,10 @@
 SUBDIRS = pam
 SUFFIXES = .tmpl .
 
-TMPLFILES = afp.conf.tmpl
-GENFILES = afp.conf
+TMPLFILES = afp.conf.tmpl dbus-session.conf.tmpl
+GENFILES = afp.conf dbus-session.conf
 CLEANFILES = $(GENFILES)
-EXTRA_DIST = afp.conf.tmpl extmap.conf netatalk-dbus.conf
+EXTRA_DIST = $(TMPLFILES) extmap.conf netatalk-dbus.conf
 
 OVERWRITE_CONFIG = @OVERWRITE_CONFIG@
 
@@ -28,6 +28,7 @@ endif
            -e s@:ETCDIR:@${pkgconfdir}@ \
            -e s@:COMPILED_BACKENDS:@"$(compiled_backends)"@ \
            -e s@:DEFAULT_CNID_SCHEME:@$(DEFAULT_CNID_SCHEME)@ \
+               -e s@:LOCALSTATEDIR:@"$(localstatedir)"@ \
            <$< >$@
 
 #
@@ -41,10 +42,11 @@ install-data-local: install-config-files
        $(INSTALL_DATA) $(srcdir)/README $(DESTDIR)$(localstatedir)/netatalk/CNID/
 
 uninstall-local:
-       for f in $(CONFFILES) $(GENFILES); do \
+       @for f in $(CONFFILES) $(GENFILES); do \
                echo rm -f $(DESTDIR)$(pkgconfdir)/$$f; \
                rm -f $(DESTDIR)$(pkgconfdir)/$$f; \
        done
+       rm -f $(DESTDIR)$(pkgconfdir)/dbus-session.conf
        rm -f $(DESTDIR)$(localstatedir)/netatalk/README
        rm -f $(DESTDIR)$(localstatedir)/netatalk/CNID/README
 if USE_DEBIAN
@@ -53,7 +55,7 @@ endif
 
 install-config-files: $(CONFFILES) $(GENFILES)
        $(mkinstalldirs) $(DESTDIR)$(pkgconfdir)
-       for f in $(CONFFILES) ; do \
+       @for f in $(CONFFILES) ; do \
                if test "x$(OVERWRITE_CONFIG)" = "xyes" -o ! -f $(DESTDIR)$(pkgconfdir)/$$f; then \
                        echo "$(INSTALL_DATA) $$f $(DESTDIR)$(pkgconfdir)"; \
                        $(INSTALL_DATA) $(srcdir)/$$f $(DESTDIR)$(pkgconfdir); \
@@ -61,7 +63,7 @@ install-config-files: $(CONFFILES) $(GENFILES)
                        echo "not overwriting $$f"; \
                fi; \
        done
-       for f in $(GENFILES); do \
+       @for f in $(GENFILES); do \
                if test "x$(OVERWRITE_CONFIG)" = "xyes" -o ! -f $(DESTDIR)$(pkgconfdir)/$$f; then \
                        echo "$(INSTALL_DATA) $$f $(DESTDIR)$(pkgconfdir)"; \
                        $(INSTALL_DATA) $$f $(DESTDIR)$(pkgconfdir); \
diff --git a/config/dbus-session.conf.tmpl b/config/dbus-session.conf.tmpl
new file mode 100644 (file)
index 0000000..f84b923
--- /dev/null
@@ -0,0 +1,45 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+  <!-- Our well-known bus type, don't change this -->
+  <type>session</type>
+
+  <!-- If we fork, keep the user's original umask to avoid affecting
+       the behavior of child processes. -->
+  <keep_umask/>
+
+  <listen>unix:path=:LOCALSTATEDIR:/netatalk/spotlight.ipc</listen>
+
+  <standard_session_servicedirs />
+
+  <policy context="default">
+    <allow user="*"/>
+    <allow own="*"/>
+    <allow send_destination="*" eavesdrop="true"/>
+    <allow receive_sender="*"/>
+    <allow eavesdrop="true"/>
+  </policy>
+
+  <!-- For the session bus, override the default relatively-low limits 
+       with essentially infinite limits, since the bus is just running 
+       as the user anyway, using up bus resources is not something we need 
+       to worry about. In some cases, we do set the limits lower than 
+       "all available memory" if exceeding the limit is almost certainly a bug, 
+       having the bus enforce a limit is nicer than a huge memory leak. But the 
+       intent is that these limits should never be hit. -->
+
+  <!-- the memory limits are 1G instead of say 4G because they can't exceed 32-bit signed int max -->
+  <limit name="max_incoming_bytes">1000000000</limit>
+  <limit name="max_outgoing_bytes">1000000000</limit>
+  <limit name="max_message_size">1000000000</limit>
+  <limit name="service_start_timeout">120000</limit>  
+  <limit name="auth_timeout">240000</limit>
+  <limit name="max_completed_connections">100000</limit>  
+  <limit name="max_incomplete_connections">10000</limit>
+  <limit name="max_connections_per_user">100000</limit>
+  <limit name="max_pending_service_starts">10000</limit>
+  <limit name="max_names_per_connection">50000</limit>
+  <limit name="max_match_rules_per_connection">50000</limit>
+  <limit name="max_replies_per_connection">50000</limit>
+
+</busconfig>
index 6f56cdc3e85b6e4d90eed5d4b9d563247016e8a5..cbbc67719ab3bc34d7d3f7de0bcbd55bdbe15be9 100644 (file)
@@ -25,6 +25,8 @@ AC_PROG_PS
 AM_PROG_CC_C_O
 AC_C_BIGENDIAN
 AC_C_INLINE
+AC_PROG_LEX
+AC_PROG_YACC
 
 dnl Check if we can use attribute unused (gcc only) from ethereal
 AC_MSG_CHECKING(to see if we can add '__attribute__((unused))' to CFLAGS)
@@ -185,6 +187,9 @@ AC_NETATALK_LIBEVENT
 dnl Check whether bundled tdb shall be used
 AC_NETATALK_TDB
 
+dnl Check for Tracker
+AC_NETATALK_SPOTLIGHT
+
 dnl libatalk API checks
 AC_DEVELOPER
 
@@ -266,6 +271,7 @@ AC_OUTPUT([Makefile
        etc/afpd/Makefile
        etc/cnid_dbd/Makefile
        etc/netatalk/Makefile
+       etc/spotlight/Makefile
        etc/uams/Makefile
        include/Makefile
        include/atalk/Makefile
@@ -281,6 +287,7 @@ AC_OUTPUT([Makefile
        libatalk/compat/Makefile
        libatalk/dsi/Makefile
        libatalk/iniparser/Makefile
+       libatalk/talloc/Makefile
        libatalk/tdb/Makefile
        libatalk/unicode/Makefile
        libatalk/unicode/charsets/Makefile
index c44790fa1921d44626b576b082ef07a54a634b68..f8ccbb76f3e30a272a7977935dfeece14958f2f6 100644 (file)
@@ -6,4 +6,4 @@ release-notes: www/ReleaseNotes
        cd www && ./create-relnotes.sh
 
 upload-release-notes: release-notes
-       scp www/ReleaseNotes.html $$USER,netatalk@web.sourceforge.net:/home/project-web/netatalk/htdocs/3.0/ReleaseNotes$(VERSION).html
+       scp www/ReleaseNotes.html $$USER,netatalk@web.sourceforge.net:/home/project-web/netatalk/htdocs/3.1/ReleaseNotes$(VERSION).html
index 026fef0fef30658df7711e00a812a1a24283eebd..150459cd50a5fe66df7addd3cd485a3bc5d5cbb1 100644 (file)
 
           <listitem>
             <para>Speficy a set of file and directory attributes that shall
-            be ignored by the server, <attribute>all</attribute> includes all
+            be ignored by the server, <option>all</option> includes all
             the other options.</para>
             <para>In OS X when the Finder sets a lock on a file/directory or you
             set the BSD uchg flag in the Terminal, all three attributes are
           </listitem>
         </varlistentry>
 
+        <varlistentry>
+          <term>spotlight =
+          <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>no</emphasis>) <type>(G)/(V)</type></term>
+
+          <listitem>
+            <para>Whether to enable Spotlight searches. Note: once the global
+            option is enabled, any volume that is not enabled won't be
+            searchable at all.</para>
+          </listitem>
+        </varlistentry>
+
         <varlistentry>
           <term>veto message = <replaceable>BOOLEAN</replaceable> (default:
           <emphasis>no</emphasis>) <type>(G)</type></term>
       mode. You can adjust this behaviour with the configuration option
       <option>mac acls</option>:</para>
 
-      <variablelist id="mac_acls">
+      <variablelist id="map_acls">
         <varlistentry>
           <term>map acls = <parameter>none|rights|mode</parameter>
           <type>(G)</type></term>
index 0b3141aba80cc91013ccd3221071f8be828a8c4a..64870a52620d0caf933880994c8d2975f1549dfd 100644 (file)
@@ -58,6 +58,6 @@ html-local: $(XML_PAGES)
        @xsltproc $(HTML_STYLESHEET) manual.xml
 
 html-upload: html-local
-       scp $(HTML_PAGES) $$USER,netatalk@web.sourceforge.net:/home/project-web/netatalk/htdocs/3.0/htmldocs/
+       scp $(HTML_PAGES) $$USER,netatalk@web.sourceforge.net:/home/project-web/netatalk/htdocs/3.1/htmldocs/
 
 endif
index 3bf6e0ff9abb5e2731319574d2dede002a865453..f82de7359707de0e15d78578f3f3c33c77c046ea 100644 (file)
       configuration file is <filename>afp.conf</filename>. It uses a ini style
       configuration syntax.</para>
 
+      <para>Support for <link linkend="spotlight">Spotlight</link><indexterm>
+          <primary>Spotlight</primary>
+        </indexterm> has been added in Netatalk 3.1. See this <link
+      linkend="spotlight-compile">section</link> for information on how to
+      compile Netatalk with Spotlight support.</para>
+
       <para>Mac OS X 10.5 (Leopard) added support for Time Machine backups
       over AFP. Two new functions ensure that backups are written to spinning
       disk, not just in the server's cache. Different host operating systems
@@ -1321,7 +1327,7 @@ aclmode = passthrough</screen>
           When a new object is created inside a directory with a default ACL,
           the default ACL is applied to the new object as it's access ACL.
           Subdirectories inherit default ACLs from their parent. There are no
-          further mechanisms of inheritance control. </para>
+          further mechanisms of inheritance control.</para>
 
           <para>Architectural differences between Posix ACLs and OS X ACLs
           especially involve:</para>
@@ -1419,7 +1425,7 @@ aclmode = passthrough</screen>
               <listitem>
                 <para>afpd silently discard entries which deny a set of
                 permissions because they they can't be represented within the
-                Posix architecture. </para>
+                Posix architecture.</para>
               </listitem>
 
               <listitem>
@@ -1511,6 +1517,367 @@ aclmode = passthrough</screen>
     </sect2>
   </sect1>
 
+  <sect1>
+    <title id="spotlight">Spotlight<indexterm>
+        <primary>Spotlight</primary>
+      </indexterm></title>
+
+    <para>Starting with version 3.1 Netatalk supports Spotlight searching.
+    Netatalk uses Gnome <ulink url="https://projects.gnome.org/tracker/">Tracker</ulink> as metadata store,
+    indexer and search engine.</para>
+
+    <sect2>
+      <title>Configuration</title>
+
+      <para>You can enable Spotlight and indexing either globally or on a per
+      volume basis with the <option>spotlight</option> option.</para>
+
+      <warning>
+        <para>Once Spotlight is enable for a single volume, all other volumes
+        for which spotlight is disabled won't be searchable at all.</para>
+      </warning>
+
+      <para>In case the <command>dbus-daemon</command> binary is not installed
+      at the path <filename>/bin/dbus-daemon</filename>, you must use the
+      global option <option>dbus daemon</option> to point to the path, eg for
+      Solaris with Tracker from OpenCSW: <screen>dbus daemon = /opt/csw/bin/dbus-daemon</screen></para>
+    </sect2>
+
+    <sect2>
+      <title>Limitations and notes</title>
+
+      <itemizedlist>
+        <listitem>
+          <para>Large filesystems</para>
+
+          <para>Tracker on Linux uses the inotify Kernel filesystem change
+          event API for tracking filesystem changes. On large filesystems this
+          may be problematic since the inotify API doesn't offer recursive
+          directory watches but instead requires that for every subdirectoy
+          watches must be added individually.</para>
+
+          <para>On Solaris the FEN file event notification system is used. It
+          is unkown which limitations and ressource consumption this Solaris
+          subsystem may have.</para>
+
+          <para>We therefor recommend to disable live filesystem monitoring
+          and let Tracker periodically scan filesystems for changes instead,
+          see the Tracker configuration options <link
+          linkend="enable-monitors">enable-monitors</link> and <link
+          linkend="crawling-interval">crawling-interval</link> below.</para>
+        </listitem>
+      </itemizedlist>
+    </sect2>
+
+    <sect2>
+      <title>Using Tracker commandline tools on the server</title>
+
+      <para>Netatalk must be running, commands must be executed as root and
+      some environent variables must be set up (adjust PREFIX to point to
+      the base directory Netatalk in installed to):<screen>$ su
+# cat .tracker_profile
+PREFIX="/"
+export XDG_DATA_HOME="$PREFIX/var/netatalk/"
+export XDG_CACHE_HOME="$PREFIX/var/netatalk/"
+export DBUS_SESSION_BUS_ADDRESS="unix:path=$PREFIX/var/netatalk/spotlight.ipc"
+# . .tracker_profile
+#
+</screen></para>
+
+      <para>When using Tracker from OpenCSW you must also update your
+      PATH:<screen># export PATH=/opt/csw/bin:$PATH</screen></para>
+
+      <sect3>
+        <title>Starting and stopping Tracker</title>
+
+        <variablelist>
+          <varlistentry>
+            <term>Querying Tracker status</term>
+
+            <listitem>
+              <screen># tracker-control -S</screen>
+            </listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term>Stop Tracker</term>
+
+            <listitem>
+              <screen># tracker-control -t</screen>
+            </listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term>Start Tracker status</term>
+
+            <listitem>
+              <screen># tracker-control -s</screen>
+            </listitem>
+          </varlistentry>
+        </variablelist>
+      </sect3>
+
+      <sect3>
+        <title>Reindex directory</title>
+
+        <screen># tracker-control -f PATH</screen>
+      </sect3>
+
+      <sect3>
+        <title>Query Tracker for information about a file or directory</title>
+
+        <screen># tracker-info PATH</screen>
+      </sect3>
+
+      <sect3>
+        <title>Search Tracker</title>
+
+        <screen># tracker-search QUERY</screen>
+      </sect3>
+    </sect2>
+
+    <sect2>
+      <title>Advanced Tracker command line configuration</title>
+
+      <para>Tracker stores its configuration via Gnome dconf backend which can
+      be modified with the command <command>gsettings</command>.</para>
+
+      <para>Gnome dconf settings are per-user settings, so, as Netatalk runs
+      the Tracker processes as root, the settings are stored in the root user
+      context and reading or changing these settings must be perfomed as root
+      and Netatalk must be running (and again the enviroment must be set up
+      as shown above).</para>
+
+      <para><screen># gsettings list-recursively | grep Tracker
+org.freedesktop.Tracker.Writeback verbosity 'debug'
+...</screen></para>
+
+      <para>The following list describes some important Tracker options and
+      their default settings.</para>
+
+      <variablelist>
+        <varlistentry>
+          <term>org.freedesktop.Tracker.Miner.Files
+          index-recursive-directories</term>
+
+          <listitem>
+            <para>This option controls which directories Tracker will index.
+            Don't change this option manually as it is automatically set by
+            Netatalk reflecting the setting of the <option>Spotlight</option>
+            option of Netatalk volumes.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term id="enable-monitors">org.freedesktop.Tracker.Miner.Files
+          enable-monitors <parameter> true</parameter></term>
+
+          <listitem>
+            <para>The value controls whether Tracker watches all configured
+            paths for modification. Depending on the filesystem modification
+            backend (FAM on Linux, FEN on Solaris), this feature may not work
+            as reliable as one might wish, so it may be safer to disable it
+            and instead rely on periodic crawling of Tracker itself. See aslo
+            the option <option>crawling-interval </option>.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term id="crawling-interval">org.freedesktop.Tracker.Miner.Files
+          crawling-interval <parameter>-1</parameter></term>
+
+          <listitem>
+            <para>Interval in days to check the filesystem is up to date in
+            the database, maximum is 365, default is -1. -2 = crawling is
+            disabled entirely, -1 = crawling *may* occur on startup (if not
+            cleanly shutdown), 0 = crawling is forced</para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+    </sect2>
+
+    <sect2>
+      <title>Supported metadata attributes</title>
+
+      <para>The following table lists the supported Spotlight metadata
+      attributes</para>
+
+      <table>
+        <title>Supported Spotlight metadata attributes</title>
+
+        <tgroup cols="2">
+          <thead>
+            <row>
+              <entry align="center">Description</entry>
+
+              <entry align="center">Spotlight Key</entry>
+            </row>
+          </thead>
+
+          <tbody>
+            <row>
+              <entry>Name</entry>
+
+              <entry>kMDItemDisplayName, kMDItemFSName</entry>
+            </row>
+
+            <row>
+              <entry>Document content (full text search)</entry>
+
+              <entry>kMDItemTextContent</entry>
+            </row>
+
+            <row>
+              <entry>File type</entry>
+
+              <entry>_kMDItemGroupId, kMDItemContentTypeTree</entry>
+            </row>
+
+            <row>
+              <entry>File modification date</entry>
+
+              <entry>kMDItemFSContentChangeDate,
+              kMDItemContentModificationDate,
+              kMDItemAttributeChangeDate</entry>
+            </row>
+
+            <row>
+              <entry>Content Creation date</entry>
+
+              <entry>kMDItemContentCreationDate</entry>
+            </row>
+
+            <row>
+              <entry>The author, or authors, of the contents of the
+              file</entry>
+
+              <entry>kMDItemAuthors, kMDItemCreator</entry>
+            </row>
+
+            <row>
+              <entry>The name of the country where the item was
+              created</entry>
+
+              <entry>kMDItemCountry</entry>
+            </row>
+
+            <row>
+              <entry>Duration</entry>
+
+              <entry>kMDItemDurationSeconds</entry>
+            </row>
+
+            <row>
+              <entry>Number of pages</entry>
+
+              <entry>kMDItemNumberOfPages</entry>
+            </row>
+
+            <row>
+              <entry>Document title</entry>
+
+              <entry>kMDItemTitle</entry>
+            </row>
+
+            <row>
+              <entry>The width, in pixels, of the contents. For example, the
+              image width or the video frame width</entry>
+
+              <entry>kMDItemPixelWidth</entry>
+            </row>
+
+            <row>
+              <entry>The height, in pixels, of the contents. For example, the
+              image height or the video frame height</entry>
+
+              <entry>kMDItemPixelHeight</entry>
+            </row>
+
+            <row>
+              <entry>The color space model used by the document
+              contents</entry>
+
+              <entry>kMDItemColorSpace</entry>
+            </row>
+
+            <row>
+              <entry>The number of bits per sample</entry>
+
+              <entry>kMDItemBitsPerSample</entry>
+            </row>
+
+            <row>
+              <entry>Focal length of the lens, in millimeters</entry>
+
+              <entry>kMDItemFocalLength</entry>
+            </row>
+
+            <row>
+              <entry>ISO speed</entry>
+
+              <entry>kMDItemISOSpeed</entry>
+            </row>
+
+            <row>
+              <entry>Orientation of the document. Possible values are 0
+              (landscape) and 1 (portrait)</entry>
+
+              <entry>kMDItemOrientation</entry>
+            </row>
+
+            <row>
+              <entry>Resolution width, in DPI</entry>
+
+              <entry>kMDItemResolutionWidthDPI</entry>
+            </row>
+
+            <row>
+              <entry>Resolution height, in DPI</entry>
+
+              <entry>kMDItemResolutionHeightDPI</entry>
+            </row>
+
+            <row>
+              <entry>Exposure time, in seconds</entry>
+
+              <entry>kMDItemExposureTimeSeconds</entry>
+            </row>
+
+            <row>
+              <entry>The composer of the music contained in the audio
+              file</entry>
+
+              <entry>kMDItemComposer</entry>
+            </row>
+
+            <row>
+              <entry>The musical genre of the song or composition</entry>
+
+              <entry>kMDItemMusicalGenre</entry>
+            </row>
+          </tbody>
+        </tgroup>
+      </table>
+
+    </sect2>
+
+    <sect2>
+      <title>References</title>
+
+      <orderedlist>
+        <listitem>
+          <para><ulink
+          url="https://developer.apple.com/library/mac/#documentation/Carbon/Reference/MDItemRef/Reference/reference.html">MDItem</ulink></para>
+        </listitem>
+
+        <listitem>
+          <para><ulink
+          url="https://live.gnome.org/Tracker/Documentation">Tracker</ulink></para>
+        </listitem>
+      </orderedlist>
+    </sect2>
+  </sect1>
+
   <sect1>
     <title>Starting and stopping Netatalk</title>
 
index a64b38462badb7135792e58c2af1173ab9fcd705..74de545b0e15b783bb3547c68504cba6657ea905 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <chapter id="installation">
   <chapterinfo>
-    <date>24.8.2012</date>
+    <date>4.8.2013</date>
   </chapterinfo>
 
   <title>Installation</title>
@@ -12,8 +12,6 @@
     !!!</para>
   </warning>
 
-  <para></para>
-
   <sect1>
     <title>How to obtain Netatalk</title>
 
@@ -54,8 +52,7 @@
       </ulink></para>
 
       <para>Solaris package: <ulink
-      url="http://www.blastwave.org/">http://www.blastwave.org/
-      </ulink></para>
+      url="http://www.opencsw.org/packages/CSWnetatalk/">http://www.opencsw.org/</ulink></para>
 
       <para>FreeBSD ports: <ulink
       url="http://www.freebsd.org/ports/index.html">http://www.freebsd.org/ports/index.html
         <title>Git</title>
 
         <para>Downloading the Git repository can be done quickly and
-        easily.</para>
+        easily:</para>
 
         <orderedlist>
           <listitem>
             <para>Make sure you have Git installed. <command>which
             git</command> should produce a path to git.</para>
 
-            <screen><prompt>$&gt;</prompt> <userinput>which git</userinput>
+            <screen><prompt>$ </prompt><userinput>which git</userinput>
 <computeroutput>/usr/bin/git</computeroutput></screen>
           </listitem>
 
-          <listitem>
-            <para>If you don't have one make a source directory.
-            <command>cd</command> to this directory.</para>
-
-            <screen><prompt>$&gt;</prompt> <userinput>mkdir /path/to/new/source/dir</userinput>
-<prompt>$&gt;</prompt> <userinput>cd /path/to/new/source/dir</userinput></screen>
-          </listitem>
-
           <listitem>
             <para>Now get the source:</para>
 
-            <screen><prompt>$&gt;</prompt> <userinput>git clone git://git.code.sf.net/p/netatalk/code netatalk-code
+            <screen><prompt>$</prompt> <userinput>git clone -b develop git://git.code.sf.net/p/netatalk/code netatalk-code
 </userinput><computeroutput>Initialized empty Git repository in /path/to/new/source/dir/netatalk/.git/
 remote: Counting objects: 2503, done.
 ...
 </computeroutput></screen>
 
-            <para>This will create a local directory called "netatalk-code"
-            containing a complete and fresh copy of the whole Netatalk source
-            from the Git repository.</para>
+            <para>This will create a local directory called
+            <filename>netatalk-code</filename> containing a complete and fresh
+            copy of the whole Netatalk source from the Git repository.</para>
           </listitem>
 
           <listitem>
             <para>In order to keep your repository copy updated, occasionally
             run:</para>
 
-            <screen><prompt>$&gt;</prompt> <userinput>git pull</userinput></screen>
+            <screen><prompt>$</prompt> <userinput>git pull</userinput></screen>
           </listitem>
 
           <listitem>
@@ -145,11 +134,13 @@ remote: Counting objects: 2503, done.
             <filename>configure</filename> script required in the next
             step.</para>
 
-            <screen><prompt>$&gt;</prompt> <userinput>./bootstrap</userinput></screen>
+            <screen><prompt>$</prompt> <userinput>./bootstrap</userinput></screen>
           </listitem>
         </orderedlist>
 
-        <para></para>
+        <para>For futher information refer to this <ulink
+        url="http://netatalk.sourceforge.net/wiki/index.php/Developer_Infos">wiki</ulink>
+        page.</para>
       </sect3>
     </sect2>
   </sect1>
@@ -167,28 +158,9 @@ remote: Counting objects: 2503, done.
           <listitem>
             <para>Berkeley DB<indexterm>
                 <primary>BDB</primary>
-
                 <secondary>Berkeley DB</secondary>
               </indexterm>.</para>
-
-            <para>At the time of writing, the following versions are
-            supported:</para>
-
-            <itemizedlist>
-              <listitem>
-                <para>minimum 4.6.x</para>
-              </listitem>
-            </itemizedlist>
-
-            <para>In case Berkeley DB is not installed on your system, please
-            download it from:</para>
-
-            <para><ulink
-            url="http://www.oracle.com/technetwork/products/berkeleydb/downloads/index.html">
-            http://www.oracle.com/technetwork/products/berkeleydb/downloads/index.html</ulink></para>
-
-            <para>and follow the <link linkend="build-bdb">installation
-            instructions</link>.</para>
+            <para>At the time of writing you need at least version 4.6.</para>
           </listitem>
 
           <listitem>
@@ -211,6 +183,18 @@ remote: Counting objects: 2503, done.
         it's functionality.</para>
 
         <itemizedlist>
+          <listitem>
+            <para>Tracker for Spotlight<indexterm>
+                <primary>Spotlight</primary>
+              </indexterm> support</para>
+
+            <para>Netatalk uses <ulink
+            url="http://projects.gnome.org/tracker/">Tracker</ulink> as the
+            metadata backend. Recent Linux distributions will provide the
+            libtracker-sparql library which is available since Tracker version
+            0.7.</para>
+          </listitem>
+
           <listitem>
             <para>mDNSresponderPOSIX or Avahi for Bonjour (aka
             Zeroconf)</para>
@@ -220,12 +204,6 @@ remote: Counting objects: 2503, done.
 
             <para>Avahi must be build with DBUS support (
             <userinput>--enable-dbus</userinput>).</para>
-
-            <para>You can download Avahi from: <ulink
-            url="http://www.avahi.org/">http://www.avahi.org/</ulink>.</para>
-
-            <para>You can download mDNSresponder from: <ulink
-            url="http://opensource.apple.com/tarballs/mDNSResponder/">http://opensource.apple.com/tarballs/mDNSResponder/</ulink>.</para>
           </listitem>
 
           <listitem>
@@ -237,9 +215,6 @@ remote: Counting objects: 2503, done.
             <para>Security options are: access control per host, domain and/or
             service; detection of host name spoofing or host address spoofing;
             booby traps to implement an early-warning system.</para>
-
-            <para>TCP Wrappers can be downloaded from: <ulink
-            url="ftp://ftp.porcupine.org/pub/security">ftp://ftp.porcupine.org/pub/security</ulink>/</para>
           </listitem>
 
           <listitem>
@@ -257,10 +232,6 @@ remote: Counting objects: 2503, done.
               </indexterm> Microsystems. Linux-PAM is a suite of shared
             libraries that enable the local system administrator to choose how
             applications authenticate users.</para>
-
-            <para>You can get the Linux PAM documentation and sources from
-            <ulink
-            url="http://www.kernel.org/pub/linux/libs/pam/">http://www.kernel.org/pub/linux/libs/pam/</ulink>.</para>
           </listitem>
 
           <listitem>
@@ -271,9 +242,6 @@ remote: Counting objects: 2503, done.
             built in conversions for, like ISO-8859-1. On glibc systems,
             Netatalk can use the glibc provided iconv implementation.
             Otherwise you can use the GNU libiconv implementation.</para>
-
-            <para>You can download GNU libiconv from: <olink><ulink
-            url="http://www.gnu.org/software/libiconv/">http://www.gnu.org/software/libiconv/</ulink></olink>.</para>
           </listitem>
         </itemizedlist>
       </sect3>
@@ -294,7 +262,7 @@ remote: Counting objects: 2503, done.
         automatically configure Netatalk for your operating system. If you
         have unusual needs, then you may wish to run</para>
 
-        <screen>$&gt; <userinput>./configure --help</userinput></screen>
+        <screen>$ <userinput>./configure --help</userinput></screen>
 
         <para>to see what special options you can enable.</para>
 
@@ -319,25 +287,72 @@ remote: Counting objects: 2503, done.
 
         <para>Now run configure with any options you need</para>
 
-        <screen><prompt>$&gt;</prompt> <userinput>./configure [arguments]</userinput></screen>
+        <screen><prompt>$</prompt> <userinput>./configure [arguments]</userinput></screen>
 
         <para>Configure will end up in an overview showing the settings the
         Netatalk Makefiles have been created with.</para>
+      </sect3>
+
+      <sect3 id="spotlight-compile">
+        <title>Spotlight<indexterm>
+            <primary>Spotlight</primary>
+          </indexterm></title>
+
+        <para>Netatalk uses Gnome <ulink url="https://projects.gnome.org/tracker/">Tracker</ulink> as the
+        metadata backend. The minimum required version is 0.7 as that's the
+        first version to support <ulink url="https://wiki.gnome.org/Tracker/Documentation">SPARQL</ulink>.</para>
+
+        <para>If not already installed, install the packages
+        <emphasis>tracker</emphasis> and <emphasis>tracker-devel</emphasis>,
+        on Solaris install <ulink url="http://www.opencsw.org/">OpenCSW</ulink> and then install
+        the Tracker package from the OpenCSW unstable repository.</para>
+
+        <para>The tracker packages are found via pkg-config, you may have to
+        pass the version suffix as you may have a newer version installed then
+        the default 0.12, eg</para>
+
+        <screen><prompt>$ </prompt><userinput>pkg-config --list-all | grep tracker
+</userinput>tracker-extract-0.16  tracker-extract - Tracker : A library to develop metadata extractors for 3rd party file types.
+tracker-sparql-0.16   tracker-sparql - Tracker : A library to perform SPARQL queries and updates in the              Tracker Store
+tracker-miner-0.16    tracker-miner - A library to develop tracker data miners</screen>
+
+        <para>So:</para>
 
-        <para>If this step fails please visit the <ulink
-        url="http://netatalk.sourceforge.net/wiki/index.php/Troubleshooting">troubleshooting
-        guide</ulink>.</para>
+        <screen><prompt>$ </prompt><userinput>./configure --with-tracker-pkgconfig-version=0.16 ...</userinput></screen>
+
+        <para>If you're using Solaris and Tracker from OpenCSW, then you need
+        to set the PKG_CONFIG_PATH environment variable, add the
+        --with-tracker-prefix configure option and add
+        LDFLAGS="-R/opt/csw/lib"</para>
+
+        <screen>PKG_CONFIG_PATH=/opt/csw/lib/pkgconfig LDFLAGS="-R/opt/csw/lib" ./configure --with-tracker-prefix=/opt/csw --with-tracker-pkgconfig-version=0.16 ...</screen>
+
+        <para>Check the configure output whether the Tracker libs were
+        found:</para>
+
+        <screen>checking for TRACKER... yes
+checking for TRACKER_MINER... yes
+...
+Configure summary:
+...
+  AFP:
+    Spotlight: yes
+...</screen>
+      </sect3>
+
+      <sect3>
+        <title>Compile and install</title>
 
         <para>Next, running</para>
 
-        <screen><prompt>$&gt;</prompt> <userinput>make</userinput></screen>
+        <screen><prompt>$</prompt> <userinput>make</userinput></screen>
 
         <para>should produce the Netatalk binaries (this step can take several
         minutes to complete).</para>
 
         <para>When the process finished you can use</para>
 
-        <screen><prompt>$&gt;</prompt> <userinput>make install</userinput></screen>
+        <screen><prompt>$</prompt> <userinput>make install</userinput></screen>
 
         <para>to install the binaries and documentation (must be done as
         "root" when using default locations).</para>
index 64ebd6c7eb4260a29a71b1274a5e8bd36510c1ec..7c067efbe57d383cee6615cac6a6703e7ce84c5b 100644 (file)
 <!ENTITY dbd.1 SYSTEM "../manpages//man1/dbd.1.xml">
 ]>
 <book id="netatalk-manual">
-  <title>Netatalk 3.0 Manual</title>
+  <title>Netatalk 3.1 Manual</title>
   
   <bookinfo>
-    <date>03-24-2013</date>
+    <date>07-01-2013</date>
     <releaseinfo>@NETATALK_VERSION@</releaseinfo>
   </bookinfo>
 
index b0b7904b6990ed4740e91119720f8284cd19812c..0c7e33718938c354b659440409ecd54fe9ed131f 100644 (file)
@@ -4,7 +4,7 @@
         <div id="menlinks">
           <a href="/" title="Return to Netatalk home">[main]</a>
           <a href="http://netatalk.sourceforge.net/wiki/" title="Netatalk Wiki">[wiki]</a>
-          <a href="/3.0/htmldocs" title="Netatalk Manual">[documentation]</a>
+          <a href="/3.1/htmldocs" title="Netatalk Manual">[documentation]</a>
           <a href="http://sourceforge.net/project/showfiles.php?group_id=8642" title="Download Netatalk from sourceforge">[downloads]</a>
           <a href="/support.php" title="Support">[support]</a>
           <a href="/links.php" title="Netatalk related links">[links]</a>
index 850be9eadea0428c7814604bdb43439644b86efa..7ec41372a86c6ab024e9ea7258e465855f467da4 100644 (file)
@@ -1,9 +1,8 @@
-Netatalk 3.0.5
-==============
+Netatalk 3.1-beta2
+===================
 
-The Netatalk development team is proud to announce version 3.0.5 of
-the Netatalk File Sharing suite. This is the latest update to the 3.0
-release series. All users are encouraged to upgrade their systems to 3.0.5.
+The Netatalk development team is proud to announce the second beta release
+of the next Netatalk version 3.1. This release is intended for testing only.
 
 Netatalk is a freely-available Open Source AFP fileserver.
 A *NIX/*BSD system running Netatalk is capable of serving many Macintosh
@@ -17,22 +16,23 @@ The suite contains:
 * cnid_dbd   - the CNID database daemon serving CNIDs for AFP volumes
 * various supporting programs and utilities
 
-Summary of major new features and enhancements in 3.0
+Summary of major new features and enhancements in 3.1
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-* New ini style configuration file afp.conf which replaces all previous
-  configuration files
-* New default AppleDouble backend using filesystem Extended Attributes,
-  conversion from AppleDouble v2 is done automatically on access
-* New service controller process "netatalk" responsible for starting and
-  restarting afpd and cnid_metad as necessary
-* AppleTalk support has been removed
-* Coherent cross-platform locking with Solaris CIFS server
+* AFP Spotlight Support with Gnome Tracker:
+  https://projects.gnome.org/tracker/
+
+Please refer to the online manual for details about compiling Netatalk
+with Spotlight support and how to configure:
+
+http://netatalk.sourceforge.net/3.1/htmldocs/installation.html#compiling-netatalk
+
+http://netatalk.sourceforge.net/3.1/htmldocs/configuration.html#id2615270
 
 Please make sure to read the upgrading section in the Netatalk online
-manual before trying to upgrade your system to 3.0!
+manual before trying to upgrade your system from version 2:
 
-  http://netatalk.sourceforge.net/3.0/htmldocs/upgrade.html
+http://netatalk.sourceforge.net/3.1/htmldocs/upgrade.html
 
 License
 ~~~~~~~
@@ -41,7 +41,35 @@ Netatalk is a Free/Open Source Software project and is released under
 the GNU General Public License (GPLv2).  The full license text is available
 at:
 
-  http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+
+Changes in 3.1-beta2
+~~~~~~~~~~~~~~~~~~~~
+* FIX: Fix special Spotlight RPC function
+
+Changes in 3.1-beta1
+~~~~~~~~~~~~~~~~~~~~
+* REM: Remove support for Tracker versions < 0.7
+* UPD: Add support for additional Spotlight RPC calls
+
+Changes in 3.1-alpha1
+~~~~~~~~~~~~~~~~~~~~~
+* NEW: AFP Spotlight support with Gnome Tracker
+* NEW: New option "spotlight" (G/V)
+* NEW: Configure option --with-tracker-pkgconfig-version
+* NEW: Configure option --with-tracker-prefix
+* NEW: If Spotlight is enabled, launch our own dbus instance
+* NEW: New option "dbus daemon" (G)
+* UPD: Add configure option --with-afpstats for overriding the
+       result of autodetecting dbus-glib presence
+
+Changes in 3.0.6
+~~~~~~~~~~~~~~~~
+* FIX: charset conversion failed when copying from Mac OS 9. Bug #523.
+* UPD: Don't force S_ISGID for directories on FreeBSD. Bug #525.
+* NEW: Add support for ZFS ACLs on FreeBSD with libsunacl. From FR#83.
+* FIX: Active Directory LDAP queries for ACL support with new options
+       "ldap user filter" and "ldap group filter". Bug #526.
 
 Changes in 3.0.5
 ~~~~~~~~~~~~~~~~
@@ -336,4 +364,4 @@ We would like to thank all contributors to the Netatalk project for
 their commitment.  Without the many suggestions, bug and problem reports,
 patches, and reviews this project wouldn't be where it is.
 
- - The Netatalk Development Team, August 2013
+ - The Netatalk Development Team, September 2013
index e5367734d3bc584baa574f0eaceb1897dd66f81e..6877a7611052062e0e211b3d21f7e27aec35b89f 100644 (file)
@@ -1,3 +1,3 @@
 # Makefile.am for etc/
 
-SUBDIRS = afpd cnid_dbd netatalk uams
+SUBDIRS = afpd cnid_dbd netatalk spotlight uams
index 5d0c03fa85cb4a199ed3beead2cdc679b820512b..4c401a6eb13144487f0a39403a5499d990920f7c 100644 (file)
@@ -3,8 +3,10 @@ Makefile.in
 afpd
 fce
 hash
-test_parse_mtab
+spot
+srp
 .deps
 .libs
-.gitignore
-afpd-afp_asp.o afpd-afp_config.o afpd-afp_dsi.o afpd-afp_options.o afpd-afprun.o afpd-afp_util.o afpd-afs.o afpd-appl.o afpd-auth.o afpd-catsearch.o afpd-desktop.o afpd-directory.o afpd-enumerate.o afpd-extattrs.o afpd-filedir.o afpd-file.o afpd-fork.o afpd-gettok.o afpd-hash.o afpd-main.o afpd-mangle.o afpd-messages.o afpd-nfsquota.o afpd-ofork.o afpd-quota.o afpd-status.o afpd-switch.o afpd-uam.o afpd-uid.o afpd-unix.o afpd-volume.o hash-hash.o
+*.o
+*.la
+*.lo
\ No newline at end of file
index ddff6c3e42d8057d800ef9bac6ec888b529db58c..8cfe75a4f94efbdfb3c1055067f7dc8daf330643 100644 (file)
@@ -7,7 +7,7 @@ CLEANFILES =
 DISTCLEANFILES =
 
 sbin_PROGRAMS = afpd
-noinst_PROGRAMS = hash fce
+noinst_PROGRAMS = hash fce spot
 
 afpd_SOURCES = \
        afp_avahi.c \
@@ -39,6 +39,8 @@ afpd_SOURCES = \
        nfsquota.c \
        ofork.c \
        quota.c \
+       spotlight.c \
+       spotlight_marshalling.c \
        status.c \
        switch.c \
        uam.c \
@@ -103,3 +105,7 @@ hash_CFLAGS = -DKAZLIB_TEST_MAIN -I$(top_srcdir)/include
 fce_SOURCES = fce_api.c fce_util.c
 fce_CFLAGS = -DFCE_TEST_MAIN -I$(top_srcdir)/include
 fce_LDADD = $(top_builddir)/libatalk/libatalk.la
+
+spot_SOURCES = spotlight.c spotlight_marshalling.c
+spot_CFLAGS = -DSPOT_TEST_MAIN
+spot_LDADD = $(top_builddir)/libatalk/libatalk.la
index e68b63a47e9386162c881ba8b8071b16bedec9db..b64d49140f402fc1ddb0f1f0205263563e9808d1 100644 (file)
@@ -39,6 +39,7 @@
 #include <atalk/fce_api.h>
 #include <atalk/globals.h>
 #include <atalk/netatalk_conf.h>
+#include <atalk/spotlight.h>
 
 #include "switch.h"
 #include "auth.h"
@@ -474,6 +475,10 @@ void afp_over_dsi(AFPObj *obj)
     int flag = 1;
     setsockopt(dsi->socket, SOL_TCP, TCP_NODELAY, &flag, sizeof(flag));
 
+    /* Initialize Spotlight */
+    if ((obj->options.flags & OPTION_SPOTLIGHT) && (obj->options.slmod_path))
+        sl_mod_load(obj->options.slmod_path);
+
     ipc_child_state(obj, DSI_RUNNING);
 
     /* get stuck here until the end */
index 27e678c23b65097371046530bde03174c78c4e29..f04404edd2f469af47152fccf96db6ccae7d23e8 100644 (file)
@@ -39,6 +39,7 @@ extern void afp_get_cmdline( int *ac, char ***av );
 #include <atalk/server_ipc.h>
 #include <atalk/uuid.h>
 #include <atalk/globals.h>
+#include <atalk/spotlight.h>
 #include <atalk/unix.h>
 
 #include "auth.h"
@@ -184,7 +185,7 @@ static int set_auth_switch(const AFPObj *obj, int expired)
         case 31:
             uam_afpserver_action(AFP_SYNCDIR, UAM_AFPSERVER_POSTAUTH, afp_syncdir, NULL);
             uam_afpserver_action(AFP_SYNCFORK, UAM_AFPSERVER_POSTAUTH, afp_syncfork, NULL);
-            uam_afpserver_action(AFP_SPOTLIGHT_PRIVATE, UAM_AFPSERVER_POSTAUTH, afp_null_nolog, NULL);
+            uam_afpserver_action(AFP_SPOTLIGHT_PRIVATE, UAM_AFPSERVER_POSTAUTH, afp_spotlight_rpc, NULL);
             uam_afpserver_action(AFP_ENUMERATE_EXT2, UAM_AFPSERVER_POSTAUTH, afp_enumerate_ext2, NULL);
 
         case 30:
index f639c49baf7ea8809de4e923d43beaf9a5a0fe16..edb55ea3eafced56c9464077a45360266f444779 100644 (file)
@@ -25,6 +25,7 @@
 #include <atalk/globals.h>
 #include <atalk/fce_api.h>
 #include <atalk/netatalk_conf.h>
+#include <atalk/spotlight.h>
 
 #include "directory.h"
 #include "dircache.h"
@@ -764,9 +765,9 @@ createfile_iderr:
     ad_flush(&ad);
     ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
     fce_register(FCE_FILE_CREATE, fullpathname(upath), NULL, fce_file);
+    sl_index_file(path);
 
     curdir->d_offcnt++;
-
     setvoltime(obj, vol );
 
     return (retvalue);
index cd65f7a9003358971df2d7c6566c0892519f28f7..ac1ce217b31904593f30b0ce4aa5d57617df323a 100644 (file)
@@ -325,7 +325,7 @@ int main(int ac, char **av)
 
     /* Initialize */
     cnid_init();
-    
+
     /* watch atp, dsi sockets and ipc parent/child file descriptor. */
     fd_set_listening_sockets(&obj);
 
diff --git a/etc/afpd/spotlight-packet.bin b/etc/afpd/spotlight-packet.bin
new file mode 100644 (file)
index 0000000..7ddff55
Binary files /dev/null and b/etc/afpd/spotlight-packet.bin differ
diff --git a/etc/afpd/spotlight-packet2.bin b/etc/afpd/spotlight-packet2.bin
new file mode 100644 (file)
index 0000000..aa3bb07
Binary files /dev/null and b/etc/afpd/spotlight-packet2.bin differ
diff --git a/etc/afpd/spotlight.c b/etc/afpd/spotlight.c
new file mode 100644 (file)
index 0000000..fff5a36
--- /dev/null
@@ -0,0 +1,804 @@
+/*
+  Copyright (c) 2012 Frank Lahm <franklahm@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.
+*/
+
+#define USE_LIST
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <strings.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <time.h>
+#include <utime.h>
+
+#include <atalk/list.h>
+#include <atalk/errchk.h>
+#include <atalk/util.h>
+#include <atalk/logger.h>
+#include <atalk/talloc.h>
+#include <atalk/dalloc.h>
+#include <atalk/byteorder.h>
+#include <atalk/netatalk_conf.h>
+#include <atalk/volume.h>
+#include <atalk/spotlight.h>
+
+#include "directory.h"
+
+static TALLOC_CTX *sl_ctx;
+static void *sl_module;
+static struct sl_module_export *sl_module_export;
+
+/* Helper functions and stuff */
+static const char *neststrings[] = {
+    "",
+    "\t",
+    "\t\t",
+    "\t\t\t",
+    "\t\t\t\t",
+    "\t\t\t\t\t",
+    "\t\t\t\t\t\t",
+};
+
+static int dd_dump(DALLOC_CTX *dd, int nestinglevel)
+{
+    const char *type;
+
+    LOG(log_debug, logtype_sl, "%s%s(#%d): {",
+        neststrings[nestinglevel], talloc_get_name(dd), talloc_array_length(dd->dd_talloc_array));
+
+    for (int n = 0; n < talloc_array_length(dd->dd_talloc_array); n++) {
+
+        type = talloc_get_name(dd->dd_talloc_array[n]);
+
+        if (STRCMP(type, ==, "DALLOC_CTX")
+                   || STRCMP(type, ==, "sl_array_t")
+                   || STRCMP(type, ==, "sl_filemeta_t")
+                   || STRCMP(type, ==, "sl_dict_t")) {
+            dd_dump(dd->dd_talloc_array[n], nestinglevel + 1);
+        } else if (STRCMP(type, ==, "uint64_t")) {
+            uint64_t i;
+            memcpy(&i, dd->dd_talloc_array[n], sizeof(uint64_t));
+            LOG(log_debug, logtype_sl, "%suint64_t: 0x%04x", neststrings[nestinglevel + 1], i);
+        } else if (STRCMP(type, ==, "char *")) {
+            LOG(log_debug, logtype_sl, "%sstring: %s", neststrings[nestinglevel + 1], (char *)dd->dd_talloc_array[n]);
+        } else if (STRCMP(type, ==, "sl_bool_t")) {
+            sl_bool_t bl;
+            memcpy(&bl, dd->dd_talloc_array[n], sizeof(sl_bool_t));
+            LOG(log_debug, logtype_sl, "%sbool: %s", neststrings[nestinglevel + 1], bl ? "true" : "false");
+        } else if (STRCMP(type, ==, "sl_nil_t")) {
+            LOG(log_debug, logtype_sl, "%snil", neststrings[nestinglevel + 1]);
+        } else if (STRCMP(type, ==, "sl_time_t")) {
+            sl_time_t t;
+            struct tm *tm;
+            char datestring[256];
+            memcpy(&t, dd->dd_talloc_array[n], sizeof(sl_time_t));
+            tm = localtime(&t.tv_sec);
+            strftime(datestring, sizeof(datestring), "%Y-%m-%d %H:%M:%S", tm);
+            LOG(log_debug, logtype_sl, "%ssl_time_t: %s.%06d", neststrings[nestinglevel + 1], datestring, t.tv_usec);
+        } else if (STRCMP(type, ==, "sl_cnids_t")) {
+            sl_cnids_t cnids;
+            memcpy(&cnids, dd->dd_talloc_array[n], sizeof(sl_cnids_t));
+            LOG(log_debug, logtype_sl, "%sCNIDs: unkn1: 0x%" PRIx16 ", unkn2: 0x%" PRIx32,
+                   neststrings[nestinglevel + 1], cnids.ca_unkn1, cnids.ca_context);
+            if (cnids.ca_cnids)
+                dd_dump(cnids.ca_cnids, nestinglevel + 2);
+        } else {
+            LOG(log_debug, logtype_sl, "%stype: %s", neststrings[nestinglevel + 1], type);
+        }
+    }
+    LOG(log_debug, logtype_sl, "%s}", neststrings[nestinglevel]);
+}
+
+#ifndef SPOT_TEST_MAIN
+/**************************************************************************************************
+ * Spotlight queries
+ **************************************************************************************************/
+
+static ATALK_LIST_HEAD(sl_queries);
+
+/*!
+ * Add a query to the list of active queries
+ */
+static int slq_add(slq_t *slq)
+{
+    list_add(&(slq->slq_list), &sl_queries);
+    return 0;
+}
+
+static int slq_remove(slq_t *slq)
+{
+    EC_INIT;
+    struct list_head *p;
+    slq_t *q = NULL;
+
+    list_for_each(p, &sl_queries) {
+        q = list_entry(p, slq_t, slq_list);
+        if ((q->slq_ctx1 == slq->slq_ctx1) && (q->slq_ctx2 == slq->slq_ctx2)) {            
+            list_del(p);
+            break;
+        }
+        q = NULL;
+    }
+
+    if (q == NULL) {
+        /* The SL query 'slq' was not found in the list, this is not supposed to happen! */
+        LOG(log_warning, logtype_sl, "slq_remove: slq not in active query list");
+    }
+
+EC_CLEANUP:
+    EC_EXIT;
+}
+
+static slq_t *slq_for_ctx(uint64_t ctx1, uint64_t ctx2)
+{
+    EC_INIT;
+    slq_t *q = NULL;
+    struct list_head *p;
+
+    list_for_each(p, &sl_queries) {
+        q = list_entry(p, slq_t, slq_list);
+
+        LOG(log_debug, logtype_sl, "slq_for_ctx(ctx1: 0x%" PRIx64 ", ctx2: 0x%" PRIx64
+            "): active: ctx1: 0x%" PRIx64 ", ctx2: 0x%" PRIx64,
+            ctx1, ctx2, q->slq_ctx1, q->slq_ctx2);
+
+        if ((q->slq_ctx1 == ctx1) && (q->slq_ctx2 == ctx2)) {            
+            break;
+        }
+        q = NULL;
+    }
+
+EC_CLEANUP:
+    if (ret != 0)
+        q = NULL;
+    return q;
+}
+
+/* Error handling for queries */
+static void slq_error(slq_t *slq)
+{
+    if (!slq)
+        return;
+    sl_module_export->sl_mod_error(slq);
+    slq_remove(slq);
+    talloc_free(slq);
+}
+
+/**************************************************************************************************
+ * Spotlight RPC functions
+ **************************************************************************************************/
+
+static int sl_rpc_fetchPropertiesForContext(const AFPObj *obj, const DALLOC_CTX *query, DALLOC_CTX *reply, const struct vol *v)
+{
+    EC_INIT;
+
+    char *s;
+    sl_dict_t *dict;
+    sl_array_t *array;
+    sl_uuid_t uuid;
+
+    if (!v->v_uuid)
+        EC_FAIL_LOG("sl_rpc_fetchPropertiesForContext: missing UUID for volume: %s", v->v_localname);
+
+    dict = talloc_zero(reply, sl_dict_t);
+
+    /* key/val 1 */
+    s = dalloc_strdup(dict, "kMDSStoreMetaScopes");
+    dalloc_add(dict, s, char *);
+
+    array = talloc_zero(dict, sl_array_t);
+    s = dalloc_strdup(array, "kMDQueryScopeComputer");
+    dalloc_add(array, s, char *);
+    dalloc_add(dict, array, sl_array_t);
+
+    /* key/val 2 */
+    s = dalloc_strdup(dict, "kMDSStorePathScopes");
+    dalloc_add(dict, s, char *);
+
+    array = talloc_zero(dict, sl_array_t);
+    s = dalloc_strdup(array, v->v_path);
+    dalloc_add(array, s, char *);
+    dalloc_add(dict, array, sl_array_t);
+
+    /* key/val 3 */
+    s = dalloc_strdup(dict, "kMDSStoreUUID");
+    dalloc_add(dict, s, char *);
+
+    memcpy(uuid.sl_uuid, v->v_uuid, 16);
+    dalloc_add_copy(dict, &uuid, sl_uuid_t);
+
+    /* key/val 4 */
+    s = dalloc_strdup(dict, "kMDSStoreHasPersistentUUID");
+    dalloc_add(dict, s, char *);
+    sl_bool_t b = true;
+    dalloc_add_copy(dict, &b, sl_bool_t);
+
+    dalloc_add(reply, dict, sl_dict_t);
+
+EC_CLEANUP:
+    EC_EXIT;
+}
+
+static int cnid_comp_fn(const void *p1, const void *p2)
+{
+    const uint64_t *cnid1 = p1, *cnid2 = p2;
+    if (*cnid1 == *cnid2)
+        return 0;
+    if (*cnid1 < *cnid2)
+        return -1;
+    else
+        return 1;            
+}
+
+static int sl_createCNIDArray(slq_t *slq, const DALLOC_CTX *p)
+{
+    EC_INIT;
+    uint64_t *cnids = NULL;
+
+    EC_NULL( cnids = talloc_array(slq, uint64_t, talloc_array_length(p)) );
+    for (int i = 0; i < talloc_array_length(p); i++)
+        memcpy(&cnids[i], p->dd_talloc_array[i], sizeof(uint64_t));
+    qsort(cnids, talloc_array_length(p), sizeof(uint64_t), cnid_comp_fn);
+
+    slq->slq_cnids = cnids;
+    slq->slq_cnids_num = talloc_array_length(p);
+
+EC_CLEANUP:
+    if (ret != 0) {
+        if (cnids)
+            talloc_free(cnids);
+    }
+    EC_EXIT;
+}
+
+static int sl_rpc_openQuery(AFPObj *obj, const DALLOC_CTX *query, DALLOC_CTX *reply, struct vol *v)
+{
+    EC_INIT;
+    char *sl_query;
+    uint64_t *uint64;
+    DALLOC_CTX *reqinfo;
+    sl_array_t *array;
+    sl_cnids_t *cnids;
+    slq_t *slq = NULL;
+
+    /* Allocate and initialize query object */
+    slq = talloc_zero(sl_ctx, slq_t);
+    slq->slq_state = SLQ_STATE_NEW;
+    slq->slq_obj = obj;
+    slq->slq_vol = v;
+
+    /* convert spotlight query charset to host charset */
+    EC_NULL_LOG( sl_query = dalloc_value_for_key(query, "DALLOC_CTX", 0, "DALLOC_CTX", 1, "kMDQueryString") );
+    char slq_host[MAXPATHLEN + 1];
+    uint16_t convflags = v->v_mtou_flags;
+    size_t slq_maclen;
+    if (convert_charset(CH_UTF8_MAC, v->v_volcharset, v->v_maccharset, sl_query, strlen(sl_query), slq_host, MAXPATHLEN, &convflags) == -1) {
+        LOG(log_error, logtype_afpd, "sl_rpc_openQuery(\"%s\"): charset conversion failed", sl_query);
+        EC_FAIL;
+    }
+    slq->slq_qstring = talloc_strdup(slq, slq_host);
+    LOG(log_debug, logtype_sl, "sl_rpc_openQuery: %s", slq->slq_qstring);
+
+    slq->slq_time = time(NULL);
+    EC_NULL_LOG( uint64 = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0, "uint64_t", 1) );
+    slq->slq_ctx1 = *uint64;
+    EC_NULL_LOG( uint64 = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0, "uint64_t", 2) );
+    slq->slq_ctx2 = *uint64;
+    EC_NULL_LOG( reqinfo = dalloc_value_for_key(query, "DALLOC_CTX", 0, "DALLOC_CTX", 1, "kMDAttributeArray") );
+    slq->slq_reqinfo = talloc_steal(slq, reqinfo);
+    if ((cnids = dalloc_value_for_key(query, "DALLOC_CTX", 0, "DALLOC_CTX", 1, "kMDQueryItemArray"))) {
+        EC_ZERO_LOG( sl_createCNIDArray(slq, cnids->ca_cnids) );
+    }
+        
+    LOG(log_maxdebug, logtype_sl, "sl_rpc_openQuery: requested attributes:");
+    dd_dump(slq->slq_reqinfo, 0);
+
+    (void)slq_add(slq);
+    
+    /* Run the query */
+    EC_ZERO( sl_module_export->sl_mod_start_search(slq) );
+
+    array = talloc_zero(reply, sl_array_t);
+    uint64_t sl_res = ret == 0 ? 0 : UINT64_MAX;
+    dalloc_add_copy(array, &sl_res, uint64_t);
+    dalloc_add(reply, array, sl_array_t);
+
+EC_CLEANUP:
+    if (ret != 0) {
+        slq_error(slq);
+    }
+    EC_EXIT;
+}
+
+static int sl_rpc_fetchQueryResultsForContext(const AFPObj *obj, const DALLOC_CTX *query, DALLOC_CTX *reply, const struct vol *v)
+{
+    EC_INIT;
+    slq_t *slq = NULL;
+    uint64_t *uint64, ctx1, ctx2;
+
+    /* Context */
+    EC_NULL_LOG (uint64 = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0, "uint64_t", 1) );
+    ctx1 = *uint64;
+    EC_NULL_LOG (uint64 = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0, "uint64_t", 2) );
+    ctx2 = *uint64;
+
+    /* Get query for context */
+    EC_NULL_LOG( slq = slq_for_ctx(ctx1, ctx2) );
+    if (slq->slq_state != SLQ_STATE_RUNNING && slq->slq_state != SLQ_STATE_DONE) {
+        EC_FAIL_LOG("Spotlight: attempt to fetch results for query that isn't active");
+    }
+
+    /* Create and pass reply handle */
+    EC_NULL( slq->slq_reply = talloc_zero(reply, sl_array_t) );
+
+    /* Fetch Tracker results*/
+    EC_ZERO_LOG( sl_module_export->sl_mod_fetch_result(slq) );
+
+    dalloc_add(reply, slq->slq_reply, sl_array_t);
+
+EC_CLEANUP:
+    if (ret != 0) {
+        slq_error(slq);
+    }
+    EC_EXIT;
+}
+
+static int sl_rpc_storeAttributesForOIDArray(const AFPObj *obj, const DALLOC_CTX *query, DALLOC_CTX *reply, const struct vol *vol)
+{
+    EC_INIT;
+    uint64_t uint64;
+    sl_array_t *array;
+    sl_cnids_t *cnids;
+    sl_time_t *sl_time;
+    cnid_t id;
+    char *path;
+    struct dir *dir;
+    
+    EC_NULL_LOG( cnids = dalloc_get(query, "DALLOC_CTX", 0, "sl_cnids_t", 2) );
+    memcpy(&uint64, cnids->ca_cnids->dd_talloc_array[0], sizeof(uint64_t));
+    id = (cnid_t)uint64;
+    LOG(log_debug, logtype_sl, "sl_rpc_storeAttributesForOIDArray: CNID: %" PRIu32, id);
+
+    if (htonl(id) == DIRDID_ROOT) {
+        path = vol->v_path;
+    } else if (id < CNID_START) {
+        EC_FAIL;
+    } else {
+        cnid_t did;
+        char buffer[12 + MAXPATHLEN + 1];
+
+        did = htonl(id);
+        EC_NULL_LOG( path = cnid_resolve(vol->v_cdb, &did, buffer, sizeof(buffer)) );
+        EC_NULL_LOG( dir = dirlookup(vol, did) );
+        EC_NEG1_LOG( movecwd(vol, dir) );
+    }
+
+    if ((sl_time = dalloc_value_for_key(query, "DALLOC_CTX", 0, "DALLOC_CTX", 1, "DALLOC_CTX", 1, "kMDItemLastUsedDate"))) {
+        struct utimbuf utimes;
+        utimes.actime = utimes.modtime = sl_time->tv_sec;
+        utime(path, &utimes);
+    }
+
+    array = talloc_zero(reply, sl_array_t);
+    uint64_t sl_res = 0;
+    dalloc_add_copy(array, &sl_res, uint64_t);
+    dalloc_add(reply, array, sl_array_t);
+
+EC_CLEANUP:
+    EC_EXIT;
+}
+
+static int sl_rpc_fetchAttributeNamesForOIDArray(const AFPObj *obj, const DALLOC_CTX *query, DALLOC_CTX *reply, const struct vol *vol)
+{
+    EC_INIT;
+    uint64_t uint64;
+    sl_cnids_t *cnids;
+    sl_time_t *sl_time;
+    cnid_t id;
+    char *path;
+    struct dir *dir;
+    
+    EC_NULL_LOG( cnids = dalloc_get(query, "DALLOC_CTX", 0, "sl_cnids_t", 1) );
+    memcpy(&uint64, cnids->ca_cnids->dd_talloc_array[0], sizeof(uint64_t));
+    id = (cnid_t)uint64;
+    LOG(log_debug, logtype_sl, "sl_rpc_fetchAttributeNamesForOIDArray: CNID: %" PRIu32, id);
+
+    if (htonl(id) == DIRDID_ROOT) {
+        path = vol->v_path;
+    } else if (id < CNID_START) {
+        EC_FAIL;
+    } else {
+        cnid_t did;
+        char buffer[12 + MAXPATHLEN + 1];
+
+        did = htonl(id);
+        EC_NULL_LOG( path = cnid_resolve(vol->v_cdb, &did, buffer, sizeof(buffer)) );
+        EC_NULL_LOG( dir = dirlookup(vol, did) );
+        EC_NEG1_LOG( movecwd(vol, dir) );
+    }
+
+    /* Result array */
+    sl_array_t *array = talloc_zero(reply, sl_array_t);
+    dalloc_add(reply, array, sl_array_t);
+
+    /* Return result value 0 */
+    uint64_t sl_res = 0;
+    dalloc_add_copy(array, &sl_res, uint64_t);
+
+    /* Return CNID array */
+    sl_cnids_t *replycnids = talloc_zero(reply, sl_cnids_t);
+    replycnids->ca_cnids = talloc_zero(cnids, DALLOC_CTX);
+    replycnids->ca_unkn1 = 0xfec;
+    replycnids->ca_context = cnids->ca_context;
+    uint64 = (uint64_t)id;
+    dalloc_add_copy(replycnids->ca_cnids, &uint64, uint64_t);
+    dalloc_add(array, replycnids, sl_cnids_t);
+
+    /* Return filemeta array */
+
+    /*
+     * FIXME: this should return the real attributes from all known metadata sources
+     * (Tracker and filesystem)
+     */
+    sl_array_t *mdattrs = talloc_zero(reply, sl_array_t);
+    dalloc_add(mdattrs, dalloc_strdup(mdattrs, "kMDItemFSName"), "char *");
+    dalloc_add(mdattrs, dalloc_strdup(mdattrs, "kMDItemDisplayName"), "char *");
+    dalloc_add(mdattrs, dalloc_strdup(mdattrs, "kMDItemFSSize"), "char *");
+    dalloc_add(mdattrs, dalloc_strdup(mdattrs, "kMDItemFSOwnerUserID"), "char *");
+    dalloc_add(mdattrs, dalloc_strdup(mdattrs, "kMDItemFSOwnerGroupID"), "char *");
+    dalloc_add(mdattrs, dalloc_strdup(mdattrs, "kMDItemFSContentChangeDate"), "char *");
+
+    sl_filemeta_t *fmeta = talloc_zero(reply, sl_filemeta_t);
+    dalloc_add(fmeta, mdattrs, sl_array_t);
+    dalloc_add(array, fmeta, sl_filemeta_t);
+
+EC_CLEANUP:
+    EC_EXIT;
+}
+
+static int sl_rpc_fetchAttributesForOIDArray(AFPObj *obj, const DALLOC_CTX *query, DALLOC_CTX *reply, const struct vol *vol)
+{
+    EC_INIT;
+    slq_t *slq = NULL;
+    uint64_t uint64;
+    sl_cnids_t *cnids;
+    sl_time_t *sl_time;
+    cnid_t id;
+    struct dir *dir;
+    sl_array_t *reqinfo;
+
+
+
+    /* Allocate and initialize query object */
+    slq = talloc_zero(reply, slq_t);
+    slq->slq_state = SLQ_STATE_ATTRS;
+    slq->slq_obj = obj;
+    slq->slq_vol = vol;
+    EC_NULL( slq->slq_reply = talloc_zero(reply, sl_array_t) );
+    EC_NULL( reqinfo = dalloc_get(query, "DALLOC_CTX", 0, "sl_array_t", 1) );
+    slq->slq_reqinfo = talloc_steal(slq, reqinfo);
+    
+    EC_NULL_LOG( cnids = dalloc_get(query, "DALLOC_CTX", 0, "sl_cnids_t", 2) );
+    memcpy(&uint64, cnids->ca_cnids->dd_talloc_array[0], sizeof(uint64_t));
+    id = (cnid_t)uint64;
+
+    if (htonl(id) == DIRDID_ROOT) {
+        slq->slq_path = talloc_strdup(slq, vol->v_path);
+    } else if (id < CNID_START) {
+        EC_FAIL;
+    } else {
+        cnid_t did;
+        char buffer[12 + MAXPATHLEN + 1];
+        char *name;
+        did = htonl(id);
+        EC_NULL( name = cnid_resolve(vol->v_cdb, &did, buffer, sizeof(buffer)) );
+        EC_NULL( dir = dirlookup(vol, did) );
+        EC_NULL( slq->slq_path = talloc_asprintf(slq, "%s/%s", bdata(dir->d_fullpath), name) );
+    }
+
+    /* Return result value 0 */
+    uint64_t sl_res = 0;
+    dalloc_add_copy(slq->slq_reply, &sl_res, uint64_t);
+
+    /* Return CNID array */
+    sl_cnids_t *replycnids = talloc_zero(reply, sl_cnids_t);
+    replycnids->ca_cnids = talloc_zero(cnids, DALLOC_CTX);
+    replycnids->ca_unkn1 = 0xfec;
+    replycnids->ca_context = cnids->ca_context;
+    uint64 = (uint64_t)id;
+    dalloc_add_copy(replycnids->ca_cnids, &uint64, uint64_t);
+    dalloc_add(slq->slq_reply, replycnids, sl_cnids_t);
+
+    /* Fetch attributes from module */
+    EC_ZERO_LOG( sl_module_export->sl_mod_fetch_attrs(slq) );
+
+    dalloc_add(reply, slq->slq_reply, sl_array_t);
+
+EC_CLEANUP:
+    EC_EXIT;
+}
+
+static int sl_rpc_closeQueryForContext(const AFPObj *obj, const DALLOC_CTX *query, DALLOC_CTX *reply, const struct vol *v)
+{
+    EC_INIT;
+    slq_t *slq = NULL;
+    uint64_t *uint64, ctx1, ctx2;
+    sl_array_t *array;
+    
+    /* Context */
+    EC_NULL_LOG (uint64 = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0, "uint64_t", 1) );
+    ctx1 = *uint64;
+    EC_NULL_LOG (uint64 = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0, "uint64_t", 2) );
+    ctx2 = *uint64;
+
+    /* Get query for context and free it */
+    EC_NULL_LOG( slq = slq_for_ctx(ctx1, ctx2) );
+    if (slq->slq_state != SLQ_STATE_DONE)
+        LOG(log_warning, logtype_sl, "Closing active query");
+    sl_module_export->sl_mod_end_search(slq);
+    slq_remove(slq);
+    talloc_free(slq);
+    slq = NULL;
+
+    array = talloc_zero(reply, sl_array_t);
+    uint64_t sl_res = 0;
+    dalloc_add_copy(array, &sl_res, uint64_t);
+    dalloc_add(reply, array, sl_array_t);
+
+EC_CLEANUP:
+    if (ret != 0) {
+        slq_error(slq);
+    }
+    EC_EXIT;
+}
+
+/**************************************************************************************************
+ * Spotlight module functions
+ **************************************************************************************************/
+
+int sl_mod_load(const char *path)
+{
+    EC_INIT;
+
+    sl_ctx = talloc_new(NULL);
+
+    if ((sl_module = mod_open(path)) == NULL) {
+        LOG(log_error, logtype_sl, "Failed to load module \'%s\': %s", path, mod_error());
+        EC_FAIL;
+    }
+
+    if ((sl_module_export = mod_symbol(sl_module, "sl_mod")) == NULL) {
+        LOG(log_error, logtype_sl, "sl_mod_load(%s): mod_symbol error for symbol %s", path, "sl_mod");
+        EC_FAIL;
+    }
+
+    sl_module_export->sl_mod_init("test");
+   
+EC_CLEANUP:
+    EC_EXIT;
+}
+
+/**
+ * Index a file
+ **/
+void sl_index_file(const char *path)
+{
+    if (sl_module_export && sl_module_export->sl_mod_index_file)
+        sl_module_export->sl_mod_index_file(path);
+}
+
+/**************************************************************************************************
+ * AFP functions
+ **************************************************************************************************/
+
+int afp_spotlight_rpc(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
+{
+    EC_INIT;
+    TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+    uint16_t vid;
+    int cmd;
+    int endianess = SL_ENC_LITTLE_ENDIAN;
+    struct vol      *vol;
+    DALLOC_CTX *query;
+    DALLOC_CTX *reply;
+    char *rpccmd;
+    int len;
+
+    *rbuflen = 0;
+
+    if (sl_module == NULL)
+        return AFPERR_NOOP;
+
+    ibuf += 2;
+    ibuflen -= 2;
+
+    vid = SVAL(ibuf, 0);
+    LOG(log_debug, logtype_sl, "afp_spotlight_rpc(vid: %" PRIu16 ")", vid);
+
+    if ((vol = getvolbyvid(vid)) == NULL) {
+        LOG(log_error, logtype_sl, "afp_spotlight_rpc: bad volume id: %" PRIu16 ")", vid);
+        ret = AFPERR_ACCESS;
+        goto EC_CLEANUP;
+    }
+
+    /*    IVAL(ibuf, 2): unknown, always 0x00008004, some flags ? */
+
+    cmd = RIVAL(ibuf, 6);
+    LOG(log_debug, logtype_sl, "afp_spotlight_rpc(cmd: %d)", cmd);
+
+    /*    IVAL(ibuf, 10: unknown, always 0x00000000 */
+
+    switch (cmd) {
+
+    case SPOTLIGHT_CMD_OPEN:
+    case SPOTLIGHT_CMD_OPEN2:
+        RSIVAL(rbuf, 0, ntohs(vid));
+        RSIVAL(rbuf, 4, 0);
+        len = strlen(vol->v_path) + 1;
+        strncpy(rbuf + 8, vol->v_path, len);
+        *rbuflen += 8 + len;
+        break;
+
+    case SPOTLIGHT_CMD_FLAGS:
+        RSIVAL(rbuf, 0, 0x0100006b); /* Whatever this value means... flags? Helios uses 0x1eefface */
+        *rbuflen += 4;
+        break;
+
+    case SPOTLIGHT_CMD_RPC:
+        EC_NULL( query = talloc_zero(tmp_ctx, DALLOC_CTX) );
+        EC_NULL( reply = talloc_zero(tmp_ctx, DALLOC_CTX) );
+        EC_NEG1_LOG( sl_unpack(query, ibuf + 22) );
+
+        LOG(log_debug, logtype_sl, "afp_spotlight_rpc: Request dump:");
+        dd_dump(query, 0);
+
+        EC_NULL_LOG( rpccmd = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0, "char *", 0) );
+
+        if (STRCMP(rpccmd, ==, "fetchPropertiesForContext:")) {
+            EC_ZERO_LOG( sl_rpc_fetchPropertiesForContext(obj, query, reply, vol) );
+        } else if (STRCMP(rpccmd, ==, "openQueryWithParams:forContext:")) {
+            EC_ZERO_LOG( sl_rpc_openQuery(obj, query, reply, vol) );
+        } else if (STRCMP(rpccmd, ==, "fetchQueryResultsForContext:")) {
+            EC_ZERO_LOG( sl_rpc_fetchQueryResultsForContext(obj, query, reply, vol) );
+        } else if (STRCMP(rpccmd, ==, "storeAttributes:forOIDArray:context:")) {
+            EC_ZERO_LOG( sl_rpc_storeAttributesForOIDArray(obj, query, reply, vol) );
+        } else if (STRCMP(rpccmd, ==, "fetchAttributeNamesForOIDArray:context:")) {
+            EC_ZERO_LOG( sl_rpc_fetchAttributeNamesForOIDArray(obj, query, reply, vol) );
+        } else if (STRCMP(rpccmd, ==, "fetchAttributes:forOIDArray:context:")) {
+            EC_ZERO_LOG( sl_rpc_fetchAttributesForOIDArray(obj, query, reply, vol) );
+        } else if (STRCMP(rpccmd, ==, "closeQueryForContext:")) {
+            EC_ZERO_LOG( sl_rpc_closeQueryForContext(obj, query, reply, vol) );
+        } else {
+            LOG(log_error, logtype_sl, "afp_spotlight_rpc: unknown Spotlight RPC: %s", rpccmd);
+        }
+
+        LOG(log_debug, logtype_sl, "afp_spotlight_rpc: Reply dump:");
+        dd_dump(reply, 0);
+
+        memset(rbuf, 0, 4);
+        *rbuflen += 4;
+
+        EC_NEG1_LOG( len = sl_pack(reply, rbuf + 4) );
+        *rbuflen += len;
+        break;
+    }
+
+EC_CLEANUP:
+    talloc_free(tmp_ctx);
+    if (ret != AFP_OK) {
+        *rbuflen = 0;
+        return AFPERR_MISC;
+    }
+    EC_EXIT;
+}
+#endif
+
+/**************************************************************************************************
+ * Testing
+ **************************************************************************************************/
+
+#ifdef SPOT_TEST_MAIN
+
+int main(int argc, char **argv)
+{
+    EC_INIT;
+    TALLOC_CTX *mem_ctx = talloc_new(NULL);
+    DALLOC_CTX *dd = talloc_zero(mem_ctx, DALLOC_CTX);
+    uint64_t i;
+
+    set_processname("spot");
+    setuplog("default:info,spotlight:debug", "/dev/tty");
+
+    LOG(log_info, logtype_sl, "Start");
+
+    i = 1;
+    dalloc_add_copy(dd, &i, uint64_t);
+    char *str = dalloc_strdup(dd, "hello world");
+    dalloc_add(dd, str, char *);
+    sl_bool_t b = true;
+    dalloc_add_copy(dd, &b, sl_bool_t);
+
+    /* add a nested array */
+    DALLOC_CTX *nested = talloc_zero(dd, DALLOC_CTX);
+    i = 3;
+    dalloc_add_copy(nested, &i, uint64_t);
+    dalloc_add(dd, nested, DALLOC_CTX);
+
+    /* test an allocated CNID array */
+    uint64_t id = 16;
+    sl_cnids_t *cnids = talloc_zero(dd, sl_cnids_t);
+    cnids->ca_cnids = talloc_zero(cnids, DALLOC_CTX);
+    cnids->ca_unkn1 = 1;
+    dalloc_add_copy(cnids->ca_cnids, &id, uint64_t);
+    dalloc_add(dd, cnids, sl_cnids_t);
+
+    /* Now the Spotlight types */
+    sl_array_t *sl_array = talloc_zero(dd, sl_array_t);
+    i = 0x1234;
+    dalloc_add_copy(sl_array, &i, uint64_t);
+
+    sl_dict_t *sl_dict = talloc_zero(dd, sl_dict_t);
+    i = 0xffff;
+    dalloc_add_copy(sl_dict, &i, uint64_t);
+    dalloc_add(sl_array, sl_dict, sl_dict_t);
+
+    dalloc_add(dd, sl_array, sl_array_t);
+
+    dd_dump(dd, 0);
+
+    /* now parse a real spotlight packet */
+    if (argc > 1) {
+        char ibuf[8192];
+        char rbuf[8192];
+        int fd;
+        size_t len;
+        DALLOC_CTX *query;
+
+        EC_NULL( query = talloc_zero(mem_ctx, DALLOC_CTX) );
+
+        EC_NEG1_LOG( fd = open(argv[1], O_RDONLY) );
+        EC_NEG1_LOG( len = read(fd, ibuf, 8192) );
+        close(fd);
+        EC_NEG1_LOG( sl_unpack(query, ibuf + 24) );
+
+        /* Now dump the whole thing */
+        dd_dump(query, 0);
+    }
+
+#if 0
+    /* packing  */
+    int qlen;
+    char buf[MAX_SLQ_DAT];
+    EC_NEG1_LOG( qlen = sl_pack(query, buf) );
+
+    EC_NEG1_LOG( fd = open("test.bin", O_RDWR) );
+    lseek(fd, 24, SEEK_SET);
+    write(fd, buf, qlen);
+    close(fd);
+#endif
+
+EC_CLEANUP:
+    if (mem_ctx) {
+        talloc_free(mem_ctx);
+        mem_ctx = NULL;
+    }
+    EC_EXIT;
+}
+#endif
diff --git a/etc/afpd/spotlight_marshalling.c b/etc/afpd/spotlight_marshalling.c
new file mode 100644 (file)
index 0000000..69733f4
--- /dev/null
@@ -0,0 +1,812 @@
+/*
+  Copyright (c) 2012 Frank Lahm <franklahm@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.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <strings.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <inttypes.h>
+
+#include <atalk/errchk.h>
+#include <atalk/util.h>
+#include <atalk/logger.h>
+#include <atalk/talloc.h>
+#include <atalk/dalloc.h>
+#include <atalk/byteorder.h>
+#include <atalk/netatalk_conf.h>
+#include <atalk/volume.h>
+#include <atalk/dsi.h>
+#include <atalk/spotlight.h>
+
+#define MAX_SLQ_DAT (DSI_DATASIZ - 64)
+#define MAX_SLQ_TOC 8192
+
+/**************************************************************************************************
+ * RPC data marshalling and unmarshalling
+ **************************************************************************************************/
+
+/* Spotlight epoch is UNIX epoch minus SPOTLIGHT_TIME_DELTA */
+#define SPOTLIGHT_TIME_DELTA INT64_C(280878921600U)
+
+#define SQ_TYPE_NULL    0x0000
+#define SQ_TYPE_COMPLEX 0x0200
+#define SQ_TYPE_INT64   0x8400
+#define SQ_TYPE_BOOL    0x0100
+#define SQ_TYPE_FLOAT   0x8500
+#define SQ_TYPE_DATA    0x0700
+#define SQ_TYPE_CNIDS   0x8700
+#define SQ_TYPE_UUID    0x0e00
+#define SQ_TYPE_DATE    0x8600
+#define SQ_TYPE_TOC     0x8800
+
+#define SQ_CPX_TYPE_ARRAY           0x0a00
+#define SQ_CPX_TYPE_STRING          0x0c00
+#define SQ_CPX_TYPE_UTF16_STRING    0x1c00
+#define SQ_CPX_TYPE_DICT            0x0d00
+#define SQ_CPX_TYPE_CNIDS           0x1a00
+#define SQ_CPX_TYPE_FILEMETA        0x1b00
+
+#define SUBQ_SAFETY_LIM 20
+
+/* Forward declarations */
+static int sl_pack_loop(DALLOC_CTX *query, char *buf, int offset, char *toc_buf, int *toc_idx);
+static int sl_unpack_loop(DALLOC_CTX *query, const char *buf, int offset, uint count, const uint toc_offset, const uint encoding);
+
+/**************************************************************************************************
+ * Wrapper functions for the *VAL macros with bound checking
+ **************************************************************************************************/
+
+static int sivalc(char *buf, off_t off, off_t maxoff, uint32_t val)
+{
+    if (off + sizeof(val) >= maxoff) {
+        LOG(log_error, logtype_sl, "sivalc: off: %zd, maxoff: %zd", off, maxoff);
+        return -1;
+    }
+    SIVAL(buf, off, val);
+    return 0;
+}
+
+static int slvalc(char *buf, off_t off, off_t maxoff, uint64_t val)
+{
+    if (off + sizeof(val) >= maxoff) {
+        LOG(log_error, logtype_sl, "slvalc: off: %zd, maxoff: %zd", off, maxoff);
+        return -1;
+    }
+    SLVAL(buf, off, val);
+    return 0;
+}
+
+/*
+* Returns the UTF-16 string encoding, by checking the 2-byte byte order mark.
+* If there is no byte order mark, -1 is returned.
+*/
+static uint spotlight_get_utf16_string_encoding(const char *buf, int offset, int query_length, uint encoding) {
+    uint utf16_encoding;
+
+    /* Assumed encoding in absence of a bom is little endian */
+    utf16_encoding = SL_ENC_LITTLE_ENDIAN;
+
+    if (query_length >= 2) {
+        uint8_t le_bom[] = {0xff, 0xfe};
+        uint8_t be_bom[] = {0xfe, 0xff};
+        if (memcmp(le_bom, buf + offset, sizeof(uint16_t)) == 0)
+            utf16_encoding = SL_ENC_LITTLE_ENDIAN | SL_ENC_UTF_16;
+        else if (memcmp(be_bom, buf + offset, sizeof(uint16_t)) == 0)
+            utf16_encoding = SL_ENC_BIG_ENDIAN | SL_ENC_UTF_16;
+    }
+
+    return utf16_encoding;
+}
+
+/**************************************************************************************************
+ * marshalling functions
+ **************************************************************************************************/
+
+#define SL_OFFSET_DELTA 16
+
+static uint64_t sl_pack_tag(uint16_t type, uint16_t size_or_count, uint32_t val)
+{
+    uint64_t tag = ((uint64_t)val << 32) | ((uint64_t)type << 16) | size_or_count;
+    return tag;
+}
+
+static int sl_pack_float(double d, char *buf, int offset)
+{
+    EC_INIT;
+
+    union {
+        double d;
+        uint64_t w;
+    } ieee_fp_union;
+
+    EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_FLOAT, 2, 1)) );
+    EC_ZERO( slvalc(buf, offset + 8, MAX_SLQ_DAT, ieee_fp_union.w) );
+
+EC_CLEANUP:
+    if (ret != 0)
+        return -1;
+    return offset + 2 * sizeof(uint64_t);
+}
+
+static int sl_pack_uint64(uint64_t u, char *buf, int offset)
+{
+    EC_INIT;
+
+    EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_INT64, 2, 1)) );
+    EC_ZERO( slvalc(buf, offset + 8, MAX_SLQ_DAT, u) );
+
+EC_CLEANUP:
+    if (ret != 0)
+        return -1;
+    return offset + 2 * sizeof(uint64_t);
+}
+
+static int sl_pack_bool(sl_bool_t bl, char *buf, int offset)
+{
+    EC_INIT;
+
+    EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_BOOL, 1, bl ? 1 : 0)) );
+
+EC_CLEANUP:
+    if (ret != 0)
+        return -1;
+    return offset + sizeof(uint64_t);
+}
+
+static int sl_pack_nil(char *buf, int offset)
+{
+    EC_INIT;
+
+    EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_NULL, 1, 1)) );
+
+EC_CLEANUP:
+    if (ret != 0)
+        return -1;
+    return offset + sizeof(uint64_t);
+}
+
+static int sl_pack_date(sl_time_t t, char *buf, int offset)
+{
+    EC_INIT;
+    uint64_t data = 0;
+
+    data = (t.tv_sec + SPOTLIGHT_TIME_DELTA) << 24;
+
+    EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_DATE, 2, 1)) );
+    EC_ZERO( slvalc(buf, offset + 8, MAX_SLQ_DAT, data) );
+
+EC_CLEANUP:
+    if (ret != 0)
+        return -1;
+    return offset + 2 * sizeof(uint64_t);
+}
+
+static int sl_pack_uuid(sl_uuid_t *uuid, char *buf, int offset)
+{
+    EC_INIT;
+
+    EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_UUID, 3, 1)) );
+    if (offset + 8 + 16 >= MAX_SLQ_DAT)
+        EC_FAIL;
+    memcpy(buf + offset + 8, uuid, 16);
+
+EC_CLEANUP:
+    if (ret != 0)
+        return -1;
+    return offset + sizeof(uint64_t) + 16;
+}
+
+static int sl_pack_CNID(sl_cnids_t *cnids, char *buf, int offset, char *toc_buf, int *toc_idx)
+{
+    EC_INIT;
+    int off = 0, len;
+    int cnid_count = talloc_array_length(cnids->ca_cnids->dd_talloc_array);
+    uint64_t id;
+
+    EC_ZERO( slvalc(toc_buf, *toc_idx * 8, MAX_SLQ_TOC, sl_pack_tag(SQ_CPX_TYPE_CNIDS, (offset + SL_OFFSET_DELTA) / 8, 0)) );
+    EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1)) );
+    *toc_idx += 1;
+    offset += 8;
+
+    len = cnid_count + 1;
+    if (cnid_count > 0)
+        len ++;
+
+    EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_CNIDS, len, 8 /* unknown meaning, but always 8 */)) );
+    offset += 8;
+
+    if (cnid_count > 0) {
+        EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(cnids->ca_unkn1, cnid_count, cnids->ca_context)) );
+        offset += 8;
+
+        for (int i = 0; i < cnid_count; i++) {
+            memcpy(&id, cnids->ca_cnids->dd_talloc_array[i], sizeof(uint64_t));
+            EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, id) );
+            offset += 8;
+        }
+    }
+    
+EC_CLEANUP:
+    if (ret != 0)
+        return -1;
+    return offset;
+}
+
+static int sl_pack_array(sl_array_t *array, char *buf, int offset, char *toc_buf, int *toc_idx)
+{
+    EC_INIT;
+    int count = talloc_array_length(array->dd_talloc_array);
+    int octets = (offset + SL_OFFSET_DELTA) / 8;
+
+    EC_ZERO( slvalc(toc_buf, *toc_idx * 8, MAX_SLQ_TOC, sl_pack_tag(SQ_CPX_TYPE_ARRAY, octets, count)) );
+    EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1)) );
+    *toc_idx += 1;
+    offset += 8;
+
+    EC_NEG1( offset = sl_pack_loop(array, buf, offset, toc_buf, toc_idx) );
+
+EC_CLEANUP:
+    if (ret != 0)
+        return -1;
+    return offset;
+}
+
+static int sl_pack_dict(sl_array_t *dict, char *buf, int offset, char *toc_buf, int *toc_idx)
+{
+    EC_INIT;
+
+    EC_ZERO( slvalc(toc_buf,
+                    *toc_idx * 8,
+                    MAX_SLQ_TOC,
+                    sl_pack_tag(SQ_CPX_TYPE_DICT,
+                                (offset + SL_OFFSET_DELTA) / 8,
+                                talloc_array_length(dict->dd_talloc_array))) );
+    EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1)) );
+    *toc_idx += 1;
+    offset += 8;
+
+    EC_NEG1( offset = sl_pack_loop(dict, buf, offset, toc_buf, toc_idx) );
+
+EC_CLEANUP:
+    if (ret != 0)
+        return -1;
+    return offset;
+}
+
+static int sl_pack_filemeta(sl_filemeta_t *fm, char *buf, int offset, char *toc_buf, int *toc_idx)
+{
+    EC_INIT;
+    int fmlen;                  /* lenght of filemeta */
+    int saveoff = offset;
+
+    EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1)) );
+    offset += 16;
+
+    EC_NEG1( fmlen = sl_pack(fm, buf + offset) );
+
+    /* Check for empty filemeta array, if it's only 40 bytes, it's only the header but no content */
+    LOG(log_debug, logtype_sl, "fmlen: %d", fmlen);
+    if (fmlen > 40)
+        offset += fmlen;
+    else
+        fmlen = 0;
+
+    EC_ZERO( slvalc(buf, saveoff + 8, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_DATA, (fmlen / 8) + 1, 8 /* unknown meaning, but always 8 */)) );
+
+    EC_ZERO( slvalc(toc_buf, *toc_idx * 8, MAX_SLQ_TOC, sl_pack_tag(SQ_CPX_TYPE_FILEMETA, (saveoff + SL_OFFSET_DELTA) / 8, fmlen / 8)) );
+    *toc_idx += 1;
+
+EC_CLEANUP:
+    if (ret != 0)
+        return -1;
+    return offset;
+}
+
+static int sl_pack_string(char *s, char *buf, int offset, char *toc_buf, int *toc_idx)
+{
+    EC_INIT;
+    int len, octets, used_in_last_octet;
+
+    len = strlen(s);
+    octets = (len / 8) + (len & 7 ? 1 : 0);
+    used_in_last_octet = 8 - (octets * 8 - len);
+
+    EC_ZERO( slvalc(toc_buf, *toc_idx * 8, MAX_SLQ_TOC, sl_pack_tag(SQ_CPX_TYPE_STRING, (offset + SL_OFFSET_DELTA) / 8, used_in_last_octet)) );
+    EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1)) );
+    *toc_idx += 1;
+    offset += 8;
+
+    EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_DATA, octets + 1, used_in_last_octet)) );
+    offset += 8;
+
+    if (offset + octets * 8 > MAX_SLQ_DAT)
+        EC_FAIL;
+    memset(buf + offset, 0, octets * 8);
+    strncpy(buf + offset, s, len);
+    offset += octets * 8;
+
+EC_CLEANUP:
+    if (ret != 0)
+        return -1;
+    return offset;
+}
+
+static int sl_pack_loop(DALLOC_CTX *query, char *buf, int offset, char *toc_buf, int *toc_idx)
+{
+    EC_INIT;
+    const char *type;
+
+    for (int n = 0; n < talloc_array_length(query->dd_talloc_array); n++) {
+
+        type = talloc_get_name(query->dd_talloc_array[n]);
+
+        if (STRCMP(type, ==, "sl_array_t")) {
+            EC_NEG1( offset = sl_pack_array(query->dd_talloc_array[n], buf, offset, toc_buf, toc_idx) );
+        } else if (STRCMP(type, ==, "sl_dict_t")) {
+            EC_NEG1( offset = sl_pack_dict(query->dd_talloc_array[n], buf, offset, toc_buf, toc_idx) );
+        } else if (STRCMP(type, ==, "sl_filemeta_t")) {
+            EC_NEG1( offset = sl_pack_filemeta(query->dd_talloc_array[n], buf, offset, toc_buf, toc_idx) );
+        } else if (STRCMP(type, ==, "uint64_t")) {
+            uint64_t i;
+            memcpy(&i, query->dd_talloc_array[n], sizeof(uint64_t));
+            EC_NEG1( offset = sl_pack_uint64(i, buf, offset) );
+        } else if (STRCMP(type, ==, "char *")) {
+            EC_NEG1( offset = sl_pack_string(query->dd_talloc_array[n], buf, offset, toc_buf, toc_idx) );
+        } else if (STRCMP(type, ==, "sl_bool_t")) {
+            sl_bool_t bl;
+            memcpy(&bl, query->dd_talloc_array[n], sizeof(sl_bool_t));
+            EC_NEG1( offset = sl_pack_bool(bl, buf, offset) );
+        } else if (STRCMP(type, ==, "double")) {
+            double d;
+            memcpy(&d, query->dd_talloc_array[n], sizeof(double));
+            EC_NEG1( offset = sl_pack_float(d, buf, offset) );
+        } else if (STRCMP(type, ==, "sl_nil_t")) {
+            EC_NEG1( offset = sl_pack_nil(buf, offset) );
+        } else if (STRCMP(type, ==, "sl_time_t")) {
+            sl_time_t t;
+            memcpy(&t, query->dd_talloc_array[n], sizeof(sl_time_t));
+            EC_NEG1( offset = sl_pack_date(t, buf, offset) );
+        } else if (STRCMP(type, ==, "sl_uuid_t")) {
+            EC_NEG1( offset = sl_pack_uuid(query->dd_talloc_array[n], buf, offset) );
+        } else if (STRCMP(type, ==, "sl_cnids_t")) {
+            EC_NEG1( offset = sl_pack_CNID(query->dd_talloc_array[n], buf, offset, toc_buf, toc_idx) );
+        }
+    }
+
+EC_CLEANUP:
+    if (ret != 0)
+        return -1;
+    return offset;
+}
+
+/**************************************************************************************************
+ * unmarshalling functions
+ **************************************************************************************************/
+
+static uint64_t sl_unpack_uint64(const char *buf, int offset, uint encoding)
+{
+    if (encoding == SL_ENC_LITTLE_ENDIAN)
+            return LVAL(buf, offset);
+        else
+            return RLVAL(buf, offset);
+}
+
+static int sl_unpack_ints(DALLOC_CTX *query, const char *buf, int offset, uint encoding)
+{
+    int count, i;
+    uint64_t query_data64;
+
+    query_data64 = sl_unpack_uint64(buf, offset, encoding);
+    count = query_data64 >> 32;
+    offset += 8;
+
+    i = 0;
+    while (i++ < count) {
+        query_data64 = sl_unpack_uint64(buf, offset, encoding);
+        dalloc_add_copy(query, &query_data64, uint64_t);
+        offset += 8;
+    }
+
+    return count;
+}
+
+static int sl_unpack_date(DALLOC_CTX *query, const char *buf, int offset, uint encoding)
+{
+    int count, i;
+    uint64_t query_data64;
+    sl_time_t t;
+
+    query_data64 = sl_unpack_uint64(buf, offset, encoding);
+    count = query_data64 >> 32;
+    offset += 8;
+
+    i = 0;
+    while (i++ < count) {
+        query_data64 = sl_unpack_uint64(buf, offset, encoding) >> 24;
+        t.tv_sec = query_data64 - SPOTLIGHT_TIME_DELTA;
+        t.tv_usec = 0;
+        dalloc_add_copy(query, &t, sl_time_t);
+        offset += 8;
+    }
+
+    return count;
+}
+
+static int sl_unpack_uuid(DALLOC_CTX *query, const char *buf, int offset, uint encoding)
+{
+    int count, i;
+    uint64_t query_data64;
+    sl_uuid_t uuid;
+    query_data64 = sl_unpack_uint64(buf, offset, encoding);
+    count = query_data64 >> 32;
+    offset += 8;
+
+    i = 0;
+    while (i++ < count) {
+        memcpy(uuid.sl_uuid, buf + offset, 16);
+        dalloc_add_copy(query, &uuid, sl_uuid_t);
+        offset += 16;
+    }
+
+    return count;
+}
+
+static int sl_unpack_floats(DALLOC_CTX *query, const char *buf, int offset, uint encoding)
+{
+    int count, i;
+    uint64_t query_data64;
+    double fval;
+    union {
+        double d;
+        uint32_t w[2];
+    } ieee_fp_union;
+
+    query_data64 = sl_unpack_uint64(buf, offset, encoding);
+    count = query_data64 >> 32;
+    offset += 8;
+
+    i = 0;
+    while (i++ < count) {
+        if (encoding == SL_ENC_LITTLE_ENDIAN) {
+#ifdef WORDS_BIGENDIAN
+            ieee_fp_union.w[0] = IVAL(buf, offset + 4);
+            ieee_fp_union.w[1] = IVAL(buf, offset);
+#else
+            ieee_fp_union.w[0] = IVAL(buf, offset);
+            ieee_fp_union.w[1] = IVAL(buf, offset + 4);
+#endif
+        } else {
+#ifdef WORDS_BIGENDIAN
+            ieee_fp_union.w[0] = RIVAL(buf, offset);
+            ieee_fp_union.w[1] = RIVAL(buf, offset + 4);
+#else
+            ieee_fp_union.w[0] = RIVAL(buf, offset + 4);
+            ieee_fp_union.w[1] = RIVAL(buf, offset);
+#endif
+        }
+        dalloc_add_copy(query, &ieee_fp_union.d, double);
+        offset += 8;
+    }
+
+    return count;
+}
+
+static int sl_unpack_CNID(DALLOC_CTX *query, const char *buf, int offset, int length, uint encoding)
+{
+    EC_INIT;
+    int count;
+    uint64_t query_data64;
+    sl_cnids_t *cnids;
+
+    EC_NULL( cnids = talloc_zero(query, sl_cnids_t) );
+    EC_NULL( cnids->ca_cnids = talloc_zero(cnids, DALLOC_CTX) );
+
+    if (length <= 16)
+        /* that's permitted, it's an empty array */
+        goto EC_CLEANUP;
+    
+    query_data64 = sl_unpack_uint64(buf, offset, encoding);
+    count = query_data64 & 0xffff;
+
+    cnids->ca_unkn1 = (query_data64 & 0xffff0000) >> 16;
+    cnids->ca_context = query_data64 >> 32;
+
+    offset += 8;
+
+    while (count --) {
+        query_data64 = sl_unpack_uint64(buf, offset, encoding);
+        dalloc_add_copy(cnids->ca_cnids, &query_data64, uint64_t);
+        offset += 8;
+    }
+
+    dalloc_add(query, cnids, sl_cnids_t);
+
+EC_CLEANUP:
+    EC_EXIT;
+}
+
+static const char *spotlight_get_qtype_string(uint64_t query_type)
+{
+    switch (query_type) {
+    case SQ_TYPE_NULL:
+        return "null";
+    case SQ_TYPE_COMPLEX:
+        return "complex";
+    case SQ_TYPE_INT64:
+        return "int64";
+    case SQ_TYPE_BOOL:
+        return "bool";
+    case SQ_TYPE_FLOAT:
+        return "float";
+    case SQ_TYPE_DATA:
+        return "data";
+    case SQ_TYPE_CNIDS:
+        return "CNIDs";
+    default:
+        return "unknown";
+    }
+}
+
+static const char *spotlight_get_cpx_qtype_string(uint64_t cpx_query_type)
+{
+    switch (cpx_query_type) {
+    case SQ_CPX_TYPE_ARRAY:
+        return "array";
+    case SQ_CPX_TYPE_STRING:
+        return "string";
+    case SQ_CPX_TYPE_UTF16_STRING:
+        return "utf-16 string";
+    case SQ_CPX_TYPE_DICT:
+        return "dictionary";
+    case SQ_CPX_TYPE_CNIDS:
+        return "CNIDs";
+    case SQ_CPX_TYPE_FILEMETA:
+        return "FileMeta";
+    default:
+        return "unknown";
+    }
+}
+
+static int sl_unpack_cpx(DALLOC_CTX *query,
+                         const char *buf,
+                         const int offset,
+                         uint cpx_query_type,
+                         uint cpx_query_count,
+                         const uint toc_offset,
+                         const uint encoding)
+{
+    EC_INIT;
+
+    int roffset = offset;
+    uint64_t query_data64;
+    uint unicode_encoding;
+    uint8_t mark_exists;
+    char *p, *tmp;
+    int qlen, used_in_last_block, slen;
+    sl_array_t *sl_array;
+    sl_dict_t *sl_dict;
+    sl_filemeta_t *sl_fm;
+
+    switch (cpx_query_type) {
+    case SQ_CPX_TYPE_ARRAY:
+        sl_array = talloc_zero(query, sl_array_t);
+        EC_NEG1_LOG( roffset = sl_unpack_loop(sl_array, buf, offset, cpx_query_count, toc_offset, encoding) );
+        dalloc_add(query, sl_array, sl_array_t);
+        break;
+
+    case SQ_CPX_TYPE_DICT:
+        sl_dict = talloc_zero(query, sl_dict_t);
+        EC_NEG1_LOG( roffset = sl_unpack_loop(sl_dict, buf, offset, cpx_query_count, toc_offset, encoding) );
+        dalloc_add(query, sl_dict, sl_dict_t);
+        break;
+
+    case SQ_CPX_TYPE_STRING:
+    case SQ_CPX_TYPE_UTF16_STRING:
+        query_data64 = sl_unpack_uint64(buf, offset, encoding);
+        qlen = (query_data64 & 0xffff) * 8;
+        used_in_last_block = query_data64 >> 32;
+        slen = qlen - 16 + used_in_last_block;
+
+        if (cpx_query_type == SQ_CPX_TYPE_STRING) {
+            p = dalloc_strndup(query, buf + offset + 8, slen);
+        } else {
+            unicode_encoding = spotlight_get_utf16_string_encoding(buf, offset + 8, slen, encoding);
+            mark_exists = (unicode_encoding & SL_ENC_UTF_16);
+            if (unicode_encoding & SL_ENC_BIG_ENDIAN)
+                EC_FAIL_LOG("Unsupported big endian UTF16 string");
+            slen -= mark_exists ? 2 : 0;
+            EC_NEG1( convert_string_allocate(CH_UCS2,
+                                             CH_UTF8,
+                                             buf + offset + (mark_exists ? 10 : 8),
+                                             slen,
+                                             &tmp) );
+            p = dalloc_strndup(query, tmp, strlen(tmp));
+            free(tmp);
+        }
+
+        dalloc_add(query, p, char *);
+        roffset += qlen;
+        break;
+
+    case SQ_CPX_TYPE_FILEMETA:
+        query_data64 = sl_unpack_uint64(buf, offset, encoding);
+        qlen = (query_data64 & 0xffff) * 8;
+        if (qlen <= 8) {
+            EC_FAIL_LOG("SQ_CPX_TYPE_FILEMETA: query_length <= 8: %d", qlen);
+        } else {
+            sl_fm = talloc_zero(query, sl_filemeta_t);
+            EC_NEG1_LOG( sl_unpack(sl_fm, buf + offset + 8) );
+            dalloc_add(query, sl_fm, sl_filemeta_t);
+        }
+        roffset += qlen;
+        break;
+
+    case SQ_CPX_TYPE_CNIDS:
+        query_data64 = sl_unpack_uint64(buf, offset, encoding);
+        qlen = (query_data64 & 0xffff) * 8;
+        EC_NEG1_LOG( sl_unpack_CNID(query, buf, offset + 8, qlen, encoding) );
+        roffset += qlen;
+        break;
+
+    default:
+        EC_FAIL;
+    }
+            
+EC_CLEANUP:
+    if (ret != 0)
+        roffset = -1;
+    return roffset;
+}
+
+static int sl_unpack_loop(DALLOC_CTX *query,
+                          const char *buf,
+                          int offset,
+                          uint count,
+                          const uint toc_offset,
+                          const uint encoding)
+{
+    EC_INIT;
+    int i, toc_index, query_length;
+    uint subcount;
+    uint64_t query_data64, query_type;
+    uint cpx_query_type, cpx_query_count;
+    sl_nil_t nil;
+    sl_bool_t b;
+
+    while (count > 0 && (offset < toc_offset)) {
+        query_data64 = sl_unpack_uint64(buf, offset, encoding);
+        query_length = (query_data64 & 0xffff) * 8;
+        query_type = (query_data64 & 0xffff0000) >> 16;
+        if (query_length == 0)
+            EC_FAIL;
+
+        switch (query_type) {
+        case SQ_TYPE_COMPLEX:
+            toc_index = (query_data64 >> 32) - 1;
+            query_data64 = sl_unpack_uint64(buf, toc_offset + toc_index * 8, encoding);
+            cpx_query_type = (query_data64 & 0xffff0000) >> 16;
+            cpx_query_count = query_data64 >> 32;
+
+            EC_NEG1_LOG( offset = sl_unpack_cpx(query, buf, offset + 8, cpx_query_type, cpx_query_count, toc_offset, encoding));
+            count--;
+            break;
+        case SQ_TYPE_NULL:
+            subcount = query_data64 >> 32;
+            if (subcount > 64)
+                EC_FAIL;
+            nil = 0;
+            for (i = 0; i < subcount; i++)
+                dalloc_add_copy(query, &nil, sl_nil_t);
+            offset += query_length;
+            count -= subcount;
+            break;
+        case SQ_TYPE_BOOL:
+            b = query_data64 >> 32;
+            dalloc_add_copy(query, &b, sl_bool_t);
+            offset += query_length;
+            count--;
+            break;
+        case SQ_TYPE_INT64:
+            EC_NEG1_LOG( subcount = sl_unpack_ints(query, buf, offset, encoding) );
+            offset += query_length;
+            count -= subcount;
+            break;
+        case SQ_TYPE_UUID:
+            EC_NEG1_LOG( subcount = sl_unpack_uuid(query, buf, offset, encoding) );
+            offset += query_length;
+            count -= subcount;
+            break;
+        case SQ_TYPE_FLOAT:
+            EC_NEG1_LOG( subcount = sl_unpack_floats(query, buf, offset, encoding) );
+            offset += query_length;
+            count -= subcount;
+            break;
+        case SQ_TYPE_DATE:
+            EC_NEG1_LOG( subcount = sl_unpack_date(query, buf, offset, encoding) );
+            offset += query_length;
+            count -= subcount;
+            break;
+        default:
+            EC_FAIL;
+        }
+    }
+
+EC_CLEANUP:
+    if (ret != 0) {
+        offset = -1;
+    }
+    return offset;
+}
+
+/**************************************************************************************************
+ * Global functions for packing und unpacking
+ **************************************************************************************************/
+
+int sl_pack(DALLOC_CTX *query, char *buf)
+{
+    EC_INIT;
+    char toc_buf[MAX_SLQ_TOC];
+    int toc_index = 0;
+    int len = 0;
+
+    memcpy(buf, "432130dm", 8);
+    EC_NEG1_LOG( len = sl_pack_loop(query, buf + 16, 0, toc_buf + 8, &toc_index) );
+    EC_ZERO( sivalc(buf, 8, MAX_SLQ_DAT, len / 8 + 1 + toc_index + 1) );
+    EC_ZERO( sivalc(buf, 12, MAX_SLQ_DAT, len / 8 + 1) );
+
+    EC_ZERO( slvalc(toc_buf, 0, MAX_SLQ_TOC, sl_pack_tag(SQ_TYPE_TOC, toc_index + 1, 0)) );
+    if ((16 + len + ((toc_index + 1 ) * 8)) >= MAX_SLQ_DAT)
+        EC_FAIL;
+    memcpy(buf + 16 + len, toc_buf, (toc_index + 1 ) * 8);
+    len += 16 + (toc_index + 1 ) * 8;
+
+EC_CLEANUP:
+    if (ret != 0)
+        len = -1;
+    return len;
+}
+
+int sl_unpack(DALLOC_CTX *query, const char *buf)
+{
+    EC_INIT;
+    int encoding, i, toc_entries;
+    uint64_t toc_offset, tquerylen, toc_entry;
+
+    if (strncmp(buf, "md031234", 8) == 0)
+        encoding = SL_ENC_BIG_ENDIAN;
+    else
+        encoding = SL_ENC_LITTLE_ENDIAN;
+
+    buf += 8;
+
+    toc_offset = ((sl_unpack_uint64(buf, 0, encoding) >> 32) - 1 ) * 8;
+    if (toc_offset < 0 || (toc_offset > 65000)) {
+        EC_FAIL;
+    }
+
+    buf += 8;
+
+    toc_entries = (int)(sl_unpack_uint64(buf, toc_offset, encoding) & 0xffff);
+
+    EC_NEG1( sl_unpack_loop(query, buf, 0, 1, toc_offset + 8, encoding) );
+
+EC_CLEANUP:
+    EC_EXIT;
+}
index 06e5a8c359134f366f170ae4258dab02f94ed0bb..6cf95eb6ccf7ed2b58cca9aac2d400f337914c27 100644 (file)
@@ -7,6 +7,7 @@ sbin_PROGRAMS = netatalk
 netatalk_SOURCES = netatalk.c
 netatalk_CFLAGS = \
        -D_PATH_CONFDIR=\"$(pkgconfdir)/\" \
+       -D_PATH_STATEDIR='"$(localstatedir)/netatalk/"' \
        -D_PATH_AFPD=\"$(sbindir)/afpd\" \
        -D_PATH_CNID_METAD=\"$(sbindir)/cnid_metad\"
 
index b3ab20a159a232c82f1ce739b6e89947b1b3484f..0e5c6554edcd0464cab4b8373a09f867b3b86ae5 100644 (file)
@@ -40,6 +40,8 @@
 #include <atalk/errchk.h>
 #include <atalk/globals.h>
 #include <atalk/netatalk_conf.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
 
 #include <event2/event.h>
 
@@ -52,11 +54,56 @@ static void kill_childs(int sig, ...);
 
 /* static variables */
 static AFPObj obj;
-static pid_t afpd_pid = -1,  cnid_metad_pid = -1;
-static uint afpd_restarts, cnid_metad_restarts;
+static pid_t afpd_pid = -1,  cnid_metad_pid = -1, dbus_pid = -1;
+static uint afpd_restarts, cnid_metad_restarts, dbus_restarts, trackerd_restarts;
 static struct event_base *base;
 struct event *sigterm_ev, *sigquit_ev, *sigchld_ev, *timer_ev;
 static int in_shutdown;
+static const char *dbus_path;
+static char *trackerd_loglev;
+
+/******************************************************************
+ * Misc stuff
+ ******************************************************************/
+
+/* Set Tracker Miners to index all our volumes */
+static int set_sl_volumes(void)
+{
+    EC_INIT;
+    const struct vol *volumes, *vol;
+    struct bstrList *vollist = bstrListCreate();
+    bstring sep = bfromcstr(", ");
+    bstring volnamelist = NULL, cmd = NULL;
+
+    EC_NULL_LOG( volumes = getvolumes() );
+
+    for (vol = volumes; vol; vol = vol->v_next) {
+        if (vol->v_flags & AFPVOL_SPOTLIGHT) {
+            bstring volnamequot = bformat("'%s'", vol->v_path);
+            bstrListPush(vollist, volnamequot);
+        }
+    }
+
+    volnamelist = bjoin(vollist, sep);
+    cmd = bformat("gsettings set org.freedesktop.Tracker.Miner.Files index-recursive-directories \"[%s]\"",
+                  bdata(volnamelist) ? bdata(volnamelist) : "");
+    LOG(log_debug, logtype_sl, "set_sl_volumes: %s", bdata(cmd));
+    system(bdata(cmd));
+
+    /* Disable default root user home indexing */
+    system("gsettings set org.freedesktop.Tracker.Miner.Files index-single-directories \"[]\"");
+
+EC_CLEANUP:
+    if (cmd)
+        bdestroy(cmd);
+    if (sep)
+        bdestroy(sep);
+    if (vollist)
+        bstrListDestroy(vollist);
+    if (volnamelist)
+        bdestroy(volnamelist);
+    EC_EXIT;
+}
 
 /******************************************************************
  * libevent helper functions
@@ -113,17 +160,23 @@ static void sigterm_cb(evutil_socket_t fd, short what, void *arg)
     event_del(sigquit_ev);
     event_del(timer_ev);
 
-    kill_childs(SIGTERM, &afpd_pid, &cnid_metad_pid, NULL);
+#ifdef HAVE_TRACKER_SPARQL
+    system("tracker-control -t");
+#endif
+    kill_childs(SIGTERM, &afpd_pid, &cnid_metad_pid, &dbus_pid, NULL);
 }
 
 /* SIGQUIT callback */
 static void sigquit_cb(evutil_socket_t fd, short what, void *arg)
 {
     LOG(log_note, logtype_afpd, "Exiting on SIGQUIT");
-    kill_childs(SIGQUIT, &afpd_pid, &cnid_metad_pid, NULL);
+#ifdef HAVE_TRACKER_SPARQL
+    system("tracker-control -t");
+#endif
+    kill_childs(SIGQUIT, &afpd_pid, &cnid_metad_pid, &dbus_pid, NULL);
 }
 
-/* SIGQUIT callback */
+/* SIGHUP callback */
 static void sighup_cb(evutil_socket_t fd, short what, void *arg)
 {
     LOG(log_note, logtype_afpd, "Received SIGHUP, sending all processes signal to reload config");
@@ -136,30 +189,30 @@ static void sigchld_cb(evutil_socket_t fd, short what, void *arg)
     int status;
     pid_t pid;
 
-    LOG(log_debug, logtype_afpd, "Got SIGCHLD event");
-  
     while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
         if (WIFEXITED(status)) {
             if (WEXITSTATUS(status))
-                LOG(log_info, logtype_afpd, "child[%d]: exited %d", pid, WEXITSTATUS(status));
+                LOG(log_info, logtype_default, "child[%d]: exited %d", pid, WEXITSTATUS(status));
             else
-                LOG(log_info, logtype_afpd, "child[%d]: done", pid);
+                LOG(log_info, logtype_default, "child[%d]: done", pid);
         } else {
             if (WIFSIGNALED(status))
-                LOG(log_info, logtype_afpd, "child[%d]: killed by signal %d", pid, WTERMSIG(status));
+                LOG(log_info, logtype_default, "child[%d]: killed by signal %d", pid, WTERMSIG(status));
             else
-                LOG(log_info, logtype_afpd, "child[%d]: died", pid);
+                LOG(log_info, logtype_default, "child[%d]: died", pid);
         }
 
         if (pid == afpd_pid)
             afpd_pid = -1;
         else if (pid == cnid_metad_pid)
             cnid_metad_pid = -1;
+        else if (pid == dbus_pid)
+            dbus_pid = -1;
         else
             LOG(log_error, logtype_afpd, "Bad pid: %d", pid);
     }
 
-    if (in_shutdown && afpd_pid == -1 && cnid_metad_pid == -1)
+    if (in_shutdown && afpd_pid == -1 && cnid_metad_pid == -1 && dbus_pid == -1)
         event_base_loopbreak(base);
 }
 
@@ -173,7 +226,7 @@ static void timer_cb(evutil_socket_t fd, short what, void *arg)
         afpd_restarts++;
         LOG(log_note, logtype_afpd, "Restarting 'afpd' (restarts: %u)", afpd_restarts);
         if ((afpd_pid = run_process(_PATH_AFPD, "-d", "-F", obj.options.configfile, NULL)) == -1) {
-            LOG(log_error, logtype_afpd, "Error starting 'afpd'");
+            LOG(log_error, logtype_default, "Error starting 'afpd'");
         }
     }
 
@@ -181,9 +234,19 @@ static void timer_cb(evutil_socket_t fd, short what, void *arg)
         cnid_metad_restarts++;
         LOG(log_note, logtype_afpd, "Restarting 'cnid_metad' (restarts: %u)", cnid_metad_restarts);
         if ((cnid_metad_pid = run_process(_PATH_CNID_METAD, "-d", "-F", obj.options.configfile, NULL)) == -1) {
-            LOG(log_error, logtype_afpd, "Error starting 'cnid_metad'");
+            LOG(log_error, logtype_default, "Error starting 'cnid_metad'");
         }
     }
+
+#ifdef HAVE_TRACKER
+    if (dbus_pid == -1) {
+        dbus_restarts++;
+        LOG(log_note, logtype_afpd, "Restarting 'dbus' (restarts: %u)", dbus_restarts);
+        if ((dbus_pid = run_process(dbus_path, "--config-file=" _PATH_CONFDIR "dbus.session.conf", NULL)) == -1) {
+            LOG(log_error, logtype_default, "Error starting '%s'", dbus_path);
+        }
+    }
+#endif
 }
 
 /******************************************************************
@@ -217,7 +280,8 @@ static void netatalk_exit(int ret)
 static pid_t run_process(const char *path, ...)
 {
     int ret, i = 0;
-    char *myargv[10];
+#define MYARVSIZE 64
+    char *myargv[MYARVSIZE];
     va_list args;
     pid_t pid;
 
@@ -229,8 +293,10 @@ static pid_t run_process(const char *path, ...)
     if (pid == 0) {
         myargv[i++] = (char *)path;
         va_start(args, path);
-        while ((myargv[i++] = va_arg(args, char *)) != NULL)
-            ;
+        while (i < MYARVSIZE) {
+            if ((myargv[i++] = va_arg(args, char *)) == NULL)
+                break;
+        }
         va_end(args);
 
         ret = execv(path, myargv);
@@ -285,6 +351,8 @@ int main(int argc, char **argv)
     if (afp_config_parse(&obj, "netatalk") != 0)
         netatalk_exit(EXITERR_CONF);
 
+    load_volumes(&obj);
+
     event_set_log_callback(libevent_logmsg_cb);
     event_set_fatal_callback(netatalk_exit);
 
@@ -326,15 +394,45 @@ int main(int argc, char **argv)
     sigdelset(&blocksigs, SIGHUP);
     sigprocmask(SIG_SETMASK, &blocksigs, NULL);
 
+#ifdef HAVE_TRACKER
+    setenv("DBUS_SESSION_BUS_ADDRESS", "unix:path=" _PATH_STATEDIR "spotlight.ipc", 1);
+    setenv("XDG_DATA_HOME", _PATH_STATEDIR, 0);
+    setenv("XDG_CACHE_HOME", _PATH_STATEDIR, 0);
+    setenv("TRACKER_USE_LOG_FILES", "1", 0);
+
+    dbus_path = atalk_iniparser_getstring(obj.iniconfig, INISEC_GLOBAL, "dbus daemon", DBUS_DAEMON_PATH);
+    LOG(log_debug, logtype_default, "DBUS: '%s'", dbus_path);
+    if ((dbus_pid = run_process(dbus_path, "--config-file=" _PATH_CONFDIR "dbus-session.conf", NULL)) == -1) {
+        LOG(log_error, logtype_default, "Error starting '%s'", dbus_path);
+        netatalk_exit(EXITERR_CONF);
+    }
+
+    /* Allow dbus some time to start up */
+    sleep(1);
+#endif
+
+#ifdef HAVE_TRACKER_SPARQL
+#ifdef SOLARIS
+    setenv("XDG_DATA_DIRS", TRACKER_PREFIX "/share", 0);
+    setenv("TRACKER_DB_ONTOLOGIES_DIR", TRACKER_PREFIX "/share/tracker/ontologies", 0);
+    setenv("TRACKER_EXTRACTOR_RULES_DIR", TRACKER_PREFIX "/share/tracker/extract-rules", 0);
+    setenv("TRACKER_LANGUAGE_STOPWORDS_DIR", TRACKER_PREFIX "/share/tracker/languages", 0);
+#endif
+    set_sl_volumes();
+    system(TRACKER_PREFIX "/bin/tracker-control -s");
+#endif
+
     /* run the event loop */
     ret = event_base_dispatch(base);
 
-    if (afpd_pid != -1 || cnid_metad_pid != -1) {
+    if (afpd_pid != -1 || cnid_metad_pid != -1 || dbus_pid != -1) {
         if (afpd_pid != -1)
             LOG(log_error, logtype_afpd, "AFP service did not shutdown, killing it");
         if (cnid_metad_pid != -1)
             LOG(log_error, logtype_afpd, "CNID database service did not shutdown, killing it");
-        kill_childs(SIGKILL, &afpd_pid, &cnid_metad_pid, NULL);
+        if (dbus_pid != -1)
+            LOG(log_error, logtype_afpd, "DBUS session daemon still running, killing it");
+        kill_childs(SIGKILL, &afpd_pid, &cnid_metad_pid, &dbus_pid, NULL);
     }
 
     LOG(log_note, logtype_afpd, "Netatalk AFP server exiting");
diff --git a/etc/spotlight/.gitignore b/etc/spotlight/.gitignore
new file mode 100644 (file)
index 0000000..4babbdf
--- /dev/null
@@ -0,0 +1,10 @@
+Makefile
+Makefile.in
+spot
+stp
+srp
+.deps
+.libs
+*.o
+*.la
+*.lo
\ No newline at end of file
diff --git a/etc/spotlight/Makefile.am b/etc/spotlight/Makefile.am
new file mode 100644 (file)
index 0000000..7fd2721
--- /dev/null
@@ -0,0 +1,38 @@
+# Makefile.am for etc/spotlight/
+
+pkgconfdir = @PKGCONFDIR@
+moduledir = @UAMS_PATH@
+module_LTLIBRARIES =
+noinst_PROGRAMS =
+noinst_HEADERS = slmod_sparql_map.h
+BUILT_SOURCES =
+
+AM_YFLAGS = -d
+
+if HAVE_TRACKER_SPARQL
+BUILT_SOURCES += slmod_sparql_parser.h
+noinst_PROGRAMS += srp
+module_LTLIBRARIES += slmod_sparql.la
+
+slmod_sparql_la_SOURCES = \
+       slmod_sparql.c \
+       slmod_sparql_map.c \
+       slmod_sparql_parser.y \
+       spotlight_rawquery_lexer.l
+
+slmod_sparql_la_CFLAGS  = \
+       -DDBUS_API_SUBJECT_TO_CHANGE \
+       @TRACKER_CFLAGS@ \
+       @TRACKER_MINER_CFLAGS@ \
+       -D_PATH_STATEDIR='"$(localstatedir)/netatalk"'
+
+slmod_sparql_la_LDFLAGS = -module -avoid-version @TRACKER_LIBS@ @TRACKER_MINER_LIBS@
+
+srp_SOURCES = \
+       slmod_sparql_map.c \
+       slmod_sparql_parser.y \
+       spotlight_rawquery_lexer.l
+
+srp_CFLAGS = -DMAIN -I$(top_srcdir)/include @TRACKER_CFLAGS@
+srp_LDADD = $(top_builddir)/libatalk/libatalk.la
+endif
diff --git a/etc/spotlight/slmod_sparql.c b/etc/spotlight/slmod_sparql.c
new file mode 100644 (file)
index 0000000..11b1dac
--- /dev/null
@@ -0,0 +1,429 @@
+/*
+  Copyright (c) 2012 Frank Lahm <franklahm@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.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <locale.h>
+
+#include <gio/gio.h>
+#include <tracker-sparql.h>
+#include <libtracker-miner/tracker-miner.h>
+
+#include <atalk/util.h>
+#include <atalk/errchk.h>
+#include <atalk/logger.h>
+#include <atalk/unix.h>
+#include <atalk/spotlight.h>
+
+#include "slmod_sparql_parser.h"
+
+#define MAX_SL_RESULTS 20
+
+static TrackerSparqlConnection *connection;
+static TrackerMinerManager *manager;
+
+static char *tracker_to_unix_path(const char *uri)
+{
+    EC_INIT;
+    GFile *f = NULL;
+    char *path = NULL;
+
+    EC_NULL_LOG( f = g_file_new_for_uri(uri) );
+    EC_NULL_LOG( path = g_file_get_path(f) );
+
+EC_CLEANUP:
+    if (f)
+        g_object_unref(f);
+    if (ret != 0)
+        return NULL;
+    return path;
+}
+
+static int sl_mod_init(void *p)
+{
+    EC_INIT;
+    GError *error = NULL;
+    const char *msg = p;
+
+    LOG(log_info, logtype_sl, "Initializing Spotlight module");
+
+    g_type_init();
+    setenv("DBUS_SESSION_BUS_ADDRESS", "unix:path=" _PATH_STATEDIR "/spotlight.ipc", 1);
+    setenv("TRACKER_SPARQL_BACKEND", "bus", 1);
+
+#ifdef DEBUG
+    setenv("TRACKER_VERBOSITY", "3", 1);
+    dup2(type_configs[logtype_sl].fd, 1);
+    dup2(type_configs[logtype_sl].fd, 2);
+#endif
+
+    become_root();
+    connection = tracker_sparql_connection_get(NULL, &error);
+    manager = tracker_miner_manager_new_full(FALSE, &error);
+    unbecome_root();
+
+    if (!connection) {
+        LOG(log_error, logtype_sl, "Couldn't obtain a direct connection to the Tracker store: %s",
+            error ? error->message : "unknown error");
+        g_clear_error(&error);
+        EC_FAIL;
+    }
+
+    if (!manager) {
+        LOG(log_error, logtype_sl, "Couldn't connect to Tracker miner");
+        g_clear_error(&error);
+        EC_FAIL;
+    }
+
+EC_CLEANUP:
+    EC_EXIT;
+}
+
+
+static void tracker_cb(GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+    slq_t *slq = user_data;
+    TrackerSparqlCursor *cursor;
+    GError *error = NULL;
+
+    LOG(log_debug, logtype_sl, "tracker_cb");
+
+    cursor = tracker_sparql_connection_query_finish(connection, res, &error);
+
+    if (error) {
+        LOG(log_error, logtype_sl, "sl_mod_fetch_result: Couldn't query the Tracker Store: '%s'",
+            error ? error->message : "unknown error");
+        g_clear_error(&error);
+        return;
+    }
+
+    slq->slq_tracker_cursor = cursor;
+}
+
+static int sl_mod_start_search(void *p)
+{
+    EC_INIT;
+    slq_t *slq = p; 
+    gchar *sparql_query;
+    GError *error = NULL;
+
+    LOG(log_debug, logtype_sl, "sl_mod_start_search: Spotlight query string: \"%s\"", slq->slq_qstring);
+
+    EC_ZERO_LOGSTR( map_spotlight_to_sparql_query(slq, &sparql_query),
+                    "Mapping Spotlight query failed: \"%s\"", slq->slq_qstring );
+    LOG(log_debug, logtype_sl, "sl_mod_start_search: SPARQL query: \"%s\"", sparql_query);
+
+#if 0
+    /* Start the async query */
+    tracker_sparql_connection_query_async(connection, sparql_query, NULL, tracker_cb, slq);
+#endif
+
+    become_root();
+    slq->slq_tracker_cursor = tracker_sparql_connection_query(connection, sparql_query, NULL, &error);
+    unbecome_root();
+
+    if (error) {
+        LOG(log_error, logtype_sl, "Couldn't query the Tracker Store: '%s'",
+            error ? error->message : "unknown error");
+        g_clear_error(&error);
+        EC_FAIL;
+    }
+    slq->slq_state = SLQ_STATE_RUNNING;
+
+EC_CLEANUP:
+    EC_EXIT;
+}
+
+static int add_filemeta(sl_array_t *reqinfo, sl_array_t *fm_array, cnid_t id, const char *path, const struct stat *sp)
+{
+    EC_INIT;
+    sl_array_t *meta;
+    sl_nil_t nil = 0;
+    int i, metacount;
+    uint64_t uint64;
+    sl_time_t sl_time;
+
+    if ((metacount = talloc_array_length(reqinfo->dd_talloc_array)) == 0) {
+        dalloc_add_copy(fm_array, &nil, sl_nil_t);
+        goto EC_CLEANUP;
+    }
+
+    LOG(log_debug, logtype_sl, "add_filemeta: metadata count: %d", metacount);
+
+    meta = talloc_zero(fm_array, sl_array_t);
+
+    for (i = 0; i < metacount; i++) {
+        if (STRCMP(reqinfo->dd_talloc_array[i], ==, "kMDItemDisplayName") ||
+            STRCMP(reqinfo->dd_talloc_array[i], ==, "kMDItemFSName")) {
+            char *p, *name;
+            if ((p = strrchr(path, '/'))) {
+                name = dalloc_strdup(meta, p + 1);
+                dalloc_add(meta, name, "char *");
+            }
+        } else if (STRCMP(reqinfo->dd_talloc_array[i], ==, "kMDItemFSSize")) {
+            uint64 = sp->st_size;
+            dalloc_add_copy(meta, &uint64, uint64_t);
+        } else if (STRCMP(reqinfo->dd_talloc_array[i], ==, "kMDItemFSOwnerUserID")) {
+            uint64 = sp->st_uid;
+            dalloc_add_copy(meta, &uint64, uint64_t);
+        } else if (STRCMP(reqinfo->dd_talloc_array[i], ==, "kMDItemFSOwnerGroupID")) {
+            uint64 = sp->st_gid;
+            dalloc_add_copy(meta, &uint64, uint64_t);
+        } else if (STRCMP(reqinfo->dd_talloc_array[i], ==, "kMDItemFSContentChangeDate")) {
+            sl_time.tv_sec = sp->st_mtime;
+            dalloc_add_copy(meta, &sl_time, sl_time_t);
+        } else {
+            dalloc_add_copy(meta, &nil, sl_nil_t);
+        }
+    }
+
+    dalloc_add(fm_array, meta, sl_array_t);
+
+EC_CLEANUP:
+    EC_EXIT;
+}
+
+static int cnid_cmp_fn(const void *p1, const void *p2)
+{
+    const uint64_t *cnid1 = p1, *cnid2 = p2;
+    if (*cnid1 == *cnid2)
+        return 0;
+    if (*cnid1 < *cnid2)
+        return -1;
+    else
+        return 1;            
+}
+
+static int sl_mod_fetch_result(void *p)
+{
+    EC_INIT;
+    slq_t *slq = p;
+    GError *error = NULL;
+    int i = 0;
+    cnid_t did, id;
+    const gchar *uri;
+    char *path;
+    sl_cnids_t *cnids;
+    sl_filemeta_t *fm;
+    sl_array_t *fm_array;
+    sl_nil_t nil;
+    uint64_t uint64;
+    gboolean qres, firstmatch = true;
+
+    if (!slq->slq_tracker_cursor) {
+        LOG(log_debug, logtype_sl, "sl_mod_fetch_result: no results found");
+        goto EC_CLEANUP;
+    }
+
+    /* Prepare CNIDs */
+    cnids = talloc_zero(slq->slq_reply, sl_cnids_t);
+    cnids->ca_cnids = talloc_zero(cnids, DALLOC_CTX);
+    cnids->ca_unkn1 = 0xadd;
+    cnids->ca_context = slq->slq_ctx2;
+
+    /* Prepare FileMeta */
+    fm = talloc_zero(slq->slq_reply, sl_filemeta_t);
+    fm_array = talloc_zero(fm, sl_array_t);
+    /* For some reason the list of results always starts with a nil entry */
+    dalloc_add_copy(fm_array, &nil, sl_nil_t);
+    dalloc_add(fm, fm_array, sl_array_t);
+
+    LOG(log_debug, logtype_sl, "sl_mod_fetch_result: now interating Tracker results cursor");
+
+    while ((slq->slq_state == SLQ_STATE_RUNNING) && (i <= MAX_SL_RESULTS)) {
+        become_root();
+        qres = tracker_sparql_cursor_next(slq->slq_tracker_cursor, NULL, &error);
+        unbecome_root();
+
+        if (!qres)
+            break;
+
+#if 0
+        if (firstmatch) {
+            /* For some reason the list of results always starts with a nil entry */
+            dalloc_add_copy(fm_array, &nil, sl_nil_t);
+            firstmatch = false;
+        }
+#endif
+
+        become_root();
+        uri = tracker_sparql_cursor_get_string(slq->slq_tracker_cursor, 0, NULL);
+        unbecome_root();
+
+        EC_NULL_LOG( path = tracker_to_unix_path(uri) );
+
+        if ((id = cnid_for_path(slq->slq_vol->v_cdb, slq->slq_vol->v_path, path, &did)) == CNID_INVALID) {
+            LOG(log_debug, logtype_sl, "sl_mod_fetch_result: cnid_for_path error: %s", path);
+            goto loop_cleanup;
+        }
+        LOG(log_debug, logtype_sl, "Result %d: CNID: %" PRIu32 ", path: \"%s\"", i, ntohl(id), path);
+
+        uint64 = ntohl(id);
+        if (slq->slq_cnids) {
+            if (!bsearch(&uint64, slq->slq_cnids, slq->slq_cnids_num, sizeof(uint64_t), cnid_cmp_fn))
+                goto loop_cleanup;
+        }
+
+        struct stat sb;
+        if (stat(path, &sb) != 0)
+            goto loop_cleanup;
+
+        dalloc_add_copy(cnids->ca_cnids, &uint64, uint64_t);
+        add_filemeta(slq->slq_reqinfo, fm_array, id, path, &sb);
+
+    loop_cleanup:
+        g_free(path);
+        i++;
+   }
+
+    if (error) {
+        LOG(log_error, logtype_sl, "Couldn't query the Tracker Store: '%s'",
+            error ? error->message : "unknown error");
+        g_clear_error (&error);
+        EC_FAIL;
+    }
+
+    if (i < MAX_SL_RESULTS)
+        slq->slq_state = SLQ_STATE_DONE;
+
+    uint64 = (i > 0) ? 35 : 0; /* OS X AFP server returns 35 here if results are found */
+    dalloc_add_copy(slq->slq_reply, &uint64, uint64_t);
+    dalloc_add(slq->slq_reply, cnids, sl_cnids_t);
+    dalloc_add(slq->slq_reply, fm, sl_filemeta_t);
+
+EC_CLEANUP:
+    if (ret != 0) {
+        if (slq->slq_tracker_cursor) {
+            g_object_unref(slq->slq_tracker_cursor);
+            slq->slq_tracker_cursor = NULL;
+        }
+    }
+    EC_EXIT;
+}
+
+/* Free ressources allocated in this module */
+static int sl_mod_close_query(void *p)
+{
+    EC_INIT;
+    slq_t *slq = p;
+
+    if (slq->slq_tracker_cursor) {
+        g_object_unref(slq->slq_tracker_cursor);
+        slq->slq_tracker_cursor = NULL;
+    }
+
+EC_CLEANUP:
+    EC_EXIT;
+}
+
+static int sl_mod_fetch_attrs(void *p)
+{
+    EC_INIT;
+    slq_t *slq = p;
+    sl_filemeta_t *fm;
+    sl_array_t *fm_array;
+    sl_nil_t nil;
+
+    LOG(log_debug, logtype_sl, "sl_mod_fetch_attrs(\"%s\")", slq->slq_path);
+
+    struct stat sb;
+    EC_ZERO( stat(slq->slq_path, &sb) );
+
+    /* Prepare FileMeta */
+    fm = talloc_zero(slq->slq_reply, sl_filemeta_t);
+    fm_array = talloc_zero(fm, sl_array_t);
+    dalloc_add(fm, fm_array, fm_array_t);
+    /* For some reason the list of results always starts with a nil entry */
+    dalloc_add_copy(fm_array, &nil, sl_nil_t);
+
+    add_filemeta(slq->slq_reqinfo, fm_array, CNID_INVALID, slq->slq_path, &sb);
+
+    /* Now add result */
+    dalloc_add(slq->slq_reply, fm, sl_filemeta_t);
+
+EC_CLEANUP:
+    EC_EXIT;
+}
+
+static int sl_mod_error(void *p)
+{
+    EC_INIT;
+    slq_t *slq = p;
+
+    if (!slq)
+        goto EC_CLEANUP;
+
+    if (slq->slq_tracker_cursor) {
+        g_object_unref(slq->slq_tracker_cursor);
+        slq->slq_tracker_cursor = NULL;
+    }
+
+EC_CLEANUP:
+    EC_EXIT;
+}
+
+static int sl_mod_index_file(const void *p)
+{
+    /*
+     * This seems to cause index problems on volumes that are watched and indexed
+     * by Tracker, so we disable this extra manual indexing for now.
+     * It's primary pupose was ensuring files created via AFP are indexed on large
+     * volumes where the filesystem event notification engine (eg FAM or FEN) may
+     * impose limits on the maximum number of watched directories.
+     */
+    return 0;
+
+#ifdef HAVE_TRACKER_MINER
+    EC_INIT;
+    const char *f = p;
+
+    if (!f)
+        goto EC_CLEANUP;
+
+    GError *error = NULL;
+    GFile *file = NULL;
+
+    file = g_file_new_for_commandline_arg(f);
+
+    become_root();
+    tracker_miner_manager_index_file(manager, file, &error);
+    unbecome_root();
+
+    if (error)
+        LOG(log_error, logtype_sl, "sl_mod_index_file(\"%s\"): indexing failed", f);
+    else
+        LOG(log_debug, logtype_sl, "sl_mod_index_file(\"%s\"): indexing file was successful", f);
+
+EC_CLEANUP:
+    if (file)
+        g_object_unref(file);
+    EC_EXIT;
+#else
+    return 0;
+#endif
+}
+
+struct sl_module_export sl_mod = {
+    SL_MODULE_VERSION,
+    sl_mod_init,
+    sl_mod_start_search,
+    sl_mod_fetch_result,
+    sl_mod_close_query,
+    sl_mod_fetch_attrs,
+    sl_mod_error,
+    sl_mod_index_file
+};
diff --git a/etc/spotlight/slmod_sparql_map.c b/etc/spotlight/slmod_sparql_map.c
new file mode 100644 (file)
index 0000000..49296d7
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+  Copyright (c) 2012 Frank Lahm <franklahm@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.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <unistd.h>
+
+#include "slmod_sparql_map.h"
+
+#define NOTSUPPORTED NULL
+#define SPECIAL      NULL
+
+struct spotlight_sparql_map spotlight_sparql_map[] = {
+    /* ssm_spotlight_attr               ssm_type,   ssm_sparql_attr */
+    {"*",                               ssmt_fts,   "fts:match"},
+
+    /* Filesystem metadata */
+    {"kMDItemFSLabel",                  ssmt_num,   NOTSUPPORTED},
+    {"kMDItemDisplayName",              ssmt_str,   "nfo:fileName"},
+    {"kMDItemFSName",                   ssmt_str,   "nfo:fileName"},
+    {"kMDItemFSContentChangeDate",      ssmt_date,  "nfo:fileLastModified"},
+
+    /* Common metadata */
+    {"kMDItemTextContent",              ssmt_fts,   "fts:match"},
+    {"kMDItemContentCreationDate",      ssmt_date,  "nie:contentCreated"},
+    {"kMDItemContentModificationDate",  ssmt_date,  "nfo:fileLastModified"},
+    {"kMDItemAttributeChangeDate",      ssmt_date,  "nfo:fileLastModified"},
+    {"kMDItemAuthors",                  ssmt_str,   "dc:creator"},
+    {"kMDItemCopyright",                ssmt_str,   "nie:copyright"},
+    {"kMDItemCountry",                  ssmt_str,   "nco:country"},
+    {"kMDItemCreator",                  ssmt_str,   "dc:creator"},
+    {"kMDItemDurationSeconds",          ssmt_num,   "nfo:duration"},
+    {"kMDItemNumberOfPages",            ssmt_num,   "nfo:pageCount"},
+    {"kMDItemTitle",                    ssmt_str,   "nie:title"},
+    {"_kMDItemGroupId",                 ssmt_type,  SPECIAL},
+    {"kMDItemContentTypeTree",          ssmt_type,  SPECIAL},
+
+    /* Image metadata */
+    {"kMDItemPixelWidth",               ssmt_num,   "nfo:width"},
+    {"kMDItemPixelHeight",              ssmt_num,   "nfo:height"},
+    {"kMDItemColorSpace",               ssmt_str,   "nexif:colorSpace"},
+    {"kMDItemBitsPerSample",            ssmt_num,   "nfo:colorDepth"},
+    {"kMDItemFocalLength",              ssmt_num,   "nmm:focalLength"},
+    {"kMDItemISOSpeed",                 ssmt_num,   "nmm:isoSpeed"},
+    {"kMDItemOrientation",              ssmt_bool,  "nfo:orientation"},
+    {"kMDItemResolutionWidthDPI",       ssmt_num,   "nfo:horizontalResolution"},
+    {"kMDItemResolutionHeightDPI",      ssmt_num,   "nfo:verticalResolution"},
+    {"kMDItemExposureTimeSeconds",      ssmt_num,   "nmm:exposureTime"},
+
+    /* Audio metadata */
+    {"kMDItemComposer",                 ssmt_str,   "nmm:composer"},
+    {"kMDItemMusicalGenre",             ssmt_str,   "nfo:genre"},
+
+    {NULL, ssmt_str, NULL}
+};
+
+struct MDTypeMap MDTypeMap[] = {
+    {"1",                       kMDTypeMapRDF,      "http://www.semanticdesktop.org/ontologies/2007/03/22/nmo#Email"},
+    {"2",                       kMDTypeMapRDF,      "http://www.semanticdesktop.org/ontologies/2007/03/22/nco#Contact"},
+    {"3",                       kMDTypeMapNotSup,   NULL}, /* PrefPane */
+    {"4",                       kMDTypeMapRDF,      "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Font"},
+    {"5",                       kMDTypeMapRDF,      "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Bookmark"},
+    {"6",                       kMDTypeMapRDF,      "http://www.semanticdesktop.org/ontologies/2007/03/22/nco#Contact"},
+    {"7",                       kMDTypeMapRDF,      "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Video"},
+    {"8",                       kMDTypeMapRDF,      "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Executable"},
+    {"9",                       kMDTypeMapRDF,      "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Folder"},
+    {"10",                      kMDTypeMapRDF,      "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Audio"},
+    {"11",                      kMDTypeMapMime,     "application/pdf"},
+    {"12",                      kMDTypeMapRDF,      "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Presentation"},
+    {"13",                      kMDTypeMapRDF,      "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Image"},
+    {"public.jpeg",             kMDTypeMapMime,     "image/jpeg"},
+    {"public.tiff",             kMDTypeMapMime,     "image/tiff"},
+    {"com.compuserve.gif",      kMDTypeMapMime,     "image/gif"},
+    {"public.png",              kMDTypeMapMime,     "image/png"},
+    {"com.microsoft.bmp",       kMDTypeMapMime,     "image/bmp"},
+    {"public.content",          kMDTypeMapRDF,      "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Document"},
+    {"public.mp3",              kMDTypeMapMime,     "audio/mpeg"},
+    {"public.mpeg-4-audio",     kMDTypeMapMime,     "audio/x-aac"},
+    {"com.apple.application",   kMDTypeMapRDF,      "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Software"},
+    {"public.text",             kMDTypeMapRDF,      "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#TextDocument"},
+    {"public.plain-text",       kMDTypeMapMime,     "text/plain"},
+    {"public.rtf",              kMDTypeMapMime,     "text/rtf"},
+    {"public.html",             kMDTypeMapMime,     "text/html"},
+    {"public.xml",              kMDTypeMapMime,     "text/xml"},
+    {"public.source-code",      kMDTypeMapRDF,      "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#SourceCode"},
+    {NULL,                      kMDTypeMapNotSup,   NULL}
+};
diff --git a/etc/spotlight/slmod_sparql_map.h b/etc/spotlight/slmod_sparql_map.h
new file mode 100644 (file)
index 0000000..250894b
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+  Copyright (c) 2012 Frank Lahm <franklahm@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.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#ifndef SPOTLIGHT_SPARQL_MAP_H
+#define SPOTLIGHT_SPARQL_MAP_H
+
+enum ssm_type {
+    ssmt_bool,   /* a boolean value that doesn't requires a SPARQL FILTER */
+    ssmt_num,    /* a numeric value that requires a SPARQL FILTER */
+    ssmt_str,    /* a string value that requieres a SPARQL FILTER */
+    ssmt_fts,    /* a string value that will be queried with SPARQL 'fts:match' */
+    ssmt_date,   /* date values are handled in a special map function map_daterange() */
+    ssmt_type    /* kMDItemContentType, requires special mapping */
+};
+
+enum kMDTypeMap {
+    kMDTypeMapNotSup,           /* not supported */
+    kMDTypeMapRDF,              /* query with rdf:type */
+    kMDTypeMapMime              /* query with nie:mimeType */
+};
+
+struct spotlight_sparql_map {
+    const char *ssm_spotlight_attr;
+    enum ssm_type ssm_type;
+    const char *ssm_sparql_attr;
+};
+
+struct MDTypeMap {
+    const char *mdtm_value;     /* MD query value of attributes '_kMDItemGroupId' and 'kMDItemContentTypeTree' */
+    enum kMDTypeMap mdtm_type;   /* whether SPARQL query must search attribute rdf:type or nie:mime_Type */
+    const char *mdtm_sparql;    /* the SPARQL query match string */
+};
+
+extern struct spotlight_sparql_map spotlight_sparql_map[];
+extern struct spotlight_sparql_map spotlight_sparql_date_map[];
+extern struct MDTypeMap MDTypeMap[];
+#endif
diff --git a/etc/spotlight/slmod_sparql_parser.c b/etc/spotlight/slmod_sparql_parser.c
new file mode 100644 (file)
index 0000000..d4082fd
--- /dev/null
@@ -0,0 +1,2037 @@
+/* A Bison parser, made by GNU Bison 2.7.  */
+
+/* Bison implementation for Yacc-like parsers in C
+   
+      Copyright (C) 1984, 1989-1990, 2000-2012 Free Software Foundation, Inc.
+   
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+   
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+   simplifying the original so-called "semantic" parser.  */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+   infringing on user name space.  This should be done even for local
+   variables, as they might otherwise be expanded by user macros.
+   There are some unavoidable exceptions within include files to
+   define necessary library symbols; they are noted "INFRINGES ON
+   USER NAME SPACE" below.  */
+
+/* Identify Bison output.  */
+#define YYBISON 1
+
+/* Bison version.  */
+#define YYBISON_VERSION "2.7"
+
+/* Skeleton name.  */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers.  */
+#define YYPURE 0
+
+/* Push parsers.  */
+#define YYPUSH 0
+
+/* Pull parsers.  */
+#define YYPULL 1
+
+
+
+
+/* Copy the first part of user declarations.  */
+/* Line 371 of yacc.c  */
+#line 1 "slmod_sparql_parser.y"
+
+  #include <atalk/standards.h>
+
+  #include <stdbool.h>
+  #include <stdio.h>
+  #include <string.h>
+  #include <time.h>
+
+  #include <gio/gio.h>
+
+  #include <atalk/talloc.h>
+  #include <atalk/logger.h>
+  #include <atalk/errchk.h>
+  #include <atalk/spotlight.h>
+
+  #include "slmod_sparql_map.h"
+
+  struct yy_buffer_state;
+  typedef struct yy_buffer_state *YY_BUFFER_STATE;
+  extern int yylex (void);
+  extern void yyerror (char const *);
+  extern void *yyterminate(void);
+  extern YY_BUFFER_STATE yy_scan_string( const char *str);
+  extern void yy_delete_buffer ( YY_BUFFER_STATE buffer );
+
+  /* forward declarations */
+  static const char *map_expr(const char *attr, char op, const char *val);
+  static const char *map_daterange(const char *dateattr, time_t date1, time_t date2);
+  static time_t isodate2unix(const char *s);
+ /* global vars, eg needed by the lexer */
+  slq_t *ssp_slq;
+
+  /* local vars */
+  static gchar *ssp_result;
+  static char sparqlvar;
+
+/* Line 371 of yacc.c  */
+#line 106 "slmod_sparql_parser.c"
+
+# ifndef YY_NULL
+#  if defined __cplusplus && 201103L <= __cplusplus
+#   define YY_NULL nullptr
+#  else
+#   define YY_NULL 0
+#  endif
+# endif
+
+/* Enabling verbose error messages.  */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 1
+#endif
+
+/* In a future release of Bison, this section will be replaced
+   by #include "y.tab.h".  */
+#ifndef YY_YY_Y_TAB_H_INCLUDED
+# define YY_YY_Y_TAB_H_INCLUDED
+/* Enabling traces.  */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+#if YYDEBUG
+extern int yydebug;
+#endif
+
+/* Tokens.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+   /* Put the tokens into the symbol table, so that GDB and other debuggers
+      know about them.  */
+   enum yytokentype {
+     WORD = 258,
+     BOOL = 259,
+     FUNC_INRANGE = 260,
+     DATE_ISO = 261,
+     OBRACE = 262,
+     CBRACE = 263,
+     EQUAL = 264,
+     UNEQUAL = 265,
+     GT = 266,
+     LT = 267,
+     COMMA = 268,
+     QUOTE = 269,
+     AND = 270,
+     OR = 271
+   };
+#endif
+/* Tokens.  */
+#define WORD 258
+#define BOOL 259
+#define FUNC_INRANGE 260
+#define DATE_ISO 261
+#define OBRACE 262
+#define CBRACE 263
+#define EQUAL 264
+#define UNEQUAL 265
+#define GT 266
+#define LT 267
+#define COMMA 268
+#define QUOTE 269
+#define AND 270
+#define OR 271
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+{
+/* Line 387 of yacc.c  */
+#line 45 "slmod_sparql_parser.y"
+
+    int ival;
+    const char *sval;
+    bool bval;
+    time_t tval;
+
+
+/* Line 387 of yacc.c  */
+#line 189 "slmod_sparql_parser.c"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+extern YYSTYPE yylval;
+
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+/* "%code provides" blocks.  */
+/* Line 387 of yacc.c  */
+#line 39 "slmod_sparql_parser.y"
+
+  #define SPRAW_TIME_OFFSET 978307200
+  extern int map_spotlight_to_sparql_query(slq_t *slq, gchar **sparql_result);
+  extern slq_t *ssp_slq;
+
+
+/* Line 387 of yacc.c  */
+#line 221 "slmod_sparql_parser.c"
+
+#endif /* !YY_YY_Y_TAB_H_INCLUDED  */
+
+/* Copy the second part of user declarations.  */
+
+/* Line 390 of yacc.c  */
+#line 228 "slmod_sparql_parser.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+#  define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+#  define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+#  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYSIZE_T size_t
+# else
+#  define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+#  if ENABLE_NLS
+#   include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+#   define YY_(Msgid) dgettext ("bison-runtime", Msgid)
+#  endif
+# endif
+# ifndef YY_
+#  define YY_(Msgid) Msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E.  */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(E) ((void) (E))
+#else
+# define YYUSE(E) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions.  */
+#ifndef lint
+# define YYID(N) (N)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int yyi)
+#else
+static int
+YYID (yyi)
+    int yyi;
+#endif
+{
+  return yyi;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols.  */
+
+# ifdef YYSTACK_USE_ALLOCA
+#  if YYSTACK_USE_ALLOCA
+#   ifdef __GNUC__
+#    define YYSTACK_ALLOC __builtin_alloca
+#   elif defined __BUILTIN_VA_ARG_INCR
+#    include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+#   elif defined _AIX
+#    define YYSTACK_ALLOC __alloca
+#   elif defined _MSC_VER
+#    include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+#    define alloca _alloca
+#   else
+#    define YYSTACK_ALLOC alloca
+#    if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+#     include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+      /* Use EXIT_SUCCESS as a witness for stdlib.h.  */
+#     ifndef EXIT_SUCCESS
+#      define EXIT_SUCCESS 0
+#     endif
+#    endif
+#   endif
+#  endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+   /* Pacify GCC's `empty if-body' warning.  */
+#  define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+    /* The OS might guarantee only one guard page at the bottom of the stack,
+       and a page size can be as small as 4096 bytes.  So we cannot safely
+       invoke alloca (N) if N exceeds 4096.  Use a slightly smaller number
+       to allow for a few compiler-allocated temporary stack slots.  */
+#   define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+#  endif
+# else
+#  define YYSTACK_ALLOC YYMALLOC
+#  define YYSTACK_FREE YYFREE
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+#   define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+#  endif
+#  if (defined __cplusplus && ! defined EXIT_SUCCESS \
+       && ! ((defined YYMALLOC || defined malloc) \
+            && (defined YYFREE || defined free)))
+#   include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#   ifndef EXIT_SUCCESS
+#    define EXIT_SUCCESS 0
+#   endif
+#  endif
+#  ifndef YYMALLOC
+#   define YYMALLOC malloc
+#   if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+#  ifndef YYFREE
+#   define YYFREE free
+#   if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+     && (! defined __cplusplus \
+        || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member.  */
+union yyalloc
+{
+  yytype_int16 yyss_alloc;
+  YYSTYPE yyvs_alloc;
+};
+
+/* The size of the maximum gap between one aligned stack and the next.  */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+   N elements.  */
+# define YYSTACK_BYTES(N) \
+     ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+      + YYSTACK_GAP_MAXIMUM)
+
+# define YYCOPY_NEEDED 1
+
+/* Relocate STACK from its old location to the new one.  The
+   local variables YYSIZE and YYSTACKSIZE give the old and new number of
+   elements in the stack, and YYPTR gives the new location of the
+   stack.  Advance YYPTR to a properly aligned location for the next
+   stack.  */
+# define YYSTACK_RELOCATE(Stack_alloc, Stack)                          \
+    do                                                                 \
+      {                                                                        \
+       YYSIZE_T yynewbytes;                                            \
+       YYCOPY (&yyptr->Stack_alloc, Stack, yysize);                    \
+       Stack = &yyptr->Stack_alloc;                                    \
+       yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+       yyptr += yynewbytes / sizeof (*yyptr);                          \
+      }                                                                        \
+    while (YYID (0))
+
+#endif
+
+#if defined YYCOPY_NEEDED && YYCOPY_NEEDED
+/* Copy COUNT objects from SRC to DST.  The source and destination do
+   not overlap.  */
+# ifndef YYCOPY
+#  if defined __GNUC__ && 1 < __GNUC__
+#   define YYCOPY(Dst, Src, Count) \
+      __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src)))
+#  else
+#   define YYCOPY(Dst, Src, Count)              \
+      do                                        \
+        {                                       \
+          YYSIZE_T yyi;                         \
+          for (yyi = 0; yyi < (Count); yyi++)   \
+            (Dst)[yyi] = (Src)[yyi];            \
+        }                                       \
+      while (YYID (0))
+#  endif
+# endif
+#endif /* !YYCOPY_NEEDED */
+
+/* YYFINAL -- State number of the termination state.  */
+#define YYFINAL  2
+/* YYLAST -- Last index in YYTABLE.  */
+#define YYLAST   52
+
+/* YYNTOKENS -- Number of terminals.  */
+#define YYNTOKENS  17
+/* YYNNTS -- Number of nonterminals.  */
+#define YYNNTS  7
+/* YYNRULES -- Number of rules.  */
+#define YYNRULES  22
+/* YYNRULES -- Number of states.  */
+#define YYNSTATES  51
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX.  */
+#define YYUNDEFTOK  2
+#define YYMAXUTOK   271
+
+#define YYTRANSLATE(YYX)                                               \
+  ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX.  */
+static const yytype_uint8 yytranslate[] =
+{
+       0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     1,     2,     3,     4,
+       5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
+      15,    16
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+   YYRHS.  */
+static const yytype_uint8 yyprhs[] =
+{
+       0,     0,     3,     4,     7,     9,    11,    15,    17,    19,
+      23,    27,    31,    37,    43,    49,    55,    62,    69,    76,
+      83,    92,    97
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS.  */
+static const yytype_int8 yyrhs[] =
+{
+      18,     0,    -1,    -1,    18,    19,    -1,    20,    -1,     4,
+      -1,    21,    16,    21,    -1,    21,    -1,    22,    -1,     7,
+      20,     8,    -1,    20,    15,    20,    -1,    20,    16,    20,
+      -1,     3,     9,    14,     3,    14,    -1,     3,    10,    14,
+       3,    14,    -1,     3,    12,    14,     3,    14,    -1,     3,
+      11,    14,     3,    14,    -1,     3,     9,    14,     3,    14,
+       3,    -1,     3,    10,    14,     3,    14,     3,    -1,     3,
+      12,    14,     3,    14,     3,    -1,     3,    11,    14,     3,
+      14,     3,    -1,     5,     7,     3,    13,    23,    13,    23,
+       8,    -1,     6,     7,     3,     8,    -1,     3,    -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined.  */
+static const yytype_uint8 yyrline[] =
+{
+       0,    67,    67,    69,    73,    83,    89,    95,    96,    97,
+      98,    99,   108,   109,   110,   111,   112,   113,   114,   115,
+     119,   123,   124
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || 1
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+   First, the terminals, then, starting at YYNTOKENS, nonterminals.  */
+static const char *const yytname[] =
+{
+  "$end", "error", "$undefined", "WORD", "BOOL", "FUNC_INRANGE",
+  "DATE_ISO", "OBRACE", "CBRACE", "EQUAL", "UNEQUAL", "GT", "LT", "COMMA",
+  "QUOTE", "AND", "OR", "$accept", "input", "line", "expr", "match",
+  "function", "date", YY_NULL
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+   token YYLEX-NUM.  */
+static const yytype_uint16 yytoknum[] =
+{
+       0,   256,   257,   258,   259,   260,   261,   262,   263,   264,
+     265,   266,   267,   268,   269,   270,   271
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives.  */
+static const yytype_uint8 yyr1[] =
+{
+       0,    17,    18,    18,    19,    20,    20,    20,    20,    20,
+      20,    20,    21,    21,    21,    21,    21,    21,    21,    21,
+      22,    23,    23
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN.  */
+static const yytype_uint8 yyr2[] =
+{
+       0,     2,     0,     2,     1,     1,     3,     1,     1,     3,
+       3,     3,     5,     5,     5,     5,     6,     6,     6,     6,
+       8,     4,     1
+};
+
+/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM.
+   Performed when YYTABLE doesn't specify something else to do.  Zero
+   means the default is an error.  */
+static const yytype_uint8 yydefact[] =
+{
+       2,     0,     1,     0,     5,     0,     0,     3,     4,     7,
+       8,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     9,    10,    11,     6,     0,
+       0,     0,     0,     0,    12,    13,    15,    14,    22,     0,
+       0,    16,    17,    19,    18,     0,     0,     0,     0,    21,
+      20
+};
+
+/* YYDEFGOTO[NTERM-NUM].  */
+static const yytype_int8 yydefgoto[] =
+{
+      -1,     1,     7,     8,     9,    10,    40
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+   STATE-NUM.  */
+#define YYPACT_NINF -10
+static const yytype_int8 yypact[] =
+{
+     -10,    10,   -10,     9,   -10,    -2,    -1,   -10,     8,    -9,
+     -10,     2,    12,    13,    14,    26,    -7,    -1,    -1,    27,
+      28,    29,    30,    31,    22,   -10,    20,   -10,   -10,    23,
+      24,    25,    32,    19,    37,    38,    39,    40,   -10,    41,
+      34,   -10,   -10,   -10,   -10,    42,    19,    36,    43,   -10,
+     -10
+};
+
+/* YYPGOTO[NTERM-NUM].  */
+static const yytype_int8 yypgoto[] =
+{
+     -10,   -10,   -10,    -6,    33,   -10,     3
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]].  What to do in state STATE-NUM.  If
+   positive, shift that token.  If negative, reduce the rule which
+   number is the opposite.  If YYTABLE_NINF, syntax error.  */
+#define YYTABLE_NINF -1
+static const yytype_uint8 yytable[] =
+{
+      16,    25,     3,     4,     5,    15,     6,    19,    17,    18,
+       2,    26,    27,     3,     4,     5,    20,     6,    11,    12,
+      13,    14,    38,    17,    18,    39,    21,    22,    23,    24,
+       3,    29,    30,    31,    32,    33,    18,    34,    35,    36,
+      41,    42,    43,    44,    49,    47,    37,    46,    45,    48,
+       0,    50,    28
+};
+
+#define yypact_value_is_default(Yystate) \
+  (!!((Yystate) == (-10)))
+
+#define yytable_value_is_error(Yytable_value) \
+  YYID (0)
+
+static const yytype_int8 yycheck[] =
+{
+       6,     8,     3,     4,     5,     7,     7,    16,    15,    16,
+       0,    17,    18,     3,     4,     5,    14,     7,     9,    10,
+      11,    12,     3,    15,    16,     6,    14,    14,    14,     3,
+       3,     3,     3,     3,     3,    13,    16,    14,    14,    14,
+       3,     3,     3,     3,     8,     3,    14,    13,     7,    46,
+      -1,     8,    19
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+   symbol of state STATE-NUM.  */
+static const yytype_uint8 yystos[] =
+{
+       0,    18,     0,     3,     4,     5,     7,    19,    20,    21,
+      22,     9,    10,    11,    12,     7,    20,    15,    16,    16,
+      14,    14,    14,    14,     3,     8,    20,    20,    21,     3,
+       3,     3,     3,    13,    14,    14,    14,    14,     3,     6,
+      23,     3,     3,     3,     3,     7,    13,     3,    23,     8,
+       8
+};
+
+#define yyerrok                (yyerrstatus = 0)
+#define yyclearin      (yychar = YYEMPTY)
+#define YYEMPTY                (-2)
+#define YYEOF          0
+
+#define YYACCEPT       goto yyacceptlab
+#define YYABORT                goto yyabortlab
+#define YYERROR                goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror.  This remains here temporarily
+   to ease the transition to the new meaning of YYERROR, for GCC.
+   Once GCC version 2 has supplanted version 1, this can go.  However,
+   YYFAIL appears to be in use.  Nevertheless, it is formally deprecated
+   in Bison 2.4.2's NEWS entry, where a plan to phase it out is
+   discussed.  */
+
+#define YYFAIL         goto yyerrlab
+#if defined YYFAIL
+  /* This is here to suppress warnings from the GCC cpp's
+     -Wunused-macros.  Normally we don't worry about that warning, but
+     some users do, and we want to make it easy for users to remove
+     YYFAIL uses, which will produce warnings from Bison 2.5.  */
+#endif
+
+#define YYRECOVERING()  (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value)                                  \
+do                                                              \
+  if (yychar == YYEMPTY)                                        \
+    {                                                           \
+      yychar = (Token);                                         \
+      yylval = (Value);                                         \
+      YYPOPSTACK (yylen);                                       \
+      yystate = *yyssp;                                         \
+      goto yybackup;                                            \
+    }                                                           \
+  else                                                          \
+    {                                                           \
+      yyerror (YY_("syntax error: cannot back up")); \
+      YYERROR;                                                 \
+    }                                                          \
+while (YYID (0))
+
+/* Error token number */
+#define YYTERROR       1
+#define YYERRCODE      256
+
+
+/* This macro is provided for backward compatibility. */
+#ifndef YY_LOCATION_PRINT
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments.  */
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested.  */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+#  include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args)                       \
+do {                                           \
+  if (yydebug)                                 \
+    YYFPRINTF Args;                            \
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)                   \
+do {                                                                     \
+  if (yydebug)                                                           \
+    {                                                                    \
+      YYFPRINTF (stderr, "%s ", Title);                                          \
+      yy_symbol_print (stderr,                                           \
+                 Type, Value); \
+      YYFPRINTF (stderr, "\n");                                                  \
+    }                                                                    \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep)
+    FILE *yyoutput;
+    int yytype;
+    YYSTYPE const * const yyvaluep;
+#endif
+{
+  FILE *yyo = yyoutput;
+  YYUSE (yyo);
+  if (!yyvaluep)
+    return;
+# ifdef YYPRINT
+  if (yytype < YYNTOKENS)
+    YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+  YYUSE (yyoutput);
+# endif
+  switch (yytype)
+    {
+      default:
+        break;
+    }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep)
+    FILE *yyoutput;
+    int yytype;
+    YYSTYPE const * const yyvaluep;
+#endif
+{
+  if (yytype < YYNTOKENS)
+    YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+  else
+    YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+  yy_symbol_value_print (yyoutput, yytype, yyvaluep);
+  YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included).                                                   |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop)
+#else
+static void
+yy_stack_print (yybottom, yytop)
+    yytype_int16 *yybottom;
+    yytype_int16 *yytop;
+#endif
+{
+  YYFPRINTF (stderr, "Stack now");
+  for (; yybottom <= yytop; yybottom++)
+    {
+      int yybot = *yybottom;
+      YYFPRINTF (stderr, " %d", yybot);
+    }
+  YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top)                           \
+do {                                                           \
+  if (yydebug)                                                 \
+    yy_stack_print ((Bottom), (Top));                          \
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced.  |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, int yyrule)
+#else
+static void
+yy_reduce_print (yyvsp, yyrule)
+    YYSTYPE *yyvsp;
+    int yyrule;
+#endif
+{
+  int yynrhs = yyr2[yyrule];
+  int yyi;
+  unsigned long int yylno = yyrline[yyrule];
+  YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+            yyrule - 1, yylno);
+  /* The symbols being reduced.  */
+  for (yyi = 0; yyi < yynrhs; yyi++)
+    {
+      YYFPRINTF (stderr, "   $%d = ", yyi + 1);
+      yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+                      &(yyvsp[(yyi + 1) - (yynrhs)])
+                                      );
+      YYFPRINTF (stderr, "\n");
+    }
+}
+
+# define YY_REDUCE_PRINT(Rule)         \
+do {                                   \
+  if (yydebug)                         \
+    yy_reduce_print (yyvsp, Rule); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace.  It is left uninitialized so that
+   multiple parsers can coexist.  */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks.  */
+#ifndef        YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+   if the built-in stack extension method is used).
+
+   Do not make this value too large; the results are undefined if
+   YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+   evaluated with infinite-precision integer arithmetic.  */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+#  if defined __GLIBC__ && defined _STRING_H
+#   define yystrlen strlen
+#  else
+/* Return the length of YYSTR.  */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+    const char *yystr;
+#endif
+{
+  YYSIZE_T yylen;
+  for (yylen = 0; yystr[yylen]; yylen++)
+    continue;
+  return yylen;
+}
+#  endif
+# endif
+
+# ifndef yystpcpy
+#  if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+#   define yystpcpy stpcpy
+#  else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+   YYDEST.  */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+    char *yydest;
+    const char *yysrc;
+#endif
+{
+  char *yyd = yydest;
+  const char *yys = yysrc;
+
+  while ((*yyd++ = *yys++) != '\0')
+    continue;
+
+  return yyd - 1;
+}
+#  endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+   quotes and backslashes, so that it's suitable for yyerror.  The
+   heuristic is that double-quoting is unnecessary unless the string
+   contains an apostrophe, a comma, or backslash (other than
+   backslash-backslash).  YYSTR is taken from yytname.  If YYRES is
+   null, do not copy; instead, return the length of what the result
+   would have been.  */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+  if (*yystr == '"')
+    {
+      YYSIZE_T yyn = 0;
+      char const *yyp = yystr;
+
+      for (;;)
+       switch (*++yyp)
+         {
+         case '\'':
+         case ',':
+           goto do_not_strip_quotes;
+
+         case '\\':
+           if (*++yyp != '\\')
+             goto do_not_strip_quotes;
+           /* Fall through.  */
+         default:
+           if (yyres)
+             yyres[yyn] = *yyp;
+           yyn++;
+           break;
+
+         case '"':
+           if (yyres)
+             yyres[yyn] = '\0';
+           return yyn;
+         }
+    do_not_strip_quotes: ;
+    }
+
+  if (! yyres)
+    return yystrlen (yystr);
+
+  return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message
+   about the unexpected token YYTOKEN for the state stack whose top is
+   YYSSP.
+
+   Return 0 if *YYMSG was successfully written.  Return 1 if *YYMSG is
+   not large enough to hold the message.  In that case, also set
+   *YYMSG_ALLOC to the required number of bytes.  Return 2 if the
+   required number of bytes is too large to store.  */
+static int
+yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,
+                yytype_int16 *yyssp, int yytoken)
+{
+  YYSIZE_T yysize0 = yytnamerr (YY_NULL, yytname[yytoken]);
+  YYSIZE_T yysize = yysize0;
+  enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+  /* Internationalized format string. */
+  const char *yyformat = YY_NULL;
+  /* Arguments of yyformat. */
+  char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+  /* Number of reported tokens (one for the "unexpected", one per
+     "expected"). */
+  int yycount = 0;
+
+  /* There are many possibilities here to consider:
+     - Assume YYFAIL is not used.  It's too flawed to consider.  See
+       <http://lists.gnu.org/archive/html/bison-patches/2009-12/msg00024.html>
+       for details.  YYERROR is fine as it does not invoke this
+       function.
+     - If this state is a consistent state with a default action, then
+       the only way this function was invoked is if the default action
+       is an error action.  In that case, don't check for expected
+       tokens because there are none.
+     - The only way there can be no lookahead present (in yychar) is if
+       this state is a consistent state with a default action.  Thus,
+       detecting the absence of a lookahead is sufficient to determine
+       that there is no unexpected or expected token to report.  In that
+       case, just report a simple "syntax error".
+     - Don't assume there isn't a lookahead just because this state is a
+       consistent state with a default action.  There might have been a
+       previous inconsistent state, consistent state with a non-default
+       action, or user semantic action that manipulated yychar.
+     - Of course, the expected token list depends on states to have
+       correct lookahead information, and it depends on the parser not
+       to perform extra reductions after fetching a lookahead from the
+       scanner and before detecting a syntax error.  Thus, state merging
+       (from LALR or IELR) and default reductions corrupt the expected
+       token list.  However, the list is correct for canonical LR with
+       one exception: it will still contain any token that will not be
+       accepted due to an error action in a later state.
+  */
+  if (yytoken != YYEMPTY)
+    {
+      int yyn = yypact[*yyssp];
+      yyarg[yycount++] = yytname[yytoken];
+      if (!yypact_value_is_default (yyn))
+        {
+          /* Start YYX at -YYN if negative to avoid negative indexes in
+             YYCHECK.  In other words, skip the first -YYN actions for
+             this state because they are default actions.  */
+          int yyxbegin = yyn < 0 ? -yyn : 0;
+          /* Stay within bounds of both yycheck and yytname.  */
+          int yychecklim = YYLAST - yyn + 1;
+          int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+          int yyx;
+
+          for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+            if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR
+                && !yytable_value_is_error (yytable[yyx + yyn]))
+              {
+                if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+                  {
+                    yycount = 1;
+                    yysize = yysize0;
+                    break;
+                  }
+                yyarg[yycount++] = yytname[yyx];
+                {
+                  YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULL, yytname[yyx]);
+                  if (! (yysize <= yysize1
+                         && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+                    return 2;
+                  yysize = yysize1;
+                }
+              }
+        }
+    }
+
+  switch (yycount)
+    {
+# define YYCASE_(N, S)                      \
+      case N:                               \
+        yyformat = S;                       \
+      break
+      YYCASE_(0, YY_("syntax error"));
+      YYCASE_(1, YY_("syntax error, unexpected %s"));
+      YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
+      YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s"));
+      YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
+      YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
+# undef YYCASE_
+    }
+
+  {
+    YYSIZE_T yysize1 = yysize + yystrlen (yyformat);
+    if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+      return 2;
+    yysize = yysize1;
+  }
+
+  if (*yymsg_alloc < yysize)
+    {
+      *yymsg_alloc = 2 * yysize;
+      if (! (yysize <= *yymsg_alloc
+             && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM))
+        *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM;
+      return 1;
+    }
+
+  /* Avoid sprintf, as that infringes on the user's name space.
+     Don't have undefined behavior even if the translation
+     produced a string with the wrong number of "%s"s.  */
+  {
+    char *yyp = *yymsg;
+    int yyi = 0;
+    while ((*yyp = *yyformat) != '\0')
+      if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount)
+        {
+          yyp += yytnamerr (yyp, yyarg[yyi++]);
+          yyformat += 2;
+        }
+      else
+        {
+          yyp++;
+          yyformat++;
+        }
+  }
+  return 0;
+}
+#endif /* YYERROR_VERBOSE */
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol.  |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep)
+    const char *yymsg;
+    int yytype;
+    YYSTYPE *yyvaluep;
+#endif
+{
+  YYUSE (yyvaluep);
+
+  if (!yymsg)
+    yymsg = "Deleting";
+  YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+  switch (yytype)
+    {
+
+      default:
+        break;
+    }
+}
+
+
+
+
+/* The lookahead symbol.  */
+int yychar;
+
+
+#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END
+#endif
+#ifndef YY_INITIAL_VALUE
+# define YY_INITIAL_VALUE(Value) /* Nothing. */
+#endif
+
+/* The semantic value of the lookahead symbol.  */
+YYSTYPE yylval YY_INITIAL_VALUE(yyval_default);
+
+/* Number of syntax errors so far.  */
+int yynerrs;
+
+
+/*----------.
+| yyparse.  |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+    void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+    int yystate;
+    /* Number of tokens to shift before error messages enabled.  */
+    int yyerrstatus;
+
+    /* The stacks and their tools:
+       `yyss': related to states.
+       `yyvs': related to semantic values.
+
+       Refer to the stacks through separate pointers, to allow yyoverflow
+       to reallocate them elsewhere.  */
+
+    /* The state stack.  */
+    yytype_int16 yyssa[YYINITDEPTH];
+    yytype_int16 *yyss;
+    yytype_int16 *yyssp;
+
+    /* The semantic value stack.  */
+    YYSTYPE yyvsa[YYINITDEPTH];
+    YYSTYPE *yyvs;
+    YYSTYPE *yyvsp;
+
+    YYSIZE_T yystacksize;
+
+  int yyn;
+  int yyresult;
+  /* Lookahead token as an internal (translated) token number.  */
+  int yytoken = 0;
+  /* The variables used to return semantic value and location from the
+     action routines.  */
+  YYSTYPE yyval;
+
+#if YYERROR_VERBOSE
+  /* Buffer for error messages, and its allocated size.  */
+  char yymsgbuf[128];
+  char *yymsg = yymsgbuf;
+  YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+#define YYPOPSTACK(N)   (yyvsp -= (N), yyssp -= (N))
+
+  /* The number of symbols on the RHS of the reduced rule.
+     Keep to zero when no symbol should be popped.  */
+  int yylen = 0;
+
+  yyssp = yyss = yyssa;
+  yyvsp = yyvs = yyvsa;
+  yystacksize = YYINITDEPTH;
+
+  YYDPRINTF ((stderr, "Starting parse\n"));
+
+  yystate = 0;
+  yyerrstatus = 0;
+  yynerrs = 0;
+  yychar = YYEMPTY; /* Cause a token to be read.  */
+  goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate.  |
+`------------------------------------------------------------*/
+ yynewstate:
+  /* In all cases, when you get here, the value and location stacks
+     have just been pushed.  So pushing a state here evens the stacks.  */
+  yyssp++;
+
+ yysetstate:
+  *yyssp = yystate;
+
+  if (yyss + yystacksize - 1 <= yyssp)
+    {
+      /* Get the current used size of the three stacks, in elements.  */
+      YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+      {
+       /* Give user a chance to reallocate the stack.  Use copies of
+          these so that the &'s don't force the real ones into
+          memory.  */
+       YYSTYPE *yyvs1 = yyvs;
+       yytype_int16 *yyss1 = yyss;
+
+       /* Each stack pointer address is followed by the size of the
+          data in use in that stack, in bytes.  This used to be a
+          conditional around just the two extra args, but that might
+          be undefined if yyoverflow is a macro.  */
+       yyoverflow (YY_("memory exhausted"),
+                   &yyss1, yysize * sizeof (*yyssp),
+                   &yyvs1, yysize * sizeof (*yyvsp),
+                   &yystacksize);
+
+       yyss = yyss1;
+       yyvs = yyvs1;
+      }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+      goto yyexhaustedlab;
+# else
+      /* Extend the stack our own way.  */
+      if (YYMAXDEPTH <= yystacksize)
+       goto yyexhaustedlab;
+      yystacksize *= 2;
+      if (YYMAXDEPTH < yystacksize)
+       yystacksize = YYMAXDEPTH;
+
+      {
+       yytype_int16 *yyss1 = yyss;
+       union yyalloc *yyptr =
+         (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+       if (! yyptr)
+         goto yyexhaustedlab;
+       YYSTACK_RELOCATE (yyss_alloc, yyss);
+       YYSTACK_RELOCATE (yyvs_alloc, yyvs);
+#  undef YYSTACK_RELOCATE
+       if (yyss1 != yyssa)
+         YYSTACK_FREE (yyss1);
+      }
+# endif
+#endif /* no yyoverflow */
+
+      yyssp = yyss + yysize - 1;
+      yyvsp = yyvs + yysize - 1;
+
+      YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+                 (unsigned long int) yystacksize));
+
+      if (yyss + yystacksize - 1 <= yyssp)
+       YYABORT;
+    }
+
+  YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+  if (yystate == YYFINAL)
+    YYACCEPT;
+
+  goto yybackup;
+
+/*-----------.
+| yybackup.  |
+`-----------*/
+yybackup:
+
+  /* Do appropriate processing given the current state.  Read a
+     lookahead token if we need one and don't already have one.  */
+
+  /* First try to decide what to do without reference to lookahead token.  */
+  yyn = yypact[yystate];
+  if (yypact_value_is_default (yyn))
+    goto yydefault;
+
+  /* Not known => get a lookahead token if don't already have one.  */
+
+  /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol.  */
+  if (yychar == YYEMPTY)
+    {
+      YYDPRINTF ((stderr, "Reading a token: "));
+      yychar = YYLEX;
+    }
+
+  if (yychar <= YYEOF)
+    {
+      yychar = yytoken = YYEOF;
+      YYDPRINTF ((stderr, "Now at end of input.\n"));
+    }
+  else
+    {
+      yytoken = YYTRANSLATE (yychar);
+      YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+    }
+
+  /* If the proper action on seeing token YYTOKEN is to reduce or to
+     detect an error, take that action.  */
+  yyn += yytoken;
+  if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+    goto yydefault;
+  yyn = yytable[yyn];
+  if (yyn <= 0)
+    {
+      if (yytable_value_is_error (yyn))
+        goto yyerrlab;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+
+  /* Count tokens shifted since error; after three, turn off error
+     status.  */
+  if (yyerrstatus)
+    yyerrstatus--;
+
+  /* Shift the lookahead token.  */
+  YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+  /* Discard the shifted token.  */
+  yychar = YYEMPTY;
+
+  yystate = yyn;
+  YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+  *++yyvsp = yylval;
+  YY_IGNORE_MAYBE_UNINITIALIZED_END
+
+  goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state.  |
+`-----------------------------------------------------------*/
+yydefault:
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+  goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction.  |
+`-----------------------------*/
+yyreduce:
+  /* yyn is the number of a rule to reduce with.  */
+  yylen = yyr2[yyn];
+
+  /* If YYLEN is nonzero, implement the default value of the action:
+     `$$ = $1'.
+
+     Otherwise, the following line sets YYVAL to garbage.
+     This behavior is undocumented and Bison
+     users should not rely upon it.  Assigning to YYVAL
+     unconditionally makes the parser a bit smaller, and it avoids a
+     GCC warning that YYVAL may be used uninitialized.  */
+  yyval = yyvsp[1-yylen];
+
+
+  YY_REDUCE_PRINT (yyn);
+  switch (yyn)
+    {
+        case 4:
+/* Line 1792 of yacc.c  */
+#line 73 "slmod_sparql_parser.y"
+    {
+    ssp_result = talloc_asprintf(ssp_slq,
+                                 "SELECT DISTINCT ?url WHERE "
+                                 "{ ?obj nie:url ?url FILTER(regex(?url, '^file://%s/')) . %s}",
+                                 ssp_slq->slq_vol->v_path, (yyvsp[(1) - (1)].sval));
+    (yyval.sval) = ssp_result;
+}
+    break;
+
+  case 5:
+/* Line 1792 of yacc.c  */
+#line 83 "slmod_sparql_parser.y"
+    {
+    if ((yyvsp[(1) - (1)].bval) == false)
+        YYACCEPT;
+    else
+        YYABORT;
+}
+    break;
+
+  case 6:
+/* Line 1792 of yacc.c  */
+#line 89 "slmod_sparql_parser.y"
+    {
+    if (strcmp((yyvsp[(1) - (3)].sval), (yyvsp[(3) - (3)].sval)) != 0)
+        (yyval.sval) = talloc_asprintf(ssp_slq, "{ %s } UNION { %s }", (yyvsp[(1) - (3)].sval), (yyvsp[(3) - (3)].sval));
+    else
+        (yyval.sval) = talloc_asprintf(ssp_slq, "%s", (yyvsp[(1) - (3)].sval));
+}
+    break;
+
+  case 7:
+/* Line 1792 of yacc.c  */
+#line 95 "slmod_sparql_parser.y"
+    {(yyval.sval) = (yyvsp[(1) - (1)].sval); if ((yyval.sval) == NULL) YYABORT;}
+    break;
+
+  case 8:
+/* Line 1792 of yacc.c  */
+#line 96 "slmod_sparql_parser.y"
+    {(yyval.sval) = (yyvsp[(1) - (1)].sval);}
+    break;
+
+  case 9:
+/* Line 1792 of yacc.c  */
+#line 97 "slmod_sparql_parser.y"
+    {(yyval.sval) = talloc_asprintf(ssp_slq, "%s", (yyvsp[(2) - (3)].sval));}
+    break;
+
+  case 10:
+/* Line 1792 of yacc.c  */
+#line 98 "slmod_sparql_parser.y"
+    {(yyval.sval) = talloc_asprintf(ssp_slq, "%s . %s", (yyvsp[(1) - (3)].sval), (yyvsp[(3) - (3)].sval));}
+    break;
+
+  case 11:
+/* Line 1792 of yacc.c  */
+#line 99 "slmod_sparql_parser.y"
+    {
+    if (strcmp((yyvsp[(1) - (3)].sval), (yyvsp[(3) - (3)].sval)) != 0)
+        (yyval.sval) = talloc_asprintf(ssp_slq, "{ %s } UNION { %s }", (yyvsp[(1) - (3)].sval), (yyvsp[(3) - (3)].sval));
+    else
+        (yyval.sval) = talloc_asprintf(ssp_slq, "%s", (yyvsp[(1) - (3)].sval));
+}
+    break;
+
+  case 12:
+/* Line 1792 of yacc.c  */
+#line 108 "slmod_sparql_parser.y"
+    {(yyval.sval) = map_expr((yyvsp[(1) - (5)].sval), '=', (yyvsp[(4) - (5)].sval));}
+    break;
+
+  case 13:
+/* Line 1792 of yacc.c  */
+#line 109 "slmod_sparql_parser.y"
+    {(yyval.sval) = map_expr((yyvsp[(1) - (5)].sval), '!', (yyvsp[(4) - (5)].sval));}
+    break;
+
+  case 14:
+/* Line 1792 of yacc.c  */
+#line 110 "slmod_sparql_parser.y"
+    {(yyval.sval) = map_expr((yyvsp[(1) - (5)].sval), '<', (yyvsp[(4) - (5)].sval));}
+    break;
+
+  case 15:
+/* Line 1792 of yacc.c  */
+#line 111 "slmod_sparql_parser.y"
+    {(yyval.sval) = map_expr((yyvsp[(1) - (5)].sval), '>', (yyvsp[(4) - (5)].sval));}
+    break;
+
+  case 16:
+/* Line 1792 of yacc.c  */
+#line 112 "slmod_sparql_parser.y"
+    {(yyval.sval) = map_expr((yyvsp[(1) - (6)].sval), '=', (yyvsp[(4) - (6)].sval));}
+    break;
+
+  case 17:
+/* Line 1792 of yacc.c  */
+#line 113 "slmod_sparql_parser.y"
+    {(yyval.sval) = map_expr((yyvsp[(1) - (6)].sval), '!', (yyvsp[(4) - (6)].sval));}
+    break;
+
+  case 18:
+/* Line 1792 of yacc.c  */
+#line 114 "slmod_sparql_parser.y"
+    {(yyval.sval) = map_expr((yyvsp[(1) - (6)].sval), '<', (yyvsp[(4) - (6)].sval));}
+    break;
+
+  case 19:
+/* Line 1792 of yacc.c  */
+#line 115 "slmod_sparql_parser.y"
+    {(yyval.sval) = map_expr((yyvsp[(1) - (6)].sval), '>', (yyvsp[(4) - (6)].sval));}
+    break;
+
+  case 20:
+/* Line 1792 of yacc.c  */
+#line 119 "slmod_sparql_parser.y"
+    {(yyval.sval) = map_daterange((yyvsp[(3) - (8)].sval), (yyvsp[(5) - (8)].tval), (yyvsp[(7) - (8)].tval));}
+    break;
+
+  case 21:
+/* Line 1792 of yacc.c  */
+#line 123 "slmod_sparql_parser.y"
+    {(yyval.tval) = isodate2unix((yyvsp[(3) - (4)].sval));}
+    break;
+
+  case 22:
+/* Line 1792 of yacc.c  */
+#line 124 "slmod_sparql_parser.y"
+    {(yyval.tval) = atoi((yyvsp[(1) - (1)].sval)) + SPRAW_TIME_OFFSET;}
+    break;
+
+
+/* Line 1792 of yacc.c  */
+#line 1585 "slmod_sparql_parser.c"
+      default: break;
+    }
+  /* User semantic actions sometimes alter yychar, and that requires
+     that yytoken be updated with the new translation.  We take the
+     approach of translating immediately before every use of yytoken.
+     One alternative is translating here after every semantic action,
+     but that translation would be missed if the semantic action invokes
+     YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or
+     if it invokes YYBACKUP.  In the case of YYABORT or YYACCEPT, an
+     incorrect destructor might then be invoked immediately.  In the
+     case of YYERROR or YYBACKUP, subsequent parser actions might lead
+     to an incorrect destructor call or verbose syntax error message
+     before the lookahead is translated.  */
+  YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+
+  *++yyvsp = yyval;
+
+  /* Now `shift' the result of the reduction.  Determine what state
+     that goes to, based on the state we popped back to and the rule
+     number reduced by.  */
+
+  yyn = yyr1[yyn];
+
+  yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+  if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+    yystate = yytable[yystate];
+  else
+    yystate = yydefgoto[yyn - YYNTOKENS];
+
+  goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+  /* Make sure we have latest lookahead translation.  See comments at
+     user semantic actions for why this is necessary.  */
+  yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar);
+
+  /* If not already recovering from an error, report this error.  */
+  if (!yyerrstatus)
+    {
+      ++yynerrs;
+#if ! YYERROR_VERBOSE
+      yyerror (YY_("syntax error"));
+#else
+# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \
+                                        yyssp, yytoken)
+      {
+        char const *yymsgp = YY_("syntax error");
+        int yysyntax_error_status;
+        yysyntax_error_status = YYSYNTAX_ERROR;
+        if (yysyntax_error_status == 0)
+          yymsgp = yymsg;
+        else if (yysyntax_error_status == 1)
+          {
+            if (yymsg != yymsgbuf)
+              YYSTACK_FREE (yymsg);
+            yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc);
+            if (!yymsg)
+              {
+                yymsg = yymsgbuf;
+                yymsg_alloc = sizeof yymsgbuf;
+                yysyntax_error_status = 2;
+              }
+            else
+              {
+                yysyntax_error_status = YYSYNTAX_ERROR;
+                yymsgp = yymsg;
+              }
+          }
+        yyerror (yymsgp);
+        if (yysyntax_error_status == 2)
+          goto yyexhaustedlab;
+      }
+# undef YYSYNTAX_ERROR
+#endif
+    }
+
+
+
+  if (yyerrstatus == 3)
+    {
+      /* If just tried and failed to reuse lookahead token after an
+        error, discard it.  */
+
+      if (yychar <= YYEOF)
+       {
+         /* Return failure if at end of input.  */
+         if (yychar == YYEOF)
+           YYABORT;
+       }
+      else
+       {
+         yydestruct ("Error: discarding",
+                     yytoken, &yylval);
+         yychar = YYEMPTY;
+       }
+    }
+
+  /* Else will try to reuse lookahead token after shifting the error
+     token.  */
+  goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR.  |
+`---------------------------------------------------*/
+yyerrorlab:
+
+  /* Pacify compilers like GCC when the user code never invokes
+     YYERROR and the label yyerrorlab therefore never appears in user
+     code.  */
+  if (/*CONSTCOND*/ 0)
+     goto yyerrorlab;
+
+  /* Do not reclaim the symbols of the rule which action triggered
+     this YYERROR.  */
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+  yystate = *yyssp;
+  goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR.  |
+`-------------------------------------------------------------*/
+yyerrlab1:
+  yyerrstatus = 3;     /* Each real token shifted decrements this.  */
+
+  for (;;)
+    {
+      yyn = yypact[yystate];
+      if (!yypact_value_is_default (yyn))
+       {
+         yyn += YYTERROR;
+         if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+           {
+             yyn = yytable[yyn];
+             if (0 < yyn)
+               break;
+           }
+       }
+
+      /* Pop the current state because it cannot handle the error token.  */
+      if (yyssp == yyss)
+       YYABORT;
+
+
+      yydestruct ("Error: popping",
+                 yystos[yystate], yyvsp);
+      YYPOPSTACK (1);
+      yystate = *yyssp;
+      YY_STACK_PRINT (yyss, yyssp);
+    }
+
+  YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+  *++yyvsp = yylval;
+  YY_IGNORE_MAYBE_UNINITIALIZED_END
+
+
+  /* Shift the error token.  */
+  YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here.  |
+`-------------------------------------*/
+yyacceptlab:
+  yyresult = 0;
+  goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here.  |
+`-----------------------------------*/
+yyabortlab:
+  yyresult = 1;
+  goto yyreturn;
+
+#if !defined yyoverflow || YYERROR_VERBOSE
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here.  |
+`-------------------------------------------------*/
+yyexhaustedlab:
+  yyerror (YY_("memory exhausted"));
+  yyresult = 2;
+  /* Fall through.  */
+#endif
+
+yyreturn:
+  if (yychar != YYEMPTY)
+    {
+      /* Make sure we have latest lookahead translation.  See comments at
+         user semantic actions for why this is necessary.  */
+      yytoken = YYTRANSLATE (yychar);
+      yydestruct ("Cleanup: discarding lookahead",
+                  yytoken, &yylval);
+    }
+  /* Do not reclaim the symbols of the rule which action triggered
+     this YYABORT or YYACCEPT.  */
+  YYPOPSTACK (yylen);
+  YY_STACK_PRINT (yyss, yyssp);
+  while (yyssp != yyss)
+    {
+      yydestruct ("Cleanup: popping",
+                 yystos[*yyssp], yyvsp);
+      YYPOPSTACK (1);
+    }
+#ifndef yyoverflow
+  if (yyss != yyssa)
+    YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+  if (yymsg != yymsgbuf)
+    YYSTACK_FREE (yymsg);
+#endif
+  /* Make sure YYID is used.  */
+  return YYID (yyresult);
+}
+
+
+/* Line 2055 of yacc.c  */
+#line 127 "slmod_sparql_parser.y"
+
+
+static time_t isodate2unix(const char *s)
+{
+    struct tm tm;
+
+    if (strptime(s, "%Y-%m-%dT%H:%M:%SZ", &tm) == NULL)
+        return (time_t)-1;
+    return mktime(&tm);
+}
+
+static const char *map_daterange(const char *dateattr, time_t date1, time_t date2)
+{
+    EC_INIT;
+    char *result = NULL;
+    struct spotlight_sparql_map *p;
+    struct tm *tmp;
+    char buf1[64], buf2[64];
+
+    EC_NULL_LOG( tmp = localtime(&date1) );
+    strftime(buf1, sizeof(buf1), "%Y-%m-%dT%H:%M:%SZ", tmp);
+    EC_NULL_LOG( tmp = localtime(&date2) );
+    strftime(buf2, sizeof(buf2), "%Y-%m-%dT%H:%M:%SZ", tmp);
+
+    for (p = spotlight_sparql_map; p->ssm_spotlight_attr; p++) {
+        if (strcmp(dateattr, p->ssm_spotlight_attr) == 0) {
+            result = talloc_asprintf(ssp_slq,
+                                     "?obj %s ?%c FILTER (?%c > '%s' && ?%c < '%s')",
+                                     p->ssm_sparql_attr,
+                                     sparqlvar,
+                                     sparqlvar,
+                                     buf1,
+                                     sparqlvar,
+                                     buf2);
+            sparqlvar++;
+            break;
+        }
+    }
+
+EC_CLEANUP:
+    if (ret != 0)
+        return NULL;
+    return result;
+}
+
+static char *map_type_search(const char *attr, char op, const char *val)
+{
+    char *result = NULL;
+    const char *sparqlAttr;
+
+    for (struct MDTypeMap *p = MDTypeMap; p->mdtm_value; p++) {
+        if (strcmp(p->mdtm_value, val) == 0) {
+            switch (p->mdtm_type) {
+            case kMDTypeMapRDF:
+                sparqlAttr = "rdf:type";
+                break;
+            case kMDTypeMapMime:
+                sparqlAttr = "nie:mimeType";
+                break;
+            default:
+                return NULL;
+            }
+            result = talloc_asprintf(ssp_slq, "?obj %s '%s'",
+                                     sparqlAttr,
+                                     p->mdtm_sparql);
+            break;
+        }
+    }
+    return result;
+}
+
+static const char *map_expr(const char *attr, char op, const char *val)
+{
+    EC_INIT;
+    char *result = NULL;
+    struct spotlight_sparql_map *p;
+    time_t t;
+    struct tm *tmp;
+    char buf1[64];
+    bstring q = NULL, search = NULL, replace = NULL;
+
+    for (p = spotlight_sparql_map; p->ssm_spotlight_attr; p++) {
+        if (strcmp(p->ssm_spotlight_attr, attr) == 0) {
+            if (p->ssm_type != ssmt_type && p->ssm_sparql_attr == NULL) {
+                yyerror("unsupported Spotlight attribute");
+                EC_FAIL;
+            }
+            switch (p->ssm_type) {
+            case ssmt_bool:
+                result = talloc_asprintf(ssp_slq, "?obj %s '%s'", p->ssm_sparql_attr, val);
+                break;
+            case ssmt_num:
+                result = talloc_asprintf(ssp_slq, "?obj %s ?%c FILTER(?%c %c%c '%s')",
+                                         p->ssm_sparql_attr,
+                                         sparqlvar,
+                                         sparqlvar,
+                                         op,
+                                         op == '!' ? '=' : ' ', /* append '=' to '!' */
+                                         val);
+                sparqlvar++;
+                break;
+            case ssmt_str:
+                q = bformat("^%s$", val);
+                search = bfromcstr("*");
+                replace = bfromcstr(".*");
+                bfindreplace(q, search, replace, 0);
+                result = talloc_asprintf(ssp_slq, "?obj %s ?%c FILTER(regex(?%c, '%s'))",
+                                         p->ssm_sparql_attr,
+                                         sparqlvar,
+                                         sparqlvar,
+                                         bdata(q));
+                sparqlvar++;
+                break;
+            case ssmt_fts:
+                result = talloc_asprintf(ssp_slq, "?obj %s '%s'", p->ssm_sparql_attr, val);
+                break;
+            case ssmt_date:
+                t = atoi(val) + SPRAW_TIME_OFFSET;
+                EC_NULL( tmp = localtime(&t) );
+                strftime(buf1, sizeof(buf1), "%Y-%m-%dT%H:%M:%SZ", tmp);
+                result = talloc_asprintf(ssp_slq, "?obj %s ?%c FILTER(?%c %c '%s')",
+                                         p->ssm_sparql_attr,
+                                         sparqlvar,
+                                         sparqlvar,
+                                         op,
+                                         buf1);
+                sparqlvar++;
+                break;
+            case ssmt_type:
+                result = map_type_search(attr, op, val);
+                break;
+            default:
+                EC_FAIL;
+            }
+            break;
+        }
+    }
+
+EC_CLEANUP:
+    if (q)
+        bdestroy(q);
+    if (search)
+        bdestroy(search);
+    if (replace)
+        bdestroy(replace);
+    return result;
+}
+
+void yyerror(const char *str)
+{
+#ifdef MAIN
+    printf("yyerror: %s\n", str);
+#else
+    LOG(log_error, logtype_sl, "yyerror: %s", str);
+#endif
+}
+int yywrap()
+{
+    return 1;
+} 
+
+/**
+ * Map a Spotlight RAW query string to a SPARQL query string
+ *
+ * @param[in]     slq            Spotlight query handle
+ * @param[out]    sparql_result  Mapped SPARQL query, string is allocated in
+ *                               talloc context of slq
+ * @return        0 on success, -1 on error
+ **/
+int map_spotlight_to_sparql_query(slq_t *slq, gchar **sparql_result)
+{
+    EC_INIT;
+    YY_BUFFER_STATE s = NULL;
+    ssp_result = NULL;
+
+    ssp_slq = slq;
+    s = yy_scan_string(slq->slq_qstring);
+    sparqlvar = 'a';
+
+    EC_ZERO( yyparse() );
+
+EC_CLEANUP:
+    if (s)
+        yy_delete_buffer(s);
+    if (ret == 0)
+        *sparql_result = ssp_result;
+    else
+        *sparql_result = NULL;
+    EC_EXIT;
+}
+
+#ifdef MAIN
+int main(int argc, char **argv)
+{
+    int ret;
+    YY_BUFFER_STATE s;
+
+    if (argc != 2) {
+        printf("usage: %s QUERY\n", argv[0]);
+        return 1;
+    }
+
+    ssp_slq = talloc_zero(NULL, slq_t);
+    struct vol *vol = talloc_zero(ssp_slq, struct vol);
+    vol->v_path = "/Volumes/test";
+    ssp_slq->slq_vol = vol;
+    sparqlvar = 'a';
+
+    s = yy_scan_string(argv[1]);
+
+    ret = yyparse();
+
+    yy_delete_buffer(s);
+
+    if (ret == 0)
+        printf("SPARQL: %s\n", ssp_result ? ssp_result : "(empty)");
+
+    return 0;
+} 
+#endif
diff --git a/etc/spotlight/slmod_sparql_parser.h b/etc/spotlight/slmod_sparql_parser.h
new file mode 100644 (file)
index 0000000..985d4cb
--- /dev/null
@@ -0,0 +1,130 @@
+/* A Bison parser, made by GNU Bison 2.7.  */
+
+/* Bison interface for Yacc-like parsers in C
+   
+      Copyright (C) 1984, 1989-1990, 2000-2012 Free Software Foundation, Inc.
+   
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+   
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+#ifndef YY_YY_Y_TAB_H_INCLUDED
+# define YY_YY_Y_TAB_H_INCLUDED
+/* Enabling traces.  */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+#if YYDEBUG
+extern int yydebug;
+#endif
+
+/* Tokens.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+   /* Put the tokens into the symbol table, so that GDB and other debuggers
+      know about them.  */
+   enum yytokentype {
+     WORD = 258,
+     BOOL = 259,
+     FUNC_INRANGE = 260,
+     DATE_ISO = 261,
+     OBRACE = 262,
+     CBRACE = 263,
+     EQUAL = 264,
+     UNEQUAL = 265,
+     GT = 266,
+     LT = 267,
+     COMMA = 268,
+     QUOTE = 269,
+     AND = 270,
+     OR = 271
+   };
+#endif
+/* Tokens.  */
+#define WORD 258
+#define BOOL 259
+#define FUNC_INRANGE 260
+#define DATE_ISO 261
+#define OBRACE 262
+#define CBRACE 263
+#define EQUAL 264
+#define UNEQUAL 265
+#define GT 266
+#define LT 267
+#define COMMA 268
+#define QUOTE 269
+#define AND 270
+#define OR 271
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+{
+/* Line 2058 of yacc.c  */
+#line 45 "slmod_sparql_parser.y"
+
+    int ival;
+    const char *sval;
+    bool bval;
+    time_t tval;
+
+
+/* Line 2058 of yacc.c  */
+#line 97 "slmod_sparql_parser.h"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+extern YYSTYPE yylval;
+
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+/* "%code provides" blocks.  */
+/* Line 2058 of yacc.c  */
+#line 39 "slmod_sparql_parser.y"
+
+  #define SPRAW_TIME_OFFSET 978307200
+  extern int map_spotlight_to_sparql_query(slq_t *slq, gchar **sparql_result);
+  extern slq_t *ssp_slq;
+
+
+/* Line 2058 of yacc.c  */
+#line 129 "slmod_sparql_parser.h"
+
+#endif /* !YY_YY_Y_TAB_H_INCLUDED  */
diff --git a/etc/spotlight/slmod_sparql_parser.y b/etc/spotlight/slmod_sparql_parser.y
new file mode 100644 (file)
index 0000000..68d3d10
--- /dev/null
@@ -0,0 +1,347 @@
+%{
+  #include <atalk/standards.h>
+
+  #include <stdbool.h>
+  #include <stdio.h>
+  #include <string.h>
+  #include <time.h>
+
+  #include <gio/gio.h>
+
+  #include <atalk/talloc.h>
+  #include <atalk/logger.h>
+  #include <atalk/errchk.h>
+  #include <atalk/spotlight.h>
+
+  #include "slmod_sparql_map.h"
+
+  struct yy_buffer_state;
+  typedef struct yy_buffer_state *YY_BUFFER_STATE;
+  extern int yylex (void);
+  extern void yyerror (char const *);
+  extern void *yyterminate(void);
+  extern YY_BUFFER_STATE yy_scan_string( const char *str);
+  extern void yy_delete_buffer ( YY_BUFFER_STATE buffer );
+
+  /* forward declarations */
+  static const char *map_expr(const char *attr, char op, const char *val);
+  static const char *map_daterange(const char *dateattr, time_t date1, time_t date2);
+  static time_t isodate2unix(const char *s);
+ /* global vars, eg needed by the lexer */
+  slq_t *ssp_slq;
+
+  /* local vars */
+  static gchar *ssp_result;
+  static char sparqlvar;
+%}
+
+%code provides {
+  #define SPRAW_TIME_OFFSET 978307200
+  extern int map_spotlight_to_sparql_query(slq_t *slq, gchar **sparql_result);
+  extern slq_t *ssp_slq;
+}
+
+%union {
+    int ival;
+    const char *sval;
+    bool bval;
+    time_t tval;
+}
+
+%expect 5
+%error-verbose
+
+%type <sval> match expr line function
+%type <tval> date
+
+%token <sval> WORD
+%token <bval> BOOL
+%token FUNC_INRANGE
+%token DATE_ISO
+%token OBRACE CBRACE EQUAL UNEQUAL GT LT COMMA QUOTE
+%left AND
+%left OR
+%%
+
+input:
+/* empty */
+| input line
+;
+     
+line:
+expr                           {
+    ssp_result = talloc_asprintf(ssp_slq,
+                                 "SELECT DISTINCT ?url WHERE "
+                                 "{ ?obj nie:url ?url FILTER(regex(?url, '^file://%s/')) . %s}",
+                                 ssp_slq->slq_vol->v_path, $1);
+    $$ = ssp_result;
+}
+;
+
+expr:
+BOOL                             {
+    if ($1 == false)
+        YYACCEPT;
+    else
+        YYABORT;
+}
+| match OR match                 {
+    if (strcmp($1, $3) != 0)
+        $$ = talloc_asprintf(ssp_slq, "{ %s } UNION { %s }", $1, $3);
+    else
+        $$ = talloc_asprintf(ssp_slq, "%s", $1);
+}
+| match                        {$$ = $1; if ($$ == NULL) YYABORT;}
+| function                     {$$ = $1;}
+| OBRACE expr CBRACE           {$$ = talloc_asprintf(ssp_slq, "%s", $2);}
+| expr AND expr                {$$ = talloc_asprintf(ssp_slq, "%s . %s", $1, $3);}
+| expr OR expr                 {
+    if (strcmp($1, $3) != 0)
+        $$ = talloc_asprintf(ssp_slq, "{ %s } UNION { %s }", $1, $3);
+    else
+        $$ = talloc_asprintf(ssp_slq, "%s", $1);
+}
+;
+
+match:
+WORD EQUAL QUOTE WORD QUOTE     {$$ = map_expr($1, '=', $4);}
+| WORD UNEQUAL QUOTE WORD QUOTE {$$ = map_expr($1, '!', $4);}
+| WORD LT QUOTE WORD QUOTE      {$$ = map_expr($1, '<', $4);}
+| WORD GT QUOTE WORD QUOTE      {$$ = map_expr($1, '>', $4);}
+| WORD EQUAL QUOTE WORD QUOTE WORD    {$$ = map_expr($1, '=', $4);}
+| WORD UNEQUAL QUOTE WORD QUOTE WORD {$$ = map_expr($1, '!', $4);}
+| WORD LT QUOTE WORD QUOTE WORD     {$$ = map_expr($1, '<', $4);}
+| WORD GT QUOTE WORD QUOTE WORD     {$$ = map_expr($1, '>', $4);}
+;
+
+function:
+FUNC_INRANGE OBRACE WORD COMMA date COMMA date CBRACE {$$ = map_daterange($3, $5, $7);}
+;
+
+date:
+DATE_ISO OBRACE WORD CBRACE    {$$ = isodate2unix($3);}
+| WORD                         {$$ = atoi($1) + SPRAW_TIME_OFFSET;}
+;
+
+%%
+
+static time_t isodate2unix(const char *s)
+{
+    struct tm tm;
+
+    if (strptime(s, "%Y-%m-%dT%H:%M:%SZ", &tm) == NULL)
+        return (time_t)-1;
+    return mktime(&tm);
+}
+
+static const char *map_daterange(const char *dateattr, time_t date1, time_t date2)
+{
+    EC_INIT;
+    char *result = NULL;
+    struct spotlight_sparql_map *p;
+    struct tm *tmp;
+    char buf1[64], buf2[64];
+
+    EC_NULL_LOG( tmp = localtime(&date1) );
+    strftime(buf1, sizeof(buf1), "%Y-%m-%dT%H:%M:%SZ", tmp);
+    EC_NULL_LOG( tmp = localtime(&date2) );
+    strftime(buf2, sizeof(buf2), "%Y-%m-%dT%H:%M:%SZ", tmp);
+
+    for (p = spotlight_sparql_map; p->ssm_spotlight_attr; p++) {
+        if (strcmp(dateattr, p->ssm_spotlight_attr) == 0) {
+            result = talloc_asprintf(ssp_slq,
+                                     "?obj %s ?%c FILTER (?%c > '%s' && ?%c < '%s')",
+                                     p->ssm_sparql_attr,
+                                     sparqlvar,
+                                     sparqlvar,
+                                     buf1,
+                                     sparqlvar,
+                                     buf2);
+            sparqlvar++;
+            break;
+        }
+    }
+
+EC_CLEANUP:
+    if (ret != 0)
+        return NULL;
+    return result;
+}
+
+static char *map_type_search(const char *attr, char op, const char *val)
+{
+    char *result = NULL;
+    const char *sparqlAttr;
+
+    for (struct MDTypeMap *p = MDTypeMap; p->mdtm_value; p++) {
+        if (strcmp(p->mdtm_value, val) == 0) {
+            switch (p->mdtm_type) {
+            case kMDTypeMapRDF:
+                sparqlAttr = "rdf:type";
+                break;
+            case kMDTypeMapMime:
+                sparqlAttr = "nie:mimeType";
+                break;
+            default:
+                return NULL;
+            }
+            result = talloc_asprintf(ssp_slq, "?obj %s '%s'",
+                                     sparqlAttr,
+                                     p->mdtm_sparql);
+            break;
+        }
+    }
+    return result;
+}
+
+static const char *map_expr(const char *attr, char op, const char *val)
+{
+    EC_INIT;
+    char *result = NULL;
+    struct spotlight_sparql_map *p;
+    time_t t;
+    struct tm *tmp;
+    char buf1[64];
+    bstring q = NULL, search = NULL, replace = NULL;
+
+    for (p = spotlight_sparql_map; p->ssm_spotlight_attr; p++) {
+        if (strcmp(p->ssm_spotlight_attr, attr) == 0) {
+            if (p->ssm_type != ssmt_type && p->ssm_sparql_attr == NULL) {
+                yyerror("unsupported Spotlight attribute");
+                EC_FAIL;
+            }
+            switch (p->ssm_type) {
+            case ssmt_bool:
+                result = talloc_asprintf(ssp_slq, "?obj %s '%s'", p->ssm_sparql_attr, val);
+                break;
+            case ssmt_num:
+                result = talloc_asprintf(ssp_slq, "?obj %s ?%c FILTER(?%c %c%c '%s')",
+                                         p->ssm_sparql_attr,
+                                         sparqlvar,
+                                         sparqlvar,
+                                         op,
+                                         op == '!' ? '=' : ' ', /* append '=' to '!' */
+                                         val);
+                sparqlvar++;
+                break;
+            case ssmt_str:
+                q = bformat("^%s$", val);
+                search = bfromcstr("*");
+                replace = bfromcstr(".*");
+                bfindreplace(q, search, replace, 0);
+                result = talloc_asprintf(ssp_slq, "?obj %s ?%c FILTER(regex(?%c, '%s'))",
+                                         p->ssm_sparql_attr,
+                                         sparqlvar,
+                                         sparqlvar,
+                                         bdata(q));
+                sparqlvar++;
+                break;
+            case ssmt_fts:
+                result = talloc_asprintf(ssp_slq, "?obj %s '%s'", p->ssm_sparql_attr, val);
+                break;
+            case ssmt_date:
+                t = atoi(val) + SPRAW_TIME_OFFSET;
+                EC_NULL( tmp = localtime(&t) );
+                strftime(buf1, sizeof(buf1), "%Y-%m-%dT%H:%M:%SZ", tmp);
+                result = talloc_asprintf(ssp_slq, "?obj %s ?%c FILTER(?%c %c '%s')",
+                                         p->ssm_sparql_attr,
+                                         sparqlvar,
+                                         sparqlvar,
+                                         op,
+                                         buf1);
+                sparqlvar++;
+                break;
+            case ssmt_type:
+                result = map_type_search(attr, op, val);
+                break;
+            default:
+                EC_FAIL;
+            }
+            break;
+        }
+    }
+
+EC_CLEANUP:
+    if (q)
+        bdestroy(q);
+    if (search)
+        bdestroy(search);
+    if (replace)
+        bdestroy(replace);
+    return result;
+}
+
+void yyerror(const char *str)
+{
+#ifdef MAIN
+    printf("yyerror: %s\n", str);
+#else
+    LOG(log_error, logtype_sl, "yyerror: %s", str);
+#endif
+}
+int yywrap()
+{
+    return 1;
+} 
+
+/**
+ * Map a Spotlight RAW query string to a SPARQL query string
+ *
+ * @param[in]     slq            Spotlight query handle
+ * @param[out]    sparql_result  Mapped SPARQL query, string is allocated in
+ *                               talloc context of slq
+ * @return        0 on success, -1 on error
+ **/
+int map_spotlight_to_sparql_query(slq_t *slq, gchar **sparql_result)
+{
+    EC_INIT;
+    YY_BUFFER_STATE s = NULL;
+    ssp_result = NULL;
+
+    ssp_slq = slq;
+    s = yy_scan_string(slq->slq_qstring);
+    sparqlvar = 'a';
+
+    EC_ZERO( yyparse() );
+
+EC_CLEANUP:
+    if (s)
+        yy_delete_buffer(s);
+    if (ret == 0)
+        *sparql_result = ssp_result;
+    else
+        *sparql_result = NULL;
+    EC_EXIT;
+}
+
+#ifdef MAIN
+int main(int argc, char **argv)
+{
+    int ret;
+    YY_BUFFER_STATE s;
+
+    if (argc != 2) {
+        printf("usage: %s QUERY\n", argv[0]);
+        return 1;
+    }
+
+    ssp_slq = talloc_zero(NULL, slq_t);
+    struct vol *vol = talloc_zero(ssp_slq, struct vol);
+    vol->v_path = "/Volumes/test";
+    ssp_slq->slq_vol = vol;
+    sparqlvar = 'a';
+
+    s = yy_scan_string(argv[1]);
+
+    ret = yyparse();
+
+    yy_delete_buffer(s);
+
+    if (ret == 0)
+        printf("SPARQL: %s\n", ssp_result ? ssp_result : "(empty)");
+
+    return 0;
+} 
+#endif
diff --git a/etc/spotlight/spotlight_rawquery_lexer.c b/etc/spotlight/spotlight_rawquery_lexer.c
new file mode 100644 (file)
index 0000000..38b7a73
--- /dev/null
@@ -0,0 +1,1862 @@
+#line 2 "spotlight_rawquery_lexer.l"
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+
+
+#line 9 "spotlight_rawquery_lexer.c"
+
+#define  YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 35
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with  platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types. 
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t; 
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN               (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN              (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN              (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX               (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX              (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX              (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX              (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX             (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX             (4294967295U)
+#endif
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else  /* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif /* defined (__STDC__) */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index.  If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition.  This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN (yy_start) = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state.  The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START (((yy_start) - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart(yyin  )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE   ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+extern int yyleng;
+
+extern FILE *yyin, *yyout;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+    #define YY_LESS_LINENO(n)
+    
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+       do \
+               { \
+               /* Undo effects of setting up yytext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+               *yy_cp = (yy_hold_char); \
+               YY_RESTORE_YY_MORE_OFFSET \
+               (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+               YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+               } \
+       while ( 0 )
+
+#define unput(c) yyunput( c, (yytext_ptr)  )
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+       {
+       FILE *yy_input_file;
+
+       char *yy_ch_buf;                /* input buffer */
+       char *yy_buf_pos;               /* current position in input buffer */
+
+       /* Size of input buffer in bytes, not including room for EOB
+        * characters.
+        */
+       yy_size_t yy_buf_size;
+
+       /* Number of characters read into yy_ch_buf, not including EOB
+        * characters.
+        */
+       int yy_n_chars;
+
+       /* Whether we "own" the buffer - i.e., we know we created it,
+        * and can realloc() it to grow it, and should free() it to
+        * delete it.
+        */
+       int yy_is_our_buffer;
+
+       /* Whether this is an "interactive" input source; if so, and
+        * if we're using stdio for input, then we want to use getc()
+        * instead of fread(), to make sure we stop fetching input after
+        * each newline.
+        */
+       int yy_is_interactive;
+
+       /* Whether we're considered to be at the beginning of a line.
+        * If so, '^' rules will be active on the next match, otherwise
+        * not.
+        */
+       int yy_at_bol;
+
+    int yy_bs_lineno; /**< The line count. */
+    int yy_bs_column; /**< The column count. */
+    
+       /* Whether to try to fill the input buffer when we reach the
+        * end of it.
+        */
+       int yy_fill_buffer;
+
+       int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+       /* When an EOF's been seen but there's still some text to process
+        * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+        * shouldn't try reading from the input source any more.  We might
+        * still have a bunch of tokens to match, though, because of
+        * possible backing-up.
+        *
+        * When we actually see the EOF, we change the status to "new"
+        * (via yyrestart()), so that the user can continue scanning by
+        * just pointing yyin at a new input file.
+        */
+#define YY_BUFFER_EOF_PENDING 2
+
+       };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* Stack of input buffers. */
+static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
+static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
+static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
+                          ? (yy_buffer_stack)[(yy_buffer_stack_top)] \
+                          : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
+
+/* yy_hold_char holds the character lost when yytext is formed. */
+static char yy_hold_char;
+static int yy_n_chars;         /* number of characters read into yy_ch_buf */
+int yyleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 0;                /* whether we need to initialize */
+static int yy_start = 0;       /* start state number */
+
+/* Flag which is used to allow yywrap()'s to do buffer switches
+ * instead of setting up a fresh yyin.  A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void yyrestart (FILE *input_file  );
+void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer  );
+YY_BUFFER_STATE yy_create_buffer (FILE *file,int size  );
+void yy_delete_buffer (YY_BUFFER_STATE b  );
+void yy_flush_buffer (YY_BUFFER_STATE b  );
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer  );
+void yypop_buffer_state (void );
+
+static void yyensure_buffer_stack (void );
+static void yy_load_buffer_state (void );
+static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file  );
+
+#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER )
+
+YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size  );
+YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str  );
+YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len  );
+
+void *yyalloc (yy_size_t  );
+void *yyrealloc (void *,yy_size_t  );
+void yyfree (void *  );
+
+#define yy_new_buffer yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+       { \
+       if ( ! YY_CURRENT_BUFFER ){ \
+        yyensure_buffer_stack (); \
+               YY_CURRENT_BUFFER_LVALUE =    \
+            yy_create_buffer(yyin,YY_BUF_SIZE ); \
+       } \
+       YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+       }
+
+#define yy_set_bol(at_bol) \
+       { \
+       if ( ! YY_CURRENT_BUFFER ){\
+        yyensure_buffer_stack (); \
+               YY_CURRENT_BUFFER_LVALUE =    \
+            yy_create_buffer(yyin,YY_BUF_SIZE ); \
+       } \
+       YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+       }
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+typedef unsigned char YY_CHAR;
+
+FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0;
+
+typedef int yy_state_type;
+
+extern int yylineno;
+
+int yylineno = 1;
+
+extern char *yytext;
+#define yytext_ptr yytext
+
+static yy_state_type yy_get_previous_state (void );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state  );
+static int yy_get_next_buffer (void );
+static void yy_fatal_error (yyconst char msg[]  );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+       (yytext_ptr) = yy_bp; \
+       yyleng = (size_t) (yy_cp - yy_bp); \
+       (yy_hold_char) = *yy_cp; \
+       *yy_cp = '\0'; \
+       (yy_c_buf_p) = yy_cp;
+
+#define YY_NUM_RULES 17
+#define YY_END_OF_BUFFER 18
+/* This struct is not used in this scanner,
+   but its presence is necessary. */
+struct yy_trans_info
+       {
+       flex_int32_t yy_verify;
+       flex_int32_t yy_nxt;
+       };
+static yyconst flex_int16_t yy_accept[57] =
+    {   0,
+        0,    0,   18,   17,   16,   17,    5,   17,   17,    6,
+        7,   15,   14,   12,   17,   13,   15,   15,   15,   17,
+       17,   17,   17,   11,    0,    8,   15,    0,    0,    0,
+       10,   15,   15,   15,    9,    0,    0,    0,   15,   15,
+       15,    0,    0,   15,   15,    4,    0,   15,    3,    0,
+       15,    0,    1,    0,    2,    0
+    } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    2,    2,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    2,    3,    4,    1,    5,    1,    6,    1,    7,
+        8,    9,    1,   10,    9,   11,    1,    9,    9,    9,
+        9,    9,    9,    9,    9,    9,    9,    9,    1,   12,
+       13,   14,    1,    1,    9,    9,    9,    9,    9,    9,
+        9,    9,   15,    9,    9,    9,    9,    9,    9,    9,
+        9,   16,    9,    9,    9,    9,    9,    9,    9,    9,
+        1,    1,    1,    1,    9,    1,   17,    9,    9,    9,
+
+       18,   19,   20,    9,   21,    9,    9,   22,   23,   24,
+       25,    9,    9,   26,   27,   28,   29,    9,    9,    9,
+        9,    9,    1,   30,    1,    1,    1,   31,   31,   31,
+       31,   31,   31,   31,   31,   31,   31,   31,   31,   31,
+       31,   31,   31,   31,   31,   31,   31,   31,   31,   31,
+       31,   31,   31,   31,   31,   31,   31,   31,   31,   31,
+       31,   31,   31,   31,   31,   31,   31,   31,   31,   31,
+       31,   31,   31,   31,   31,   31,   31,   31,   31,   31,
+       31,   31,   31,   31,   31,   31,   31,   31,   31,   31,
+       31,    1,    1,   32,   32,   32,   32,   32,   32,   32,
+
+       32,   32,   32,   32,   32,   32,   32,   32,   32,   32,
+       32,   32,   32,   32,   32,   32,   32,   32,   32,   32,
+       32,   32,   32,   33,   33,   33,   33,   33,   33,   33,
+       33,   33,   33,   33,   33,   33,   33,   33,   33,   34,
+       34,   34,   34,   34,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1
+    } ;
+
+static yyconst flex_int32_t yy_meta[35] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    2,    1,
+        2,    1,    1,    1,    2,    2,    2,    2,    2,    2,
+        2,    2,    2,    2,    2,    2,    2,    2,    2,    1,
+        1,    2,    2,    2
+    } ;
+
+static yyconst flex_int16_t yy_base[58] =
+    {   0,
+        0,    0,   91,   92,   92,   77,   92,   61,   82,   92,
+       92,    3,   92,   92,   74,   92,   14,   25,   15,   56,
+       54,   53,   52,   92,   61,   92,   57,   49,   48,   47,
+       92,   27,   28,   16,   92,   46,   45,   52,   36,   30,
+       37,   43,   55,   48,   38,   47,   59,   39,   45,   47,
+       40,   40,   42,   40,   92,   92,   42
+    } ;
+
+static yyconst flex_int16_t yy_def[58] =
+    {   0,
+       56,    1,   56,   56,   56,   56,   56,   56,   56,   56,
+       56,   57,   56,   56,   56,   56,   57,   17,   17,   56,
+       56,   56,   56,   56,   56,   56,   17,   56,   56,   56,
+       56,   17,   17,   17,   56,   56,   56,   56,   17,   17,
+       17,   56,   56,   17,   17,   17,   56,   17,   17,   56,
+       17,   56,   17,   56,   56,    0,   56
+    } ;
+
+static yyconst flex_int16_t yy_nxt[127] =
+    {   0,
+        4,    5,    6,    7,    8,    9,   10,   11,   12,   13,
+       12,   14,   15,   16,   17,   12,   12,   12,   18,   12,
+       12,   12,   12,   12,   12,   12,   12,   19,   12,   20,
+        4,   21,   22,   23,   28,   29,   30,   32,   27,   27,
+       34,   33,   39,   27,   41,   28,   29,   30,   27,   40,
+       27,   27,   44,   27,   46,   49,   45,   53,   51,   27,
+       27,   27,   27,   27,   55,   27,   54,   52,   27,   50,
+       27,   48,   47,   27,   43,   42,   27,   37,   36,   27,
+       27,   38,   37,   36,   27,   35,   31,   26,   25,   24,
+       56,    3,   56,   56,   56,   56,   56,   56,   56,   56,
+
+       56,   56,   56,   56,   56,   56,   56,   56,   56,   56,
+       56,   56,   56,   56,   56,   56,   56,   56,   56,   56,
+       56,   56,   56,   56,   56,   56
+    } ;
+
+static yyconst flex_int16_t yy_chk[127] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,   12,   12,   12,   17,   19,   34,
+       19,   18,   32,   57,   34,   17,   17,   17,   18,   33,
+       32,   33,   39,   40,   41,   45,   40,   51,   48,   39,
+       41,   45,   48,   51,   54,   53,   52,   50,   49,   47,
+       46,   44,   43,   42,   38,   37,   36,   30,   29,   28,
+       27,   25,   23,   22,   21,   20,   15,    9,    8,    6,
+        3,   56,   56,   56,   56,   56,   56,   56,   56,   56,
+
+       56,   56,   56,   56,   56,   56,   56,   56,   56,   56,
+       56,   56,   56,   56,   56,   56,   56,   56,   56,   56,
+       56,   56,   56,   56,   56,   56
+    } ;
+
+static yy_state_type yy_last_accepting_state;
+static char *yy_last_accepting_cpos;
+
+extern int yy_flex_debug;
+int yy_flex_debug = 0;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *yytext;
+#line 1 "spotlight_rawquery_lexer.l"
+
+#line 8 "spotlight_rawquery_lexer.l"
+#include <stdbool.h>
+#include <gio/gio.h>
+#include <atalk/talloc.h>
+#include <atalk/spotlight.h>
+#ifdef HAVE_TRACKER_SPARQL
+#include "slmod_sparql_parser.h"
+#define SLQ_VAR ssp_slq
+#endif
+#line 509 "spotlight_rawquery_lexer.c"
+
+#define INITIAL 0
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+static int yy_init_globals (void );
+
+/* Accessor methods to globals.
+   These are made visible to non-reentrant scanners for convenience. */
+
+int yylex_destroy (void );
+
+int yyget_debug (void );
+
+void yyset_debug (int debug_flag  );
+
+YY_EXTRA_TYPE yyget_extra (void );
+
+void yyset_extra (YY_EXTRA_TYPE user_defined  );
+
+FILE *yyget_in (void );
+
+void yyset_in  (FILE * in_str  );
+
+FILE *yyget_out (void );
+
+void yyset_out  (FILE * out_str  );
+
+int yyget_leng (void );
+
+char *yyget_text (void );
+
+int yyget_lineno (void );
+
+void yyset_lineno (int line_number  );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap (void );
+#else
+extern int yywrap (void );
+#endif
+#endif
+
+    static void yyunput (int c,char *buf_ptr  );
+    
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int );
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * );
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (void );
+#else
+static int input (void );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO fwrite( yytext, yyleng, 1, yyout )
+#endif
+
+/* Gets input and stuffs it into "buf".  number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+       if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+               { \
+               int c = '*'; \
+               int n; \
+               for ( n = 0; n < max_size && \
+                            (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+                       buf[n] = (char) c; \
+               if ( c == '\n' ) \
+                       buf[n++] = (char) c; \
+               if ( c == EOF && ferror( yyin ) ) \
+                       YY_FATAL_ERROR( "input in flex scanner failed" ); \
+               result = n; \
+               } \
+       else \
+               { \
+               errno=0; \
+               while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
+                       { \
+                       if( errno != EINTR) \
+                               { \
+                               YY_FATAL_ERROR( "input in flex scanner failed" ); \
+                               break; \
+                               } \
+                       errno=0; \
+                       clearerr(yyin); \
+                       } \
+               }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int yylex (void);
+
+#define YY_DECL int yylex (void)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+       YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+       register yy_state_type yy_current_state;
+       register char *yy_cp, *yy_bp;
+       register int yy_act;
+    
+#line 27 "spotlight_rawquery_lexer.l"
+
+#line 693 "spotlight_rawquery_lexer.c"
+
+       if ( !(yy_init) )
+               {
+               (yy_init) = 1;
+
+#ifdef YY_USER_INIT
+               YY_USER_INIT;
+#endif
+
+               if ( ! (yy_start) )
+                       (yy_start) = 1; /* first start state */
+
+               if ( ! yyin )
+                       yyin = stdin;
+
+               if ( ! yyout )
+                       yyout = stdout;
+
+               if ( ! YY_CURRENT_BUFFER ) {
+                       yyensure_buffer_stack ();
+                       YY_CURRENT_BUFFER_LVALUE =
+                               yy_create_buffer(yyin,YY_BUF_SIZE );
+               }
+
+               yy_load_buffer_state( );
+               }
+
+       while ( 1 )             /* loops until end-of-file is reached */
+               {
+               yy_cp = (yy_c_buf_p);
+
+               /* Support of yytext. */
+               *yy_cp = (yy_hold_char);
+
+               /* yy_bp points to the position in yy_ch_buf of the start of
+                * the current run.
+                */
+               yy_bp = yy_cp;
+
+               yy_current_state = (yy_start);
+yy_match:
+               do
+                       {
+                       register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+                       if ( yy_accept[yy_current_state] )
+                               {
+                               (yy_last_accepting_state) = yy_current_state;
+                               (yy_last_accepting_cpos) = yy_cp;
+                               }
+                       while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+                               {
+                               yy_current_state = (int) yy_def[yy_current_state];
+                               if ( yy_current_state >= 57 )
+                                       yy_c = yy_meta[(unsigned int) yy_c];
+                               }
+                       yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+                       ++yy_cp;
+                       }
+               while ( yy_base[yy_current_state] != 92 );
+
+yy_find_action:
+               yy_act = yy_accept[yy_current_state];
+               if ( yy_act == 0 )
+                       { /* have to back up */
+                       yy_cp = (yy_last_accepting_cpos);
+                       yy_current_state = (yy_last_accepting_state);
+                       yy_act = yy_accept[yy_current_state];
+                       }
+
+               YY_DO_BEFORE_ACTION;
+
+do_action:     /* This label is used only to access EOF actions. */
+
+               switch ( yy_act )
+       { /* beginning of action switch */
+                       case 0: /* must back up */
+                       /* undo the effects of YY_DO_BEFORE_ACTION */
+                       *yy_cp = (yy_hold_char);
+                       yy_cp = (yy_last_accepting_cpos);
+                       yy_current_state = (yy_last_accepting_state);
+                       goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 28 "spotlight_rawquery_lexer.l"
+return FUNC_INRANGE;
+       YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 29 "spotlight_rawquery_lexer.l"
+return DATE_ISO;
+       YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 30 "spotlight_rawquery_lexer.l"
+{yylval.bval = false; return BOOL;}
+       YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 31 "spotlight_rawquery_lexer.l"
+{yylval.bval = true; return BOOL;}
+       YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 32 "spotlight_rawquery_lexer.l"
+return QUOTE;
+       YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 33 "spotlight_rawquery_lexer.l"
+return OBRACE;
+       YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 34 "spotlight_rawquery_lexer.l"
+return CBRACE;
+       YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 35 "spotlight_rawquery_lexer.l"
+return AND;
+       YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 36 "spotlight_rawquery_lexer.l"
+return OR;
+       YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 37 "spotlight_rawquery_lexer.l"
+return EQUAL;
+       YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 38 "spotlight_rawquery_lexer.l"
+return UNEQUAL;
+       YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 39 "spotlight_rawquery_lexer.l"
+return LT;
+       YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 40 "spotlight_rawquery_lexer.l"
+return GT;
+       YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 41 "spotlight_rawquery_lexer.l"
+return COMMA;
+       YY_BREAK
+case 15:
+YY_RULE_SETUP
+#line 42 "spotlight_rawquery_lexer.l"
+{yylval.sval = talloc_strdup(SLQ_VAR, yytext); return WORD;}
+       YY_BREAK
+case 16:
+/* rule 16 can match eol */
+YY_RULE_SETUP
+#line 43 "spotlight_rawquery_lexer.l"
+/* ignore */
+       YY_BREAK
+case 17:
+YY_RULE_SETUP
+#line 44 "spotlight_rawquery_lexer.l"
+ECHO;
+       YY_BREAK
+#line 862 "spotlight_rawquery_lexer.c"
+case YY_STATE_EOF(INITIAL):
+       yyterminate();
+
+       case YY_END_OF_BUFFER:
+               {
+               /* Amount of text matched not including the EOB char. */
+               int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1;
+
+               /* Undo the effects of YY_DO_BEFORE_ACTION. */
+               *yy_cp = (yy_hold_char);
+               YY_RESTORE_YY_MORE_OFFSET
+
+               if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+                       {
+                       /* We're scanning a new file or input source.  It's
+                        * possible that this happened because the user
+                        * just pointed yyin at a new source and called
+                        * yylex().  If so, then we have to assure
+                        * consistency between YY_CURRENT_BUFFER and our
+                        * globals.  Here is the right place to do so, because
+                        * this is the first action (other than possibly a
+                        * back-up) that will match for the new input source.
+                        */
+                       (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+                       YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+                       YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+                       }
+
+               /* Note that here we test for yy_c_buf_p "<=" to the position
+                * of the first EOB in the buffer, since yy_c_buf_p will
+                * already have been incremented past the NUL character
+                * (since all states make transitions on EOB to the
+                * end-of-buffer state).  Contrast this with the test
+                * in input().
+                */
+               if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+                       { /* This was really a NUL. */
+                       yy_state_type yy_next_state;
+
+                       (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text;
+
+                       yy_current_state = yy_get_previous_state(  );
+
+                       /* Okay, we're now positioned to make the NUL
+                        * transition.  We couldn't have
+                        * yy_get_previous_state() go ahead and do it
+                        * for us because it doesn't know how to deal
+                        * with the possibility of jamming (and we don't
+                        * want to build jamming into it because then it
+                        * will run more slowly).
+                        */
+
+                       yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+                       yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+
+                       if ( yy_next_state )
+                               {
+                               /* Consume the NUL. */
+                               yy_cp = ++(yy_c_buf_p);
+                               yy_current_state = yy_next_state;
+                               goto yy_match;
+                               }
+
+                       else
+                               {
+                               yy_cp = (yy_c_buf_p);
+                               goto yy_find_action;
+                               }
+                       }
+
+               else switch ( yy_get_next_buffer(  ) )
+                       {
+                       case EOB_ACT_END_OF_FILE:
+                               {
+                               (yy_did_buffer_switch_on_eof) = 0;
+
+                               if ( yywrap( ) )
+                                       {
+                                       /* Note: because we've taken care in
+                                        * yy_get_next_buffer() to have set up
+                                        * yytext, we can now set up
+                                        * yy_c_buf_p so that if some total
+                                        * hoser (like flex itself) wants to
+                                        * call the scanner after we return the
+                                        * YY_NULL, it'll still work - another
+                                        * YY_NULL will get returned.
+                                        */
+                                       (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ;
+
+                                       yy_act = YY_STATE_EOF(YY_START);
+                                       goto do_action;
+                                       }
+
+                               else
+                                       {
+                                       if ( ! (yy_did_buffer_switch_on_eof) )
+                                               YY_NEW_FILE;
+                                       }
+                               break;
+                               }
+
+                       case EOB_ACT_CONTINUE_SCAN:
+                               (yy_c_buf_p) =
+                                       (yytext_ptr) + yy_amount_of_matched_text;
+
+                               yy_current_state = yy_get_previous_state(  );
+
+                               yy_cp = (yy_c_buf_p);
+                               yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+                               goto yy_match;
+
+                       case EOB_ACT_LAST_MATCH:
+                               (yy_c_buf_p) =
+                               &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)];
+
+                               yy_current_state = yy_get_previous_state(  );
+
+                               yy_cp = (yy_c_buf_p);
+                               yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+                               goto yy_find_action;
+                       }
+               break;
+               }
+
+       default:
+               YY_FATAL_ERROR(
+                       "fatal flex scanner internal error--no action found" );
+       } /* end of action switch */
+               } /* end of scanning one token */
+} /* end of yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ *     EOB_ACT_LAST_MATCH -
+ *     EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ *     EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (void)
+{
+       register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+       register char *source = (yytext_ptr);
+       register int number_to_move, i;
+       int ret_val;
+
+       if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] )
+               YY_FATAL_ERROR(
+               "fatal flex scanner internal error--end of buffer missed" );
+
+       if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+               { /* Don't try to fill the buffer, so this is an EOF. */
+               if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 )
+                       {
+                       /* We matched a single character, the EOB, so
+                        * treat this as a final EOF.
+                        */
+                       return EOB_ACT_END_OF_FILE;
+                       }
+
+               else
+                       {
+                       /* We matched some text prior to the EOB, first
+                        * process it.
+                        */
+                       return EOB_ACT_LAST_MATCH;
+                       }
+               }
+
+       /* Try to read more data. */
+
+       /* First move last chars to start of buffer. */
+       number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1;
+
+       for ( i = 0; i < number_to_move; ++i )
+               *(dest++) = *(source++);
+
+       if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+               /* don't do the read, it's not guaranteed to return an EOF,
+                * just force an EOF
+                */
+               YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0;
+
+       else
+               {
+                       int num_to_read =
+                       YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+               while ( num_to_read <= 0 )
+                       { /* Not enough room in the buffer - grow it. */
+
+                       /* just a shorter name for the current buffer */
+                       YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+                       int yy_c_buf_p_offset =
+                               (int) ((yy_c_buf_p) - b->yy_ch_buf);
+
+                       if ( b->yy_is_our_buffer )
+                               {
+                               int new_size = b->yy_buf_size * 2;
+
+                               if ( new_size <= 0 )
+                                       b->yy_buf_size += b->yy_buf_size / 8;
+                               else
+                                       b->yy_buf_size *= 2;
+
+                               b->yy_ch_buf = (char *)
+                                       /* Include room in for 2 EOB chars. */
+                                       yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2  );
+                               }
+                       else
+                               /* Can't grow it, we don't own it. */
+                               b->yy_ch_buf = 0;
+
+                       if ( ! b->yy_ch_buf )
+                               YY_FATAL_ERROR(
+                               "fatal error - scanner input buffer overflow" );
+
+                       (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+                       num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+                                               number_to_move - 1;
+
+                       }
+
+               if ( num_to_read > YY_READ_BUF_SIZE )
+                       num_to_read = YY_READ_BUF_SIZE;
+
+               /* Read in more data. */
+               YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+                       (yy_n_chars), (size_t) num_to_read );
+
+               YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+               }
+
+       if ( (yy_n_chars) == 0 )
+               {
+               if ( number_to_move == YY_MORE_ADJ )
+                       {
+                       ret_val = EOB_ACT_END_OF_FILE;
+                       yyrestart(yyin  );
+                       }
+
+               else
+                       {
+                       ret_val = EOB_ACT_LAST_MATCH;
+                       YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+                               YY_BUFFER_EOF_PENDING;
+                       }
+               }
+
+       else
+               ret_val = EOB_ACT_CONTINUE_SCAN;
+
+       if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+               /* Extend the array by 50%, plus the number we really need. */
+               yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1);
+               YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size  );
+               if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+                       YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+       }
+
+       (yy_n_chars) += number_to_move;
+       YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
+       YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
+
+       (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+       return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+    static yy_state_type yy_get_previous_state (void)
+{
+       register yy_state_type yy_current_state;
+       register char *yy_cp;
+    
+       yy_current_state = (yy_start);
+
+       for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
+               {
+               register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+               if ( yy_accept[yy_current_state] )
+                       {
+                       (yy_last_accepting_state) = yy_current_state;
+                       (yy_last_accepting_cpos) = yy_cp;
+                       }
+               while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+                       {
+                       yy_current_state = (int) yy_def[yy_current_state];
+                       if ( yy_current_state >= 57 )
+                               yy_c = yy_meta[(unsigned int) yy_c];
+                       }
+               yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+               }
+
+       return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ *     next_state = yy_try_NUL_trans( current_state );
+ */
+    static yy_state_type yy_try_NUL_trans  (yy_state_type yy_current_state )
+{
+       register int yy_is_jam;
+       register char *yy_cp = (yy_c_buf_p);
+
+       register YY_CHAR yy_c = 1;
+       if ( yy_accept[yy_current_state] )
+               {
+               (yy_last_accepting_state) = yy_current_state;
+               (yy_last_accepting_cpos) = yy_cp;
+               }
+       while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+               {
+               yy_current_state = (int) yy_def[yy_current_state];
+               if ( yy_current_state >= 57 )
+                       yy_c = yy_meta[(unsigned int) yy_c];
+               }
+       yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+       yy_is_jam = (yy_current_state == 56);
+
+       return yy_is_jam ? 0 : yy_current_state;
+}
+
+    static void yyunput (int c, register char * yy_bp )
+{
+       register char *yy_cp;
+    
+    yy_cp = (yy_c_buf_p);
+
+       /* undo effects of setting up yytext */
+       *yy_cp = (yy_hold_char);
+
+       if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+               { /* need to shift things up to make room */
+               /* +2 for EOB chars. */
+               register int number_to_move = (yy_n_chars) + 2;
+               register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+                                       YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
+               register char *source =
+                               &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
+
+               while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+                       *--dest = *--source;
+
+               yy_cp += (int) (dest - source);
+               yy_bp += (int) (dest - source);
+               YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
+                       (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
+
+               if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+                       YY_FATAL_ERROR( "flex scanner push-back overflow" );
+               }
+
+       *--yy_cp = (char) c;
+
+       (yytext_ptr) = yy_bp;
+       (yy_hold_char) = *yy_cp;
+       (yy_c_buf_p) = yy_cp;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+    static int yyinput (void)
+#else
+    static int input  (void)
+#endif
+
+{
+       int c;
+    
+       *(yy_c_buf_p) = (yy_hold_char);
+
+       if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
+               {
+               /* yy_c_buf_p now points to the character we want to return.
+                * If this occurs *before* the EOB characters, then it's a
+                * valid NUL; if not, then we've hit the end of the buffer.
+                */
+               if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+                       /* This was really a NUL. */
+                       *(yy_c_buf_p) = '\0';
+
+               else
+                       { /* need more input */
+                       int offset = (yy_c_buf_p) - (yytext_ptr);
+                       ++(yy_c_buf_p);
+
+                       switch ( yy_get_next_buffer(  ) )
+                               {
+                               case EOB_ACT_LAST_MATCH:
+                                       /* This happens because yy_g_n_b()
+                                        * sees that we've accumulated a
+                                        * token and flags that we need to
+                                        * try matching the token before
+                                        * proceeding.  But for input(),
+                                        * there's no matching to consider.
+                                        * So convert the EOB_ACT_LAST_MATCH
+                                        * to EOB_ACT_END_OF_FILE.
+                                        */
+
+                                       /* Reset buffer status. */
+                                       yyrestart(yyin );
+
+                                       /*FALLTHROUGH*/
+
+                               case EOB_ACT_END_OF_FILE:
+                                       {
+                                       if ( yywrap( ) )
+                                               return EOF;
+
+                                       if ( ! (yy_did_buffer_switch_on_eof) )
+                                               YY_NEW_FILE;
+#ifdef __cplusplus
+                                       return yyinput();
+#else
+                                       return input();
+#endif
+                                       }
+
+                               case EOB_ACT_CONTINUE_SCAN:
+                                       (yy_c_buf_p) = (yytext_ptr) + offset;
+                                       break;
+                               }
+                       }
+               }
+
+       c = *(unsigned char *) (yy_c_buf_p);    /* cast for 8-bit char's */
+       *(yy_c_buf_p) = '\0';   /* preserve yytext */
+       (yy_hold_char) = *++(yy_c_buf_p);
+
+       return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ * 
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+    void yyrestart  (FILE * input_file )
+{
+    
+       if ( ! YY_CURRENT_BUFFER ){
+        yyensure_buffer_stack ();
+               YY_CURRENT_BUFFER_LVALUE =
+            yy_create_buffer(yyin,YY_BUF_SIZE );
+       }
+
+       yy_init_buffer(YY_CURRENT_BUFFER,input_file );
+       yy_load_buffer_state( );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ * 
+ */
+    void yy_switch_to_buffer  (YY_BUFFER_STATE  new_buffer )
+{
+    
+       /* TODO. We should be able to replace this entire function body
+        * with
+        *              yypop_buffer_state();
+        *              yypush_buffer_state(new_buffer);
+     */
+       yyensure_buffer_stack ();
+       if ( YY_CURRENT_BUFFER == new_buffer )
+               return;
+
+       if ( YY_CURRENT_BUFFER )
+               {
+               /* Flush out information for old buffer. */
+               *(yy_c_buf_p) = (yy_hold_char);
+               YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+               YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+               }
+
+       YY_CURRENT_BUFFER_LVALUE = new_buffer;
+       yy_load_buffer_state( );
+
+       /* We don't actually know whether we did this switch during
+        * EOF (yywrap()) processing, but the only time this flag
+        * is looked at is after yywrap() is called, so it's safe
+        * to go ahead and always set it.
+        */
+       (yy_did_buffer_switch_on_eof) = 1;
+}
+
+static void yy_load_buffer_state  (void)
+{
+       (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+       (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+       yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+       (yy_hold_char) = *(yy_c_buf_p);
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ * 
+ * @return the allocated buffer state.
+ */
+    YY_BUFFER_STATE yy_create_buffer  (FILE * file, int  size )
+{
+       YY_BUFFER_STATE b;
+    
+       b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state )  );
+       if ( ! b )
+               YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+       b->yy_buf_size = size;
+
+       /* yy_ch_buf has to be 2 characters longer than the size given because
+        * we need to put in 2 end-of-buffer characters.
+        */
+       b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2  );
+       if ( ! b->yy_ch_buf )
+               YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+       b->yy_is_our_buffer = 1;
+
+       yy_init_buffer(b,file );
+
+       return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with yy_create_buffer()
+ * 
+ */
+    void yy_delete_buffer (YY_BUFFER_STATE  b )
+{
+    
+       if ( ! b )
+               return;
+
+       if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+               YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+       if ( b->yy_is_our_buffer )
+               yyfree((void *) b->yy_ch_buf  );
+
+       yyfree((void *) b  );
+}
+
+#ifndef __cplusplus
+extern int isatty (int );
+#endif /* __cplusplus */
+    
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a yyrestart() or at EOF.
+ */
+    static void yy_init_buffer  (YY_BUFFER_STATE  b, FILE * file )
+
+{
+       int oerrno = errno;
+    
+       yy_flush_buffer(b );
+
+       b->yy_input_file = file;
+       b->yy_fill_buffer = 1;
+
+    /* If b is the current buffer, then yy_init_buffer was _probably_
+     * called from yyrestart() or through yy_get_next_buffer.
+     * In that case, we don't want to reset the lineno or column.
+     */
+    if (b != YY_CURRENT_BUFFER){
+        b->yy_bs_lineno = 1;
+        b->yy_bs_column = 0;
+    }
+
+        b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+    
+       errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ * 
+ */
+    void yy_flush_buffer (YY_BUFFER_STATE  b )
+{
+       if ( ! b )
+               return;
+
+       b->yy_n_chars = 0;
+
+       /* We always need two end-of-buffer characters.  The first causes
+        * a transition to the end-of-buffer state.  The second causes
+        * a jam in that state.
+        */
+       b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+       b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+       b->yy_buf_pos = &b->yy_ch_buf[0];
+
+       b->yy_at_bol = 1;
+       b->yy_buffer_status = YY_BUFFER_NEW;
+
+       if ( b == YY_CURRENT_BUFFER )
+               yy_load_buffer_state( );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ *  the current state. This function will allocate the stack
+ *  if necessary.
+ *  @param new_buffer The new state.
+ *  
+ */
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer )
+{
+       if (new_buffer == NULL)
+               return;
+
+       yyensure_buffer_stack();
+
+       /* This block is copied from yy_switch_to_buffer. */
+       if ( YY_CURRENT_BUFFER )
+               {
+               /* Flush out information for old buffer. */
+               *(yy_c_buf_p) = (yy_hold_char);
+               YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+               YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+               }
+
+       /* Only push if top exists. Otherwise, replace top. */
+       if (YY_CURRENT_BUFFER)
+               (yy_buffer_stack_top)++;
+       YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+       /* copied from yy_switch_to_buffer. */
+       yy_load_buffer_state( );
+       (yy_did_buffer_switch_on_eof) = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ *  The next element becomes the new top.
+ *  
+ */
+void yypop_buffer_state (void)
+{
+       if (!YY_CURRENT_BUFFER)
+               return;
+
+       yy_delete_buffer(YY_CURRENT_BUFFER );
+       YY_CURRENT_BUFFER_LVALUE = NULL;
+       if ((yy_buffer_stack_top) > 0)
+               --(yy_buffer_stack_top);
+
+       if (YY_CURRENT_BUFFER) {
+               yy_load_buffer_state( );
+               (yy_did_buffer_switch_on_eof) = 1;
+       }
+}
+
+/* Allocates the stack if it does not exist.
+ *  Guarantees space for at least one push.
+ */
+static void yyensure_buffer_stack (void)
+{
+       int num_to_alloc;
+    
+       if (!(yy_buffer_stack)) {
+
+               /* First allocation is just for 2 elements, since we don't know if this
+                * scanner will even need a stack. We use 2 instead of 1 to avoid an
+                * immediate realloc on the next call.
+         */
+               num_to_alloc = 1;
+               (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc
+                                                               (num_to_alloc * sizeof(struct yy_buffer_state*)
+                                                               );
+               if ( ! (yy_buffer_stack) )
+                       YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+                                                                 
+               memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+                               
+               (yy_buffer_stack_max) = num_to_alloc;
+               (yy_buffer_stack_top) = 0;
+               return;
+       }
+
+       if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){
+
+               /* Increase the buffer to prepare for a possible push. */
+               int grow_size = 8 /* arbitrary grow size */;
+
+               num_to_alloc = (yy_buffer_stack_max) + grow_size;
+               (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc
+                                                               ((yy_buffer_stack),
+                                                               num_to_alloc * sizeof(struct yy_buffer_state*)
+                                                               );
+               if ( ! (yy_buffer_stack) )
+                       YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+               /* zero only the new slots.*/
+               memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
+               (yy_buffer_stack_max) = num_to_alloc;
+       }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ * 
+ * @return the newly allocated buffer state object. 
+ */
+YY_BUFFER_STATE yy_scan_buffer  (char * base, yy_size_t  size )
+{
+       YY_BUFFER_STATE b;
+    
+       if ( size < 2 ||
+            base[size-2] != YY_END_OF_BUFFER_CHAR ||
+            base[size-1] != YY_END_OF_BUFFER_CHAR )
+               /* They forgot to leave room for the EOB's. */
+               return 0;
+
+       b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state )  );
+       if ( ! b )
+               YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+       b->yy_buf_size = size - 2;      /* "- 2" to take care of EOB's */
+       b->yy_buf_pos = b->yy_ch_buf = base;
+       b->yy_is_our_buffer = 0;
+       b->yy_input_file = 0;
+       b->yy_n_chars = b->yy_buf_size;
+       b->yy_is_interactive = 0;
+       b->yy_at_bol = 1;
+       b->yy_fill_buffer = 0;
+       b->yy_buffer_status = YY_BUFFER_NEW;
+
+       yy_switch_to_buffer(b  );
+
+       return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to yylex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ * 
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ *       yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE yy_scan_string (yyconst char * yystr )
+{
+    
+       return yy_scan_bytes(yystr,strlen(yystr) );
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
+ * 
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_bytes  (yyconst char * yybytes, int  _yybytes_len )
+{
+       YY_BUFFER_STATE b;
+       char *buf;
+       yy_size_t n;
+       int i;
+    
+       /* Get memory for full buffer, including space for trailing EOB's. */
+       n = _yybytes_len + 2;
+       buf = (char *) yyalloc(n  );
+       if ( ! buf )
+               YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+       for ( i = 0; i < _yybytes_len; ++i )
+               buf[i] = yybytes[i];
+
+       buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+       b = yy_scan_buffer(buf,n );
+       if ( ! b )
+               YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+       /* It's okay to grow etc. this buffer, and we should throw it
+        * away when we're done.
+        */
+       b->yy_is_our_buffer = 1;
+
+       return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg )
+{
+       (void) fprintf( stderr, "%s\n", msg );
+       exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+       do \
+               { \
+               /* Undo effects of setting up yytext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+               yytext[yyleng] = (yy_hold_char); \
+               (yy_c_buf_p) = yytext + yyless_macro_arg; \
+               (yy_hold_char) = *(yy_c_buf_p); \
+               *(yy_c_buf_p) = '\0'; \
+               yyleng = yyless_macro_arg; \
+               } \
+       while ( 0 )
+
+/* Accessor  methods (get/set functions) to struct members. */
+
+/** Get the current line number.
+ * 
+ */
+int yyget_lineno  (void)
+{
+        
+    return yylineno;
+}
+
+/** Get the input stream.
+ * 
+ */
+FILE *yyget_in  (void)
+{
+        return yyin;
+}
+
+/** Get the output stream.
+ * 
+ */
+FILE *yyget_out  (void)
+{
+        return yyout;
+}
+
+/** Get the length of the current token.
+ * 
+ */
+int yyget_leng  (void)
+{
+        return yyleng;
+}
+
+/** Get the current token.
+ * 
+ */
+
+char *yyget_text  (void)
+{
+        return yytext;
+}
+
+/** Set the current line number.
+ * @param line_number
+ * 
+ */
+void yyset_lineno (int  line_number )
+{
+    
+    yylineno = line_number;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ * 
+ * @see yy_switch_to_buffer
+ */
+void yyset_in (FILE *  in_str )
+{
+        yyin = in_str ;
+}
+
+void yyset_out (FILE *  out_str )
+{
+        yyout = out_str ;
+}
+
+int yyget_debug  (void)
+{
+        return yy_flex_debug;
+}
+
+void yyset_debug (int  bdebug )
+{
+        yy_flex_debug = bdebug ;
+}
+
+static int yy_init_globals (void)
+{
+        /* Initialization is the same as for the non-reentrant scanner.
+     * This function is called from yylex_destroy(), so don't allocate here.
+     */
+
+    (yy_buffer_stack) = 0;
+    (yy_buffer_stack_top) = 0;
+    (yy_buffer_stack_max) = 0;
+    (yy_c_buf_p) = (char *) 0;
+    (yy_init) = 0;
+    (yy_start) = 0;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+    yyin = stdin;
+    yyout = stdout;
+#else
+    yyin = (FILE *) 0;
+    yyout = (FILE *) 0;
+#endif
+
+    /* For future reference: Set errno on error, since we are called by
+     * yylex_init()
+     */
+    return 0;
+}
+
+/* yylex_destroy is for both reentrant and non-reentrant scanners. */
+int yylex_destroy  (void)
+{
+    
+    /* Pop the buffer stack, destroying each element. */
+       while(YY_CURRENT_BUFFER){
+               yy_delete_buffer(YY_CURRENT_BUFFER  );
+               YY_CURRENT_BUFFER_LVALUE = NULL;
+               yypop_buffer_state();
+       }
+
+       /* Destroy the stack itself. */
+       yyfree((yy_buffer_stack) );
+       (yy_buffer_stack) = NULL;
+
+    /* Reset the globals. This is important in a non-reentrant scanner so the next time
+     * yylex() is called, initialization will occur. */
+    yy_init_globals( );
+
+    return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n )
+{
+       register int i;
+       for ( i = 0; i < n; ++i )
+               s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s )
+{
+       register int n;
+       for ( n = 0; s[n]; ++n )
+               ;
+
+       return n;
+}
+#endif
+
+void *yyalloc (yy_size_t  size )
+{
+       return (void *) malloc( size );
+}
+
+void *yyrealloc  (void * ptr, yy_size_t  size )
+{
+       /* The cast to (char *) in the following accommodates both
+        * implementations that use char* generic pointers, and those
+        * that use void* generic pointers.  It works with the latter
+        * because both ANSI C and C++ allow castless assignment from
+        * any pointer type to void*, and deal with argument conversions
+        * as though doing an assignment.
+        */
+       return (void *) realloc( (char *) ptr, size );
+}
+
+void yyfree (void * ptr )
+{
+       free( (char *) ptr );   /* see yyrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 44 "spotlight_rawquery_lexer.l"
+
+
+
diff --git a/etc/spotlight/spotlight_rawquery_lexer.l b/etc/spotlight/spotlight_rawquery_lexer.l
new file mode 100644 (file)
index 0000000..aa1e25f
--- /dev/null
@@ -0,0 +1,44 @@
+%top{
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+}
+
+%{
+#include <stdbool.h>
+#include <gio/gio.h>
+#include <atalk/talloc.h>
+#include <atalk/spotlight.h>
+#ifdef HAVE_TRACKER_SPARQL
+#include "slmod_sparql_parser.h"
+#define SLQ_VAR ssp_slq
+#endif
+%}
+
+ASC     [a-zA-Z0-9_\*\:\-\.]
+U       [\x80-\xbf]
+U2      [\xc2-\xdf]
+U3      [\xe0-\xef]
+U4      [\xf0-\xf4]
+
+UANY    {ASC}|{U2}{U}|{U3}{U}{U}|{U4}{U}{U}{U}
+UONLY   {U2}{U}|{U3}{U}{U}|{U4}{U}{U}{U}
+
+%%
+InRange                               return FUNC_INRANGE;
+\$time\.iso                           return DATE_ISO;
+false                                 {yylval.bval = false; return BOOL;}
+true                                  {yylval.bval = true; return BOOL;}
+\"                                    return QUOTE;
+\(                                    return OBRACE;
+\)                                    return CBRACE;
+\&\&                                  return AND;
+\|\|                                  return OR;
+\=\=                                  return EQUAL;
+\!\=                                  return UNEQUAL;
+\<                                    return LT;
+\>                                    return GT;
+\,                                    return COMMA;
+{UANY}+                               {yylval.sval = talloc_strdup(SLQ_VAR, yytext); return WORD;}
+[ \t\n]                               /* ignore */
+%%
index 8857759d2f41b12c1e345b5d060d0db16515655e..8e79b450d4c802e9e6e516c598c2ced645270476 100644 (file)
@@ -952,7 +952,6 @@ static void uam_cleanup(void)
     gcry_mpi_release(g);
 }
 
-
 UAM_MODULE_EXPORT struct uam_export uams_dhx2 = {
     UAM_MODULE_SERVER,
     UAM_MODULE_VERSION,
index 42085187de9665cefac9ca580b6c2ce541b59270..2f63e0f676e76edea239c6738f93c6751e3522e4 100644 (file)
@@ -42,7 +42,11 @@ noinst_HEADERS = \
        ftw.h \
        dsi.h \
        ldapconfig.h \
-       fce_api.h
+       talloc.h \
+       dalloc.h \
+       byteorder.h \
+       fce_api.h \
+       spotlight.h
 
 EXTRA_DIST = afp_dtrace.d
 
diff --git a/include/atalk/byteorder.h b/include/atalk/byteorder.h
new file mode 100644 (file)
index 0000000..0ea3972
--- /dev/null
@@ -0,0 +1,217 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB Byte handling
+   Copyright (C) Andrew Tridgell 1992-1998
+   
+   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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _BYTEORDER_H
+#define _BYTEORDER_H
+#include <arpa/inet.h>
+
+/*
+   This file implements macros for machine independent short and 
+   int manipulation
+
+Here is a description of this file that I emailed to the samba list once:
+
+> I am confused about the way that byteorder.h works in Samba. I have
+> looked at it, and I would have thought that you might make a distinction
+> between LE and BE machines, but you only seem to distinguish between 386
+> and all other architectures.
+> 
+> Can you give me a clue?
+
+sure.
+
+The distinction between 386 and other architectures is only there as
+an optimisation. You can take it out completely and it will make no
+difference. The routines (macros) in byteorder.h are totally byteorder
+independent. The 386 optimsation just takes advantage of the fact that
+the x86 processors don't care about alignment, so we don't have to
+align ints on int boundaries etc. If there are other processors out
+there that aren't alignment sensitive then you could also define
+CAREFUL_ALIGNMENT=0 on those processors as well.
+
+Ok, now to the macros themselves. I'll take a simple example, say we
+want to extract a 2 byte integer from a SMB packet and put it into a
+type called uint16 that is in the local machines byte order, and you
+want to do it with only the assumption that uint16 is _at_least_ 16
+bits long (this last condition is very important for architectures
+that don't have any int types that are 2 bytes long)
+
+You do this:
+
+#define CVAL(buf,pos) (((unsigned char *)(buf))[pos])
+#define PVAL(buf,pos) ((unsigned)CVAL(buf,pos))
+#define SVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+1)<<8)
+
+then to extract a uint16 value at offset 25 in a buffer you do this:
+
+char *buffer = foo_bar();
+uint16 xx = SVAL(buffer,25);
+
+We are using the byteoder independence of the ANSI C bitshifts to do
+the work. A good optimising compiler should turn this into efficient
+code, especially if it happens to have the right byteorder :-)
+
+I know these macros can be made a bit tidier by removing some of the
+casts, but you need to look at byteorder.h as a whole to see the
+reasoning behind them. byteorder.h defines the following macros:
+
+SVAL(buf,pos) - extract a 2 byte SMB value
+IVAL(buf,pos) - extract a 4 byte SMB value
+LVAL(buf,pos) - extract a 8 byte SMB value
+SVALS(buf,pos) signed version of SVAL()
+IVALS(buf,pos) signed version of IVAL()
+
+SSVAL(buf,pos,val) - put a 2 byte SMB value into a buffer
+SIVAL(buf,pos,val) - put a 4 byte SMB value into a buffer
+SSVALS(buf,pos,val) - signed version of SSVAL()
+SIVALS(buf,pos,val) - signed version of SIVAL()
+
+RSVAL(buf,pos) - like SVAL() but for NMB byte ordering
+RSVALS(buf,pos) - like SVALS() but for NMB byte ordering
+RIVAL(buf,pos) - like IVAL() but for NMB byte ordering
+RIVALS(buf,pos) - like IVALS() but for NMB byte ordering
+RSSVAL(buf,pos,val) - like SSVAL() but for NMB ordering
+RSIVAL(buf,pos,val) - like SIVAL() but for NMB ordering
+RSIVALS(buf,pos,val) - like SIVALS() but for NMB ordering
+
+it also defines lots of intermediate macros, just ignore those :-)
+
+*/
+
+#undef CAREFUL_ALIGNMENT
+
+/* we know that the 386 can handle misalignment and has the "right" 
+   byteorder */
+#ifdef __i386__
+#define CAREFUL_ALIGNMENT 0
+#endif
+
+#ifndef CAREFUL_ALIGNMENT
+#define CAREFUL_ALIGNMENT 1
+#endif
+
+#define CVAL(buf,pos) ((unsigned)(((const unsigned char *)(buf))[pos]))
+#define CVAL_NC(buf,pos) (((unsigned char *)(buf))[pos]) /* Non-const version of CVAL */
+#define PVAL(buf,pos) (CVAL(buf,pos))
+#define SCVAL(buf,pos,val) (CVAL_NC(buf,pos) = (val))
+
+
+#if CAREFUL_ALIGNMENT
+
+#ifdef WORDS_BIGENDIAN
+
+#define SVAL(buf,pos) (PVAL(buf,(pos)+1)|PVAL(buf,pos)<<8)
+#define SVALS(buf,pos) ((int16)SVAL(buf,pos))
+#define IVAL(buf,pos) (SVAL(buf,pos)|SVAL(buf,(pos)+2)<<16)
+#define IVALS(buf,pos) ((int32_t)IVAL(buf,pos))
+#define LVAL(buf,pos) (IVAL(buf,pos)|IVAL(buf,(pos)+4)<<32)
+#define LVALS(buf,pos) ((int64_t)LVAL(buf,pos))
+
+#define SSVALX(buf,pos,val) (CVAL_NC(buf,pos+1)=(unsigned char)((val)&0xFF),CVAL_NC(buf,pos)=(unsigned char)((val)>>8))
+#define SIVALX(buf,pos,val) (SSVALX(buf,pos,((val)&0xFFFF)),SSVALX(buf,pos+2,(val)>>16))
+#define SLVALX(buf,pos,val) (SIVALX(buf,pos,((val)&0xFFFFFFFF)),SIVALX(buf,pos+4,(val)>>32))
+
+#define SSVAL(buf,pos,val) SSVALX((buf),(pos),((uint16_t)(val)))
+#define SSVALS(buf,pos,val) SSVALX((buf),(pos),((int16)(val)))
+#define SIVAL(buf,pos,val) SIVALX((buf),(pos),((uint32_t)(val)))
+#define SIVALS(buf,pos,val) SIVALX((buf),(pos),((int32_t)(val)))
+#define SLVAL(buf,pos,val) SLVALX((buf),(pos),((uint64_t)(val)))
+#define SLVALS(buf,pos,val) SLVALX((buf),(pos),((int64_t)(val)))
+
+#else
+
+#define SVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+1)<<8)
+#define SVALS(buf,pos) ((int16)SVAL(buf,pos))
+#define IVAL(buf,pos) (SVAL(buf,pos)|SVAL(buf,(pos)+2)<<16)
+#define IVALS(buf,pos) ((int32_t)IVAL(buf,pos))
+#define LVAL(buf,pos) (IVAL(buf,pos)|((uint64_t)IVAL(buf,(pos)+4))<<32)
+#define LVALS(buf,pos) ((int64_t)LVAL(buf,pos))
+
+#define SSVALX(buf,pos,val) (CVAL_NC(buf,pos)=(unsigned char)((val)&0xFF),CVAL_NC(buf,pos+1)=(unsigned char)((val)>>8))
+#define SIVALX(buf,pos,val) (SSVALX(buf,pos,((val)&0xFFFF)),SSVALX(buf,pos+2,(val)>>16))
+#define SLVALX(buf,pos,val) (SIVALX(buf,pos,((val)&0xFFFFFFFF)),SIVALX(buf,pos+4,(val)>>32))
+
+#define SSVAL(buf,pos,val) SSVALX((buf),(pos),((uint16_t)(val)))
+#define SSVALS(buf,pos,val) SSVALX((buf),(pos),((int16)(val)))
+#define SIVAL(buf,pos,val) SIVALX((buf),(pos),((uint32_t)(val)))
+#define SIVALS(buf,pos,val) SIVALX((buf),(pos),((int32_t)(val)))
+#define SLVAL(buf,pos,val) SLVALX((buf),(pos),((uint64_t)(val)))
+#define SLVALS(buf,pos,val) SLVALX((buf),(pos),((int64_t)(val)))
+
+#endif
+
+#else /* CAREFUL_ALIGNMENT */
+
+/* this handles things for architectures like the 386 that can handle
+   alignment errors */
+/*
+   WARNING: This section is dependent on the length of int16 and int32
+   being correct 
+*/
+
+/* get single value from an SMB buffer */
+#define SVAL(buf,pos) (*(const uint16_t *)((const char *)(buf) + (pos)))
+#define SVAL_NC(buf,pos) (*(uint16_t *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define IVAL(buf,pos) (*(const uint32_t *)((const char *)(buf) + (pos)))
+#define IVAL_NC(buf,pos) (*(uint32_t *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define LVAL(buf,pos) (*(const uint64_t *)((const char *)(buf) + (pos)))
+#define LVAL_NC(buf,pos) (*(uint64_t *)((char *)(buf) + (pos)))
+#define SVALS(buf,pos) (*(const int16_t *)((const char *)(buf) + (pos)))
+#define SVALS_NC(buf,pos) (*(int16 *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define IVALS(buf,pos) (*(const int32_t *)((const char *)(buf) + (pos)))
+#define IVALS_NC(buf,pos) (*(int32_t *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define LVALS(buf,pos) (*(const int64_t *)((const char *)(buf) + (pos)))
+#define LVALS_NC(buf,pos) (*(int64_t *)((char *)(buf) + (pos)))
+
+/* store single value in an SMB buffer */
+#define SSVAL(buf,pos,val) SVAL_NC(buf,pos)=((uint16_t)(val))
+#define SIVAL(buf,pos,val) IVAL_NC(buf,pos)=((uint32_t)(val))
+#define SLVAL(buf,pos,val) LVAL_NC(buf,pos)=((uint64_t)(val))
+#define SSVALS(buf,pos,val) SVALS_NC(buf,pos)=((int16)(val))
+#define SIVALS(buf,pos,val) IVALS_NC(buf,pos)=((int32_t)(val))
+#define SLVALS(buf,pos,val) LVALS_NC(buf,pos)=((int64_t)(val))
+
+#endif /* CAREFUL_ALIGNMENT */
+
+/* now the reverse routines - these are used in nmb packets (mostly) */
+#define SREV(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF))
+#define IREV(x) ((SREV(x)<<16) | (SREV((x)>>16)))
+#define LREV(x) ((IREV(x)<<32) | (IREV((x)>>32)))
+
+#define RSVAL(buf,pos) SREV(SVAL(buf,pos))
+#define RSVALS(buf,pos) SREV(SVALS(buf,pos))
+#define RIVAL(buf,pos) IREV(IVAL(buf,pos))
+#define RIVALS(buf,pos) IREV(IVALS(buf,pos))
+#define RLVAL(buf,pos) LREV(LVAL(buf,pos))
+#define RLVALS(buf,pos) LREV(LVALS(buf,pos))
+
+#define RSSVAL(buf,pos,val) SSVAL(buf,pos,SREV(val))
+#define RSSVALS(buf,pos,val) SSVALS(buf,pos,SREV(val))
+#define RSIVAL(buf,pos,val) SIVAL(buf,pos,IREV(val))
+#define RSIVALS(buf,pos,val) SIVALS(buf,pos,IREV(val))
+
+#define RSLVAL(buf,pos,val) SLVAL(buf,pos,LREV(val))
+#define RSLVALS(buf,pos,val) SLVALS(buf,pos,LREV(val))
+
+/* Alignment macros. */
+#define ALIGN4(p,base) ((p) + ((4 - (PTR_DIFF((p), (base)) & 3)) & 3))
+#define ALIGN2(p,base) ((p) + ((2 - (PTR_DIFF((p), (base)) & 1)) & 1))
+
+#endif /* _BYTEORDER_H */
diff --git a/include/atalk/dalloc.h b/include/atalk/dalloc.h
new file mode 100644 (file)
index 0000000..4e963cd
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+  Copyright (c) 2012 Frank Lahm <franklahm@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.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#ifndef DALLOC_H
+#define DALLOC_H
+
+#include <atalk/talloc.h>
+
+/* dynamic datastore */
+typedef struct {
+    void **dd_talloc_array;
+} DALLOC_CTX;
+
+/* Use dalloc_add_copy() macro, not this function */
+extern int dalloc_add_talloc_chunk(DALLOC_CTX *dd, void *talloc_chunk, void *obj, size_t size);
+
+#define dalloc_add_copy(d, obj, type) dalloc_add_talloc_chunk((d), talloc((d), type), (obj), sizeof(type));
+#define dalloc_add(d, obj, type) dalloc_add_talloc_chunk((d), NULL, (obj), 0);
+extern void *dalloc_get(const DALLOC_CTX *d, ...);
+extern void *dalloc_value_for_key(const DALLOC_CTX *d, ...);
+extern int dalloc_size(DALLOC_CTX *d);
+extern char *dalloc_strdup(const void *ctx, const char *string);
+extern char *dalloc_strndup(const void *ctx, const char *string, size_t n);
+#endif  /* DALLOC_H */
index c31b522c39579489e02e4df4ba806298de033950..c816d96494398436a6be6972ff53bf6300a73a25 100644 (file)
@@ -53,7 +53,7 @@ struct dsi_block {
     uint32_t dsi_reserved;   /* reserved field */
 };
 
-#define DSI_DATASIZ       8192
+#define DSI_DATASIZ       65536
 
 /* child and parent processes might interpret a couple of these
  * differently. */
index ccc66eb6f4ac1a9ae7ff52a57c3291d219e3ed02..47576bec029424ce74de5bbb4a48da544e904e75 100644 (file)
@@ -19,9 +19,9 @@
 #define EC_STATUS(a) ret = (a)
 #define EC_EXIT_STATUS(a) do { ret = (a); goto cleanup; } while (0)
 #define EC_FAIL do { ret = -1; goto cleanup; } while (0)
-#define EC_FAIL_LOG(a, ...)                     \
+#define EC_FAIL_LOG(...)                     \
     do {               \
-        LOG(log_error, logtype_default, a, __VA_ARGS__);   \
+        LOG(log_error, logtype_default, __VA_ARGS__);   \
         ret = -1;      \
         goto cleanup;  \
     } while (0)
index 9095e2e309b39dca6afa0a1fc23b9e8d6ec35edd..b876280860ce015a4f8b703a7269816f61cd6201 100644 (file)
@@ -57,6 +57,8 @@
 #define OPTION_ACL2MODE      (1 << 10)
 #define OPTION_SHARE_RESERV  (1 << 11) /* whether to use Solaris fcntl F_SHARE locks */
 #define OPTION_DBUS_AFPSTATS (1 << 12) /* whether to run dbus thread for afpstats */
+#define OPTION_SPOTLIGHT     (1 << 13) /* whether to initialize Spotlight support */
+#define OPTION_SPOTLIGHT_VOL (1 << 14) /* whether spotlight shall be enabled by default for volumes */
 
 #define PASSWD_NONE     0
 #define PASSWD_SET     (1 << 0)
@@ -120,6 +122,7 @@ struct afp_options {
     char *mimicmodel;
     char *adminauthuser;
     char *ignored_attr;
+    char *slmod_path;
     struct afp_volume_name volfile;
 };
 
index 1847c0d50b8ee746e583b749b3e8ec02e683f5f8..3f1396558487994a65792543ffc14322043ab74f 100644 (file)
@@ -93,6 +93,7 @@ enum logtypes {
   logtype_uams,
   logtype_fce,
   logtype_ad,
+  logtype_sl,
   logtype_end_of_list_marker  /* don't put any logtypes after this */
 };
 
diff --git a/include/atalk/spotlight.h b/include/atalk/spotlight.h
new file mode 100644 (file)
index 0000000..0ca8a24
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+  Copyright (c) 2012 Frank Lahm <franklahm@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.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#ifndef SPOTLIGHT_H
+#define SPOTLIGHT_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <atalk/dalloc.h>
+#include <atalk/globals.h>
+#include <atalk/volume.h>
+
+/**************************************************************************************************
+ * Spotlight module stuff
+ **************************************************************************************************/
+
+#define SL_MODULE_VERSION 1
+
+struct sl_module_export {
+    int sl_mod_version;
+    int (*sl_mod_init)        (void *);
+    int (*sl_mod_start_search)(void *);
+    int (*sl_mod_fetch_result)(void *);
+    int (*sl_mod_end_search)  (void *);
+    int (*sl_mod_fetch_attrs) (void *);
+    int (*sl_mod_error)       (void *);
+    int (*sl_mod_index_file)  (const void *);
+};
+
+extern int sl_mod_load(const char *path);
+extern void sl_index_file(const char *path);
+
+/**************************************************************************************************
+ * Spotlight RPC and marshalling stuff
+ **************************************************************************************************/
+
+/* FPSpotlightRPC subcommand codes */
+#define SPOTLIGHT_CMD_OPEN    1
+#define SPOTLIGHT_CMD_FLAGS   2
+#define SPOTLIGHT_CMD_RPC     3
+#define SPOTLIGHT_CMD_OPEN2   4
+
+/* Can be ored and used as flags */
+#define SL_ENC_LITTLE_ENDIAN 1
+#define SL_ENC_BIG_ENDIAN    2
+#define SL_ENC_UTF_16        4
+
+typedef DALLOC_CTX     sl_array_t;    /* an array of elements                                           */
+typedef DALLOC_CTX     sl_dict_t;     /* an array of key/value elements                                 */
+typedef DALLOC_CTX     sl_filemeta_t; /* contains one sl_array_t                                        */
+typedef int            sl_nil_t;      /* a nil element                                                  */
+typedef bool           sl_bool_t;     /* a boolean, we avoid bool_t as it's a define for something else */
+typedef struct timeval sl_time_t;     /* a boolean, we avoid bool_t as it's a define for something else */
+typedef struct {
+    char sl_uuid[16];
+}  sl_uuid_t;                         /* a UUID                                                         */
+typedef struct {
+    uint16_t   ca_unkn1;
+    uint32_t   ca_context;
+    DALLOC_CTX *ca_cnids;
+}  sl_cnids_t;                        /* an array of CNIDs                                              */
+
+/**************************************************************************************************
+ * Some helper stuff dealing with queries
+ **************************************************************************************************/
+
+/* Internal query state */
+typedef enum {
+    SLQ_STATE_NEW      = 1,           /* Query received from client                                     */
+    SLQ_STATE_RUNNING  = 2,           /* Query dispatched to Tracker                                    */
+    SLQ_STATE_DONE     = 3,           /* Tracker finished                                               */
+    SLQ_STATE_END      = 4,           /* Query results returned to client                               */
+    SLQ_STATE_ATTRS    = 5            /* Fetch metadata for an object                                   */
+} slq_state_t;
+
+/* Internal query data structure */
+typedef struct _slq_t {
+    struct list_head  slq_list;           /* queries are stored in a list                                   */
+    slq_state_t       slq_state;          /* State                                                          */
+    AFPObj           *slq_obj;            /* global AFPObj handle                                           */
+    const struct vol *slq_vol;            /* volume handle                                                  */
+    DALLOC_CTX       *slq_reply;          /* reply handle                                                   */
+    time_t            slq_time;           /* timestamp where we received this query                         */
+    uint64_t          slq_ctx1;           /* client context 1                                               */
+    uint64_t          slq_ctx2;           /* client context 2                                               */
+    sl_array_t       *slq_reqinfo;        /* array with requested metadata                                  */
+    const char       *slq_qstring;        /* the Spotlight query string                                     */
+    uint64_t         *slq_cnids;          /* Pointer to array with CNIDs to which a query applies           */
+    size_t            slq_cnids_num;      /* Size of slq_cnids array                                        */
+    const char       *slq_path;           /* Path to file or dir, used in fetchAttributes                   */
+    void             *slq_tracker_cursor; /* Tracker SPARQL query result cursor                             */
+} slq_t;
+
+/**************************************************************************************************
+ * Function declarations
+ **************************************************************************************************/
+
+extern int afp_spotlight_rpc(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen);
+extern int sl_pack(DALLOC_CTX *query, char *buf);
+extern int sl_unpack(DALLOC_CTX *query, const char *buf);
+
+#endif /* SPOTLIGHT_H */
diff --git a/include/atalk/talloc.h b/include/atalk/talloc.h
new file mode 100644 (file)
index 0000000..96c7e24
--- /dev/null
@@ -0,0 +1,1711 @@
+#ifndef _TALLOC_H_
+#define _TALLOC_H_
+/* 
+   Unix SMB/CIFS implementation.
+   Samba temporary memory allocation functions
+
+   Copyright (C) Andrew Tridgell 2004-2005
+   Copyright (C) Stefan Metzmacher 2006
+   
+     ** NOTE! The following LGPL license applies to the talloc
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup talloc The talloc API
+ *
+ * talloc is a hierarchical, reference counted memory pool system with
+ * destructors. It is the core memory allocator used in Samba.
+ *
+ * @{
+ */
+
+#define TALLOC_VERSION_MAJOR 2
+#define TALLOC_VERSION_MINOR 0
+
+int talloc_version_major(void);
+int talloc_version_minor(void);
+
+/**
+ * @brief Define a talloc parent type
+ *
+ * As talloc is a hierarchial memory allocator, every talloc chunk is a
+ * potential parent to other talloc chunks. So defining a separate type for a
+ * talloc chunk is not strictly necessary. TALLOC_CTX is defined nevertheless,
+ * as it provides an indicator for function arguments. You will frequently
+ * write code like
+ *
+ * @code
+ *      struct foo *foo_create(TALLOC_CTX *mem_ctx)
+ *      {
+ *              struct foo *result;
+ *              result = talloc(mem_ctx, struct foo);
+ *              if (result == NULL) return NULL;
+ *                      ... initialize foo ...
+ *              return result;
+ *      }
+ * @endcode
+ *
+ * In this type of allocating functions it is handy to have a general
+ * TALLOC_CTX type to indicate which parent to put allocated structures on.
+ */
+typedef void TALLOC_CTX;
+
+/*
+  this uses a little trick to allow __LINE__ to be stringified
+*/
+#ifndef __location__
+#define __TALLOC_STRING_LINE1__(s)    #s
+#define __TALLOC_STRING_LINE2__(s)   __TALLOC_STRING_LINE1__(s)
+#define __TALLOC_STRING_LINE3__  __TALLOC_STRING_LINE2__(__LINE__)
+#define __location__ __FILE__ ":" __TALLOC_STRING_LINE3__
+#endif
+
+#ifndef TALLOC_DEPRECATED
+#define TALLOC_DEPRECATED 0
+#endif
+
+#ifndef PRINTF_ATTRIBUTE
+#if (__GNUC__ >= 3)
+/** Use gcc attribute to check printf fns.  a1 is the 1-based index of
+ * the parameter containing the format, and a2 the index of the first
+ * argument. Note that some gcc 2.x versions don't handle this
+ * properly **/
+#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2)))
+#else
+#define PRINTF_ATTRIBUTE(a1, a2)
+#endif
+#endif
+
+#ifdef DOXYGEN
+/**
+ * @brief Create a new talloc context.
+ *
+ * The talloc() macro is the core of the talloc library. It takes a memory
+ * context and a type, and returns a pointer to a new area of memory of the
+ * given type.
+ *
+ * The returned pointer is itself a talloc context, so you can use it as the
+ * context argument to more calls to talloc if you wish.
+ *
+ * The returned pointer is a "child" of the supplied context. This means that if
+ * you talloc_free() the context then the new child disappears as well.
+ * Alternatively you can free just the child.
+ *
+ * @param[in]  ctx      A talloc context to create a new reference on or NULL to
+ *                      create a new top level context.
+ *
+ * @param[in]  type     The type of memory to allocate.
+ *
+ * @return              A type casted talloc context or NULL on error.
+ *
+ * @code
+ *      unsigned int *a, *b;
+ *
+ *      a = talloc(NULL, unsigned int);
+ *      b = talloc(a, unsigned int);
+ * @endcode
+ *
+ * @see talloc_zero
+ * @see talloc_array
+ * @see talloc_steal
+ * @see talloc_free
+ */
+void *talloc(const void *ctx, #type);
+#else
+#define talloc(ctx, type) (type *)talloc_named_const(ctx, sizeof(type), #type)
+void *_talloc(const void *context, size_t size);
+#endif
+
+/**
+ * @brief Create a new top level talloc context.
+ *
+ * This function creates a zero length named talloc context as a top level
+ * context. It is equivalent to:
+ *
+ * @code
+ *      talloc_named(NULL, 0, fmt, ...);
+ * @endcode
+ * @param[in]  fmt      Format string for the name.
+ *
+ * @param[in]  ...      Additional printf-style arguments.
+ *
+ * @return              The allocated memory chunk, NULL on error.
+ *
+ * @see talloc_named()
+ */
+void *talloc_init(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2);
+
+#ifdef DOXYGEN
+/**
+ * @brief Free a chunk of talloc memory.
+ *
+ * The talloc_free() function frees a piece of talloc memory, and all its
+ * children. You can call talloc_free() on any pointer returned by
+ * talloc().
+ *
+ * The return value of talloc_free() indicates success or failure, with 0
+ * returned for success and -1 for failure. A possible failure condition
+ * is if the pointer had a destructor attached to it and the destructor
+ * returned -1. See talloc_set_destructor() for details on
+ * destructors. Likewise, if "ptr" is NULL, then the function will make
+ * no modifications and return -1.
+ *
+ * From version 2.0 and onwards, as a special case, talloc_free() is
+ * refused on pointers that have more than one parent associated, as talloc
+ * would have no way of knowing which parent should be removed. This is
+ * different from older versions in the sense that always the reference to
+ * the most recently established parent has been destroyed. Hence to free a
+ * pointer that has more than one parent please use talloc_unlink().
+ *
+ * To help you find problems in your code caused by this behaviour, if
+ * you do try and free a pointer with more than one parent then the
+ * talloc logging function will be called to give output like this:
+ *
+ * @code
+ *   ERROR: talloc_free with references at some_dir/source/foo.c:123
+ *     reference at some_dir/source/other.c:325
+ *     reference at some_dir/source/third.c:121
+ * @endcode
+ *
+ * Please see the documentation for talloc_set_log_fn() and
+ * talloc_set_log_stderr() for more information on talloc logging
+ * functions.
+ *
+ * talloc_free() operates recursively on its children.
+ *
+ * @param[in]  ptr      The chunk to be freed.
+ *
+ * @return              Returns 0 on success and -1 on error. A possible
+ *                      failure condition is if the pointer had a destructor
+ *                      attached to it and the destructor returned -1. Likewise,
+ *                      if "ptr" is NULL, then the function will make no
+ *                      modifications and returns -1.
+ *
+ * Example:
+ * @code
+ *      unsigned int *a, *b;
+ *      a = talloc(NULL, unsigned int);
+ *      b = talloc(a, unsigned int);
+ *
+ *      talloc_free(a); // Frees a and b
+ * @endcode
+ *
+ * @see talloc_set_destructor()
+ * @see talloc_unlink()
+ */
+int talloc_free(void *ptr);
+#else
+#define talloc_free(ctx) _talloc_free(ctx, __location__)
+int _talloc_free(void *ptr, const char *location);
+#endif
+
+/**
+ * @brief Free a talloc chunk's children.
+ *
+ * The function walks along the list of all children of a talloc context and
+ * talloc_free()s only the children, not the context itself.
+ *
+ * A NULL argument is handled as no-op.
+ *
+ * @param[in]  ptr      The chunk that you want to free the children of
+ *                      (NULL is allowed too)
+ */
+void talloc_free_children(void *ptr);
+
+#ifdef DOXYGEN
+/**
+ * @brief Assign a destructor function to be called when a chunk is freed.
+ *
+ * The function talloc_set_destructor() sets the "destructor" for the pointer
+ * "ptr". A destructor is a function that is called when the memory used by a
+ * pointer is about to be released. The destructor receives the pointer as an
+ * argument, and should return 0 for success and -1 for failure.
+ *
+ * The destructor can do anything it wants to, including freeing other pieces
+ * of memory. A common use for destructors is to clean up operating system
+ * resources (such as open file descriptors) contained in the structure the
+ * destructor is placed on.
+ *
+ * You can only place one destructor on a pointer. If you need more than one
+ * destructor then you can create a zero-length child of the pointer and place
+ * an additional destructor on that.
+ *
+ * To remove a destructor call talloc_set_destructor() with NULL for the
+ * destructor.
+ *
+ * If your destructor attempts to talloc_free() the pointer that it is the
+ * destructor for then talloc_free() will return -1 and the free will be
+ * ignored. This would be a pointless operation anyway, as the destructor is
+ * only called when the memory is just about to go away.
+ *
+ * @param[in]  ptr      The talloc chunk to add a destructor to.
+ *
+ * @param[in]  destructor  The destructor function to be called. NULL to remove
+ *                         it.
+ *
+ * Example:
+ * @code
+ *      static int destroy_fd(int *fd) {
+ *              close(*fd);
+ *              return 0;
+ *      }
+ *
+ *      int *open_file(const char *filename) {
+ *              int *fd = talloc(NULL, int);
+ *              *fd = open(filename, O_RDONLY);
+ *              if (*fd < 0) {
+ *                      talloc_free(fd);
+ *                      return NULL;
+ *              }
+ *              // Whenever they free this, we close the file.
+ *              talloc_set_destructor(fd, destroy_fd);
+ *              return fd;
+ *      }
+ * @endcode
+ *
+ * @see talloc()
+ * @see talloc_free()
+ */
+void talloc_set_destructor(const void *ptr, int (*destructor)(void *));
+
+/**
+ * @brief Change a talloc chunk's parent.
+ *
+ * The talloc_steal() function changes the parent context of a talloc
+ * pointer. It is typically used when the context that the pointer is
+ * currently a child of is going to be freed and you wish to keep the
+ * memory for a longer time.
+ *
+ * To make the changed hierarchy less error-prone, you might consider to use
+ * talloc_move().
+ *
+ * If you try and call talloc_steal() on a pointer that has more than one
+ * parent then the result is ambiguous. Talloc will choose to remove the
+ * parent that is currently indicated by talloc_parent() and replace it with
+ * the chosen parent. You will also get a message like this via the talloc
+ * logging functions:
+ *
+ * @code
+ *   WARNING: talloc_steal with references at some_dir/source/foo.c:123
+ *     reference at some_dir/source/other.c:325
+ *     reference at some_dir/source/third.c:121
+ * @endcode
+ *
+ * To unambiguously change the parent of a pointer please see the function
+ * talloc_reparent(). See the talloc_set_log_fn() documentation for more
+ * information on talloc logging.
+ *
+ * @param[in]  new_ctx  The new parent context.
+ *
+ * @param[in]  ptr      The talloc chunk to move.
+ *
+ * @return              Returns the pointer that you pass it. It does not have
+ *                      any failure modes.
+ *
+ * @note It is possible to produce loops in the parent/child relationship
+ * if you are not careful with talloc_steal(). No guarantees are provided
+ * as to your sanity or the safety of your data if you do this.
+ */
+void *talloc_steal(const void *new_ctx, const void *ptr);
+#else /* DOXYGEN */
+/* try to make talloc_set_destructor() and talloc_steal() type safe,
+   if we have a recent gcc */
+#if (__GNUC__ >= 3)
+#define _TALLOC_TYPEOF(ptr) __typeof__(ptr)
+#define talloc_set_destructor(ptr, function)                                 \
+       do {                                                                  \
+               int (*_talloc_destructor_fn)(_TALLOC_TYPEOF(ptr)) = (function);       \
+               _talloc_set_destructor((ptr), (int (*)(void *))_talloc_destructor_fn); \
+       } while(0)
+/* this extremely strange macro is to avoid some braindamaged warning
+   stupidity in gcc 4.1.x */
+#define talloc_steal(ctx, ptr) ({ _TALLOC_TYPEOF(ptr) __talloc_steal_ret = (_TALLOC_TYPEOF(ptr))_talloc_steal_loc((ctx),(ptr), __location__); __talloc_steal_ret; })
+#else /* __GNUC__ >= 3 */
+#define talloc_set_destructor(ptr, function) \
+       _talloc_set_destructor((ptr), (int (*)(void *))(function))
+#define _TALLOC_TYPEOF(ptr) void *
+#define talloc_steal(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_steal_loc((ctx),(ptr), __location__)
+#endif /* __GNUC__ >= 3 */
+void _talloc_set_destructor(const void *ptr, int (*_destructor)(void *));
+void *_talloc_steal_loc(const void *new_ctx, const void *ptr, const char *location);
+#endif /* DOXYGEN */
+
+/**
+ * @brief Assign a name to a talloc chunk.
+ *
+ * Each talloc pointer has a "name". The name is used principally for
+ * debugging purposes, although it is also possible to set and get the name on
+ * a pointer in as a way of "marking" pointers in your code.
+ *
+ * The main use for names on pointer is for "talloc reports". See
+ * talloc_report() and talloc_report_full() for details. Also see
+ * talloc_enable_leak_report() and talloc_enable_leak_report_full().
+ *
+ * The talloc_set_name() function allocates memory as a child of the
+ * pointer. It is logically equivalent to:
+ *
+ * @code
+ *      talloc_set_name_const(ptr, talloc_asprintf(ptr, fmt, ...));
+ * @endcode
+ *
+ * @param[in]  ptr      The talloc chunk to assign a name to.
+ *
+ * @param[in]  fmt      Format string for the name.
+ *
+ * @param[in]  ...      Add printf-style additional arguments.
+ *
+ * @return              The assigned name, NULL on error.
+ *
+ * @note Multiple calls to talloc_set_name() will allocate more memory without
+ * releasing the name. All of the memory is released when the ptr is freed
+ * using talloc_free().
+ */
+const char *talloc_set_name(const void *ptr, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+
+#ifdef DOXYGEN
+/**
+ * @brief Change a talloc chunk's parent.
+ *
+ * This function has the same effect as talloc_steal(), and additionally sets
+ * the source pointer to NULL. You would use it like this:
+ *
+ * @code
+ *      struct foo *X = talloc(tmp_ctx, struct foo);
+ *      struct foo *Y;
+ *      Y = talloc_move(new_ctx, &X);
+ * @endcode
+ *
+ * @param[in]  new_ctx  The new parent context.
+ *
+ * @param[in]  pptr     Pointer to the talloc chunk to move.
+ *
+ * @return              The pointer of the talloc chunk it has been moved to,
+ *                      NULL on error.
+ */
+void *talloc_move(const void *new_ctx, void **pptr);
+#else
+#define talloc_move(ctx, pptr) (_TALLOC_TYPEOF(*(pptr)))_talloc_move((ctx),(void *)(pptr))
+void *_talloc_move(const void *new_ctx, const void *pptr);
+#endif
+
+/**
+ * @brief Assign a name to a talloc chunk.
+ *
+ * The function is just like talloc_set_name(), but it takes a string constant,
+ * and is much faster. It is extensively used by the "auto naming" macros, such
+ * as talloc_p().
+ *
+ * This function does not allocate any memory. It just copies the supplied
+ * pointer into the internal representation of the talloc ptr. This means you
+ * must not pass a name pointer to memory that will disappear before the ptr
+ * is freed with talloc_free().
+ *
+ * @param[in]  ptr      The talloc chunk to assign a name to.
+ *
+ * @param[in]  name     Format string for the name.
+ */
+void talloc_set_name_const(const void *ptr, const char *name);
+
+/**
+ * @brief Create a named talloc chunk.
+ *
+ * The talloc_named() function creates a named talloc pointer. It is
+ * equivalent to:
+ *
+ * @code
+ *      ptr = talloc_size(context, size);
+ *      talloc_set_name(ptr, fmt, ....);
+ * @endcode
+ *
+ * @param[in]  context  The talloc context to hang the result off.
+ *
+ * @param[in]  size     Number of char's that you want to allocate.
+ *
+ * @param[in]  fmt      Format string for the name.
+ *
+ * @param[in]  ...      Additional printf-style arguments.
+ *
+ * @return              The allocated memory chunk, NULL on error.
+ *
+ * @see talloc_set_name()
+ */
+void *talloc_named(const void *context, size_t size,
+                  const char *fmt, ...) PRINTF_ATTRIBUTE(3,4);
+
+/**
+ * @brief Basic routine to allocate a chunk of memory.
+ *
+ * This is equivalent to:
+ *
+ * @code
+ *      ptr = talloc_size(context, size);
+ *      talloc_set_name_const(ptr, name);
+ * @endcode
+ *
+ * @param[in]  context  The parent context.
+ *
+ * @param[in]  size     The number of char's that we want to allocate.
+ *
+ * @param[in]  name     The name the talloc block has.
+ *
+ * @return             The allocated memory chunk, NULL on error.
+ */
+void *talloc_named_const(const void *context, size_t size, const char *name);
+
+#ifdef DOXYGEN
+/**
+ * @brief Untyped allocation.
+ *
+ * The function should be used when you don't have a convenient type to pass to
+ * talloc(). Unlike talloc(), it is not type safe (as it returns a void *), so
+ * you are on your own for type checking.
+ *
+ * Best to use talloc() or talloc_array() instead.
+ *
+ * @param[in]  ctx     The talloc context to hang the result off.
+ *
+ * @param[in]  size    Number of char's that you want to allocate.
+ *
+ * @return             The allocated memory chunk, NULL on error.
+ *
+ * Example:
+ * @code
+ *      void *mem = talloc_size(NULL, 100);
+ * @endcode
+ */
+void *talloc_size(const void *ctx, size_t size);
+#else
+#define talloc_size(ctx, size) talloc_named_const(ctx, size, __location__)
+#endif
+
+#ifdef DOXYGEN
+/**
+ * @brief Allocate into a typed pointer.
+ *
+ * The talloc_ptrtype() macro should be used when you have a pointer and want
+ * to allocate memory to point at with this pointer. When compiling with
+ * gcc >= 3 it is typesafe. Note this is a wrapper of talloc_size() and
+ * talloc_get_name() will return the current location in the source file and
+ * not the type.
+ *
+ * @param[in]  ctx      The talloc context to hang the result off.
+ *
+ * @param[in]  type     The pointer you want to assign the result to.
+ *
+ * @return              The properly casted allocated memory chunk, NULL on
+ *                      error.
+ *
+ * Example:
+ * @code
+ *       unsigned int *a = talloc_ptrtype(NULL, a);
+ * @endcode
+ */
+void *talloc_ptrtype(const void *ctx, #type);
+#else
+#define talloc_ptrtype(ctx, ptr) (_TALLOC_TYPEOF(ptr))talloc_size(ctx, sizeof(*(ptr)))
+#endif
+
+#ifdef DOXYGEN
+/**
+ * @brief Allocate a new 0-sized talloc chunk.
+ *
+ * This is a utility macro that creates a new memory context hanging off an
+ * existing context, automatically naming it "talloc_new: __location__" where
+ * __location__ is the source line it is called from. It is particularly
+ * useful for creating a new temporary working context.
+ *
+ * @param[in]  ctx      The talloc parent context.
+ *
+ * @return              A new talloc chunk, NULL on error.
+ */
+void *talloc_new(const void *ctx);
+#else
+#define talloc_new(ctx) talloc_named_const(ctx, 0, "talloc_new: " __location__)
+#endif
+
+#ifdef DOXYGEN
+/**
+ * @brief Allocate a 0-initizialized structure.
+ *
+ * The macro is equivalent to:
+ *
+ * @code
+ *      ptr = talloc(ctx, type);
+ *      if (ptr) memset(ptr, 0, sizeof(type));
+ * @endcode
+ *
+ * @param[in]  ctx      The talloc context to hang the result off.
+ *
+ * @param[in]  type     The type that we want to allocate.
+ *
+ * @return              Pointer to a piece of memory, properly cast to 'type *',
+ *                      NULL on error.
+ *
+ * Example:
+ * @code
+ *      unsigned int *a, *b;
+ *      a = talloc_zero(NULL, unsigned int);
+ *      b = talloc_zero(a, unsigned int);
+ * @endcode
+ *
+ * @see talloc()
+ * @see talloc_zero_size()
+ * @see talloc_zero_array()
+ */
+void *talloc_zero(const void *ctx, #type);
+
+/**
+ * @brief Allocate untyped, 0-initialized memory.
+ *
+ * @param[in]  ctx      The talloc context to hang the result off.
+ *
+ * @param[in]  size     Number of char's that you want to allocate.
+ *
+ * @return              The allocated memory chunk.
+ */
+void *talloc_zero_size(const void *ctx, size_t size);
+#else
+#define talloc_zero(ctx, type) (type *)_talloc_zero(ctx, sizeof(type), #type)
+#define talloc_zero_size(ctx, size) _talloc_zero(ctx, size, __location__)
+void *_talloc_zero(const void *ctx, size_t size, const char *name);
+#endif
+
+/**
+ * @brief Return the name of a talloc chunk.
+ *
+ * @param[in]  ptr      The talloc chunk.
+ *
+ * @return              The current name for the given talloc pointer.
+ *
+ * @see talloc_set_name()
+ */
+const char *talloc_get_name(const void *ptr);
+
+/**
+ * @brief Verify that a talloc chunk carries a specified name.
+ *
+ * This function checks if a pointer has the specified name. If it does
+ * then the pointer is returned.
+ *
+ * @param[in]  ptr       The talloc chunk to check.
+ *
+ * @param[in]  name      The name to check against.
+ *
+ * @return               The pointer if the name matches, NULL if it doesn't.
+ */
+void *talloc_check_name(const void *ptr, const char *name);
+
+/**
+ * @brief Get the parent chunk of a pointer.
+ *
+ * @param[in]  ptr      The talloc pointer to inspect.
+ *
+ * @return              The talloc parent of ptr, NULL on error.
+ */
+void *talloc_parent(const void *ptr);
+
+/**
+ * @brief Get a talloc chunk's parent name.
+ *
+ * @param[in]  ptr      The talloc pointer to inspect.
+ *
+ * @return              The name of ptr's parent chunk.
+ */
+const char *talloc_parent_name(const void *ptr);
+
+/**
+ * @brief Get the total size of a talloc chunk including its children.
+ *
+ * The function returns the total size in bytes used by this pointer and all
+ * child pointers. Mostly useful for debugging.
+ *
+ * Passing NULL is allowed, but it will only give a meaningful result if
+ * talloc_enable_leak_report() or talloc_enable_leak_report_full() has
+ * been called.
+ *
+ * @param[in]  ptr      The talloc chunk.
+ *
+ * @return              The total size.
+ */
+size_t talloc_total_size(const void *ptr);
+
+/**
+ * @brief Get the number of talloc chunks hanging off a chunk.
+ *
+ * The talloc_total_blocks() function returns the total memory block
+ * count used by this pointer and all child pointers. Mostly useful for
+ * debugging.
+ *
+ * Passing NULL is allowed, but it will only give a meaningful result if
+ * talloc_enable_leak_report() or talloc_enable_leak_report_full() has
+ * been called.
+ *
+ * @param[in]  ptr      The talloc chunk.
+ *
+ * @return              The total size.
+ */
+size_t talloc_total_blocks(const void *ptr);
+
+#ifdef DOXYGEN
+/**
+ * @brief Duplicate a memory area into a talloc chunk.
+ *
+ * The function is equivalent to:
+ *
+ * @code
+ *      ptr = talloc_size(ctx, size);
+ *      if (ptr) memcpy(ptr, p, size);
+ * @endcode
+ *
+ * @param[in]  t        The talloc context to hang the result off.
+ *
+ * @param[in]  p        The memory chunk you want to duplicate.
+ *
+ * @param[in]  size     Number of char's that you want copy.
+ *
+ * @return              The allocated memory chunk.
+ *
+ * @see talloc_size()
+ */
+void *talloc_memdup(const void *t, const void *p, size_t size);
+#else
+#define talloc_memdup(t, p, size) _talloc_memdup(t, p, size, __location__)
+void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name);
+#endif
+
+#ifdef DOXYGEN
+/**
+ * @brief Assign a type to a talloc chunk.
+ *
+ * This macro allows you to force the name of a pointer to be of a particular
+ * type. This can be used in conjunction with talloc_get_type() to do type
+ * checking on void* pointers.
+ *
+ * It is equivalent to this:
+ *
+ * @code
+ *      talloc_set_name_const(ptr, #type)
+ * @endcode
+ *
+ * @param[in]  ptr      The talloc chunk to assign the type to.
+ *
+ * @param[in]  type     The type to assign.
+ */
+void talloc_set_type(const char *ptr, #type);
+
+/**
+ * @brief Get a typed pointer out of a talloc pointer.
+ *
+ * This macro allows you to do type checking on talloc pointers. It is
+ * particularly useful for void* private pointers. It is equivalent to
+ * this:
+ *
+ * @code
+ *      (type *)talloc_check_name(ptr, #type)
+ * @endcode
+ *
+ * @param[in]  ptr      The talloc pointer to check.
+ *
+ * @param[in]  type     The type to check against.
+ *
+ * @return              The properly casted pointer given by ptr, NULL on error.
+ */
+type *talloc_get_type(const void *ptr, #type);
+#else
+#define talloc_set_type(ptr, type) talloc_set_name_const(ptr, #type)
+#define talloc_get_type(ptr, type) (type *)talloc_check_name(ptr, #type)
+#endif
+
+#ifdef DOXYGEN
+/**
+ * @brief Safely turn a void pointer into a typed pointer.
+ *
+ * This macro is used together with talloc(mem_ctx, struct foo). If you had to
+ * assing the talloc chunk pointer to some void pointer variable,
+ * talloc_get_type_abort() is the recommended way to get the convert the void
+ * pointer back to a typed pointer.
+ *
+ * @param[in]  ptr      The void pointer to convert.
+ *
+ * @param[in]  type     The type that this chunk contains
+ *
+ * @return              The same value as ptr, type-checked and properly cast.
+ */
+void *talloc_get_type_abort(const void *ptr, #type);
+#else
+#define talloc_get_type_abort(ptr, type) (type *)_talloc_get_type_abort(ptr, #type, __location__)
+void *_talloc_get_type_abort(const void *ptr, const char *name, const char *location);
+#endif
+
+/**
+ * @brief Find a parent context by name.
+ *
+ * Find a parent memory context of the current context that has the given
+ * name. This can be very useful in complex programs where it may be
+ * difficult to pass all information down to the level you need, but you
+ * know the structure you want is a parent of another context.
+ *
+ * @param[in]  ctx      The talloc chunk to start from.
+ *
+ * @param[in]  name     The name of the parent we look for.
+ *
+ * @return              The memory context we are looking for, NULL if not
+ *                      found.
+ */
+void *talloc_find_parent_byname(const void *ctx, const char *name);
+
+#ifdef DOXYGEN
+/**
+ * @brief Find a parent context by type.
+ *
+ * Find a parent memory context of the current context that has the given
+ * name. This can be very useful in complex programs where it may be
+ * difficult to pass all information down to the level you need, but you
+ * know the structure you want is a parent of another context.
+ *
+ * Like talloc_find_parent_byname() but takes a type, making it typesafe.
+ *
+ * @param[in]  ptr      The talloc chunk to start from.
+ *
+ * @param[in]  type     The type of the parent to look for.
+ *
+ * @return              The memory context we are looking for, NULL if not
+ *                      found.
+ */
+void *talloc_find_parent_bytype(const void *ptr, #type);
+#else
+#define talloc_find_parent_bytype(ptr, type) (type *)talloc_find_parent_byname(ptr, #type)
+#endif
+
+/**
+ * @brief Allocate a talloc pool.
+ *
+ * A talloc pool is a pure optimization for specific situations. In the
+ * release process for Samba 3.2 we found out that we had become considerably
+ * slower than Samba 3.0 was. Profiling showed that malloc(3) was a large CPU
+ * consumer in benchmarks. For Samba 3.2 we have internally converted many
+ * static buffers to dynamically allocated ones, so malloc(3) being beaten
+ * more was no surprise. But it made us slower.
+ *
+ * talloc_pool() is an optimization to call malloc(3) a lot less for the use
+ * pattern Samba has: The SMB protocol is mainly a request/response protocol
+ * where we have to allocate a certain amount of memory per request and free
+ * that after the SMB reply is sent to the client.
+ *
+ * talloc_pool() creates a talloc chunk that you can use as a talloc parent
+ * exactly as you would use any other ::TALLOC_CTX. The difference is that
+ * when you talloc a child of this pool, no malloc(3) is done. Instead, talloc
+ * just increments a pointer inside the talloc_pool. This also works
+ * recursively. If you use the child of the talloc pool as a parent for
+ * grand-children, their memory is also taken from the talloc pool.
+ *
+ * If you talloc_free() children of a talloc pool, the memory is not given
+ * back to the system. Instead, free(3) is only called if the talloc_pool()
+ * itself is released with talloc_free().
+ *
+ * The downside of a talloc pool is that if you talloc_move() a child of a
+ * talloc pool to a talloc parent outside the pool, the whole pool memory is
+ * not free(3)'ed until that moved chunk is also talloc_free()ed.
+ *
+ * @param[in]  context  The talloc context to hang the result off.
+ *
+ * @param[in]  size     Size of the talloc pool.
+ *
+ * @return              The allocated talloc pool, NULL on error.
+ */
+void *talloc_pool(const void *context, size_t size);
+
+/**
+ * @brief Free a talloc chunk and NULL out the pointer.
+ *
+ * TALLOC_FREE() frees a pointer and sets it to NULL. Use this if you want
+ * immediate feedback (i.e. crash) if you use a pointer after having free'ed
+ * it.
+ *
+ * @param[in]  ctx      The chunk to be freed.
+ */
+#define TALLOC_FREE(ctx) do { talloc_free(ctx); ctx=NULL; } while(0)
+
+/* @} ******************************************************************/
+
+/**
+ * \defgroup talloc_ref The talloc reference function.
+ * @ingroup talloc
+ *
+ * This module contains the definitions around talloc references
+ *
+ * @{
+ */
+
+/**
+ * @brief Increase the reference count of a talloc chunk.
+ *
+ * The talloc_increase_ref_count(ptr) function is exactly equivalent to:
+ *
+ * @code
+ *      talloc_reference(NULL, ptr);
+ * @endcode
+ *
+ * You can use either syntax, depending on which you think is clearer in
+ * your code.
+ *
+ * @param[in]  ptr      The pointer to increase the reference count.
+ *
+ * @return              0 on success, -1 on error.
+ */
+int talloc_increase_ref_count(const void *ptr);
+
+/**
+ * @brief Get the number of references to a talloc chunk.
+ *
+ * @param[in]  ptr      The pointer to retrieve the reference count from.
+ *
+ * @return              The number of references.
+ */
+size_t talloc_reference_count(const void *ptr);
+
+#ifdef DOXYGEN
+/**
+ * @brief Create an additional talloc parent to a pointer.
+ *
+ * The talloc_reference() function makes "context" an additional parent of
+ * ptr. Each additional reference consumes around 48 bytes of memory on intel
+ * x86 platforms.
+ *
+ * If ptr is NULL, then the function is a no-op, and simply returns NULL.
+ *
+ * After creating a reference you can free it in one of the following ways:
+ *
+ * - you can talloc_free() any parent of the original pointer. That
+ *   will reduce the number of parents of this pointer by 1, and will
+ *   cause this pointer to be freed if it runs out of parents.
+ *
+ * - you can talloc_free() the pointer itself if it has at maximum one
+ *   parent. This behaviour has been changed since the release of version
+ *   2.0. Further informations in the description of "talloc_free".
+ *
+ * For more control on which parent to remove, see talloc_unlink()
+ * @param[in]  ctx      The additional parent.
+ *
+ * @param[in]  ptr      The pointer you want to create an additional parent for.
+ *
+ * @return              The original pointer 'ptr', NULL if talloc ran out of
+ *                      memory in creating the reference.
+ *
+ * Example:
+ * @code
+ *      unsigned int *a, *b, *c;
+ *      a = talloc(NULL, unsigned int);
+ *      b = talloc(NULL, unsigned int);
+ *      c = talloc(a, unsigned int);
+ *      // b also serves as a parent of c.
+ *      talloc_reference(b, c);
+ * @endcode
+ *
+ * @see talloc_unlink()
+ */
+void *talloc_reference(const void *ctx, const void *ptr);
+#else
+#define talloc_reference(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_reference_loc((ctx),(ptr), __location__)
+void *_talloc_reference_loc(const void *context, const void *ptr, const char *location);
+#endif
+
+/**
+ * @brief Remove a specific parent from a talloc chunk.
+ *
+ * The function removes a specific parent from ptr. The context passed must
+ * either be a context used in talloc_reference() with this pointer, or must be
+ * a direct parent of ptr.
+ *
+ * You can just use talloc_free() instead of talloc_unlink() if there
+ * is at maximum one parent. This behaviour has been changed since the
+ * release of version 2.0. Further informations in the description of
+ * "talloc_free".
+ *
+ * @param[in]  context  The talloc parent to remove.
+ *
+ * @param[in]  ptr      The talloc ptr you want to remove the parent from.
+ *
+ * @return              0 on success, -1 on error.
+ *
+ * @note If the parent has already been removed using talloc_free() then
+ * this function will fail and will return -1.  Likewise, if ptr is NULL,
+ * then the function will make no modifications and return -1.
+ *
+ * Example:
+ * @code
+ *      unsigned int *a, *b, *c;
+ *      a = talloc(NULL, unsigned int);
+ *      b = talloc(NULL, unsigned int);
+ *      c = talloc(a, unsigned int);
+ *      // b also serves as a parent of c.
+ *      talloc_reference(b, c);
+ *      talloc_unlink(b, c);
+ * @endcode
+ */
+int talloc_unlink(const void *context, void *ptr);
+
+/**
+ * @brief Provide a talloc context that is freed at program exit.
+ *
+ * This is a handy utility function that returns a talloc context
+ * which will be automatically freed on program exit. This can be used
+ * to reduce the noise in memory leak reports.
+ *
+ * Never use this in code that might be used in objects loaded with
+ * dlopen and unloaded with dlclose. talloc_autofree_context()
+ * internally uses atexit(3). Some platforms like modern Linux handles
+ * this fine, but for example FreeBSD does not deal well with dlopen()
+ * and atexit() used simultaneously: dlclose() does not clean up the
+ * list of atexit-handlers, so when the program exits the code that
+ * was registered from within talloc_autofree_context() is gone, the
+ * program crashes at exit.
+ *
+ * @return              A talloc context, NULL on error.
+ */
+void *talloc_autofree_context(void);
+
+/**
+ * @brief Get the size of a talloc chunk.
+ *
+ * This function lets you know the amount of memory allocated so far by
+ * this context. It does NOT account for subcontext memory.
+ * This can be used to calculate the size of an array.
+ *
+ * @param[in]  ctx      The talloc chunk.
+ *
+ * @return              The size of the talloc chunk.
+ */
+size_t talloc_get_size(const void *ctx);
+
+/**
+ * @brief Show the parentage of a context.
+ *
+ * @param[in]  context            The talloc context to look at.
+ *
+ * @param[in]  file               The output to use, a file, stdout or stderr.
+ */
+void talloc_show_parents(const void *context, FILE *file);
+
+/**
+ * @brief Check if a context is parent of a talloc chunk.
+ *
+ * This checks if context is referenced in the talloc hierarchy above ptr.
+ *
+ * @param[in]  context  The assumed talloc context.
+ *
+ * @param[in]  ptr      The talloc chunk to check.
+ *
+ * @return              Return 1 if this is the case, 0 if not.
+ */
+int talloc_is_parent(const void *context, const void *ptr);
+
+/**
+ * @brief Change the parent context of a talloc pointer.
+ *
+ * The function changes the parent context of a talloc pointer. It is typically
+ * used when the context that the pointer is currently a child of is going to be
+ * freed and you wish to keep the memory for a longer time.
+ *
+ * The difference between talloc_reparent() and talloc_steal() is that
+ * talloc_reparent() can specify which parent you wish to change. This is
+ * useful when a pointer has multiple parents via references.
+ *
+ * @param[in]  old_parent
+ * @param[in]  new_parent
+ * @param[in]  ptr
+ *
+ * @return              Return the pointer you passed. It does not have any
+ *                      failure modes.
+ */
+void *talloc_reparent(const void *old_parent, const void *new_parent, const void *ptr);
+
+/* @} ******************************************************************/
+
+/**
+ * @defgroup talloc_array The talloc array functions
+ * @ingroup talloc
+ *
+ * Talloc contains some handy helpers for handling Arrays conveniently
+ *
+ * @{
+ */
+
+#ifdef DOXYGEN
+/**
+ * @brief Allocate an array.
+ *
+ * The macro is equivalent to:
+ *
+ * @code
+ *      (type *)talloc_size(ctx, sizeof(type) * count);
+ * @endcode
+ *
+ * except that it provides integer overflow protection for the multiply,
+ * returning NULL if the multiply overflows.
+ *
+ * @param[in]  ctx      The talloc context to hang the result off.
+ *
+ * @param[in]  type     The type that we want to allocate.
+ *
+ * @param[in]  count    The number of 'type' elements you want to allocate.
+ *
+ * @return              The allocated result, properly cast to 'type *', NULL on
+ *                      error.
+ *
+ * Example:
+ * @code
+ *      unsigned int *a, *b;
+ *      a = talloc_zero(NULL, unsigned int);
+ *      b = talloc_array(a, unsigned int, 100);
+ * @endcode
+ *
+ * @see talloc()
+ * @see talloc_zero_array()
+ */
+void *talloc_array(const void *ctx, #type, unsigned count);
+#else
+#define talloc_array(ctx, type, count) (type *)_talloc_array(ctx, sizeof(type), count, #type)
+void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name);
+#endif
+
+#ifdef DOXYGEN
+/**
+ * @brief Allocate an array.
+ *
+ * @param[in]  ctx      The talloc context to hang the result off.
+ *
+ * @param[in]  size     The size of an array element.
+ *
+ * @param[in]  count    The number of elements you want to allocate.
+ *
+ * @return              The allocated result, NULL on error.
+ */
+void *talloc_array_size(const void *ctx, size_t size, unsigned count);
+#else
+#define talloc_array_size(ctx, size, count) _talloc_array(ctx, size, count, __location__)
+#endif
+
+#ifdef DOXYGEN
+/**
+ * @brief Allocate an array into a typed pointer.
+ *
+ * The macro should be used when you have a pointer to an array and want to
+ * allocate memory of an array to point at with this pointer. When compiling
+ * with gcc >= 3 it is typesafe. Note this is a wrapper of talloc_array_size()
+ * and talloc_get_name() will return the current location in the source file
+ * and not the type.
+ *
+ * @param[in]  ctx      The talloc context to hang the result off.
+ *
+ * @param[in]  ptr      The pointer you want to assign the result to.
+ *
+ * @param[in]  count    The number of elements you want to allocate.
+ *
+ * @return              The allocated memory chunk, properly casted. NULL on
+ *                      error.
+ */
+void *talloc_array_ptrtype(const void *ctx, const void *ptr, unsigned count);
+#else
+#define talloc_array_ptrtype(ctx, ptr, count) (_TALLOC_TYPEOF(ptr))talloc_array_size(ctx, sizeof(*(ptr)), count)
+#endif
+
+#ifdef DOXYGEN
+/**
+ * @brief Get the number of elements in a talloc'ed array.
+ *
+ * A talloc chunk carries its own size, so for talloc'ed arrays it is not
+ * necessary to store the number of elements explicitly.
+ *
+ * @param[in]  ctx      The allocated array.
+ *
+ * @return              The number of elements in ctx.
+ */
+size_t talloc_array_length(const void *ctx);
+#else
+#define talloc_array_length(ctx) (talloc_get_size(ctx)/sizeof(*ctx))
+#endif
+
+#ifdef DOXYGEN
+/**
+ * @brief Allocate a zero-initialized array
+ *
+ * @param[in]  ctx      The talloc context to hang the result off.
+ *
+ * @param[in]  type     The type that we want to allocate.
+ *
+ * @param[in]  count    The number of "type" elements you want to allocate.
+ *
+ * @return              The allocated result casted to "type *", NULL on error.
+ *
+ * The talloc_zero_array() macro is equivalent to:
+ *
+ * @code
+ *     ptr = talloc_array(ctx, type, count);
+ *     if (ptr) memset(ptr, sizeof(type) * count);
+ * @endcode
+ */
+void *talloc_zero_array(const void *ctx, #type, unsigned count);
+#else
+#define talloc_zero_array(ctx, type, count) (type *)_talloc_zero_array(ctx, sizeof(type), count, #type)
+void *_talloc_zero_array(const void *ctx,
+                        size_t el_size,
+                        unsigned count,
+                        const char *name);
+#endif
+
+#ifdef DOXYGEN
+/**
+ * @brief Change the size of a talloc array.
+ *
+ * The macro changes the size of a talloc pointer. The 'count' argument is the
+ * number of elements of type 'type' that you want the resulting pointer to
+ * hold.
+ *
+ * talloc_realloc() has the following equivalences:
+ *
+ * @code
+ *      talloc_realloc(ctx, NULL, type, 1) ==> talloc(ctx, type);
+ *      talloc_realloc(ctx, NULL, type, N) ==> talloc_array(ctx, type, N);
+ *      talloc_realloc(ctx, ptr, type, 0)  ==> talloc_free(ptr);
+ * @endcode
+ *
+ * The "context" argument is only used if "ptr" is NULL, otherwise it is
+ * ignored.
+ *
+ * @param[in]  ctx      The parent context used if ptr is NULL.
+ *
+ * @param[in]  ptr      The chunk to be resized.
+ *
+ * @param[in]  type     The type of the array element inside ptr.
+ *
+ * @param[in]  count    The intended number of array elements.
+ *
+ * @return              The new array, NULL on error. The call will fail either
+ *                      due to a lack of memory, or because the pointer has more
+ *                      than one parent (see talloc_reference()).
+ */
+void *talloc_realloc(const void *ctx, void *ptr, #type, size_t count);
+#else
+#define talloc_realloc(ctx, p, type, count) (type *)_talloc_realloc_array(ctx, p, sizeof(type), count, #type)
+void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name);
+#endif
+
+#ifdef DOXYGEN
+/**
+ * @brief Untyped realloc to change the size of a talloc array.
+ *
+ * The macro is useful when the type is not known so the typesafe
+ * talloc_realloc() cannot be used.
+ *
+ * @param[in]  ctx      The parent context used if 'ptr' is NULL.
+ *
+ * @param[in]  ptr      The chunk to be resized.
+ *
+ * @param[in]  size     The new chunk size.
+ *
+ * @return              The new array, NULL on error.
+ */
+void *talloc_realloc_size(const void *ctx, void *ptr, size_t size);
+#else
+#define talloc_realloc_size(ctx, ptr, size) _talloc_realloc(ctx, ptr, size, __location__)
+void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name);
+#endif
+
+/**
+ * @brief Provide a function version of talloc_realloc_size.
+ *
+ * This is a non-macro version of talloc_realloc(), which is useful as
+ * libraries sometimes want a ralloc function pointer. A realloc()
+ * implementation encapsulates the functionality of malloc(), free() and
+ * realloc() in one call, which is why it is useful to be able to pass around
+ * a single function pointer.
+ *
+ * @param[in]  context  The parent context used if ptr is NULL.
+ *
+ * @param[in]  ptr      The chunk to be resized.
+ *
+ * @param[in]  size     The new chunk size.
+ *
+ * @return              The new chunk, NULL on error.
+ */
+void *talloc_realloc_fn(const void *context, void *ptr, size_t size);
+
+/* @} ******************************************************************/
+
+/**
+ * @defgroup talloc_string The talloc string functions.
+ * @ingroup talloc
+ *
+ * talloc string allocation and manipulation functions.
+ * @{
+ */
+
+/**
+ * @brief Duplicate a string into a talloc chunk.
+ *
+ * This function is equivalent to:
+ *
+ * @code
+ *      ptr = talloc_size(ctx, strlen(p)+1);
+ *      if (ptr) memcpy(ptr, p, strlen(p)+1);
+ * @endcode
+ *
+ * This functions sets the name of the new pointer to the passed
+ * string. This is equivalent to:
+ *
+ * @code
+ *      talloc_set_name_const(ptr, ptr)
+ * @endcode
+ *
+ * @param[in]  t        The talloc context to hang the result off.
+ *
+ * @param[in]  p        The string you want to duplicate.
+ *
+ * @return              The duplicated string, NULL on error.
+ */
+char *talloc_strdup(const void *t, const char *p);
+
+/**
+ * @brief Append a string to given string and duplicate the result.
+ *
+ * @param[in]  s        The destination to append to.
+ *
+ * @param[in]  a        The string you want to append.
+ *
+ * @return              The duplicated string, NULL on error.
+ *
+ * @see talloc_strdup()
+ */
+char *talloc_strdup_append(char *s, const char *a);
+
+/**
+ * @brief Append a string to a given buffer and duplicate the result.
+ *
+ * @param[in]  s        The destination buffer to append to.
+ *
+ * @param[in]  a        The string you want to append.
+ *
+ * @return              The duplicated string, NULL on error.
+ *
+ * @see talloc_strdup()
+ */
+char *talloc_strdup_append_buffer(char *s, const char *a);
+
+/**
+ * @brief Duplicate a length-limited string into a talloc chunk.
+ *
+ * This function is the talloc equivalent of the C library function strndup(3).
+ *
+ * This functions sets the name of the new pointer to the passed string. This is
+ * equivalent to:
+ *
+ * @code
+ *      talloc_set_name_const(ptr, ptr)
+ * @endcode
+ *
+ * @param[in]  t        The talloc context to hang the result off.
+ *
+ * @param[in]  p        The string you want to duplicate.
+ *
+ * @param[in]  n        The maximum string length to duplicate.
+ *
+ * @return              The duplicated string, NULL on error.
+ */
+char *talloc_strndup(const void *t, const char *p, size_t n);
+
+/**
+ * @brief Append at most n characters of a string to given string and duplicate
+ *        the result.
+ *
+ * @param[in]  s        The destination string to append to.
+ *
+ * @param[in]  a        The source string you want to append.
+ *
+ * @param[in]  n        The number of characters you want to append from the
+ *                      string.
+ *
+ * @return              The duplicated string, NULL on error.
+ *
+ * @see talloc_strndup()
+ */
+char *talloc_strndup_append(char *s, const char *a, size_t n);
+
+/**
+ * @brief Append at most n characters of a string to given buffer and duplicate
+ *        the result.
+ *
+ * @param[in]  s        The destination buffer to append to.
+ *
+ * @param[in]  a        The source string you want to append.
+ *
+ * @param[in]  n        The number of characters you want to append from the
+ *                      string.
+ *
+ * @return              The duplicated string, NULL on error.
+ *
+ * @see talloc_strndup()
+ */
+char *talloc_strndup_append_buffer(char *s, const char *a, size_t n);
+
+/**
+ * @brief Format a string given a va_list.
+ *
+ * This function is the talloc equivalent of the C library function
+ * vasprintf(3).
+ *
+ * This functions sets the name of the new pointer to the new string. This is
+ * equivalent to:
+ *
+ * @code
+ *      talloc_set_name_const(ptr, ptr)
+ * @endcode
+ *
+ * @param[in]  t        The talloc context to hang the result off.
+ *
+ * @param[in]  fmt      The format string.
+ *
+ * @param[in]  ap       The parameters used to fill fmt.
+ *
+ * @return              The formatted string, NULL on error.
+ */
+char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
+
+/**
+ * @brief Format a string given a va_list and append it to the given destination
+ *        string.
+ *
+ * @param[in]  s        The destination string to append to.
+ *
+ * @param[in]  fmt      The format string.
+ *
+ * @param[in]  ap       The parameters used to fill fmt.
+ *
+ * @return              The formatted string, NULL on error.
+ *
+ * @see talloc_vasprintf()
+ */
+char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
+
+/**
+ * @brief Format a string given a va_list and append it to the given destination
+ *        buffer.
+ *
+ * @param[in]  s        The destination buffer to append to.
+ *
+ * @param[in]  fmt      The format string.
+ *
+ * @param[in]  ap       The parameters used to fill fmt.
+ *
+ * @return              The formatted string, NULL on error.
+ *
+ * @see talloc_vasprintf()
+ */
+char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
+
+/**
+ * @brief Format a string.
+ *
+ * This function is the talloc equivalent of the C library function asprintf(3).
+ *
+ * This functions sets the name of the new pointer to the new string. This is
+ * equivalent to:
+ *
+ * @code
+ *      talloc_set_name_const(ptr, ptr)
+ * @endcode
+ *
+ * @param[in]  t        The talloc context to hang the result off.
+ *
+ * @param[in]  fmt      The format string.
+ *
+ * @param[in]  ...      The parameters used to fill fmt.
+ *
+ * @return              The formatted string, NULL on error.
+ */
+char *talloc_asprintf(const void *t, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+
+/**
+ * @brief Append a formatted string to another string.
+ *
+ * This function appends the given formatted string to the given string. Use
+ * this variant when the string in the current talloc buffer may have been
+ * truncated in length.
+ *
+ * This functions sets the name of the new pointer to the new
+ * string. This is equivalent to:
+ *
+ * @code
+ *      talloc_set_name_const(ptr, ptr)
+ * @endcode
+ *
+ * @param[in]  s        The string to append to.
+ *
+ * @param[in]  fmt      The format string.
+ *
+ * @param[in]  ...      The parameters used to fill fmt.
+ *
+ * @return              The formatted string, NULL on error.
+ */
+char *talloc_asprintf_append(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+
+/**
+ * @brief Append a formatted string to another string.
+ *
+ * @param[in]  s        The string to append to
+ *
+ * @param[in]  fmt      The format string.
+ *
+ * @param[in]  ...      The parameters used to fill fmt.
+ *
+ * @return              The formatted string, NULL on error.
+ */
+char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+
+/* @} ******************************************************************/
+
+/**
+ * @defgroup talloc_debug The talloc debugging support functions
+ * @ingroup talloc
+ *
+ * To aid memory debugging, talloc contains routines to inspect the currently
+ * allocated memory hierarchy.
+ *
+ * @{
+ */
+
+/**
+ * @brief Walk a complete talloc hierarchy.
+ *
+ * This provides a more flexible reports than talloc_report(). It
+ * will recursively call the callback for the entire tree of memory
+ * referenced by the pointer. References in the tree are passed with
+ * is_ref = 1 and the pointer that is referenced.
+ *
+ * You can pass NULL for the pointer, in which case a report is
+ * printed for the top level memory context, but only if
+ * talloc_enable_leak_report() or talloc_enable_leak_report_full()
+ * has been called.
+ *
+ * The recursion is stopped when depth >= max_depth.
+ * max_depth = -1 means only stop at leaf nodes.
+ *
+ * @param[in]  ptr      The talloc chunk.
+ *
+ * @param[in]  depth    Internal parameter to control recursion. Call with 0.
+ *
+ * @param[in]  max_depth  Maximum recursion level.
+ *
+ * @param[in]  callback  Function to be called on every chunk.
+ *
+ * @param[in]  private_data  Private pointer passed to callback.
+ */
+void talloc_report_depth_cb(const void *ptr, int depth, int max_depth,
+                           void (*callback)(const void *ptr,
+                                            int depth, int max_depth,
+                                            int is_ref,
+                                            void *private_data),
+                           void *private_data);
+
+/**
+ * @brief Print a talloc hierarchy.
+ *
+ * This provides a more flexible reports than talloc_report(). It
+ * will let you specify the depth and max_depth.
+ *
+ * @param[in]  ptr      The talloc chunk.
+ *
+ * @param[in]  depth    Internal parameter to control recursion. Call with 0.
+ *
+ * @param[in]  max_depth  Maximum recursion level.
+ *
+ * @param[in]  f        The file handle to print to.
+ */
+void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f);
+
+/**
+ * @brief Print a summary report of all memory used by ptr.
+ *
+ * This provides a more detailed report than talloc_report(). It will
+ * recursively print the entire tree of memory referenced by the
+ * pointer. References in the tree are shown by giving the name of the
+ * pointer that is referenced.
+ *
+ * You can pass NULL for the pointer, in which case a report is printed
+ * for the top level memory context, but only if
+ * talloc_enable_leak_report() or talloc_enable_leak_report_full() has
+ * been called.
+ *
+ * @param[in]  ptr      The talloc chunk.
+ *
+ * @param[in]  f        The file handle to print to.
+ *
+ * Example:
+ * @code
+ *      unsigned int *a, *b;
+ *      a = talloc(NULL, unsigned int);
+ *      b = talloc(a, unsigned int);
+ *      fprintf(stderr, "Dumping memory tree for a:\n");
+ *      talloc_report_full(a, stderr);
+ * @endcode
+ *
+ * @see talloc_report()
+ */
+void talloc_report_full(const void *ptr, FILE *f);
+
+/**
+ * @brief Print a summary report of all memory used by ptr.
+ *
+ * This function prints a summary report of all memory used by ptr. One line of
+ * report is printed for each immediate child of ptr, showing the total memory
+ * and number of blocks used by that child.
+ *
+ * You can pass NULL for the pointer, in which case a report is printed
+ * for the top level memory context, but only if talloc_enable_leak_report()
+ * or talloc_enable_leak_report_full() has been called.
+ *
+ * @param[in]  ptr      The talloc chunk.
+ *
+ * @param[in]  f        The file handle to print to.
+ *
+ * Example:
+ * @code
+ *      unsigned int *a, *b;
+ *      a = talloc(NULL, unsigned int);
+ *      b = talloc(a, unsigned int);
+ *      fprintf(stderr, "Summary of memory tree for a:\n");
+ *      talloc_report(a, stderr);
+ * @endcode
+ *
+ * @see talloc_report_full()
+ */
+void talloc_report(const void *ptr, FILE *f);
+
+/**
+ * @brief Enable tracking the use of NULL memory contexts.
+ *
+ * This enables tracking of the NULL memory context without enabling leak
+ * reporting on exit. Useful for when you want to do your own leak
+ * reporting call via talloc_report_null_full();
+ */
+void talloc_enable_null_tracking(void);
+
+/**
+ * @brief Enable tracking the use of NULL memory contexts.
+ *
+ * This enables tracking of the NULL memory context without enabling leak
+ * reporting on exit. Useful for when you want to do your own leak
+ * reporting call via talloc_report_null_full();
+ */
+void talloc_enable_null_tracking_no_autofree(void);
+
+/**
+ * @brief Disable tracking of the NULL memory context.
+ *
+ * This disables tracking of the NULL memory context.
+ */
+void talloc_disable_null_tracking(void);
+
+/**
+ * @brief Enable leak report when a program exits.
+ *
+ * This enables calling of talloc_report(NULL, stderr) when the program
+ * exits. In Samba4 this is enabled by using the --leak-report command
+ * line option.
+ *
+ * For it to be useful, this function must be called before any other
+ * talloc function as it establishes a "null context" that acts as the
+ * top of the tree. If you don't call this function first then passing
+ * NULL to talloc_report() or talloc_report_full() won't give you the
+ * full tree printout.
+ *
+ * Here is a typical talloc report:
+ *
+ * @code
+ * talloc report on 'null_context' (total 267 bytes in 15 blocks)
+ *      libcli/auth/spnego_parse.c:55  contains     31 bytes in   2 blocks
+ *      libcli/auth/spnego_parse.c:55  contains     31 bytes in   2 blocks
+ *      iconv(UTF8,CP850)              contains     42 bytes in   2 blocks
+ *      libcli/auth/spnego_parse.c:55  contains     31 bytes in   2 blocks
+ *      iconv(CP850,UTF8)              contains     42 bytes in   2 blocks
+ *      iconv(UTF8,UTF-16LE)           contains     45 bytes in   2 blocks
+ *      iconv(UTF-16LE,UTF8)           contains     45 bytes in   2 blocks
+ * @endcode
+ */
+void talloc_enable_leak_report(void);
+
+/**
+ * @brief Enable full leak report when a program exits.
+ *
+ * This enables calling of talloc_report_full(NULL, stderr) when the
+ * program exits. In Samba4 this is enabled by using the
+ * --leak-report-full command line option.
+ *
+ * For it to be useful, this function must be called before any other
+ * talloc function as it establishes a "null context" that acts as the
+ * top of the tree. If you don't call this function first then passing
+ * NULL to talloc_report() or talloc_report_full() won't give you the
+ * full tree printout.
+ *
+ * Here is a typical full report:
+ *
+ * @code
+ * full talloc report on 'root' (total 18 bytes in 8 blocks)
+ *      p1                             contains     18 bytes in   7 blocks (ref 0)
+ *      r1                             contains     13 bytes in   2 blocks (ref 0)
+ *      reference to: p2
+ *      p2                             contains      1 bytes in   1 blocks (ref 1)
+ *      x3                             contains      1 bytes in   1 blocks (ref 0)
+ *      x2                             contains      1 bytes in   1 blocks (ref 0)
+ *      x1                             contains      1 bytes in   1 blocks (ref 0)
+ * @endcode
+ */
+void talloc_enable_leak_report_full(void);
+
+/* @} ******************************************************************/
+
+void talloc_set_abort_fn(void (*abort_fn)(const char *reason));
+void talloc_set_log_fn(void (*log_fn)(const char *message));
+void talloc_set_log_stderr(void);
+
+#if TALLOC_DEPRECATED
+#define talloc_zero_p(ctx, type) talloc_zero(ctx, type)
+#define talloc_p(ctx, type) talloc(ctx, type)
+#define talloc_array_p(ctx, type, count) talloc_array(ctx, type, count)
+#define talloc_realloc_p(ctx, p, type, count) talloc_realloc(ctx, p, type, count)
+#define talloc_destroy(ctx) talloc_free(ctx)
+#define talloc_append_string(c, s, a) (s?talloc_strdup_append(s,a):talloc_strdup(c, a))
+#endif
+
+#ifndef TALLOC_MAX_DEPTH
+#define TALLOC_MAX_DEPTH 10000
+#endif
+
+#ifdef __cplusplus
+} /* end of extern "C" */
+#endif
+
+#endif
index 7000499ba68198ed1943e983bcbde0747b06cef2..386b6c28c9b1c1ddfb8560752e66480595788627 100644 (file)
@@ -19,6 +19,7 @@
 
 #include <atalk/unicode.h>
 #include <atalk/bstrlib.h>
+#include <atalk/cnid.h>
 
 /* exit error codes */
 #define EXITERR_CLNT 1  /* client related error */
@@ -198,6 +199,7 @@ extern int ochmod(char *path, mode_t mode, const struct stat *st, int options);
  *****************************************************************/
 
 extern bstring rel_path_in_vol(const char *path, const char *volpath);
+extern cnid_t cnid_for_path(struct _cnid_db *cdb, const char *volpath, const char *path, cnid_t *did);
 
 /******************************************************************
  * cnid.c
index a9e497a6d76ddf4cf4053b673da181de09a03a79..7172887a2d489947316df310aa44966277602cc6 100644 (file)
@@ -118,6 +118,7 @@ struct vol {
   Keep in sync with libatalk/util/volinfo.c
 */
 #define AFPVOL_NOV2TOEACONV (1 << 5) /* no adouble:v2 to adouble:ea conversion */
+#define AFPVOL_SPOTLIGHT (1 << 6)   /* Index volume for Spotlight searches */
 #define AFPVOL_RO        (1 << 8)   /* read-only volume */
 #define AFPVOL_NOSTAT    (1 << 16)  /* advertise the volume even if we can't stat() it
                                      * maybe because it will be mounted later in preexec */
index 62f1af30c9f23d65c9d149831cf62607d9f70926..d8733b35e92d66f5f67e5415e0b64503bcb4268a 100644 (file)
@@ -34,7 +34,7 @@ VERSION_INFO = 6:0:0
 #   3.0.4           5:0:0
 #   3.0.5           6:0:0
 
-SUBDIRS = acl adouble bstring compat cnid dsi iniparser util unicode vfs
+SUBDIRS = acl adouble bstring compat cnid dsi iniparser talloc util unicode vfs
 
 lib_LTLIBRARIES = libatalk.la
 
@@ -52,6 +52,7 @@ libatalk_la_LIBADD  = \
        compat/libcompat.la     \
        dsi/libdsi.la           \
        iniparser/libiniparser.la \
+       talloc/libtalloc.la       \
        unicode/libunicode.la \
        util/libutil.la         \
        vfs/libvfs.la
@@ -63,6 +64,7 @@ libatalk_la_DEPENDENCIES = \
        cnid/libcnid.la \
        compat/libcompat.la     \
        dsi/libdsi.la           \
+       talloc/libtalloc.la       \
        iniparser/libiniparser.la \
        unicode/libunicode.la \
        util/libutil.la         \
diff --git a/libatalk/talloc/Makefile.am b/libatalk/talloc/Makefile.am
new file mode 100644 (file)
index 0000000..8616eac
--- /dev/null
@@ -0,0 +1,4 @@
+# Makefile.am for libatalk/talloc/
+
+noinst_LTLIBRARIES = libtalloc.la
+libtalloc_la_SOURCES = talloc.c dalloc.c
diff --git a/libatalk/talloc/dalloc.c b/libatalk/talloc/dalloc.c
new file mode 100644 (file)
index 0000000..99dd7ec
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+  Copyright (c) 2012 Frank Lahm <franklahm@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.
+*/
+
+/*!
+  @file
+  Typesafe, dynamic object store based on talloc
+  Usage:
+
+  //
+  // Define some terminal types:
+  //
+
+  // A key/value store aka dictionary that supports retrieving elements by key
+  typedef dict_t DALLOC_CTX;
+
+  // An ordered set that can store different objects which can be retrieved by number
+  typedef set_t DALLOC_CTX;
+
+  //
+  // Create an dalloc object and add elementes of different type
+  //
+
+  // Allocate a new talloc context
+  TALLOC_CTX *mem_ctx = talloc_new(NULL);
+  // Create a new dalloc object
+  DALLOC_CTX *d = talloc_zero(mem_ctx, DALLOC_CTX);
+  // Store an int value in the object
+  uint64_t i = 1;
+  dalloc_add_copy(d, &i, uint64_t);
+  // Store a string
+  char *str = dalloc_strdup(d, "hello world");
+  dalloc_add(d, str, char *);
+  // Add a nested object, you later can't fetch this directly
+  DALLOC_CTX *nested = talloc_zero(d, DALLOC_CTX);
+  dalloc_add(d, nested, DALLOC_CTX);
+
+  // Add an int value to the nested object, this can be fetched
+  i = 2;
+  dalloc_add_copy(nested, &i, uint64_t);
+
+  // Add a nested set
+  set_t *set = talloc_zero(nested, set_t);
+  dalloc_add(nested, set, set_t);
+  // Add an int value to the set
+  i = 3;
+  dalloc_add_copy(set, &i, uint64_t);
+
+  // Add a dictionary (key/value store)
+  dict_t *dict = talloc_zero(nested, dict_t);
+  dalloc_add(nested, dict, dict_t);
+
+  // Store a string as key in the dict
+  str = dalloc_strdup(d, "key");
+  dalloc_add(dict, str, char *);
+
+  // Add a value for the key
+  i = 4;
+  dalloc_add_copy(dict, &i, uint64_t);
+
+  //
+  // Fetching value references
+  // You can fetch anything that is not a DALLOC_CTXs, because passing
+  // "DALLOC_CTXs" as type to the functions dalloc_get() and dalloc_value_for_key()
+  // tells the function to step into that object and expect more arguments that specify
+  // which element to fetch.
+  //
+
+  // Get reference to an objects element by position
+  uint64_t *p = dalloc_get(d, "uint64_t", 0);
+  // p now points to the first int with a value of 1
+
+  // Get reference to the "hello world" string
+  str = dalloc_get(d, "char *", 1);
+
+  // You can't fetch a pure DALLOC_CTX
+  nested = dalloc_get(d, "DALLOC_CTX", 2);
+  // But you can do this
+  p = dalloc_get(d, "DALLOC_CTX", 2, "uint64_t", 0);
+  // p now points to the value 2
+
+  // You can fetch types that are typedefd DALLOC_CTXs
+  set = dalloc_get(d, "DALLOC_CTX", 2, "set_t", 1);
+
+  // Fetch int from set, note that you must use DALLOC_CTX as type for the set
+  p = dalloc_get(d, "DALLOC_CTX", 2, "DALLOC_CTX", 1, "uint64_t", 0);
+  // p points to 3
+
+  // Fetch value by key from dictionary
+  p = dalloc_value_for_key(d, "DALLOC_CTX", 2, "DALLOC_CTX", 2, "key");
+  // p now point to 4
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <strings.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <inttypes.h>
+
+#include <atalk/errchk.h>
+#include <atalk/util.h>
+#include <atalk/logger.h>
+#include <atalk/talloc.h>
+#include <atalk/bstrlib.h>
+#include <atalk/dalloc.h>
+
+/* Use dalloc_add_copy() macro, not this function */
+int dalloc_add_talloc_chunk(DALLOC_CTX *dd, void *talloc_chunk, void *obj, size_t size)
+{
+    if (talloc_chunk) {
+        /* Called from dalloc_add_copy() macro */
+        dd->dd_talloc_array = talloc_realloc(dd,
+                                             dd->dd_talloc_array,
+                                             void *,
+                                             talloc_array_length(dd->dd_talloc_array) + 1);
+        memcpy(talloc_chunk, obj, size);
+        dd->dd_talloc_array[talloc_array_length(dd->dd_talloc_array) - 1] = talloc_chunk;
+    } else {
+        /* Called from dalloc_add() macro */
+        dd->dd_talloc_array = talloc_realloc(dd,
+                                             dd->dd_talloc_array,
+                                             void *,
+                                             talloc_array_length(dd->dd_talloc_array) + 1);
+        dd->dd_talloc_array[talloc_array_length(dd->dd_talloc_array) - 1] = obj;
+
+    }
+    return 0;
+}
+
+/* Get number of elements, returns 0 if the structure is empty or not initialized */
+int dalloc_size(DALLOC_CTX *d)
+{
+    if (!d || !d->dd_talloc_array)
+        return 0;
+    return talloc_array_length(d->dd_talloc_array);
+}
+
+/*
+ * Get pointer to value from a DALLOC object
+ *
+ * Returns pointer to object from a DALLOC object. Nested object interation
+ * is supported by using the type string "DALLOC_CTX". Any other type string
+ * designates the requested objects type.
+ */
+void *dalloc_get(const DALLOC_CTX *d, ...)
+{
+    EC_INIT;
+    void *p = NULL;
+    va_list args;
+    const char *type;
+    int elem;
+    const char *elemtype;
+
+    va_start(args, d);
+    type = va_arg(args, const char *);
+
+    while (STRCMP(type, ==, "DALLOC_CTX")) {
+        elem = va_arg(args, int);
+        if (elem >= talloc_array_length(d->dd_talloc_array)) {
+            LOG(log_error, logtype_sl, "dalloc_get(%s): bound check error: %d >= %d",
+                type, elem >= talloc_array_length(d->dd_talloc_array));
+            EC_FAIL;
+        }
+        d = d->dd_talloc_array[elem];
+        type = va_arg(args, const char *);
+    }
+
+    elem = va_arg(args, int);
+    if (elem >= talloc_array_length(d->dd_talloc_array)) {
+        LOG(log_error, logtype_sl, "dalloc_get(%s): bound check error: %d >= %d",
+            type, elem,  talloc_array_length(d->dd_talloc_array));
+        EC_FAIL;
+    }
+
+    if (!(p = talloc_check_name(d->dd_talloc_array[elem], type))) {
+        LOG(log_error, logtype_sl, "dalloc_get(%d/%s): type mismatch: %s",
+            type, elem, talloc_get_name(d->dd_talloc_array[elem]));
+    }
+
+    va_end(args);
+
+EC_CLEANUP:
+    if (ret != 0)
+        p = NULL;
+    return p;
+}
+
+void *dalloc_value_for_key(const DALLOC_CTX *d, ...)
+{
+    EC_INIT;
+    void *p = NULL;
+    va_list args;
+    const char *type;
+    int elem;
+    const char *elemtype;
+    char *s;
+
+    va_start(args, d);
+    type = va_arg(args, const char *);
+
+    while (STRCMP(type, ==, "DALLOC_CTX")) {
+        elem = va_arg(args, int);
+        AFP_ASSERT(elem < talloc_array_length(d->dd_talloc_array));
+        d = d->dd_talloc_array[elem];
+        type = va_arg(args, const char *);
+    }
+
+    for (elem = 0; elem + 1 < talloc_array_length(d->dd_talloc_array); elem += 2) {
+        if (STRCMP(talloc_get_name(d->dd_talloc_array[elem]), !=, "char *")) {
+            LOG(log_error, logtype_default, "dalloc_value_for_key: key not a string: %s",
+                talloc_get_name(d->dd_talloc_array[elem]));
+            EC_FAIL;
+        }
+        if (STRCMP((char *)d->dd_talloc_array[elem], ==, type)) {
+            p = d->dd_talloc_array[elem + 1];
+            break;
+        }            
+    }
+    va_end(args);
+
+EC_CLEANUP:
+    if (ret != 0)
+        p = NULL;
+    return p;
+}
+
+char *dalloc_strdup(const void *ctx, const char *string)
+{
+    EC_INIT;
+    char *p;
+
+    EC_NULL( p = talloc_strdup(ctx, string) );
+    talloc_set_name(p, "char *");
+
+EC_CLEANUP:
+    if (ret != 0) {
+        if (p)
+            talloc_free(p);
+        p = NULL;
+    }
+    return p;
+}
+
+char *dalloc_strndup(const void *ctx, const char *string, size_t n)
+{
+    EC_INIT;
+    char *p;
+
+    EC_NULL( p = talloc_strndup(ctx, string, n) );
+    talloc_set_name(p, "char *");
+
+EC_CLEANUP:
+    if (ret != 0) {
+        if (p)
+            talloc_free(p);
+        p = NULL;
+    }
+    return p;
+}
diff --git a/libatalk/talloc/talloc.c b/libatalk/talloc/talloc.c
new file mode 100644 (file)
index 0000000..78dffa8
--- /dev/null
@@ -0,0 +1,2379 @@
+/* 
+   Samba Unix SMB/CIFS implementation.
+
+   Samba trivial allocation library - new interface
+
+   NOTE: Please read talloc_guide.txt for full documentation
+
+   Copyright (C) Andrew Tridgell 2004
+   Copyright (C) Stefan Metzmacher 2006
+   
+     ** NOTE! The following LGPL license applies to the talloc
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+  inspired by http://swapped.cc/halloc/
+*/
+
+#include "config.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h>
+
+#include <atalk/compat.h>
+#include <atalk/util.h>
+#include <atalk/talloc.h>
+
+#define _PUBLIC_ extern
+
+/** 
+ * pointer difference macro 
+ */
+#define PTR_DIFF(p1,p2) ((ptrdiff_t)(((const char *)(p1)) - (const char *)(p2)))
+
+
+#ifdef TALLOC_BUILD_VERSION_MAJOR
+#if (TALLOC_VERSION_MAJOR != TALLOC_BUILD_VERSION_MAJOR)
+#error "TALLOC_VERSION_MAJOR != TALLOC_BUILD_VERSION_MAJOR"
+#endif
+#endif
+
+#ifdef TALLOC_BUILD_VERSION_MINOR
+#if (TALLOC_VERSION_MINOR != TALLOC_BUILD_VERSION_MINOR)
+#error "TALLOC_VERSION_MINOR != TALLOC_BUILD_VERSION_MINOR"
+#endif
+#endif
+
+/* Special macros that are no-ops except when run under Valgrind on
+ * x86.  They've moved a little bit from valgrind 1.0.4 to 1.9.4 */
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+        /* memcheck.h includes valgrind.h */
+#include <valgrind/memcheck.h>
+#elif defined(HAVE_VALGRIND_H)
+#include <valgrind.h>
+#endif
+
+/* use this to force every realloc to change the pointer, to stress test
+   code that might not cope */
+#define ALWAYS_REALLOC 0
+
+
+#define MAX_TALLOC_SIZE 0x10000000
+#define TALLOC_MAGIC_BASE 0xe814ec70
+#define TALLOC_MAGIC ( \
+       TALLOC_MAGIC_BASE + \
+       (TALLOC_VERSION_MAJOR << 12) + \
+       (TALLOC_VERSION_MINOR << 4) \
+)
+
+#define TALLOC_FLAG_FREE 0x01
+#define TALLOC_FLAG_LOOP 0x02
+#define TALLOC_FLAG_POOL 0x04          /* This is a talloc pool */
+#define TALLOC_FLAG_POOLMEM 0x08       /* This is allocated in a pool */
+#define TALLOC_MAGIC_REFERENCE ((const char *)1)
+
+/* by default we abort when given a bad pointer (such as when talloc_free() is called 
+   on a pointer that came from malloc() */
+#ifndef TALLOC_ABORT
+#define TALLOC_ABORT(reason) abort()
+#endif
+
+#ifndef discard_const_p
+#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
+# define discard_const_p(type, ptr) ((type *)((intptr_t)(ptr)))
+#else
+# define discard_const_p(type, ptr) ((type *)(ptr))
+#endif
+#endif
+
+/* these macros gain us a few percent of speed on gcc */
+#if (__GNUC__ >= 3)
+/* the strange !! is to ensure that __builtin_expect() takes either 0 or 1
+   as its first argument */
+#ifndef likely
+#define likely(x)   __builtin_expect(!!(x), 1)
+#endif
+#ifndef unlikely
+#define unlikely(x) __builtin_expect(!!(x), 0)
+#endif
+#else
+#ifndef likely
+#define likely(x) (x)
+#endif
+#ifndef unlikely
+#define unlikely(x) (x)
+#endif
+#endif
+
+/* this null_context is only used if talloc_enable_leak_report() or
+   talloc_enable_leak_report_full() is called, otherwise it remains
+   NULL
+*/
+static void *null_context;
+static void *autofree_context;
+
+/* used to enable fill of memory on free, which can be useful for
+ * catching use after free errors when valgrind is too slow
+ */
+static struct {
+       bool initialised;
+       bool enabled;
+       uint8_t fill_value;
+} talloc_fill;
+
+#define TALLOC_FILL_ENV "TALLOC_FREE_FILL"
+
+/*
+ * do not wipe the header, to allow the
+ * double-free logic to still work
+ */
+#define TC_INVALIDATE_FULL_FILL_CHUNK(_tc) do { \
+       if (unlikely(talloc_fill.enabled)) { \
+               size_t _flen = (_tc)->size; \
+               char *_fptr = (char *)TC_PTR_FROM_CHUNK(_tc); \
+               memset(_fptr, talloc_fill.fill_value, _flen); \
+       } \
+} while (0)
+
+#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS)
+/* Mark the whole chunk as not accessable */
+#define TC_INVALIDATE_FULL_VALGRIND_CHUNK(_tc) do { \
+       size_t _flen = TC_HDR_SIZE + (_tc)->size; \
+       char *_fptr = (char *)(_tc); \
+       VALGRIND_MAKE_MEM_NOACCESS(_fptr, _flen); \
+} while(0)
+#else
+#define TC_INVALIDATE_FULL_VALGRIND_CHUNK(_tc) do { } while (0)
+#endif
+
+#define TC_INVALIDATE_FULL_CHUNK(_tc) do { \
+       TC_INVALIDATE_FULL_FILL_CHUNK(_tc); \
+       TC_INVALIDATE_FULL_VALGRIND_CHUNK(_tc); \
+} while (0)
+
+#define TC_INVALIDATE_SHRINK_FILL_CHUNK(_tc, _new_size) do { \
+       if (unlikely(talloc_fill.enabled)) { \
+               size_t _flen = (_tc)->size - (_new_size); \
+               char *_fptr = (char *)TC_PTR_FROM_CHUNK(_tc); \
+               _fptr += (_new_size); \
+               memset(_fptr, talloc_fill.fill_value, _flen); \
+       } \
+} while (0)
+
+#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS)
+/* Mark the unused bytes not accessable */
+#define TC_INVALIDATE_SHRINK_VALGRIND_CHUNK(_tc, _new_size) do { \
+       size_t _flen = (_tc)->size - (_new_size); \
+       char *_fptr = (char *)TC_PTR_FROM_CHUNK(_tc); \
+       _fptr += (_new_size); \
+       VALGRIND_MAKE_MEM_NOACCESS(_fptr, _flen); \
+} while (0)
+#else
+#define TC_INVALIDATE_SHRINK_VALGRIND_CHUNK(_tc, _new_size) do { } while (0)
+#endif
+
+#define TC_INVALIDATE_SHRINK_CHUNK(_tc, _new_size) do { \
+       TC_INVALIDATE_SHRINK_FILL_CHUNK(_tc, _new_size); \
+       TC_INVALIDATE_SHRINK_VALGRIND_CHUNK(_tc, _new_size); \
+} while (0)
+
+#define TC_UNDEFINE_SHRINK_FILL_CHUNK(_tc, _new_size) do { \
+       if (unlikely(talloc_fill.enabled)) { \
+               size_t _flen = (_tc)->size - (_new_size); \
+               char *_fptr = (char *)TC_PTR_FROM_CHUNK(_tc); \
+               _fptr += (_new_size); \
+               memset(_fptr, talloc_fill.fill_value, _flen); \
+       } \
+} while (0)
+
+#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_UNDEFINED)
+/* Mark the unused bytes as undefined */
+#define TC_UNDEFINE_SHRINK_VALGRIND_CHUNK(_tc, _new_size) do { \
+       size_t _flen = (_tc)->size - (_new_size); \
+       char *_fptr = (char *)TC_PTR_FROM_CHUNK(_tc); \
+       _fptr += (_new_size); \
+       VALGRIND_MAKE_MEM_UNDEFINED(_fptr, _flen); \
+} while (0)
+#else
+#define TC_UNDEFINE_SHRINK_VALGRIND_CHUNK(_tc, _new_size) do { } while (0)
+#endif
+
+#define TC_UNDEFINE_SHRINK_CHUNK(_tc, _new_size) do { \
+       TC_UNDEFINE_SHRINK_FILL_CHUNK(_tc, _new_size); \
+       TC_UNDEFINE_SHRINK_VALGRIND_CHUNK(_tc, _new_size); \
+} while (0)
+
+#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_UNDEFINED)
+/* Mark the new bytes as undefined */
+#define TC_UNDEFINE_GROW_VALGRIND_CHUNK(_tc, _new_size) do { \
+       size_t _old_used = TC_HDR_SIZE + (_tc)->size; \
+       size_t _new_used = TC_HDR_SIZE + (_new_size); \
+       size_t _flen = _new_used - _old_used; \
+       char *_fptr = _old_used + (char *)(_tc); \
+       VALGRIND_MAKE_MEM_UNDEFINED(_fptr, _flen); \
+} while (0)
+#else
+#define TC_UNDEFINE_GROW_VALGRIND_CHUNK(_tc, _new_size) do { } while (0)
+#endif
+
+#define TC_UNDEFINE_GROW_CHUNK(_tc, _new_size) do { \
+       TC_UNDEFINE_GROW_VALGRIND_CHUNK(_tc, _new_size); \
+} while (0)
+
+struct talloc_reference_handle {
+       struct talloc_reference_handle *next, *prev;
+       void *ptr;
+       const char *location;
+};
+
+typedef int (*talloc_destructor_t)(void *);
+
+struct talloc_chunk {
+       struct talloc_chunk *next, *prev;
+       struct talloc_chunk *parent, *child;
+       struct talloc_reference_handle *refs;
+       talloc_destructor_t destructor;
+       const char *name;
+       size_t size;
+       unsigned flags;
+
+       /*
+        * "pool" has dual use:
+        *
+        * For the talloc pool itself (i.e. TALLOC_FLAG_POOL is set), "pool"
+        * marks the end of the currently allocated area.
+        *
+        * For members of the pool (i.e. TALLOC_FLAG_POOLMEM is set), "pool"
+        * is a pointer to the struct talloc_chunk of the pool that it was
+        * allocated from. This way children can quickly find the pool to chew
+        * from.
+        */
+       void *pool;
+};
+
+/* 16 byte alignment seems to keep everyone happy */
+#define TC_ALIGN16(s) (((s)+15)&~15)
+#define TC_HDR_SIZE TC_ALIGN16(sizeof(struct talloc_chunk))
+#define TC_PTR_FROM_CHUNK(tc) ((void *)(TC_HDR_SIZE + (char*)tc))
+
+_PUBLIC_ int talloc_version_major(void)
+{
+       return TALLOC_VERSION_MAJOR;
+}
+
+_PUBLIC_ int talloc_version_minor(void)
+{
+       return TALLOC_VERSION_MINOR;
+}
+
+static void (*talloc_log_fn)(const char *message);
+
+_PUBLIC_ void talloc_set_log_fn(void (*log_fn)(const char *message))
+{
+       talloc_log_fn = log_fn;
+}
+
+static void talloc_log(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2);
+static void talloc_log(const char *fmt, ...)
+{
+       va_list ap;
+       char *message;
+
+       if (!talloc_log_fn) {
+               return;
+       }
+
+       va_start(ap, fmt);
+       message = talloc_vasprintf(NULL, fmt, ap);
+       va_end(ap);
+
+       talloc_log_fn(message);
+       talloc_free(message);
+}
+
+static void talloc_log_stderr(const char *message)
+{
+       fprintf(stderr, "%s", message);
+}
+
+_PUBLIC_ void talloc_set_log_stderr(void)
+{
+       talloc_set_log_fn(talloc_log_stderr);
+}
+
+static void (*talloc_abort_fn)(const char *reason);
+
+_PUBLIC_ void talloc_set_abort_fn(void (*abort_fn)(const char *reason))
+{
+       talloc_abort_fn = abort_fn;
+}
+
+static void talloc_abort(const char *reason)
+{
+       talloc_log("%s\n", reason);
+
+       if (!talloc_abort_fn) {
+               TALLOC_ABORT(reason);
+       }
+
+       talloc_abort_fn(reason);
+}
+
+static void talloc_abort_magic(unsigned magic)
+{
+       unsigned striped = magic - TALLOC_MAGIC_BASE;
+       unsigned major = (striped & 0xFFFFF000) >> 12;
+       unsigned minor = (striped & 0x00000FF0) >> 4;
+       talloc_log("Bad talloc magic[0x%08X/%u/%u] expected[0x%08X/%u/%u]\n",
+                  magic, major, minor,
+                  TALLOC_MAGIC, TALLOC_VERSION_MAJOR, TALLOC_VERSION_MINOR);
+       talloc_abort("Bad talloc magic value - wrong talloc version used/mixed");
+}
+
+static void talloc_abort_access_after_free(void)
+{
+       talloc_abort("Bad talloc magic value - access after free");
+}
+
+static void talloc_abort_unknown_value(void)
+{
+       talloc_abort("Bad talloc magic value - unknown value");
+}
+
+/* panic if we get a bad magic value */
+static inline struct talloc_chunk *talloc_chunk_from_ptr(const void *ptr)
+{
+       const char *pp = (const char *)ptr;
+       struct talloc_chunk *tc = discard_const_p(struct talloc_chunk, pp - TC_HDR_SIZE);
+       if (unlikely((tc->flags & (TALLOC_FLAG_FREE | ~0xF)) != TALLOC_MAGIC)) { 
+               if ((tc->flags & (~0xFFF)) == TALLOC_MAGIC_BASE) {
+                       talloc_abort_magic(tc->flags & (~0xF));
+                       return NULL;
+               }
+
+               if (tc->flags & TALLOC_FLAG_FREE) {
+                       talloc_log("talloc: access after free error - first free may be at %s\n", tc->name);
+                       talloc_abort_access_after_free();
+                       return NULL;
+               } else {
+                       talloc_abort_unknown_value();
+                       return NULL;
+               }
+       }
+       return tc;
+}
+
+/* hook into the front of the list */
+#define _TLIST_ADD(list, p) \
+do { \
+        if (!(list)) { \
+               (list) = (p); \
+               (p)->next = (p)->prev = NULL; \
+       } else { \
+               (list)->prev = (p); \
+               (p)->next = (list); \
+               (p)->prev = NULL; \
+               (list) = (p); \
+       }\
+} while (0)
+
+/* remove an element from a list - element doesn't have to be in list. */
+#define _TLIST_REMOVE(list, p) \
+do { \
+       if ((p) == (list)) { \
+               (list) = (p)->next; \
+               if (list) (list)->prev = NULL; \
+       } else { \
+               if ((p)->prev) (p)->prev->next = (p)->next; \
+               if ((p)->next) (p)->next->prev = (p)->prev; \
+       } \
+       if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \
+} while (0)
+
+
+/*
+  return the parent chunk of a pointer
+*/
+static inline struct talloc_chunk *talloc_parent_chunk(const void *ptr)
+{
+       struct talloc_chunk *tc;
+
+       if (unlikely(ptr == NULL)) {
+               return NULL;
+       }
+
+       tc = talloc_chunk_from_ptr(ptr);
+       while (tc->prev) tc=tc->prev;
+
+       return tc->parent;
+}
+
+_PUBLIC_ void *talloc_parent(const void *ptr)
+{
+       struct talloc_chunk *tc = talloc_parent_chunk(ptr);
+       return tc? TC_PTR_FROM_CHUNK(tc) : NULL;
+}
+
+/*
+  find parents name
+*/
+_PUBLIC_ const char *talloc_parent_name(const void *ptr)
+{
+       struct talloc_chunk *tc = talloc_parent_chunk(ptr);
+       return tc? tc->name : NULL;
+}
+
+/*
+  A pool carries an in-pool object count count in the first 16 bytes.
+  bytes. This is done to support talloc_steal() to a parent outside of the
+  pool. The count includes the pool itself, so a talloc_free() on a pool will
+  only destroy the pool if the count has dropped to zero. A talloc_free() of a
+  pool member will reduce the count, and eventually also call free(3) on the
+  pool memory.
+
+  The object count is not put into "struct talloc_chunk" because it is only
+  relevant for talloc pools and the alignment to 16 bytes would increase the
+  memory footprint of each talloc chunk by those 16 bytes.
+*/
+
+#define TALLOC_POOL_HDR_SIZE 16
+
+#define TC_POOL_SPACE_LEFT(_pool_tc) \
+       PTR_DIFF(TC_HDR_SIZE + (_pool_tc)->size + (char *)(_pool_tc), \
+                (_pool_tc)->pool)
+
+#define TC_POOL_FIRST_CHUNK(_pool_tc) \
+       ((void *)(TC_HDR_SIZE + TALLOC_POOL_HDR_SIZE + (char *)(_pool_tc)))
+
+#define TC_POOLMEM_CHUNK_SIZE(_tc) \
+       TC_ALIGN16(TC_HDR_SIZE + (_tc)->size)
+
+#define TC_POOLMEM_NEXT_CHUNK(_tc) \
+       ((void *)(TC_POOLMEM_CHUNK_SIZE(tc) + (char*)(_tc)))
+
+/* Mark the whole remaining pool as not accessable */
+#define TC_INVALIDATE_FILL_POOL(_pool_tc) do { \
+       if (unlikely(talloc_fill.enabled)) { \
+               size_t _flen = TC_POOL_SPACE_LEFT(_pool_tc); \
+               char *_fptr = (char *)(_pool_tc)->pool; \
+               memset(_fptr, talloc_fill.fill_value, _flen); \
+       } \
+} while(0)
+
+#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS)
+/* Mark the whole remaining pool as not accessable */
+#define TC_INVALIDATE_VALGRIND_POOL(_pool_tc) do { \
+       size_t _flen = TC_POOL_SPACE_LEFT(_pool_tc); \
+       char *_fptr = (char *)(_pool_tc)->pool; \
+       VALGRIND_MAKE_MEM_NOACCESS(_fptr, _flen); \
+} while(0)
+#else
+#define TC_INVALIDATE_VALGRIND_POOL(_pool_tc) do { } while (0)
+#endif
+
+#define TC_INVALIDATE_POOL(_pool_tc) do { \
+       TC_INVALIDATE_FILL_POOL(_pool_tc); \
+       TC_INVALIDATE_VALGRIND_POOL(_pool_tc); \
+} while (0)
+
+static unsigned int *talloc_pool_objectcount(struct talloc_chunk *tc)
+{
+       return (unsigned int *)((char *)tc + TC_HDR_SIZE);
+}
+
+/*
+  Allocate from a pool
+*/
+
+static struct talloc_chunk *talloc_alloc_pool(struct talloc_chunk *parent,
+                                             size_t size)
+{
+       struct talloc_chunk *pool_ctx = NULL;
+       size_t space_left;
+       struct talloc_chunk *result;
+       size_t chunk_size;
+
+       if (parent == NULL) {
+               return NULL;
+       }
+
+       if (parent->flags & TALLOC_FLAG_POOL) {
+               pool_ctx = parent;
+       }
+       else if (parent->flags & TALLOC_FLAG_POOLMEM) {
+               pool_ctx = (struct talloc_chunk *)parent->pool;
+       }
+
+       if (pool_ctx == NULL) {
+               return NULL;
+       }
+
+       space_left = TC_POOL_SPACE_LEFT(pool_ctx);
+
+       /*
+        * Align size to 16 bytes
+        */
+       chunk_size = TC_ALIGN16(size);
+
+       if (space_left < chunk_size) {
+               return NULL;
+       }
+
+       result = (struct talloc_chunk *)pool_ctx->pool;
+
+#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_UNDEFINED)
+       VALGRIND_MAKE_MEM_UNDEFINED(result, size);
+#endif
+
+       pool_ctx->pool = (void *)((char *)result + chunk_size);
+
+       result->flags = TALLOC_MAGIC | TALLOC_FLAG_POOLMEM;
+       result->pool = pool_ctx;
+
+       *talloc_pool_objectcount(pool_ctx) += 1;
+
+       return result;
+}
+
+/* 
+   Allocate a bit of memory as a child of an existing pointer
+*/
+static inline void *__talloc(const void *context, size_t size)
+{
+       struct talloc_chunk *tc = NULL;
+
+       if (unlikely(context == NULL)) {
+               context = null_context;
+       }
+
+       if (unlikely(size >= MAX_TALLOC_SIZE)) {
+               return NULL;
+       }
+
+       if (context != NULL) {
+               tc = talloc_alloc_pool(talloc_chunk_from_ptr(context),
+                                      TC_HDR_SIZE+size);
+       }
+
+       if (tc == NULL) {
+               tc = (struct talloc_chunk *)malloc(TC_HDR_SIZE+size);
+               if (unlikely(tc == NULL)) return NULL;
+               tc->flags = TALLOC_MAGIC;
+               tc->pool  = NULL;
+       }
+
+       tc->size = size;
+       tc->destructor = NULL;
+       tc->child = NULL;
+       tc->name = NULL;
+       tc->refs = NULL;
+
+       if (likely(context)) {
+               struct talloc_chunk *parent = talloc_chunk_from_ptr(context);
+
+               if (parent->child) {
+                       parent->child->parent = NULL;
+                       tc->next = parent->child;
+                       tc->next->prev = tc;
+               } else {
+                       tc->next = NULL;
+               }
+               tc->parent = parent;
+               tc->prev = NULL;
+               parent->child = tc;
+       } else {
+               tc->next = tc->prev = tc->parent = NULL;
+       }
+
+       return TC_PTR_FROM_CHUNK(tc);
+}
+
+/*
+ * Create a talloc pool
+ */
+
+_PUBLIC_ void *talloc_pool(const void *context, size_t size)
+{
+       void *result = __talloc(context, size + TALLOC_POOL_HDR_SIZE);
+       struct talloc_chunk *tc;
+
+       if (unlikely(result == NULL)) {
+               return NULL;
+       }
+
+       tc = talloc_chunk_from_ptr(result);
+
+       tc->flags |= TALLOC_FLAG_POOL;
+       tc->pool = TC_POOL_FIRST_CHUNK(tc);
+
+       *talloc_pool_objectcount(tc) = 1;
+
+       TC_INVALIDATE_POOL(tc);
+
+       return result;
+}
+
+/*
+  setup a destructor to be called on free of a pointer
+  the destructor should return 0 on success, or -1 on failure.
+  if the destructor fails then the free is failed, and the memory can
+  be continued to be used
+*/
+_PUBLIC_ void _talloc_set_destructor(const void *ptr, int (*destructor)(void *))
+{
+       struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+       tc->destructor = destructor;
+}
+
+/*
+  increase the reference count on a piece of memory. 
+*/
+_PUBLIC_ int talloc_increase_ref_count(const void *ptr)
+{
+       if (unlikely(!talloc_reference(null_context, ptr))) {
+               return -1;
+       }
+       return 0;
+}
+
+/*
+  helper for talloc_reference()
+
+  this is referenced by a function pointer and should not be inline
+*/
+static int talloc_reference_destructor(struct talloc_reference_handle *handle)
+{
+       struct talloc_chunk *ptr_tc = talloc_chunk_from_ptr(handle->ptr);
+       _TLIST_REMOVE(ptr_tc->refs, handle);
+       return 0;
+}
+
+/*
+   more efficient way to add a name to a pointer - the name must point to a 
+   true string constant
+*/
+static inline void _talloc_set_name_const(const void *ptr, const char *name)
+{
+       struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+       tc->name = name;
+}
+
+/*
+  internal talloc_named_const()
+*/
+static inline void *_talloc_named_const(const void *context, size_t size, const char *name)
+{
+       void *ptr;
+
+       ptr = __talloc(context, size);
+       if (unlikely(ptr == NULL)) {
+               return NULL;
+       }
+
+       _talloc_set_name_const(ptr, name);
+
+       return ptr;
+}
+
+/*
+  make a secondary reference to a pointer, hanging off the given context.
+  the pointer remains valid until both the original caller and this given
+  context are freed.
+  
+  the major use for this is when two different structures need to reference the 
+  same underlying data, and you want to be able to free the two instances separately,
+  and in either order
+*/
+_PUBLIC_ void *_talloc_reference_loc(const void *context, const void *ptr, const char *location)
+{
+       struct talloc_chunk *tc;
+       struct talloc_reference_handle *handle;
+       if (unlikely(ptr == NULL)) return NULL;
+
+       tc = talloc_chunk_from_ptr(ptr);
+       handle = (struct talloc_reference_handle *)_talloc_named_const(context,
+                                                  sizeof(struct talloc_reference_handle),
+                                                  TALLOC_MAGIC_REFERENCE);
+       if (unlikely(handle == NULL)) return NULL;
+
+       /* note that we hang the destructor off the handle, not the
+          main context as that allows the caller to still setup their
+          own destructor on the context if they want to */
+       talloc_set_destructor(handle, talloc_reference_destructor);
+       handle->ptr = discard_const_p(void, ptr);
+       handle->location = location;
+       _TLIST_ADD(tc->refs, handle);
+       return handle->ptr;
+}
+
+static void *_talloc_steal_internal(const void *new_ctx, const void *ptr);
+
+static inline void _talloc_free_poolmem(struct talloc_chunk *tc,
+                                       const char *location)
+{
+       struct talloc_chunk *pool;
+       void *next_tc;
+       unsigned int *pool_object_count;
+
+       pool = (struct talloc_chunk *)tc->pool;
+       next_tc = TC_POOLMEM_NEXT_CHUNK(tc);
+
+       tc->flags |= TALLOC_FLAG_FREE;
+
+       /* we mark the freed memory with where we called the free
+        * from. This means on a double free error we can report where
+        * the first free came from
+        */
+       tc->name = location;
+
+       TC_INVALIDATE_FULL_CHUNK(tc);
+
+       pool_object_count = talloc_pool_objectcount(pool);
+
+       if (unlikely(*pool_object_count == 0)) {
+               talloc_abort("Pool object count zero!");
+               return;
+       }
+
+       *pool_object_count -= 1;
+
+       if (unlikely(*pool_object_count == 1 && !(pool->flags & TALLOC_FLAG_FREE))) {
+               /*
+                * if there is just one object left in the pool
+                * and pool->flags does not have TALLOC_FLAG_FREE,
+                * it means this is the pool itself and
+                * the rest is available for new objects
+                * again.
+                */
+               pool->pool = TC_POOL_FIRST_CHUNK(pool);
+               TC_INVALIDATE_POOL(pool);
+       } else if (unlikely(*pool_object_count == 0)) {
+               /*
+                * we mark the freed memory with where we called the free
+                * from. This means on a double free error we can report where
+                * the first free came from
+                */
+               pool->name = location;
+
+               TC_INVALIDATE_FULL_CHUNK(pool);
+               free(pool);
+       } else if (pool->pool == next_tc) {
+               /*
+                * if pool->pool still points to end of
+                * 'tc' (which is stored in the 'next_tc' variable),
+                * we can reclaim the memory of 'tc'.
+                */
+               pool->pool = tc;
+       }
+}
+
+static inline void _talloc_free_children_internal(struct talloc_chunk *tc,
+                                                 void *ptr,
+                                                 const char *location);
+
+/* 
+   internal talloc_free call
+*/
+static inline int _talloc_free_internal(void *ptr, const char *location)
+{
+       struct talloc_chunk *tc;
+
+       if (unlikely(ptr == NULL)) {
+               return -1;
+       }
+
+       /* possibly initialised the talloc fill value */
+       if (unlikely(!talloc_fill.initialised)) {
+               const char *fill = getenv(TALLOC_FILL_ENV);
+               if (fill != NULL) {
+                       talloc_fill.enabled = true;
+                       talloc_fill.fill_value = strtoul(fill, NULL, 0);
+               }
+               talloc_fill.initialised = true;
+       }
+
+       tc = talloc_chunk_from_ptr(ptr);
+
+       if (unlikely(tc->refs)) {
+               int is_child;
+               /* check if this is a reference from a child or
+                * grandchild back to it's parent or grandparent
+                *
+                * in that case we need to remove the reference and
+                * call another instance of talloc_free() on the current
+                * pointer.
+                */
+               is_child = talloc_is_parent(tc->refs, ptr);
+               _talloc_free_internal(tc->refs, location);
+               if (is_child) {
+                       return _talloc_free_internal(ptr, location);
+               }
+               return -1;
+       }
+
+       if (unlikely(tc->flags & TALLOC_FLAG_LOOP)) {
+               /* we have a free loop - stop looping */
+               return 0;
+       }
+
+       if (unlikely(tc->destructor)) {
+               talloc_destructor_t d = tc->destructor;
+               if (d == (talloc_destructor_t)-1) {
+                       return -1;
+               }
+               tc->destructor = (talloc_destructor_t)-1;
+               if (d(ptr) == -1) {
+                       tc->destructor = d;
+                       return -1;
+               }
+               tc->destructor = NULL;
+       }
+
+       if (tc->parent) {
+               _TLIST_REMOVE(tc->parent->child, tc);
+               if (tc->parent->child) {
+                       tc->parent->child->parent = tc->parent;
+               }
+       } else {
+               if (tc->prev) tc->prev->next = tc->next;
+               if (tc->next) tc->next->prev = tc->prev;
+               tc->prev = tc->next = NULL;
+       }
+
+       tc->flags |= TALLOC_FLAG_LOOP;
+
+       _talloc_free_children_internal(tc, ptr, location);
+
+       tc->flags |= TALLOC_FLAG_FREE;
+
+       /* we mark the freed memory with where we called the free
+        * from. This means on a double free error we can report where
+        * the first free came from 
+        */      
+       tc->name = location;
+
+       if (tc->flags & TALLOC_FLAG_POOL) {
+               unsigned int *pool_object_count;
+
+               pool_object_count = talloc_pool_objectcount(tc);
+
+               if (unlikely(*pool_object_count == 0)) {
+                       talloc_abort("Pool object count zero!");
+                       return 0;
+               }
+
+               *pool_object_count -= 1;
+
+               if (unlikely(*pool_object_count == 0)) {
+                       TC_INVALIDATE_FULL_CHUNK(tc);
+                       free(tc);
+               }
+       } else if (tc->flags & TALLOC_FLAG_POOLMEM) {
+               _talloc_free_poolmem(tc, location);
+       } else {
+               TC_INVALIDATE_FULL_CHUNK(tc);
+               free(tc);
+       }
+       return 0;
+}
+
+/* 
+   move a lump of memory from one talloc context to another return the
+   ptr on success, or NULL if it could not be transferred.
+   passing NULL as ptr will always return NULL with no side effects.
+*/
+static void *_talloc_steal_internal(const void *new_ctx, const void *ptr)
+{
+       struct talloc_chunk *tc, *new_tc;
+
+       if (unlikely(!ptr)) {
+               return NULL;
+       }
+
+       if (unlikely(new_ctx == NULL)) {
+               new_ctx = null_context;
+       }
+
+       tc = talloc_chunk_from_ptr(ptr);
+
+       if (unlikely(new_ctx == NULL)) {
+               if (tc->parent) {
+                       _TLIST_REMOVE(tc->parent->child, tc);
+                       if (tc->parent->child) {
+                               tc->parent->child->parent = tc->parent;
+                       }
+               } else {
+                       if (tc->prev) tc->prev->next = tc->next;
+                       if (tc->next) tc->next->prev = tc->prev;
+               }
+               
+               tc->parent = tc->next = tc->prev = NULL;
+               return discard_const_p(void, ptr);
+       }
+
+       new_tc = talloc_chunk_from_ptr(new_ctx);
+
+       if (unlikely(tc == new_tc || tc->parent == new_tc)) {
+               return discard_const_p(void, ptr);
+       }
+
+       if (tc->parent) {
+               _TLIST_REMOVE(tc->parent->child, tc);
+               if (tc->parent->child) {
+                       tc->parent->child->parent = tc->parent;
+               }
+       } else {
+               if (tc->prev) tc->prev->next = tc->next;
+               if (tc->next) tc->next->prev = tc->prev;
+               tc->prev = tc->next = NULL;
+       }
+
+       tc->parent = new_tc;
+       if (new_tc->child) new_tc->child->parent = NULL;
+       _TLIST_ADD(new_tc->child, tc);
+
+       return discard_const_p(void, ptr);
+}
+
+/* 
+   move a lump of memory from one talloc context to another return the
+   ptr on success, or NULL if it could not be transferred.
+   passing NULL as ptr will always return NULL with no side effects.
+*/
+_PUBLIC_ void *_talloc_steal_loc(const void *new_ctx, const void *ptr, const char *location)
+{
+       struct talloc_chunk *tc;
+
+       if (unlikely(ptr == NULL)) {
+               return NULL;
+       }
+       
+       tc = talloc_chunk_from_ptr(ptr);
+       
+       if (unlikely(tc->refs != NULL) && talloc_parent(ptr) != new_ctx) {
+               struct talloc_reference_handle *h;
+
+               talloc_log("WARNING: talloc_steal with references at %s\n",
+                          location);
+
+               for (h=tc->refs; h; h=h->next) {
+                       talloc_log("\treference at %s\n",
+                                  h->location);
+               }
+       }
+
+#if 0
+       /* this test is probably too expensive to have on in the
+          normal build, but it useful for debugging */
+       if (talloc_is_parent(new_ctx, ptr)) {
+               talloc_log("WARNING: stealing into talloc child at %s\n", location);
+       }
+#endif
+       
+       return _talloc_steal_internal(new_ctx, ptr);
+}
+
+/* 
+   this is like a talloc_steal(), but you must supply the old
+   parent. This resolves the ambiguity in a talloc_steal() which is
+   called on a context that has more than one parent (via references)
+
+   The old parent can be either a reference or a parent
+*/
+_PUBLIC_ void *talloc_reparent(const void *old_parent, const void *new_parent, const void *ptr)
+{
+       struct talloc_chunk *tc;
+       struct talloc_reference_handle *h;
+
+       if (unlikely(ptr == NULL)) {
+               return NULL;
+       }
+
+       if (old_parent == talloc_parent(ptr)) {
+               return _talloc_steal_internal(new_parent, ptr);
+       }
+
+       tc = talloc_chunk_from_ptr(ptr);
+       for (h=tc->refs;h;h=h->next) {
+               if (talloc_parent(h) == old_parent) {
+                       if (_talloc_steal_internal(new_parent, h) != h) {
+                               return NULL;
+                       }
+                       return discard_const_p(void, ptr);
+               }
+       }       
+
+       /* it wasn't a parent */
+       return NULL;
+}
+
+/*
+  remove a secondary reference to a pointer. This undo's what
+  talloc_reference() has done. The context and pointer arguments
+  must match those given to a talloc_reference()
+*/
+static inline int talloc_unreference(const void *context, const void *ptr)
+{
+       struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+       struct talloc_reference_handle *h;
+
+       if (unlikely(context == NULL)) {
+               context = null_context;
+       }
+
+       for (h=tc->refs;h;h=h->next) {
+               struct talloc_chunk *p = talloc_parent_chunk(h);
+               if (p == NULL) {
+                       if (context == NULL) break;
+               } else if (TC_PTR_FROM_CHUNK(p) == context) {
+                       break;
+               }
+       }
+       if (h == NULL) {
+               return -1;
+       }
+
+       return _talloc_free_internal(h, __location__);
+}
+
+/*
+  remove a specific parent context from a pointer. This is a more
+  controlled varient of talloc_free()
+*/
+_PUBLIC_ int talloc_unlink(const void *context, void *ptr)
+{
+       struct talloc_chunk *tc_p, *new_p;
+       void *new_parent;
+
+       if (ptr == NULL) {
+               return -1;
+       }
+
+       if (context == NULL) {
+               context = null_context;
+       }
+
+       if (talloc_unreference(context, ptr) == 0) {
+               return 0;
+       }
+
+       if (context == NULL) {
+               if (talloc_parent_chunk(ptr) != NULL) {
+                       return -1;
+               }
+       } else {
+               if (talloc_chunk_from_ptr(context) != talloc_parent_chunk(ptr)) {
+                       return -1;
+               }
+       }
+       
+       tc_p = talloc_chunk_from_ptr(ptr);
+
+       if (tc_p->refs == NULL) {
+               return _talloc_free_internal(ptr, __location__);
+       }
+
+       new_p = talloc_parent_chunk(tc_p->refs);
+       if (new_p) {
+               new_parent = TC_PTR_FROM_CHUNK(new_p);
+       } else {
+               new_parent = NULL;
+       }
+
+       if (talloc_unreference(new_parent, ptr) != 0) {
+               return -1;
+       }
+
+       _talloc_steal_internal(new_parent, ptr);
+
+       return 0;
+}
+
+/*
+  add a name to an existing pointer - va_list version
+*/
+static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
+
+static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap)
+{
+       struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+       tc->name = talloc_vasprintf(ptr, fmt, ap);
+       if (likely(tc->name)) {
+               _talloc_set_name_const(tc->name, ".name");
+       }
+       return tc->name;
+}
+
+/*
+  add a name to an existing pointer
+*/
+_PUBLIC_ const char *talloc_set_name(const void *ptr, const char *fmt, ...)
+{
+       const char *name;
+       va_list ap;
+       va_start(ap, fmt);
+       name = talloc_set_name_v(ptr, fmt, ap);
+       va_end(ap);
+       return name;
+}
+
+
+/*
+  create a named talloc pointer. Any talloc pointer can be named, and
+  talloc_named() operates just like talloc() except that it allows you
+  to name the pointer.
+*/
+_PUBLIC_ void *talloc_named(const void *context, size_t size, const char *fmt, ...)
+{
+       va_list ap;
+       void *ptr;
+       const char *name;
+
+       ptr = __talloc(context, size);
+       if (unlikely(ptr == NULL)) return NULL;
+
+       va_start(ap, fmt);
+       name = talloc_set_name_v(ptr, fmt, ap);
+       va_end(ap);
+
+       if (unlikely(name == NULL)) {
+               _talloc_free_internal(ptr, __location__);
+               return NULL;
+       }
+
+       return ptr;
+}
+
+/*
+  return the name of a talloc ptr, or "UNNAMED"
+*/
+_PUBLIC_ const char *talloc_get_name(const void *ptr)
+{
+       struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+       if (unlikely(tc->name == TALLOC_MAGIC_REFERENCE)) {
+               return ".reference";
+       }
+       if (likely(tc->name)) {
+               return tc->name;
+       }
+       return "UNNAMED";
+}
+
+
+/*
+  check if a pointer has the given name. If it does, return the pointer,
+  otherwise return NULL
+*/
+_PUBLIC_ void *talloc_check_name(const void *ptr, const char *name)
+{
+       const char *pname;
+       if (unlikely(ptr == NULL)) return NULL;
+       pname = talloc_get_name(ptr);
+       if (likely(pname == name || strcmp(pname, name) == 0)) {
+               return discard_const_p(void, ptr);
+       }
+       return NULL;
+}
+
+static void talloc_abort_type_missmatch(const char *location,
+                                       const char *name,
+                                       const char *expected)
+{
+       const char *reason;
+
+       reason = talloc_asprintf(NULL,
+                                "%s: Type mismatch: name[%s] expected[%s]",
+                                location,
+                                name?name:"NULL",
+                                expected);
+       if (!reason) {
+               reason = "Type mismatch";
+       }
+
+       talloc_abort(reason);
+}
+
+_PUBLIC_ void *_talloc_get_type_abort(const void *ptr, const char *name, const char *location)
+{
+       const char *pname;
+
+       if (unlikely(ptr == NULL)) {
+               talloc_abort_type_missmatch(location, NULL, name);
+               return NULL;
+       }
+
+       pname = talloc_get_name(ptr);
+       if (likely(pname == name || strcmp(pname, name) == 0)) {
+               return discard_const_p(void, ptr);
+       }
+
+       talloc_abort_type_missmatch(location, pname, name);
+       return NULL;
+}
+
+/*
+  this is for compatibility with older versions of talloc
+*/
+_PUBLIC_ void *talloc_init(const char *fmt, ...)
+{
+       va_list ap;
+       void *ptr;
+       const char *name;
+
+       ptr = __talloc(NULL, 0);
+       if (unlikely(ptr == NULL)) return NULL;
+
+       va_start(ap, fmt);
+       name = talloc_set_name_v(ptr, fmt, ap);
+       va_end(ap);
+
+       if (unlikely(name == NULL)) {
+               _talloc_free_internal(ptr, __location__);
+               return NULL;
+       }
+
+       return ptr;
+}
+
+static inline void _talloc_free_children_internal(struct talloc_chunk *tc,
+                                                 void *ptr,
+                                                 const char *location)
+{
+       while (tc->child) {
+               /* we need to work out who will own an abandoned child
+                  if it cannot be freed. In priority order, the first
+                  choice is owner of any remaining reference to this
+                  pointer, the second choice is our parent, and the
+                  final choice is the null context. */
+               void *child = TC_PTR_FROM_CHUNK(tc->child);
+               const void *new_parent = null_context;
+               if (unlikely(tc->child->refs)) {
+                       struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs);
+                       if (p) new_parent = TC_PTR_FROM_CHUNK(p);
+               }
+               if (unlikely(_talloc_free_internal(child, location) == -1)) {
+                       if (new_parent == null_context) {
+                               struct talloc_chunk *p = talloc_parent_chunk(ptr);
+                               if (p) new_parent = TC_PTR_FROM_CHUNK(p);
+                       }
+                       _talloc_steal_internal(new_parent, child);
+               }
+       }
+}
+
+/*
+  this is a replacement for the Samba3 talloc_destroy_pool functionality. It
+  should probably not be used in new code. It's in here to keep the talloc
+  code consistent across Samba 3 and 4.
+*/
+_PUBLIC_ void talloc_free_children(void *ptr)
+{
+       struct talloc_chunk *tc_name = NULL;
+       struct talloc_chunk *tc;
+
+       if (unlikely(ptr == NULL)) {
+               return;
+       }
+
+       tc = talloc_chunk_from_ptr(ptr);
+
+       /* we do not want to free the context name if it is a child .. */
+       if (likely(tc->child)) {
+               for (tc_name = tc->child; tc_name; tc_name = tc_name->next) {
+                       if (tc->name == TC_PTR_FROM_CHUNK(tc_name)) break;
+               }
+               if (tc_name) {
+                       _TLIST_REMOVE(tc->child, tc_name);
+                       if (tc->child) {
+                               tc->child->parent = tc;
+                       }
+               }
+       }
+
+       _talloc_free_children_internal(tc, ptr, __location__);
+
+       /* .. so we put it back after all other children have been freed */
+       if (tc_name) {
+               if (tc->child) {
+                       tc->child->parent = NULL;
+               }
+               tc_name->parent = tc;
+               _TLIST_ADD(tc->child, tc_name);
+       }
+}
+
+/* 
+   Allocate a bit of memory as a child of an existing pointer
+*/
+_PUBLIC_ void *_talloc(const void *context, size_t size)
+{
+       return __talloc(context, size);
+}
+
+/*
+  externally callable talloc_set_name_const()
+*/
+_PUBLIC_ void talloc_set_name_const(const void *ptr, const char *name)
+{
+       _talloc_set_name_const(ptr, name);
+}
+
+/*
+  create a named talloc pointer. Any talloc pointer can be named, and
+  talloc_named() operates just like talloc() except that it allows you
+  to name the pointer.
+*/
+_PUBLIC_ void *talloc_named_const(const void *context, size_t size, const char *name)
+{
+       return _talloc_named_const(context, size, name);
+}
+
+/* 
+   free a talloc pointer. This also frees all child pointers of this 
+   pointer recursively
+
+   return 0 if the memory is actually freed, otherwise -1. The memory
+   will not be freed if the ref_count is > 1 or the destructor (if
+   any) returns non-zero
+*/
+_PUBLIC_ int _talloc_free(void *ptr, const char *location)
+{
+       struct talloc_chunk *tc;
+
+       if (unlikely(ptr == NULL)) {
+               return -1;
+       }
+       
+       tc = talloc_chunk_from_ptr(ptr);
+       
+       if (unlikely(tc->refs != NULL)) {
+               struct talloc_reference_handle *h;
+
+               if (talloc_parent(ptr) == null_context && tc->refs->next == NULL) {
+                       /* in this case we do know which parent should
+                          get this pointer, as there is really only
+                          one parent */
+                       return talloc_unlink(null_context, ptr);
+               }
+
+               talloc_log("ERROR: talloc_free with references at %s\n",
+                          location);
+
+               for (h=tc->refs; h; h=h->next) {
+                       talloc_log("\treference at %s\n",
+                                  h->location);
+               }
+               return -1;
+       }
+       
+       return _talloc_free_internal(ptr, location);
+}
+
+
+
+/*
+  A talloc version of realloc. The context argument is only used if
+  ptr is NULL
+*/
+_PUBLIC_ void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name)
+{
+       struct talloc_chunk *tc;
+       void *new_ptr;
+       bool malloced = false;
+       struct talloc_chunk *pool_tc = NULL;
+
+       /* size zero is equivalent to free() */
+       if (unlikely(size == 0)) {
+               talloc_unlink(context, ptr);
+               return NULL;
+       }
+
+       if (unlikely(size >= MAX_TALLOC_SIZE)) {
+               return NULL;
+       }
+
+       /* realloc(NULL) is equivalent to malloc() */
+       if (ptr == NULL) {
+               return _talloc_named_const(context, size, name);
+       }
+
+       tc = talloc_chunk_from_ptr(ptr);
+
+       /* don't allow realloc on referenced pointers */
+       if (unlikely(tc->refs)) {
+               return NULL;
+       }
+
+       /* don't let anybody try to realloc a talloc_pool */
+       if (unlikely(tc->flags & TALLOC_FLAG_POOL)) {
+               return NULL;
+       }
+
+       /* don't let anybody try to realloc a talloc_pool */
+       if (unlikely(tc->flags & TALLOC_FLAG_POOLMEM)) {
+               pool_tc = (struct talloc_chunk *)tc->pool;
+       }
+
+#if (ALWAYS_REALLOC == 0)
+       /* don't shrink if we have less than 1k to gain */
+       if (size < tc->size) {
+               if (pool_tc) {
+                       void *next_tc = TC_POOLMEM_NEXT_CHUNK(tc);
+                       TC_INVALIDATE_SHRINK_CHUNK(tc, size);
+                       tc->size = size;
+                       if (next_tc == pool_tc->pool) {
+                               pool_tc->pool = TC_POOLMEM_NEXT_CHUNK(tc);
+                       }
+                       return ptr;
+               } else if ((tc->size - size) < 1024) {
+                       /*
+                        * if we call TC_INVALIDATE_SHRINK_CHUNK() here
+                        * we would need to call TC_UNDEFINE_GROW_CHUNK()
+                        * after each realloc call, which slows down
+                        * testing a lot :-(.
+                        *
+                        * That is why we only mark memory as undefined here.
+                        */
+                       TC_UNDEFINE_SHRINK_CHUNK(tc, size);
+
+                       /* do not shrink if we have less than 1k to gain */
+                       tc->size = size;
+                       return ptr;
+               }
+       } else if (tc->size == size) {
+               /*
+                * do not change the pointer if it is exactly
+                * the same size.
+                */
+               return ptr;
+       }
+#endif
+
+       /* by resetting magic we catch users of the old memory */
+       tc->flags |= TALLOC_FLAG_FREE;
+
+#if ALWAYS_REALLOC
+       if (pool_tc) {
+               new_ptr = talloc_alloc_pool(tc, size + TC_HDR_SIZE);
+               *talloc_pool_objectcount(pool_tc) -= 1;
+
+               if (new_ptr == NULL) {
+                       new_ptr = malloc(TC_HDR_SIZE+size);
+                       malloced = true;
+               }
+
+               if (new_ptr) {
+                       memcpy(new_ptr, tc, MIN(tc->size,size) + TC_HDR_SIZE);
+                       TC_INVALIDATE_FULL_CHUNK(tc);
+               }
+       } else {
+               new_ptr = malloc(size + TC_HDR_SIZE);
+               if (new_ptr) {
+                       memcpy(new_ptr, tc, MIN(tc->size, size) + TC_HDR_SIZE);
+                       free(tc);
+               }
+       }
+#else
+       if (pool_tc) {
+               void *next_tc = TC_POOLMEM_NEXT_CHUNK(tc);
+               size_t old_chunk_size = TC_POOLMEM_CHUNK_SIZE(tc);
+               size_t new_chunk_size = TC_ALIGN16(TC_HDR_SIZE + size);
+               size_t space_needed;
+               size_t space_left;
+               unsigned int chunk_count = *talloc_pool_objectcount(pool_tc);
+
+               if (!(pool_tc->flags & TALLOC_FLAG_FREE)) {
+                       chunk_count -= 1;
+               }
+
+               if (chunk_count == 1) {
+                       /*
+                        * optimize for the case where 'tc' is the only
+                        * chunk in the pool.
+                        */
+                       space_needed = new_chunk_size;
+                       space_left = pool_tc->size - TALLOC_POOL_HDR_SIZE;
+
+                       if (space_left >= space_needed) {
+                               size_t old_used = TC_HDR_SIZE + tc->size;
+                               size_t new_used = TC_HDR_SIZE + size;
+                               pool_tc->pool = TC_POOL_FIRST_CHUNK(pool_tc);
+#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_UNDEFINED)
+                               /*
+                                * we need to prepare the memmove into
+                                * the unaccessable area.
+                                */
+                               {
+                                       size_t diff = PTR_DIFF(tc, pool_tc->pool);
+                                       size_t flen = MIN(diff, old_used);
+                                       char *fptr = (char *)pool_tc->pool;
+                                       VALGRIND_MAKE_MEM_UNDEFINED(fptr, flen);
+                               }
+#endif
+                               memmove(pool_tc->pool, tc, old_used);
+                               new_ptr = pool_tc->pool;
+
+                               tc = (struct talloc_chunk *)new_ptr;
+                               TC_UNDEFINE_GROW_CHUNK(tc, size);
+
+                               /*
+                                * first we do not align the pool pointer
+                                * because we want to invalidate the padding
+                                * too.
+                                */
+                               pool_tc->pool = new_used + (char *)new_ptr;
+                               TC_INVALIDATE_POOL(pool_tc);
+
+                               /* now the aligned pointer */
+                               pool_tc->pool = new_chunk_size + (char *)new_ptr;
+                               goto got_new_ptr;
+                       }
+
+                       next_tc = NULL;
+               }
+
+               if (new_chunk_size == old_chunk_size) {
+                       TC_UNDEFINE_GROW_CHUNK(tc, size);
+                       tc->flags &= ~TALLOC_FLAG_FREE;
+                       tc->size = size;
+                       return ptr;
+               }
+
+               if (next_tc == pool_tc->pool) {
+                       /*
+                        * optimize for the case where 'tc' is the last
+                        * chunk in the pool.
+                        */
+                       space_needed = new_chunk_size - old_chunk_size;
+                       space_left = TC_POOL_SPACE_LEFT(pool_tc);
+
+                       if (space_left >= space_needed) {
+                               TC_UNDEFINE_GROW_CHUNK(tc, size);
+                               tc->flags &= ~TALLOC_FLAG_FREE;
+                               tc->size = size;
+                               pool_tc->pool = TC_POOLMEM_NEXT_CHUNK(tc);
+                               return ptr;
+                       }
+               }
+
+               new_ptr = talloc_alloc_pool(tc, size + TC_HDR_SIZE);
+
+               if (new_ptr == NULL) {
+                       new_ptr = malloc(TC_HDR_SIZE+size);
+                       malloced = true;
+               }
+
+               if (new_ptr) {
+                       memcpy(new_ptr, tc, MIN(tc->size,size) + TC_HDR_SIZE);
+
+                       _talloc_free_poolmem(tc, __location__ "_talloc_realloc");
+               }
+       }
+       else {
+               new_ptr = realloc(tc, size + TC_HDR_SIZE);
+       }
+got_new_ptr:
+#endif
+       if (unlikely(!new_ptr)) {       
+               tc->flags &= ~TALLOC_FLAG_FREE; 
+               return NULL; 
+       }
+
+       tc = (struct talloc_chunk *)new_ptr;
+       tc->flags &= ~TALLOC_FLAG_FREE;
+       if (malloced) {
+               tc->flags &= ~TALLOC_FLAG_POOLMEM;
+       }
+       if (tc->parent) {
+               tc->parent->child = tc;
+       }
+       if (tc->child) {
+               tc->child->parent = tc;
+       }
+
+       if (tc->prev) {
+               tc->prev->next = tc;
+       }
+       if (tc->next) {
+               tc->next->prev = tc;
+       }
+
+       tc->size = size;
+       _talloc_set_name_const(TC_PTR_FROM_CHUNK(tc), name);
+
+       return TC_PTR_FROM_CHUNK(tc);
+}
+
+/*
+  a wrapper around talloc_steal() for situations where you are moving a pointer
+  between two structures, and want the old pointer to be set to NULL
+*/
+_PUBLIC_ void *_talloc_move(const void *new_ctx, const void *_pptr)
+{
+       const void **pptr = discard_const_p(const void *,_pptr);
+       void *ret = talloc_steal(new_ctx, discard_const_p(void, *pptr));
+       (*pptr) = NULL;
+       return ret;
+}
+
+/*
+  return the total size of a talloc pool (subtree)
+*/
+_PUBLIC_ size_t talloc_total_size(const void *ptr)
+{
+       size_t total = 0;
+       struct talloc_chunk *c, *tc;
+
+       if (ptr == NULL) {
+               ptr = null_context;
+       }
+       if (ptr == NULL) {
+               return 0;
+       }
+
+       tc = talloc_chunk_from_ptr(ptr);
+
+       if (tc->flags & TALLOC_FLAG_LOOP) {
+               return 0;
+       }
+
+       tc->flags |= TALLOC_FLAG_LOOP;
+
+       if (likely(tc->name != TALLOC_MAGIC_REFERENCE)) {
+               total = tc->size;
+       }
+       for (c=tc->child;c;c=c->next) {
+               total += talloc_total_size(TC_PTR_FROM_CHUNK(c));
+       }
+
+       tc->flags &= ~TALLOC_FLAG_LOOP;
+
+       return total;
+}
+
+/*
+  return the total number of blocks in a talloc pool (subtree)
+*/
+_PUBLIC_ size_t talloc_total_blocks(const void *ptr)
+{
+       size_t total = 0;
+       struct talloc_chunk *c, *tc;
+
+       if (ptr == NULL) {
+               ptr = null_context;
+       }
+       if (ptr == NULL) {
+               return 0;
+       }
+
+       tc = talloc_chunk_from_ptr(ptr);
+
+       if (tc->flags & TALLOC_FLAG_LOOP) {
+               return 0;
+       }
+
+       tc->flags |= TALLOC_FLAG_LOOP;
+
+       total++;
+       for (c=tc->child;c;c=c->next) {
+               total += talloc_total_blocks(TC_PTR_FROM_CHUNK(c));
+       }
+
+       tc->flags &= ~TALLOC_FLAG_LOOP;
+
+       return total;
+}
+
+/*
+  return the number of external references to a pointer
+*/
+_PUBLIC_ size_t talloc_reference_count(const void *ptr)
+{
+       struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+       struct talloc_reference_handle *h;
+       size_t ret = 0;
+
+       for (h=tc->refs;h;h=h->next) {
+               ret++;
+       }
+       return ret;
+}
+
+/*
+  report on memory usage by all children of a pointer, giving a full tree view
+*/
+_PUBLIC_ void talloc_report_depth_cb(const void *ptr, int depth, int max_depth,
+                           void (*callback)(const void *ptr,
+                                            int depth, int max_depth,
+                                            int is_ref,
+                                            void *private_data),
+                           void *private_data)
+{
+       struct talloc_chunk *c, *tc;
+
+       if (ptr == NULL) {
+               ptr = null_context;
+       }
+       if (ptr == NULL) return;
+
+       tc = talloc_chunk_from_ptr(ptr);
+
+       if (tc->flags & TALLOC_FLAG_LOOP) {
+               return;
+       }
+
+       callback(ptr, depth, max_depth, 0, private_data);
+
+       if (max_depth >= 0 && depth >= max_depth) {
+               return;
+       }
+
+       tc->flags |= TALLOC_FLAG_LOOP;
+       for (c=tc->child;c;c=c->next) {
+               if (c->name == TALLOC_MAGIC_REFERENCE) {
+                       struct talloc_reference_handle *h = (struct talloc_reference_handle *)TC_PTR_FROM_CHUNK(c);
+                       callback(h->ptr, depth + 1, max_depth, 1, private_data);
+               } else {
+                       talloc_report_depth_cb(TC_PTR_FROM_CHUNK(c), depth + 1, max_depth, callback, private_data);
+               }
+       }
+       tc->flags &= ~TALLOC_FLAG_LOOP;
+}
+
+static void talloc_report_depth_FILE_helper(const void *ptr, int depth, int max_depth, int is_ref, void *_f)
+{
+       const char *name = talloc_get_name(ptr);
+       FILE *f = (FILE *)_f;
+
+       if (is_ref) {
+               fprintf(f, "%*sreference to: %s\n", depth*4, "", name);
+               return;
+       }
+
+       if (depth == 0) {
+               fprintf(f,"%stalloc report on '%s' (total %6lu bytes in %3lu blocks)\n", 
+                       (max_depth < 0 ? "full " :""), name,
+                       (unsigned long)talloc_total_size(ptr),
+                       (unsigned long)talloc_total_blocks(ptr));
+               return;
+       }
+
+       fprintf(f, "%*s%-30s contains %6lu bytes in %3lu blocks (ref %d) %p\n", 
+               depth*4, "",
+               name,
+               (unsigned long)talloc_total_size(ptr),
+               (unsigned long)talloc_total_blocks(ptr),
+               (int)talloc_reference_count(ptr), ptr);
+
+#if 0
+       fprintf(f, "content: ");
+       if (talloc_total_size(ptr)) {
+               int tot = talloc_total_size(ptr);
+               int i;
+
+               for (i = 0; i < tot; i++) {
+                       if ((((char *)ptr)[i] > 31) && (((char *)ptr)[i] < 126)) {
+                               fprintf(f, "%c", ((char *)ptr)[i]);
+                       } else {
+                               fprintf(f, "~%02x", ((char *)ptr)[i]);
+                       }
+               }
+       }
+       fprintf(f, "\n");
+#endif
+}
+
+/*
+  report on memory usage by all children of a pointer, giving a full tree view
+*/
+_PUBLIC_ void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f)
+{
+       if (f) {
+               talloc_report_depth_cb(ptr, depth, max_depth, talloc_report_depth_FILE_helper, f);
+               fflush(f);
+       }
+}
+
+/*
+  report on memory usage by all children of a pointer, giving a full tree view
+*/
+_PUBLIC_ void talloc_report_full(const void *ptr, FILE *f)
+{
+       talloc_report_depth_file(ptr, 0, -1, f);
+}
+
+/*
+  report on memory usage by all children of a pointer
+*/
+_PUBLIC_ void talloc_report(const void *ptr, FILE *f)
+{
+       talloc_report_depth_file(ptr, 0, 1, f);
+}
+
+/*
+  report on any memory hanging off the null context
+*/
+static void talloc_report_null(void)
+{
+       if (talloc_total_size(null_context) != 0) {
+               talloc_report(null_context, stderr);
+       }
+}
+
+/*
+  report on any memory hanging off the null context
+*/
+static void talloc_report_null_full(void)
+{
+       if (talloc_total_size(null_context) != 0) {
+               talloc_report_full(null_context, stderr);
+       }
+}
+
+/*
+  enable tracking of the NULL context
+*/
+_PUBLIC_ void talloc_enable_null_tracking(void)
+{
+       if (null_context == NULL) {
+               null_context = _talloc_named_const(NULL, 0, "null_context");
+               if (autofree_context != NULL) {
+                       talloc_reparent(NULL, null_context, autofree_context);
+               }
+       }
+}
+
+/*
+  enable tracking of the NULL context, not moving the autofree context
+  into the NULL context. This is needed for the talloc testsuite
+*/
+_PUBLIC_ void talloc_enable_null_tracking_no_autofree(void)
+{
+       if (null_context == NULL) {
+               null_context = _talloc_named_const(NULL, 0, "null_context");
+       }
+}
+
+/*
+  disable tracking of the NULL context
+*/
+_PUBLIC_ void talloc_disable_null_tracking(void)
+{
+       if (null_context != NULL) {
+               /* we have to move any children onto the real NULL
+                  context */
+               struct talloc_chunk *tc, *tc2;
+               tc = talloc_chunk_from_ptr(null_context);
+               for (tc2 = tc->child; tc2; tc2=tc2->next) {
+                       if (tc2->parent == tc) tc2->parent = NULL;
+                       if (tc2->prev == tc) tc2->prev = NULL;
+               }
+               for (tc2 = tc->next; tc2; tc2=tc2->next) {
+                       if (tc2->parent == tc) tc2->parent = NULL;
+                       if (tc2->prev == tc) tc2->prev = NULL;
+               }
+               tc->child = NULL;
+               tc->next = NULL;
+       }
+       talloc_free(null_context);
+       null_context = NULL;
+}
+
+/*
+  enable leak reporting on exit
+*/
+_PUBLIC_ void talloc_enable_leak_report(void)
+{
+       talloc_enable_null_tracking();
+       atexit(talloc_report_null);
+}
+
+/*
+  enable full leak reporting on exit
+*/
+_PUBLIC_ void talloc_enable_leak_report_full(void)
+{
+       talloc_enable_null_tracking();
+       atexit(talloc_report_null_full);
+}
+
+/* 
+   talloc and zero memory. 
+*/
+_PUBLIC_ void *_talloc_zero(const void *ctx, size_t size, const char *name)
+{
+       void *p = _talloc_named_const(ctx, size, name);
+
+       if (p) {
+               memset(p, '\0', size);
+       }
+
+       return p;
+}
+
+/*
+  memdup with a talloc. 
+*/
+_PUBLIC_ void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name)
+{
+       void *newp = _talloc_named_const(t, size, name);
+
+       if (likely(newp)) {
+               memcpy(newp, p, size);
+       }
+
+       return newp;
+}
+
+static inline char *__talloc_strlendup(const void *t, const char *p, size_t len)
+{
+       char *ret;
+
+       ret = (char *)__talloc(t, len + 1);
+       if (unlikely(!ret)) return NULL;
+
+       memcpy(ret, p, len);
+       ret[len] = 0;
+
+       _talloc_set_name_const(ret, ret);
+       return ret;
+}
+
+/*
+  strdup with a talloc
+*/
+_PUBLIC_ char *talloc_strdup(const void *t, const char *p)
+{
+       if (unlikely(!p)) return NULL;
+       return __talloc_strlendup(t, p, strlen(p));
+}
+
+/*
+  strndup with a talloc
+*/
+_PUBLIC_ char *talloc_strndup(const void *t, const char *p, size_t n)
+{
+       if (unlikely(!p)) return NULL;
+       return __talloc_strlendup(t, p, strnlen(p, n));
+}
+
+static inline char *__talloc_strlendup_append(char *s, size_t slen,
+                                             const char *a, size_t alen)
+{
+       char *ret;
+
+       ret = talloc_realloc(NULL, s, char, slen + alen + 1);
+       if (unlikely(!ret)) return NULL;
+
+       /* append the string and the trailing \0 */
+       memcpy(&ret[slen], a, alen);
+       ret[slen+alen] = 0;
+
+       _talloc_set_name_const(ret, ret);
+       return ret;
+}
+
+/*
+ * Appends at the end of the string.
+ */
+_PUBLIC_ char *talloc_strdup_append(char *s, const char *a)
+{
+       if (unlikely(!s)) {
+               return talloc_strdup(NULL, a);
+       }
+
+       if (unlikely(!a)) {
+               return s;
+       }
+
+       return __talloc_strlendup_append(s, strlen(s), a, strlen(a));
+}
+
+/*
+ * Appends at the end of the talloc'ed buffer,
+ * not the end of the string.
+ */
+_PUBLIC_ char *talloc_strdup_append_buffer(char *s, const char *a)
+{
+       size_t slen;
+
+       if (unlikely(!s)) {
+               return talloc_strdup(NULL, a);
+       }
+
+       if (unlikely(!a)) {
+               return s;
+       }
+
+       slen = talloc_get_size(s);
+       if (likely(slen > 0)) {
+               slen--;
+       }
+
+       return __talloc_strlendup_append(s, slen, a, strlen(a));
+}
+
+/*
+ * Appends at the end of the string.
+ */
+_PUBLIC_ char *talloc_strndup_append(char *s, const char *a, size_t n)
+{
+       if (unlikely(!s)) {
+               return talloc_strdup(NULL, a);
+       }
+
+       if (unlikely(!a)) {
+               return s;
+       }
+
+       return __talloc_strlendup_append(s, strlen(s), a, strnlen(a, n));
+}
+
+/*
+ * Appends at the end of the talloc'ed buffer,
+ * not the end of the string.
+ */
+_PUBLIC_ char *talloc_strndup_append_buffer(char *s, const char *a, size_t n)
+{
+       size_t slen;
+
+       if (unlikely(!s)) {
+               return talloc_strdup(NULL, a);
+       }
+
+       if (unlikely(!a)) {
+               return s;
+       }
+
+       slen = talloc_get_size(s);
+       if (likely(slen > 0)) {
+               slen--;
+       }
+
+       return __talloc_strlendup_append(s, slen, a, strnlen(a, n));
+}
+
+_PUBLIC_ char *talloc_vasprintf(const void *t, const char *fmt, va_list ap)
+{
+       int len;
+       char *ret;
+       va_list ap2;
+       char c;
+
+       /* this call looks strange, but it makes it work on older solaris boxes */
+       va_copy(ap2, ap);
+       len = vsnprintf(&c, 1, fmt, ap2);
+       va_end(ap2);
+       if (unlikely(len < 0)) {
+               return NULL;
+       }
+
+       ret = (char *)__talloc(t, len+1);
+       if (unlikely(!ret)) return NULL;
+
+       va_copy(ap2, ap);
+       vsnprintf(ret, len+1, fmt, ap2);
+       va_end(ap2);
+
+       _talloc_set_name_const(ret, ret);
+       return ret;
+}
+
+
+/*
+  Perform string formatting, and return a pointer to newly allocated
+  memory holding the result, inside a memory pool.
+ */
+_PUBLIC_ char *talloc_asprintf(const void *t, const char *fmt, ...)
+{
+       va_list ap;
+       char *ret;
+
+       va_start(ap, fmt);
+       ret = talloc_vasprintf(t, fmt, ap);
+       va_end(ap);
+       return ret;
+}
+
+static inline char *__talloc_vaslenprintf_append(char *s, size_t slen,
+                                                const char *fmt, va_list ap)
+                                                PRINTF_ATTRIBUTE(3,0);
+
+static inline char *__talloc_vaslenprintf_append(char *s, size_t slen,
+                                                const char *fmt, va_list ap)
+{
+       ssize_t alen;
+       va_list ap2;
+       char c;
+
+       va_copy(ap2, ap);
+       alen = vsnprintf(&c, 1, fmt, ap2);
+       va_end(ap2);
+
+       if (alen <= 0) {
+               /* Either the vsnprintf failed or the format resulted in
+                * no characters being formatted. In the former case, we
+                * ought to return NULL, in the latter we ought to return
+                * the original string. Most current callers of this
+                * function expect it to never return NULL.
+                */
+               return s;
+       }
+
+       s = talloc_realloc(NULL, s, char, slen + alen + 1);
+       if (!s) return NULL;
+
+       va_copy(ap2, ap);
+       vsnprintf(s + slen, alen + 1, fmt, ap2);
+       va_end(ap2);
+
+       _talloc_set_name_const(s, s);
+       return s;
+}
+
+/**
+ * Realloc @p s to append the formatted result of @p fmt and @p ap,
+ * and return @p s, which may have moved.  Good for gradually
+ * accumulating output into a string buffer. Appends at the end
+ * of the string.
+ **/
+_PUBLIC_ char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap)
+{
+       if (unlikely(!s)) {
+               return talloc_vasprintf(NULL, fmt, ap);
+       }
+
+       return __talloc_vaslenprintf_append(s, strlen(s), fmt, ap);
+}
+
+/**
+ * Realloc @p s to append the formatted result of @p fmt and @p ap,
+ * and return @p s, which may have moved. Always appends at the
+ * end of the talloc'ed buffer, not the end of the string.
+ **/
+_PUBLIC_ char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap)
+{
+       size_t slen;
+
+       if (unlikely(!s)) {
+               return talloc_vasprintf(NULL, fmt, ap);
+       }
+
+       slen = talloc_get_size(s);
+       if (likely(slen > 0)) {
+               slen--;
+       }
+
+       return __talloc_vaslenprintf_append(s, slen, fmt, ap);
+}
+
+/*
+  Realloc @p s to append the formatted result of @p fmt and return @p
+  s, which may have moved.  Good for gradually accumulating output
+  into a string buffer.
+ */
+_PUBLIC_ char *talloc_asprintf_append(char *s, const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       s = talloc_vasprintf_append(s, fmt, ap);
+       va_end(ap);
+       return s;
+}
+
+/*
+  Realloc @p s to append the formatted result of @p fmt and return @p
+  s, which may have moved.  Good for gradually accumulating output
+  into a buffer.
+ */
+_PUBLIC_ char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       s = talloc_vasprintf_append_buffer(s, fmt, ap);
+       va_end(ap);
+       return s;
+}
+
+/*
+  alloc an array, checking for integer overflow in the array size
+*/
+_PUBLIC_ void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name)
+{
+       if (count >= MAX_TALLOC_SIZE/el_size) {
+               return NULL;
+       }
+       return _talloc_named_const(ctx, el_size * count, name);
+}
+
+/*
+  alloc an zero array, checking for integer overflow in the array size
+*/
+_PUBLIC_ void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name)
+{
+       if (count >= MAX_TALLOC_SIZE/el_size) {
+               return NULL;
+       }
+       return _talloc_zero(ctx, el_size * count, name);
+}
+
+/*
+  realloc an array, checking for integer overflow in the array size
+*/
+_PUBLIC_ void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name)
+{
+       if (count >= MAX_TALLOC_SIZE/el_size) {
+               return NULL;
+       }
+       return _talloc_realloc(ctx, ptr, el_size * count, name);
+}
+
+/*
+  a function version of talloc_realloc(), so it can be passed as a function pointer
+  to libraries that want a realloc function (a realloc function encapsulates
+  all the basic capabilities of an allocation library, which is why this is useful)
+*/
+_PUBLIC_ void *talloc_realloc_fn(const void *context, void *ptr, size_t size)
+{
+       return _talloc_realloc(context, ptr, size, NULL);
+}
+
+
+static int talloc_autofree_destructor(void *ptr)
+{
+       autofree_context = NULL;
+       return 0;
+}
+
+static void talloc_autofree(void)
+{
+       talloc_free(autofree_context);
+}
+
+/*
+  return a context which will be auto-freed on exit
+  this is useful for reducing the noise in leak reports
+*/
+_PUBLIC_ void *talloc_autofree_context(void)
+{
+       if (autofree_context == NULL) {
+               autofree_context = _talloc_named_const(NULL, 0, "autofree_context");
+               talloc_set_destructor(autofree_context, talloc_autofree_destructor);
+               atexit(talloc_autofree);
+       }
+       return autofree_context;
+}
+
+_PUBLIC_ size_t talloc_get_size(const void *context)
+{
+       struct talloc_chunk *tc;
+
+       if (context == NULL) {
+               context = null_context;
+       }
+       if (context == NULL) {
+               return 0;
+       }
+
+       tc = talloc_chunk_from_ptr(context);
+
+       return tc->size;
+}
+
+/*
+  find a parent of this context that has the given name, if any
+*/
+_PUBLIC_ void *talloc_find_parent_byname(const void *context, const char *name)
+{
+       struct talloc_chunk *tc;
+
+       if (context == NULL) {
+               return NULL;
+       }
+
+       tc = talloc_chunk_from_ptr(context);
+       while (tc) {
+               if (tc->name && strcmp(tc->name, name) == 0) {
+                       return TC_PTR_FROM_CHUNK(tc);
+               }
+               while (tc && tc->prev) tc = tc->prev;
+               if (tc) {
+                       tc = tc->parent;
+               }
+       }
+       return NULL;
+}
+
+/*
+  show the parentage of a context
+*/
+_PUBLIC_ void talloc_show_parents(const void *context, FILE *file)
+{
+       struct talloc_chunk *tc;
+
+       if (context == NULL) {
+               fprintf(file, "talloc no parents for NULL\n");
+               return;
+       }
+
+       tc = talloc_chunk_from_ptr(context);
+       fprintf(file, "talloc parents of '%s'\n", talloc_get_name(context));
+       while (tc) {
+               fprintf(file, "\t'%s'\n", talloc_get_name(TC_PTR_FROM_CHUNK(tc)));
+               while (tc && tc->prev) tc = tc->prev;
+               if (tc) {
+                       tc = tc->parent;
+               }
+       }
+       fflush(file);
+}
+
+/*
+  return 1 if ptr is a parent of context
+*/
+static int _talloc_is_parent(const void *context, const void *ptr, int depth)
+{
+       struct talloc_chunk *tc;
+
+       if (context == NULL) {
+               return 0;
+       }
+
+       tc = talloc_chunk_from_ptr(context);
+       while (tc && depth > 0) {
+               if (TC_PTR_FROM_CHUNK(tc) == ptr) return 1;
+               while (tc && tc->prev) tc = tc->prev;
+               if (tc) {
+                       tc = tc->parent;
+                       depth--;
+               }
+       }
+       return 0;
+}
+
+/*
+  return 1 if ptr is a parent of context
+*/
+_PUBLIC_ int talloc_is_parent(const void *context, const void *ptr)
+{
+       return _talloc_is_parent(context, ptr, TALLOC_MAX_DEPTH);
+}
index b56f9f354d754e6fde0c155562bd8536fa7ca1f4..abf6f3266b006ec5e5bf64228b7309399723463a 100644 (file)
@@ -17,6 +17,6 @@ libunicode_la_SOURCES = \
 
 libunicode_la_LIBADD = $(LIBUNICODE_DEPS)
 
-noinst_HEADERS = utf16_casetable.h precompose.h byteorder.h
+noinst_HEADERS = utf16_casetable.h precompose.h
 
 LIBS=@ICONV_LIBS@
diff --git a/libatalk/unicode/byteorder.h b/libatalk/unicode/byteorder.h
deleted file mode 100644 (file)
index cc9a7f0..0000000
+++ /dev/null
@@ -1,189 +0,0 @@
-/* 
-   Unix SMB/CIFS implementation.
-   SMB Byte handling
-   Copyright (C) Andrew Tridgell 1992-1998
-   
-   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., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#ifndef _BYTEORDER_H
-#define _BYTEORDER_H
-#include <arpa/inet.h>
-
-/*
-   This file implements macros for machine independent short and 
-   int manipulation
-
-Here is a description of this file that I emailed to the samba list once:
-
-> I am confused about the way that byteorder.h works in Samba. I have
-> looked at it, and I would have thought that you might make a distinction
-> between LE and BE machines, but you only seem to distinguish between 386
-> and all other architectures.
-> 
-> Can you give me a clue?
-
-sure.
-
-The distinction between 386 and other architectures is only there as
-an optimisation. You can take it out completely and it will make no
-difference. The routines (macros) in byteorder.h are totally byteorder
-independent. The 386 optimsation just takes advantage of the fact that
-the x86 processors don't care about alignment, so we don't have to
-align ints on int boundaries etc. If there are other processors out
-there that aren't alignment sensitive then you could also define
-CAREFUL_ALIGNMENT=0 on those processors as well.
-
-Ok, now to the macros themselves. I'll take a simple example, say we
-want to extract a 2 byte integer from a SMB packet and put it into a
-type called uint16 that is in the local machines byte order, and you
-want to do it with only the assumption that uint16 is _at_least_ 16
-bits long (this last condition is very important for architectures
-that don't have any int types that are 2 bytes long)
-
-You do this:
-
-#define CVAL(buf,pos) (((unsigned char *)(buf))[pos])
-#define PVAL(buf,pos) ((unsigned)CVAL(buf,pos))
-#define SVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+1)<<8)
-
-then to extract a uint16 value at offset 25 in a buffer you do this:
-
-char *buffer = foo_bar();
-uint16 xx = SVAL(buffer,25);
-
-We are using the byteoder independence of the ANSI C bitshifts to do
-the work. A good optimising compiler should turn this into efficient
-code, especially if it happens to have the right byteorder :-)
-
-I know these macros can be made a bit tidier by removing some of the
-casts, but you need to look at byteorder.h as a whole to see the
-reasoning behind them. byteorder.h defines the following macros:
-
-SVAL(buf,pos) - extract a 2 byte SMB value
-IVAL(buf,pos) - extract a 4 byte SMB value
-SVALS(buf,pos) signed version of SVAL()
-IVALS(buf,pos) signed version of IVAL()
-
-SSVAL(buf,pos,val) - put a 2 byte SMB value into a buffer
-SIVAL(buf,pos,val) - put a 4 byte SMB value into a buffer
-SSVALS(buf,pos,val) - signed version of SSVAL()
-SIVALS(buf,pos,val) - signed version of SIVAL()
-
-RSVAL(buf,pos) - like SVAL() but for NMB byte ordering
-RSVALS(buf,pos) - like SVALS() but for NMB byte ordering
-RIVAL(buf,pos) - like IVAL() but for NMB byte ordering
-RIVALS(buf,pos) - like IVALS() but for NMB byte ordering
-RSSVAL(buf,pos,val) - like SSVAL() but for NMB ordering
-RSIVAL(buf,pos,val) - like SIVAL() but for NMB ordering
-RSIVALS(buf,pos,val) - like SIVALS() but for NMB ordering
-
-it also defines lots of intermediate macros, just ignore those :-)
-
-*/
-
-#undef CAREFUL_ALIGNMENT
-
-/* we know that the 386 can handle misalignment and has the "right" 
-   byteorder */
-#ifdef __i386__
-#define CAREFUL_ALIGNMENT 0
-#endif
-
-#ifndef CAREFUL_ALIGNMENT
-#define CAREFUL_ALIGNMENT 1
-#endif
-
-#define CVAL(buf,pos) ((unsigned)(((const unsigned char *)(buf))[pos]))
-#define CVAL_NC(buf,pos) (((unsigned char *)(buf))[pos]) /* Non-const version of CVAL */
-#define PVAL(buf,pos) (CVAL(buf,pos))
-#define SCVAL(buf,pos,val) (CVAL_NC(buf,pos) = (val))
-
-
-#if CAREFUL_ALIGNMENT
-
-#if BYTE_ORDER==BIG_ENDIAN
-
-#define SVAL(buf,pos) (PVAL(buf,(pos)+1)|PVAL(buf,pos)<<8)
-#define IVAL(buf,pos) (SVAL(buf,pos)|SVAL(buf,(pos)+2)<<16)
-#define SSVALX(buf,pos,val) (CVAL_NC(buf,pos+1)=(unsigned char)((val)&0xFF),CVAL_NC(buf,pos)=(unsigned char)((val)>>8))
-#define SIVALX(buf,pos,val) (SSVALX(buf,pos,val&0xFFFF),SSVALX(buf,pos+2,val>>16))
-#define SVALS(buf,pos) ((int16)SVAL(buf,pos))
-#define IVALS(buf,pos) ((int32_t)IVAL(buf,pos))
-#define SSVAL(buf,pos,val) SSVALX((buf),(pos),((uint16_t)(val)))
-#define SIVAL(buf,pos,val) SIVALX((buf),(pos),((uint32_t)(val)))
-#define SSVALS(buf,pos,val) SSVALX((buf),(pos),((int16)(val)))
-#define SIVALS(buf,pos,val) SIVALX((buf),(pos),((int32_t)(val)))
-
-#else
-
-#define SVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+1)<<8)
-#define IVAL(buf,pos) (SVAL(buf,pos)|SVAL(buf,(pos)+2)<<16)
-#define SSVALX(buf,pos,val) (CVAL_NC(buf,pos)=(unsigned char)((val)&0xFF),CVAL_NC(buf,pos+1)=(unsigned char)((val)>>8))
-#define SIVALX(buf,pos,val) (SSVALX(buf,pos,val&0xFFFF),SSVALX(buf,pos+2,val>>16))
-#define SVALS(buf,pos) ((int16)SVAL(buf,pos))
-#define IVALS(buf,pos) ((int32_t)IVAL(buf,pos))
-#define SSVAL(buf,pos,val) SSVALX((buf),(pos),((uint16_t)(val)))
-#define SIVAL(buf,pos,val) SIVALX((buf),(pos),((uint32_t)(val)))
-#define SSVALS(buf,pos,val) SSVALX((buf),(pos),((int16)(val)))
-#define SIVALS(buf,pos,val) SIVALX((buf),(pos),((int32_t)(val)))
-
-#endif
-
-#else /* CAREFUL_ALIGNMENT */
-
-/* this handles things for architectures like the 386 that can handle
-   alignment errors */
-/*
-   WARNING: This section is dependent on the length of int16 and int32
-   being correct 
-*/
-
-/* get single value from an SMB buffer */
-#define SVAL(buf,pos) (*(const uint16_t *)((const char *)(buf) + (pos)))
-#define SVAL_NC(buf,pos) (*(uint16_t *)((char *)(buf) + (pos))) /* Non const version of above. */
-#define IVAL(buf,pos) (*(const uint32_t *)((const char *)(buf) + (pos)))
-#define IVAL_NC(buf,pos) (*(uint32_t *)((char *)(buf) + (pos))) /* Non const version of above. */
-#define SVALS(buf,pos) (*(const int16_t *)((const char *)(buf) + (pos)))
-#define SVALS_NC(buf,pos) (*(int16 *)((char *)(buf) + (pos))) /* Non const version of above. */
-#define IVALS(buf,pos) (*(const int32_t *)((const char *)(buf) + (pos)))
-#define IVALS_NC(buf,pos) (*(int32_t *)((char *)(buf) + (pos))) /* Non const version of above. */
-
-/* store single value in an SMB buffer */
-#define SSVAL(buf,pos,val) SVAL_NC(buf,pos)=((uint16_t)(val))
-#define SIVAL(buf,pos,val) IVAL_NC(buf,pos)=((uint32_t)(val))
-#define SSVALS(buf,pos,val) SVALS_NC(buf,pos)=((int16)(val))
-#define SIVALS(buf,pos,val) IVALS_NC(buf,pos)=((int32_t)(val))
-
-#endif /* CAREFUL_ALIGNMENT */
-
-/* now the reverse routines - these are used in nmb packets (mostly) */
-#define SREV(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF))
-#define IREV(x) ((SREV(x)<<16) | (SREV((x)>>16)))
-
-#define RSVAL(buf,pos) SREV(SVAL(buf,pos))
-#define RSVALS(buf,pos) SREV(SVALS(buf,pos))
-#define RIVAL(buf,pos) IREV(IVAL(buf,pos))
-#define RIVALS(buf,pos) IREV(IVALS(buf,pos))
-#define RSSVAL(buf,pos,val) SSVAL(buf,pos,SREV(val))
-#define RSSVALS(buf,pos,val) SSVALS(buf,pos,SREV(val))
-#define RSIVAL(buf,pos,val) SIVAL(buf,pos,IREV(val))
-#define RSIVALS(buf,pos,val) SIVALS(buf,pos,IREV(val))
-
-/* Alignment macros. */
-#define ALIGN4(p,base) ((p) + ((4 - (PTR_DIFF((p), (base)) & 3)) & 3))
-#define ALIGN2(p,base) ((p) + ((2 - (PTR_DIFF((p), (base)) & 1)) & 1))
-
-#endif /* _BYTEORDER_H */
index 4e772e165a16e4f60f0f7dd331d176bf542645da..7242b576776a1198a4776f31bb2b14f2d9342330 100644 (file)
@@ -42,8 +42,7 @@
 #include <atalk/unicode.h>
 #include <atalk/util.h>
 #include <atalk/compat.h>
-
-#include "byteorder.h"
+#include <atalk/byteorder.h>
 
 
 /**
index 5dab15a6284a6ce323a245594a9498e90e2b2ebf..3c4a48c706b309d4c9bc312b18af0f0b5c486cec 100644 (file)
@@ -19,7 +19,7 @@
 
 #include <atalk/unicode.h>
 #include <iconv.h>
-#include "../byteorder.h"
+#include <atalk/byteorder.h>
 
 #define CJK_PUSH_BUFFER 4
 #define CJK_PULL_BUFFER 8
index 7b36997d804a035a4f6b86f1d0686d95df9b6759..0347c3e755cc2752bd83b16880f9cb020d8d9f5b 100644 (file)
 
 #include <atalk/unicode.h>
 #include <atalk/logger.h>
+#include <atalk/byteorder.h>
 
 #include "generic_mb.h"
-#include "../byteorder.h"
+
 
 
 /* ------------------------ */
index 91a022bd330983a0dcb2df6eb5decefba8cbf7c6..34d96902b16e6ac3c0ace2e2c85f7a2d7f231b75 100644 (file)
@@ -34,8 +34,8 @@
 
 #include <atalk/unicode.h>
 #include <atalk/logger.h>
+#include <atalk/byteorder.h>
 
-#include "../byteorder.h"
 #include "mac_hebrew.h"
 
 static size_t   mac_hebrew_pull(void *,char **, size_t *, char **, size_t *);
index 7c5eb67e3ce0ddd653696ef014d657d324e07c15..c44df315de2ed4797e53d44554543849ec48e69b 100644 (file)
@@ -43,7 +43,7 @@
 
 #include <atalk/unicode.h>
 #include <atalk/logger.h>
-#include "byteorder.h"
+#include <atalk/byteorder.h>
 
 
 /**
index 896a2611e6b4857485b7a5e85b7a2db146ecdc3c..4a3003988c2351017f9df0adfff553b17d78c4f6 100644 (file)
@@ -34,7 +34,7 @@
 #include <atalk/unicode.h>
 #include <atalk/logger.h>
 #include <atalk/unicode.h>
-#include "byteorder.h"
+#include <atalk/byteorder.h>
 
 /* Given a trailing UTF-8 byte, get the contribution from it to
  * the Unicode scalar value for a particular bit shift amount
index 0eba9a3c25faaa4ce255db5fdff95aaeaf1e315c..5108a4c8e86f0ef8d73d97f7300f025eeae84b92 100644 (file)
@@ -22,8 +22,9 @@
 #include <arpa/inet.h>
 
 #include <atalk/unicode.h>
+#include <atalk/byteorder.h>
+
 #include "precompose.h"
-#include "byteorder.h"
 
 /*******************************************************************
  Convert a string to lower case.
index 0cc56d49514ea4ae5ba454a009c4bdf3e5620948..7fb079004cc586f9008e1248fb94b65b114b5c66 100644 (file)
@@ -35,7 +35,7 @@
 #include <sys/types.h>
 #include <sys/param.h>
 #include <sys/stat.h>
-
+#include <arpa/inet.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
@@ -80,8 +80,7 @@ bstring rel_path_in_vol(const char *path, const char *volpath)
         return NULL;
 
     EC_NEG1_LOG(cwd = open(".", O_RDONLY));
-
-    EC_ZERO_LOGSTR(lstat(path, &st), "lstat(%s): %s", path, strerror(errno));
+    EC_ZERO( lstat(path, &st) );
 
     if (path[0] == '/') {
         EC_NULL(fpath = bfromcstr(path));
@@ -134,3 +133,69 @@ EC_CLEANUP:
         return NULL;
     return fpath;
 }
+
+/*!
+ * Resolves CNID of a given path
+ *
+ * path might be:
+ * (a) relative:
+ *     "dir/subdir" with cwd: "/afp_volume/topdir"
+ * (b) absolute:
+ *     "/afp_volume/dir/subdir"
+ *
+ * path MUST be pointing inside vol, this is usually the case as vol has been build from
+ * path using loadvolinfo and friends.
+ *
+ * @param cdb     (r) CNID db handle
+ * @param volpath (r) UNIX path of volume
+ * @param path    (r) path, see above
+ * @param did     (w) parent CNID of returned CNID
+ *
+ * @returns CNID of path
+ */
+cnid_t cnid_for_path(struct _cnid_db *cdb,
+                     const char *volpath,
+                     const char *path,
+                     cnid_t *did)
+{
+    EC_INIT;
+
+    cnid_t cnid;
+    bstring rpath = NULL;
+    bstring statpath = NULL;
+    struct bstrList *l = NULL;
+    struct stat st;
+
+    cnid = htonl(2);
+
+    EC_NULL(rpath = rel_path_in_vol(path, volpath));
+    EC_NULL(statpath = bfromcstr(volpath));
+    EC_ZERO(bcatcstr(statpath, "/"));
+
+    l = bsplit(rpath, '/');
+    for (int i = 0; i < l->qty ; i++) {
+        *did = cnid;
+
+        EC_ZERO( bconcat(statpath, l->entry[i]) );
+        EC_ZERO( lstat(cfrombstr(statpath), &st) );
+
+        if ((cnid = cnid_add(cdb,
+                             &st,
+                             *did,
+                             cfrombstr(l->entry[i]),
+                             blength(l->entry[i]),
+                             0)) == CNID_INVALID) {
+            EC_FAIL;
+        }
+        EC_ZERO(bcatcstr(statpath, "/"));
+    }
+
+EC_CLEANUP:
+    bdestroy(rpath);
+    bstrListDestroy(l);
+    bdestroy(statpath);
+    if (ret != 0)
+        return CNID_INVALID;
+
+    return cnid;
+}
index 944234266eb4dc716144d5b1dc3f74021c341e49..4bf0e33802551ee3fac2a17cfadae4117360ae2d 100644 (file)
@@ -62,6 +62,7 @@ Netatalk 2001 (c)
   "UAMS",                            \
   "FCE",                             \
   "ad",                              \
+  "Spotlight",                       \
   "end_of_list_marker"}
 
 /* =========================================================================
@@ -86,8 +87,9 @@ UAM_MODULE_EXPORT logtype_conf_t type_configs[logtype_end_of_list_marker] = {
     DEFAULT_LOG_CONFIG, /* logtype_afpd */
     DEFAULT_LOG_CONFIG, /* logtype_dsi */
     DEFAULT_LOG_CONFIG, /* logtype_uams */
-    DEFAULT_LOG_CONFIG,  /* logtype_fce */
-    DEFAULT_LOG_CONFIG  /* logtype_ad */
+    DEFAULT_LOG_CONFIG, /* logtype_fce */
+    DEFAULT_LOG_CONFIG, /* logtype_ad */
+    DEFAULT_LOG_CONFIG  /* logtype_sl */
 };
 
 static void syslog_setup(int loglevel, enum logtypes logtype, int display_options, int facility);
index 9ed1c776d845097939cfd3fd689d41d534a00679..5ad325693c1ccd46668cee3c560318fb21696920 100644 (file)
@@ -777,6 +777,10 @@ static struct vol *creatvol(AFPObj *obj,
         volume->v_flags |= AFPVOL_NOV2TOEACONV;
     if (getoption_bool(obj->iniconfig, section, "follow symlinks", preset, 0))
         volume->v_flags |= AFPVOL_FOLLOWSYM;
+    if (getoption_bool(obj->iniconfig, section, "spotlight", preset, obj->options.flags & OPTION_SPOTLIGHT_VOL)) {
+        volume->v_flags |= AFPVOL_SPOTLIGHT;
+        obj->options.flags |= OPTION_SPOTLIGHT;
+    }
     if (getoption_bool(obj->iniconfig, section, "delete veto files", preset, 0))
         volume->v_flags |= AFPVOL_DELVETO;
 
@@ -940,16 +944,16 @@ static struct vol *creatvol(AFPObj *obj,
     initvol_vfs(volume);
 
     /* get/store uuid from file in afpd master*/
-    if (!(pwd) && (volume->v_flags & AFPVOL_TM)) {
-        char *uuid = get_vol_uuid(obj, volume->v_localname);
-        if (!uuid) {
-            LOG(log_error, logtype_afpd, "Volume '%s': couldn't get UUID",
-                volume->v_localname);
-        } else {
-            volume->v_uuid = uuid;
-            LOG(log_debug, logtype_afpd, "Volume '%s': UUID '%s'",
-                volume->v_localname, volume->v_uuid);
-        }
+    become_root();
+    char *uuid = get_vol_uuid(obj, volume->v_localname);
+    unbecome_root();
+    if (!uuid) {
+        LOG(log_error, logtype_afpd, "Volume '%s': couldn't get UUID",
+            volume->v_localname);
+    } else {
+        volume->v_uuid = uuid;
+        LOG(log_debug, logtype_afpd, "Volume '%s': UUID '%s'",
+            volume->v_localname, volume->v_uuid);
     }
 
     /* no errors shall happen beyond this point because the cleanup would mess the volume chain up */
@@ -1734,6 +1738,9 @@ int afp_config_parse(AFPObj *AFPObj, char *processname)
     options->configfile  = AFPObj->cmdlineconfigfile ? strdup(AFPObj->cmdlineconfigfile) : strdup(_PATH_CONFDIR "afp.conf");
     options->sigconffile = strdup(_PATH_STATEDIR "afp_signature.conf");
     options->uuidconf    = strdup(_PATH_STATEDIR "afp_voluuid.conf");
+#ifdef HAVE_TRACKER_SPARQL
+    options->slmod_path  = strdup(_PATH_AFPDUAMPATH "slmod_sparql.so");
+#endif
     options->flags       = OPTION_UUID | AFPObj->cmdlineflags;
     
     if ((config = atalk_iniparser_load(AFPObj->options.configfile)) == NULL)
@@ -1763,6 +1770,8 @@ int afp_config_parse(AFPObj *AFPObj, char *processname)
         options->flags |= OPTION_DBUS_AFPSTATS;
     if (atalk_iniparser_getboolean(config, INISEC_GLOBAL, "afp read locks", 0))
         options->flags |= OPTION_AFP_READ_LOCK;
+    if (atalk_iniparser_getboolean(config, INISEC_GLOBAL, "spotlight", 0))
+        options->flags |= OPTION_SPOTLIGHT_VOL;
     if (atalk_iniparser_getboolean(config, INISEC_GLOBAL, "veto message", 0))
         options->flags |= OPTION_VETOMSG;
     if (!atalk_iniparser_getboolean(config, INISEC_GLOBAL, "save password", 1))
@@ -2011,6 +2020,8 @@ void afp_config_free(AFPObj *obj)
         CONFIG_ARG_FREE(obj->options.fqdn);
     if (obj->options.ignored_attr)
         CONFIG_ARG_FREE(obj->options.ignored_attr);
+    if (obj->options.slmod_path)
+        CONFIG_ARG_FREE(obj->options.slmod_path);
 
     if (obj->options.unixcodepage)
         CONFIG_ARG_FREE(obj->options.unixcodepage);
index be0fb3a6a2af22a294b62ece9308e883f3c676af..86c4c711c16cb84f4784f395d0789dfff043d3d3 100644 (file)
@@ -77,16 +77,19 @@ AC_DEFUN([AC_NETATALK_DTRACE], [
 
 dnl Check for dbus-glib, for AFP stats
 AC_DEFUN([AC_NETATALK_DBUS_GLIB], [
-    atalk_cv_with_dbus=no
+  atalk_cv_with_dbus=no
+
+  AC_ARG_WITH(afpstats,
+    AS_HELP_STRING(
+      [--with-afpstats],
+      [Enable AFP statistics via dbus (default: enabled if dbus found)]
+    ),,[withval=auto]
+  )
+
+  if test x"$withval" != x"no" ; then
     PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.1, have_dbus=yes, have_dbus=no)
     PKG_CHECK_MODULES(DBUS_GLIB, dbus-glib-1, have_dbus_glib=yes, have_dbus_glib=no)
     PKG_CHECK_MODULES(DBUS_GTHREAD, gthread-2.0, have_dbus_gthread=yes, have_dbus_gthread=no)
-    AC_SUBST(DBUS_CFLAGS)
-    AC_SUBST(DBUS_LIBS)
-    AC_SUBST(DBUS_GLIB_CFLAGS)
-    AC_SUBST(DBUS_GLIB_LIBS)
-    AC_SUBST(DBUS_GTHREAD_CFLAGS)
-    AC_SUBST(DBUS_GTHREAD_LIBS)
     if test x$have_dbus_glib = xyes -a x$have_dbus = xyes -a x$have_dbus_gthread = xyes ; then
         saved_CFLAGS=$CFLAGS
         saved_LIBS=$LIBS
@@ -96,20 +99,32 @@ AC_DEFUN([AC_NETATALK_DBUS_GLIB], [
         CFLAGS="$saved_CFLAGS"
         LIBS="$saved_LIBS"
     fi
-    AM_CONDITIONAL(HAVE_DBUS_GLIB, test x$atalk_cv_with_dbus = xyes)
+  fi
 
-    AC_ARG_WITH(
-        dbus-sysconf-dir,
-        [AS_HELP_STRING([--with-dbus-sysconf-dir=PATH],[Path to dbus system bus security configuration directory (default: ${sysconfdir}/dbus-1/system.d/)])],
-        ac_cv_dbus_sysdir=$withval,
-        ac_cv_dbus_sysdir='${sysconfdir}/dbus-1/system.d'
-    )
+  if test x"$withval" = x"yes" -a x"$atalk_cv_with_dbus" = x"no"; then
+    AC_MSG_ERROR([afpstats requested but dbus-glib not found])
+  fi
 
-    if test x$atalk_cv_with_dbus = xyes ; then
-        AC_DEFINE(HAVE_DBUS_GLIB, 1, [Define if support for dbus-glib was found])
-        DBUS_SYS_DIR="$ac_cv_dbus_sysdir"
-        AC_SUBST(DBUS_SYS_DIR)
-    fi
+  AC_ARG_WITH(
+      dbus-sysconf-dir,
+      [AS_HELP_STRING([--with-dbus-sysconf-dir=PATH],[Path to dbus system bus security configuration directory (default: ${sysconfdir}/dbus-1/system.d/)])],
+      ac_cv_dbus_sysdir=$withval,
+      ac_cv_dbus_sysdir='${sysconfdir}/dbus-1/system.d'
+  )
+  DBUS_SYS_DIR=""
+  if test x$atalk_cv_with_dbus = xyes ; then
+      AC_DEFINE(HAVE_DBUS_GLIB, 1, [Define if support for dbus-glib was found])
+      DBUS_SYS_DIR="$ac_cv_dbus_sysdir"
+  fi
+
+  AC_SUBST(DBUS_SYS_DIR)
+  AC_SUBST(DBUS_CFLAGS)
+  AC_SUBST(DBUS_LIBS)
+  AC_SUBST(DBUS_GLIB_CFLAGS)
+  AC_SUBST(DBUS_GLIB_LIBS)
+  AC_SUBST(DBUS_GTHREAD_CFLAGS)
+  AC_SUBST(DBUS_GTHREAD_LIBS)
+  AM_CONDITIONAL(HAVE_DBUS_GLIB, test x$atalk_cv_with_dbus = xyes)
 ])
 
 dnl Whether to enable developer build
@@ -125,6 +140,52 @@ AC_DEFUN([AC_DEVELOPER], [
     AM_CONDITIONAL(DEVELOPER, test x"$enable_dev" = x"yes")
 ])
 
+dnl Tracker, for Spotlight
+AC_DEFUN([AC_NETATALK_SPOTLIGHT], [
+    ac_cv_have_tracker=no
+    ac_cv_tracker_pkg_version_default=0.12
+    ac_cv_tracker_pkg_version_min=0.12
+
+    dnl Tracker SPARQL
+    AC_ARG_WITH([tracker-pkgconfig-version],
+      [AS_HELP_STRING([--with-tracker-pkgconfig-version=VERSION],[Version suffix of the Tracker SPARQL and tracker-miner pkg in pkg-config (default: 0.12)])],
+      [ac_cv_tracker_pkg_version=$withval],
+      [ac_cv_tracker_pkg_version=$ac_cv_tracker_pkg_version_default]
+    )
+
+    AC_ARG_WITH([tracker-prefix],
+      [AS_HELP_STRING([--with-tracker-prefix=PATH],[Prefix of Tracker installation (default: none)])],
+      [ac_cv_tracker_prefix=$withval],
+      [ac_cv_tracker_prefix="`pkg-config --variable=prefix tracker-sparql-$ac_cv_tracker_pkg_version`"]
+    )
+
+    AC_ARG_VAR([PKG_CONFIG_PATH], [Path to additional pkg-config packages])
+    PKG_CHECK_MODULES([TRACKER], [tracker-sparql-$ac_cv_tracker_pkg_version >= $ac_cv_tracker_pkg_version_min], [ac_cv_have_tracker_sparql=yes], [ac_cv_have_tracker_sparql=no])
+    PKG_CHECK_MODULES([TRACKER_MINER], [tracker-miner-$ac_cv_tracker_pkg_version >= $ac_cv_tracker_pkg_version_min], [ac_cv_have_tracker_miner=yes], [ac_cv_have_tracker_miner=no])
+
+    if test x"$ac_cv_have_tracker_sparql" = x"no" -o x"$ac_cv_have_tracker_miner" = x"no" ; then
+        if test x"$need_tracker_sparql" = x"yes" ; then
+            AC_MSG_ERROR([$ac_cv_tracker_pkg not found])
+        fi
+    else
+        AC_DEFINE(HAVE_TRACKER, 1, [Define if Tracker is available])
+        AC_DEFINE(HAVE_TRACKER_SPARQL, 1, [Define if Tracker SPARQL is available])
+        AC_DEFINE(HAVE_TRACKER_MINER, 1, [Define if Tracker miner library is available])
+        AC_DEFINE_UNQUOTED(TRACKER_PREFIX, ["$ac_cv_tracker_prefix"], [Path to Tracker])
+        AC_DEFINE([DBUS_DAEMON_PATH], ["/bin/dbus-daemon"], [Path to dbus-daemon])
+    fi
+
+    if test x"$ac_cv_have_tracker_sparql" = x"yes" ; then
+       ac_cv_have_tracker=yes
+    fi
+
+    AC_SUBST(TRACKER_CFLAGS)
+    AC_SUBST(TRACKER_LIBS)
+    AC_SUBST(TRACKER_MINER_CFLAGS)
+    AC_SUBST(TRACKER_MINER_LIBS)
+    AM_CONDITIONAL(HAVE_TRACKER_SPARQL, [test x"$ac_cv_have_tracker_sparql" = x"yes"])
+])
+
 dnl Whether to disable bundled libevent
 AC_DEFUN([AC_NETATALK_LIBEVENT], [
     AC_MSG_CHECKING([whether to use bundled libevent])
index 810c896613267920b99faccba5b1c9a5de3bcce9..d3261192cc6694bada5ba14a23c9e23859c7906a 100644 (file)
@@ -12,6 +12,7 @@ AC_DEFUN([AC_NETATALK_CONFIG_SUMMARY], [
        AC_MSG_RESULT([    AFP:])
        AC_MSG_RESULT([         Extended Attributes: $neta_cv_eas])
        AC_MSG_RESULT([         ACL support: $ac_cv_have_acls])
+       AC_MSG_RESULT([         Spotlight: $ac_cv_have_tracker])
        AC_MSG_RESULT([    CNID:])
        AC_MSG_RESULT([         backends: $compiled_backends])
        AC_MSG_RESULT([    UAMS:])
@@ -53,7 +54,7 @@ dnl   AC_MSG_RESULT([         Samba sharemode interop: $neta_cv_have_smbshmd])
        AC_MSG_RESULT([         ACL support:             $with_acl_support])
        AC_MSG_RESULT([         Kerberos support:        $with_kerberos])
        AC_MSG_RESULT([         LDAP support:            $netatalk_cv_ldap])
-       AC_MSG_RESULT([         dbus support:            $atalk_cv_with_dbus])
+       AC_MSG_RESULT([         AFP stats via dbus:      $atalk_cv_with_dbus])
        AC_MSG_RESULT([         dtrace probes:           $WDTRACE])
        AC_MSG_RESULT([    Paths:])
        AC_MSG_RESULT([         Netatalk lockfile:       $ac_cv_netatalk_lock])
@@ -90,6 +91,11 @@ AC_DEFUN([AC_NETATALK_LIBS_SUMMARY], [
        AC_MSG_RESULT([    PTHREADS:])
        AC_MSG_RESULT([        LIBS   = $PTHREAD_LIBS])
        AC_MSG_RESULT([        CFLAGS = $PTHREAD_CFLAGS])
+       if test x"$ac_cv_have_tracker" = x"yes"; then
+               AC_MSG_RESULT([    TRACKER:])
+               AC_MSG_RESULT([        LIBS   = $TRACKER_LIBS])
+               AC_MSG_RESULT([        CFLAGS = $TRACKER_CFLAGS])
+       fi
        if test x"$neta_cv_have_openssl" = x"yes"; then
                AC_MSG_RESULT([    SSL:])
                AC_MSG_RESULT([        LIBS   = $SSL_LIBS])
index 462670d64d2632b0d64de3d383ecee592c1c605e..74d7a609a0c13f28708c4d7581e2acedd7550c93 100644 (file)
@@ -571,7 +571,7 @@ and should be quoted\&. Extended characters are allowed\&.
 ignored attributes = \fIall | nowrite | nodelete | norename\fR \fB(G)/(V)\fR
 .RS 4
 Speficy a set of file and directory attributes that shall be ignored by the server,
-<attribute>all</attribute>
+\fBall\fR
 includes all the other options\&.
 .sp
 In OS X when the Finder sets a lock on a file/directory or you set the BSD uchg flag in the Terminal, all three attributes are used\&. Thus in order to ignore the Finder lock/BSD uchg flag, add set
@@ -595,6 +595,11 @@ solaris share reservations = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(G)\fR
 Use share reservations on Solaris\&. Solaris CIFS server uses this too, so this makes a lock coherent multi protocol server\&.
 .RE
 .PP
+spotlight = \fIBOOLEAN\fR (default: \fIno\fR) \fB(G)/(V)\fR
+.RS 4
+Whether to enable Spotlight searches\&. Note: once the global option is enabled, any volume that is not enabled won\*(Aqt be searchable at all\&.
+.RE
+.PP
 veto message = \fIBOOLEAN\fR (default: \fIno\fR) \fB(G)\fR
 .RS 4
 Send optional AFP messages for vetoed files\&. Then whenever a client tries to access any file or directory with a vetoed name, it will be sent an AFP message indicating the name and the directory\&.
index 34e28f484a371874a1c5a2f57e0ed554980d78b9..6e7c2f46aec3620acd52822430fbcdeedb4cf126 100644 (file)
@@ -38,6 +38,8 @@ test_SOURCES =  test.c subtests.c afpfunc_helpers.c \
                                $(top_srcdir)/etc/afpd/ofork.c \
                                $(top_srcdir)/etc/afpd/quota.c \
                                $(top_srcdir)/etc/afpd/status.c \
+                               $(top_srcdir)/etc/afpd/spotlight.c \
+                               $(top_srcdir)/etc/afpd/spotlight_marshalling.c \
                                $(top_srcdir)/etc/afpd/switch.c \
                                $(top_srcdir)/etc/afpd/uam.c \
                                $(top_srcdir)/etc/afpd/unix.c \