ltmain.sh
autom4te.cache
autoscan.log
+ylwrap
*.rpm
*.deb
*.dsc
-3.0.1dev
\ No newline at end of file
+stp3
\ No newline at end of file
/* 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);
/* 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;
/* 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;
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;
}
/* 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;
}
}
/* 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;
}
/* 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;
}
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
*
#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 *);
Makefile.in
*.o
afp.conf
+dbus-session.conf
\ No newline at end of file
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
+EXTRA_DIST = afp.conf.tmpl extmap.conf dbus-session.conf.tmpl
OVERWRITE_CONFIG = @OVERWRITE_CONFIG@
--- /dev/null
+<!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=/tmp/spotlight.ipc</listen>
+ <allow_anonymous />
+
+ <standard_session_servicedirs />
+
+ <policy context="default">
+ <!-- Allow everything to be sent -->
+ <allow send_destination="*" eavesdrop="true"/>
+ <!-- Allow everything to be received -->
+ <allow eavesdrop="true"/>
+ <!-- Allow anyone to own anything -->
+ <allow own="*"/>
+ </policy>
+
+ <!-- raise the service start timeout to 40 seconds as it can timeout
+ on the live cd on slow machines -->
+ <limit name="service_start_timeout">60000</limit>
+
+ <!-- Config files are placed here that among other things,
+ further restrict the above policy for specific services. -->
+ <includedir>/etc/dbus-1/session.d</includedir>
+
+ <!-- This is included last so local configuration can override what's
+ in this standard file -->
+ <include ignore_missing="yes">session-local.conf</include>
+
+ <include if_selinux_enabled="yes" selinux_root_relative="yes">contexts/dbus_contexts</include>
+
+ <!-- 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>
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)
dnl Check whether bundled libevent shall not be used
AC_NETATALK_LIBEVENT
+dnl Check for Tracker
+AC_NETATALK_SPOTLIGHT
+
dnl FHS stuff has to be done last because it overrides other defaults
AC_NETATALK_FHS
libatalk/compat/Makefile
libatalk/dsi/Makefile
libatalk/iniparser/Makefile
+ libatalk/talloc/Makefile
libatalk/tdb/Makefile
libatalk/unicode/Makefile
libatalk/unicode/charsets/Makefile
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
pkgconfdir = @PKGCONFDIR@
sbin_PROGRAMS = afpd
-noinst_PROGRAMS = hash fce
+noinst_PROGRAMS = hash fce spot srp
afpd_SOURCES = \
afp_avahi.c \
nfsquota.c \
ofork.c \
quota.c \
+ spotlight.c \
+ spotlight_marshalling.c \
status.c \
switch.c \
uam.c \
noinst_HEADERS = auth.h afp_config.h desktop.h directory.h fce_api_internal.h file.h \
filedir.h fork.h icon.h mangle.h misc.h status.h switch.h \
uam_auth.h uid.h unix.h volume.h hash.h acls.h acl_mappings.h extattrs.h \
- dircache.h afp_zeroconf.h afp_avahi.h afp_mdns.h
+ dircache.h afp_zeroconf.h afp_avahi.h afp_mdns.h spotlight.h \
+ spotlight_rawquery_parser.h spotlight_SPARQL_map.h
hash_SOURCES = hash.c
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
+
+AM_YFLAGS = -d
+BUILT_SOURCES = spotlight_rawquery_parser.h
+
+if HAVE_TRACKER
+spot_SOURCES = spotlight.c spotlight_marshalling.c
+spot_CFLAGS = -DSPOT_TEST_MAIN
+spot_LDADD = $(top_builddir)/libatalk/libatalk.la
+
+moduledir = @UAMS_PATH@
+module_LTLIBRARIES = mod_spotlight.la
+
+mod_spotlight_la_SOURCES = \
+ spotlight_module.c \
+ spotlight_SPARQL_map.c \
+ spotlight_rawquery_parser.y \
+ spotlight_rawquery_lexer.l
+mod_spotlight_la_CFLAGS = -DDBUS_API_SUBJECT_TO_CHANGE @TRACKER_CFLAGS@ @TRACKER_MINER_CFLAGS@
+mod_spotlight_la_LDFLAGS = -module -avoid-version @TRACKER_LIBS@ @TRACKER_MINER_LIBS@
+
+srp_SOURCES = \
+ spotlight_SPARQL_map.c \
+ spotlight_rawquery_parser.y \
+ spotlight_rawquery_lexer.l
+srp_CFLAGS = -DMAIN -I$(top_srcdir)/include @TRACKER_CFLAGS@
+srp_LDADD = $(top_builddir)/libatalk/libatalk.la
+endif
#include "auth.h"
#include "fork.h"
#include "dircache.h"
+#include "spotlight.h"
#ifndef SOL_TCP
#define SOL_TCP IPPROTO_TCP
int flag = 1;
setsockopt(dsi->socket, SOL_TCP, TCP_NODELAY, &flag, sizeof(flag));
+ /* Initialize Spotlight */
+ if (obj->options.flags & OPTION_SPOTLIGHT)
+ sl_mod_load(_PATH_AFPDUAMPATH "mod_spotlight.so");
+
/* get stuck here until the end */
while (1) {
if (sigsetjmp(recon_jmp, 1) != 0)
#include "status.h"
#include "fork.h"
#include "extattrs.h"
+#include "spotlight.h"
#ifdef HAVE_ACLS
#include "acls.h"
#endif
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:
#include "file.h"
#include "filedir.h"
#include "unix.h"
+#include "spotlight.h"
/* the format for the finderinfo fields (from IM: Toolbox Essentials):
* field bytes subfield bytes
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);
#include "fork.h"
#include "uam_auth.h"
#include "afp_zeroconf.h"
+#include "spotlight.h"
#define AFP_LISTENERS 32
#define FDSET_SAFETY 5
/* Initialize */
cnid_init();
-
+
/* watch atp, dsi sockets and ipc parent/child file descriptor. */
if (obj.options.flags & OPTION_KEEPSESSIONS) {
--- /dev/null
+/*
+ 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 <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 "spotlight.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_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]);
+}
+
+/**************************************************************************************************
+ * 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 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;
+ 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;
+ EC_NULL_LOG( sl_query = dalloc_value_for_key(query, "DALLOC_CTX", 0, "DALLOC_CTX", 1, "kMDQueryString") );
+ LOG(log_debug, logtype_sl, "sl_rpc_openQuery: %s", sl_query);
+ slq->slq_qstring = talloc_steal(slq, sl_query);
+
+ 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);
+
+ 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_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: %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)
+{
+ 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, ==, "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;
+}
+
+/**************************************************************************************************
+ * 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
--- /dev/null
+/*
+ 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_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_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 */
+ const char *slq_qstring; /* the Spotlight query string */
+ sl_array_t *slq_reqinfo; /* array with requested metadata */
+ void *slq_tracker_cursor; /* Tracker 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 */
--- /dev/null
+/*
+ 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 "spotlight_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}
+};
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ 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 "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;
+
+ /* check for byte order mark */
+ utf16_encoding = SL_ENC_BIG_ENDIAN;
+ if (query_length >= 2) {
+ uint16_t byte_order_mark;
+ if (encoding == SL_ENC_LITTLE_ENDIAN)
+ byte_order_mark = SVAL(buf, offset);
+ else
+ byte_order_mark = RSVAL(buf, offset);
+
+ if (byte_order_mark == 0xFFFE) {
+ utf16_encoding = SL_ENC_BIG_ENDIAN | SL_ENC_UTF_16;
+ }
+ else if (byte_order_mark == 0xFEFF) {
+ utf16_encoding = SL_ENC_LITTLE_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(0x0add, 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;
+ 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 - 8 + 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);
+ unicode_encoding &= ~SL_ENC_UTF_16;
+ EC_NEG1( convert_string_allocate(CH_UCS2, CH_UTF8, buf + offset + (mark_exists ? 18 : 16), slen, &p) );
+ }
+
+ 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;
+}
--- /dev/null
+/*
+ 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 "spotlight.h"
+#include "spotlight_rawquery_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=/tmp/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)
+{
+ EC_INIT;
+ sl_array_t *meta;
+ sl_nil_t nil = 0;
+ int i, metacount;
+
+ 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")) {
+ char *p, *name;
+ if ((p = strrchr(path, '/'))) {
+ name = dalloc_strdup(meta, p + 1);
+ dalloc_add(meta, name, "char *");
+ }
+ } else {
+ dalloc_add_copy(meta, &nil, sl_nil_t);
+ }
+ }
+
+ dalloc_add(fm_array, meta, sl_array_t);
+
+EC_CLEANUP:
+ EC_EXIT;
+}
+
+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);
+ 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 (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;
+ }
+
+ 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_error, 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);
+ dalloc_add_copy(cnids->ca_cnids, &uint64, uint64_t);
+ add_filemeta(slq->slq_reqinfo, fm_array, id, path);
+
+ 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_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)
+{
+#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_error,
+ sl_mod_index_file
+};
--- /dev/null
+
+#line 3 "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[48] =
+ { 0,
+ 0, 0, 18, 17, 16, 17, 6, 17, 17, 7,
+ 8, 5, 15, 13, 17, 14, 5, 5, 5, 17,
+ 12, 0, 9, 5, 11, 5, 5, 5, 10, 0,
+ 5, 5, 5, 0, 5, 5, 4, 0, 5, 3,
+ 0, 5, 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, 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, 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, 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, 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
+ } ;
+
+static yyconst flex_int32_t yy_meta[31] =
+ { 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
+ } ;
+
+static yyconst flex_int16_t yy_base[49] =
+ { 0,
+ 0, 0, 57, 58, 58, 43, 58, 27, 48, 58,
+ 58, 0, 58, 58, 40, 58, 28, 34, 24, 19,
+ 58, 27, 58, 0, 58, 31, 24, 16, 58, 21,
+ 26, 15, 23, 22, 15, 20, 0, 26, 16, 0,
+ 14, 16, 6, 0, 7, 58, 58, 29
+ } ;
+
+static yyconst flex_int16_t yy_def[49] =
+ { 0,
+ 47, 1, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 48, 47, 47, 47, 47, 48, 48, 48, 47,
+ 47, 47, 47, 48, 47, 48, 48, 48, 47, 47,
+ 48, 48, 48, 47, 48, 48, 48, 47, 48, 48,
+ 47, 48, 47, 48, 47, 47, 0, 47
+ } ;
+
+static yyconst flex_int16_t yy_nxt[89] =
+ { 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,
+ 24, 46, 45, 44, 43, 42, 41, 40, 39, 38,
+ 37, 36, 35, 34, 33, 32, 31, 30, 29, 28,
+ 27, 26, 25, 23, 22, 21, 47, 3, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47
+ } ;
+
+static yyconst flex_int16_t yy_chk[89] =
+ { 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,
+ 48, 45, 43, 42, 41, 39, 38, 36, 35, 34,
+ 33, 32, 31, 30, 28, 27, 26, 22, 20, 19,
+ 18, 17, 15, 9, 8, 6, 3, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47
+ } ;
+
+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 2 "spotlight_rawquery_lexer.l"
+#include <string.h>
+#include <stdbool.h>
+#include <gio/gio.h>
+#include <atalk/talloc.h>
+#include "spotlight.h"
+#include "spotlight_rawquery_parser.h"
+#line 486 "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 do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0)
+#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 = '*'; \
+ unsigned 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 10 "spotlight_rawquery_lexer.l"
+
+#line 670 "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 >= 48 )
+ 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] != 58 );
+
+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 11 "spotlight_rawquery_lexer.l"
+return FUNC_INRANGE;
+ YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 12 "spotlight_rawquery_lexer.l"
+return DATE_ISO;
+ YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 13 "spotlight_rawquery_lexer.l"
+{yylval.bval = false; return BOOL;}
+ YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 14 "spotlight_rawquery_lexer.l"
+{yylval.bval = true; return BOOL;}
+ YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 15 "spotlight_rawquery_lexer.l"
+{yylval.sval = talloc_strdup(ssp_slq, yytext); return WORD;}
+ YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 16 "spotlight_rawquery_lexer.l"
+return QUOTE;
+ YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 17 "spotlight_rawquery_lexer.l"
+return OBRACE;
+ YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 18 "spotlight_rawquery_lexer.l"
+return CBRACE;
+ YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 19 "spotlight_rawquery_lexer.l"
+return AND;
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 20 "spotlight_rawquery_lexer.l"
+return OR;
+ YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 21 "spotlight_rawquery_lexer.l"
+return EQUAL;
+ YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 22 "spotlight_rawquery_lexer.l"
+return UNEQUAL;
+ YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 23 "spotlight_rawquery_lexer.l"
+return LT;
+ YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 24 "spotlight_rawquery_lexer.l"
+return GT;
+ YY_BREAK
+case 15:
+YY_RULE_SETUP
+#line 25 "spotlight_rawquery_lexer.l"
+return COMMA;
+ YY_BREAK
+case 16:
+/* rule 16 can match eol */
+YY_RULE_SETUP
+#line 26 "spotlight_rawquery_lexer.l"
+/* ignore */
+ YY_BREAK
+case 17:
+YY_RULE_SETUP
+#line 27 "spotlight_rawquery_lexer.l"
+ECHO;
+ YY_BREAK
+#line 839 "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 >= 48 )
+ 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 >= 48 )
+ 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 == 47);
+
+ 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 27 "spotlight_rawquery_lexer.l"
+
+
+
--- /dev/null
+%{
+#include <string.h>
+#include <stdbool.h>
+#include <gio/gio.h>
+#include <atalk/talloc.h>
+#include "spotlight.h"
+#include "spotlight_rawquery_parser.h"
+%}
+
+%%
+InRange return FUNC_INRANGE;
+\$time\.iso return DATE_ISO;
+false {yylval.bval = false; return BOOL;}
+true {yylval.bval = true; return BOOL;}
+[a-zA-Z0-9_\*\:\-\.]+ {yylval.sval = talloc_strdup(ssp_slq, yytext); return WORD;}
+\" return QUOTE;
+\( return OBRACE;
+\) return CBRACE;
+\&\& return AND;
+\|\| return OR;
+\=\= return EQUAL;
+\!\= return UNEQUAL;
+\< return LT;
+\> return GT;
+\, return COMMA;
+[ \t\n] /* ignore */
+%%
--- /dev/null
+/* A Bison parser, made by GNU Bison 2.5. */
+
+/* Bison implementation for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2011 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.5"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 0
+
+/* Push parsers. */
+#define YYPUSH 0
+
+/* Pull parsers. */
+#define YYPULL 1
+
+/* Using locations. */
+#define YYLSP_NEEDED 0
+
+
+
+/* Copy the first part of user declarations. */
+
+/* Line 268 of yacc.c */
+#line 1 "spotlight_rawquery_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 "spotlight_SPARQL_map.h"
+ #include "spotlight.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 268 of yacc.c */
+#line 110 "spotlight_rawquery_parser.c"
+
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 1
+#endif
+
+/* Enabling the token table. */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#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 293 of yacc.c */
+#line 45 "spotlight_rawquery_parser.y"
+
+ int ival;
+ const char *sval;
+ bool bval;
+ time_t tval;
+
+
+
+/* Line 293 of yacc.c */
+#line 187 "spotlight_rawquery_parser.c"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+/* "%code provides" blocks. */
+
+/* Line 340 of yacc.c */
+#line 39 "spotlight_rawquery_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 340 of yacc.c */
+#line 206 "spotlight_rawquery_parser.c"
+
+/* Copy the second part of user declarations. */
+
+
+/* Line 343 of yacc.c */
+#line 212 "spotlight_rawquery_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 */
+# 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 FROM to TO. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined __GNUC__ && 1 < __GNUC__
+# define YYCOPY(To, From, Count) \
+ __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+# else
+# define YYCOPY(To, From, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (To)[yyi] = (From)[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 || YYTOKEN_TABLE
+/* 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", 0
+};
+#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 && yylen == 1) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ YYPOPSTACK (1); \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror (YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (YYID (0))
+
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+ If N is 0, then set CURRENT to the empty location which ends
+ the previous symbol: RHS[0] (always defined). */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do \
+ if (YYID (N)) \
+ { \
+ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
+ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
+ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
+ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
+ } \
+ else \
+ { \
+ (Current).first_line = (Current).last_line = \
+ YYRHSLOC (Rhs, 0).last_line; \
+ (Current).first_column = (Current).last_column = \
+ YYRHSLOC (Rhs, 0).last_column; \
+ } \
+ while (YYID (0))
+#endif
+
+
+/* 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
+{
+ 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 (0, yytname[yytoken]);
+ YYSIZE_T yysize = yysize0;
+ YYSIZE_T yysize1;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ /* Internationalized format string. */
+ const char *yyformat = 0;
+ /* 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];
+ yysize1 = yysize + yytnamerr (0, 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_
+ }
+
+ 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;
+ }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes. */
+#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 */
+
+
+/* The lookahead symbol. */
+int yychar;
+
+/* The semantic value of the lookahead symbol. */
+YYSTYPE yylval;
+
+/* 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 thru 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;
+ /* 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;
+
+ yytoken = 0;
+ yyss = yyssa;
+ yyvs = yyvsa;
+ yystacksize = YYINITDEPTH;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+ yyssp = yyss;
+ yyvsp = yyvs;
+
+ 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;
+ *++yyvsp = yylval;
+
+ 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 1806 of yacc.c */
+#line 73 "spotlight_rawquery_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 1806 of yacc.c */
+#line 83 "spotlight_rawquery_parser.y"
+ {
+ if ((yyvsp[(1) - (1)].bval) == false)
+ YYACCEPT;
+ else
+ YYABORT;
+}
+ break;
+
+ case 6:
+
+/* Line 1806 of yacc.c */
+#line 89 "spotlight_rawquery_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 1806 of yacc.c */
+#line 95 "spotlight_rawquery_parser.y"
+ {(yyval.sval) = (yyvsp[(1) - (1)].sval); if ((yyval.sval) == NULL) YYABORT;}
+ break;
+
+ case 8:
+
+/* Line 1806 of yacc.c */
+#line 96 "spotlight_rawquery_parser.y"
+ {(yyval.sval) = (yyvsp[(1) - (1)].sval);}
+ break;
+
+ case 9:
+
+/* Line 1806 of yacc.c */
+#line 97 "spotlight_rawquery_parser.y"
+ {(yyval.sval) = talloc_asprintf(ssp_slq, "%s", (yyvsp[(2) - (3)].sval));}
+ break;
+
+ case 10:
+
+/* Line 1806 of yacc.c */
+#line 98 "spotlight_rawquery_parser.y"
+ {(yyval.sval) = talloc_asprintf(ssp_slq, "%s . %s", (yyvsp[(1) - (3)].sval), (yyvsp[(3) - (3)].sval));}
+ break;
+
+ case 11:
+
+/* Line 1806 of yacc.c */
+#line 99 "spotlight_rawquery_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 1806 of yacc.c */
+#line 108 "spotlight_rawquery_parser.y"
+ {(yyval.sval) = map_expr((yyvsp[(1) - (5)].sval), '=', (yyvsp[(4) - (5)].sval));}
+ break;
+
+ case 13:
+
+/* Line 1806 of yacc.c */
+#line 109 "spotlight_rawquery_parser.y"
+ {(yyval.sval) = map_expr((yyvsp[(1) - (5)].sval), '!', (yyvsp[(4) - (5)].sval));}
+ break;
+
+ case 14:
+
+/* Line 1806 of yacc.c */
+#line 110 "spotlight_rawquery_parser.y"
+ {(yyval.sval) = map_expr((yyvsp[(1) - (5)].sval), '<', (yyvsp[(4) - (5)].sval));}
+ break;
+
+ case 15:
+
+/* Line 1806 of yacc.c */
+#line 111 "spotlight_rawquery_parser.y"
+ {(yyval.sval) = map_expr((yyvsp[(1) - (5)].sval), '>', (yyvsp[(4) - (5)].sval));}
+ break;
+
+ case 16:
+
+/* Line 1806 of yacc.c */
+#line 112 "spotlight_rawquery_parser.y"
+ {(yyval.sval) = map_expr((yyvsp[(1) - (6)].sval), '=', (yyvsp[(4) - (6)].sval));}
+ break;
+
+ case 17:
+
+/* Line 1806 of yacc.c */
+#line 113 "spotlight_rawquery_parser.y"
+ {(yyval.sval) = map_expr((yyvsp[(1) - (6)].sval), '!', (yyvsp[(4) - (6)].sval));}
+ break;
+
+ case 18:
+
+/* Line 1806 of yacc.c */
+#line 114 "spotlight_rawquery_parser.y"
+ {(yyval.sval) = map_expr((yyvsp[(1) - (6)].sval), '<', (yyvsp[(4) - (6)].sval));}
+ break;
+
+ case 19:
+
+/* Line 1806 of yacc.c */
+#line 115 "spotlight_rawquery_parser.y"
+ {(yyval.sval) = map_expr((yyvsp[(1) - (6)].sval), '>', (yyvsp[(4) - (6)].sval));}
+ break;
+
+ case 20:
+
+/* Line 1806 of yacc.c */
+#line 119 "spotlight_rawquery_parser.y"
+ {(yyval.sval) = map_daterange((yyvsp[(3) - (8)].sval), (yyvsp[(5) - (8)].tval), (yyvsp[(7) - (8)].tval));}
+ break;
+
+ case 21:
+
+/* Line 1806 of yacc.c */
+#line 123 "spotlight_rawquery_parser.y"
+ {(yyval.tval) = isodate2unix((yyvsp[(3) - (4)].sval));}
+ break;
+
+ case 22:
+
+/* Line 1806 of yacc.c */
+#line 124 "spotlight_rawquery_parser.y"
+ {(yyval.tval) = atoi((yyvsp[(1) - (1)].sval)) + SPRAW_TIME_OFFSET;}
+ break;
+
+
+
+/* Line 1806 of yacc.c */
+#line 1622 "spotlight_rawquery_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);
+ }
+
+ *++yyvsp = yylval;
+
+
+ /* 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 2067 of yacc.c */
+#line 127 "spotlight_rawquery_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);
+}
+
+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;
+}
+
+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 (p->ssm_sparql_attr && strcmp(p->ssm_spotlight_attr, attr) == 0) {
+ 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:
+ yyerror("unknown Spotlight attribute type");
+ 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
+
--- /dev/null
+/* A Bison parser, made by GNU Bison 2.5. */
+
+/* Bison interface for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2011 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. */
+
+
+/* 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 2068 of yacc.c */
+#line 45 "spotlight_rawquery_parser.y"
+
+ int ival;
+ const char *sval;
+ bool bval;
+ time_t tval;
+
+
+
+/* Line 2068 of yacc.c */
+#line 91 "spotlight_rawquery_parser.h"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+extern YYSTYPE yylval;
+
+
+/* "%code provides" blocks. */
+
+/* Line 2068 of yacc.c */
+#line 39 "spotlight_rawquery_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 2068 of yacc.c */
+#line 113 "spotlight_rawquery_parser.h"
--- /dev/null
+%{
+ #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 "spotlight_SPARQL_map.h"
+ #include "spotlight.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);
+}
+
+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;
+}
+
+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 (p->ssm_sparql_attr && strcmp(p->ssm_spotlight_attr, attr) == 0) {
+ 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:
+ yyerror("unknown Spotlight attribute type");
+ 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
#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>
/* 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;
static struct event_base *base;
struct event *sigterm_ev, *sigquit_ev, *sigchld_ev, *timer_ev;
static int in_shutdown;
+static const char *dbus_path;
+
+/******************************************************************
+ * 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) {
+ 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));
+ LOG(log_debug, logtype_sl, "set_sl_volumes: %s", bdata(cmd));
+ system(bdata(cmd));
+
+EC_CLEANUP:
+ if (cmd)
+ bdestroy(cmd);
+ if (sep)
+ bdestroy(sep);
+ if (vollist)
+ bstrListDestroy(vollist);
+ if (volnamelist)
+ bdestroy(volnamelist);
+ EC_EXIT;
+}
/******************************************************************
* libevent helper functions
event_del(sigquit_ev);
event_del(timer_ev);
- kill_childs(SIGTERM, &afpd_pid, &cnid_metad_pid, NULL);
+ system("tracker-control -t");
+ 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);
+ system("tracker-control -t");
+ kill_childs(SIGQUIT, &afpd_pid, &cnid_metad_pid, &dbus_pid, NULL);
}
/* SIGQUIT callback */
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);
}
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'");
}
}
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'");
+ }
+ }
+
+ 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);
}
}
}
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);
sigdelset(&blocksigs, SIGHUP);
sigprocmask(SIG_SETMASK, &blocksigs, NULL);
+ dbus_path = iniparser_getstring(obj.iniconfig, INISEC_GLOBAL, "dbus daemon path", "/bin/dbus-daemon");
+ 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);
+
+ setenv("DBUS_SESSION_BUS_ADDRESS", "unix:path=/tmp/spotlight.ipc", 1);
+ set_sl_volumes();
+ system(TRACKER_PREFIX "/bin/tracker-control -s");
+
/* 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");
gcry_mpi_release(g);
}
-
UAM_MODULE_EXPORT struct uam_export uams_dhx2 = {
UAM_MODULE_SERVER,
UAM_MODULE_VERSION,
ftw.h \
dsi.h \
ldapconfig.h \
- fce_api.h
\ No newline at end of file
+ talloc.h \
+ dalloc.h \
+ byteorder.h \
+ fce_api.h
--- /dev/null
+/*
+ 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 */
--- /dev/null
+/*
+ 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 */
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. */
#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)
#define OPTION_NOZEROCONF (1 << 9)
#define OPTION_KEEPSESSIONS (1 << 10) /* preserve sessions across master afpd restart with SIGQUIT */
#define OPTION_SHARE_RESERV (1 << 11) /* whether to use Solaris fcntl F_SHARE locks */
-
+#define OPTION_SPOTLIGHT (1 << 12) /* whether to enable Spotlight support */
#define PASSWD_NONE 0
#define PASSWD_SET (1 << 0)
#define PASSWD_NOSAVE (1 << 1)
logtype_uams,
logtype_fce,
logtype_ad,
+ logtype_sl,
logtype_end_of_list_marker /* don't put any logtypes after this */
};
--- /dev/null
+#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
#include <atalk/unicode.h>
#include <atalk/bstrlib.h>
+#include <atalk/cnid.h>
/* exit error codes */
#define EXITERR_CLNT 1 /* client related error */
*****************************************************************/
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
# 3.0.1 2:0:0
# 3.0.2 3:0:1
-SUBDIRS = acl adouble bstring compat cnid dsi iniparser tdb util unicode vfs
+SUBDIRS = acl adouble bstring compat cnid dsi iniparser talloc tdb util unicode vfs
lib_LTLIBRARIES = libatalk.la
compat/libcompat.la \
dsi/libdsi.la \
iniparser/libiniparser.la \
+ talloc/libtalloc.la \
tdb/libtdb.la \
unicode/libunicode.la \
util/libutil.la \
cnid/libcnid.la \
compat/libcompat.la \
dsi/libdsi.la \
+ talloc/libtalloc.la \
iniparser/libiniparser.la \
tdb/libtdb.la \
unicode/libunicode.la \
--- /dev/null
+# Makefile.am for libatalk/talloc/
+
+noinst_LTLIBRARIES = libtalloc.la
+libtalloc_la_SOURCES = talloc.c dalloc.c
--- /dev/null
+/*
+ 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/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);
+}
+
+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 *);
+
+ if (STRCMP(type, !=, "DALLOC_CTX"))
+ EC_FAIL;
+
+ 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 *);
+ }
+
+ elem = va_arg(args, int);
+ AFP_ASSERT(elem < talloc_array_length(d->dd_talloc_array));
+
+ p = talloc_check_name(d->dd_talloc_array[elem], type);
+
+ 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 *);
+
+ if (STRCMP(type, !=, "DALLOC_CTX"))
+ EC_FAIL;
+
+ 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;
+}
--- /dev/null
+/*
+ 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);
+}
libunicode_la_LIBADD = $(LIBUNICODE_DEPS)
-noinst_HEADERS = utf16_casetable.h precompose.h byteorder.h
+noinst_HEADERS = utf16_casetable.h precompose.h
LIBS=@ICONV_LIBS@
+++ /dev/null
-/*
- 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 */
#include <atalk/unicode.h>
#include <atalk/util.h>
#include <atalk/compat.h>
-
-#include "byteorder.h"
+#include <atalk/byteorder.h>
/**
#include <atalk/unicode.h>
#include <iconv.h>
-#include "../byteorder.h"
+#include <atalk/byteorder.h>
#define CJK_PUSH_BUFFER 4
#define CJK_PULL_BUFFER 8
#include <atalk/unicode.h>
#include <atalk/logger.h>
+#include <atalk/byteorder.h>
#include "generic_mb.h"
-#include "../byteorder.h"
+
/* ------------------------ */
#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 *);
#include <atalk/unicode.h>
#include <atalk/logger.h>
-#include "byteorder.h"
+#include <atalk/byteorder.h>
/**
#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
#include <arpa/inet.h>
#include <atalk/unicode.h>
+#include <atalk/byteorder.h>
+
#include "precompose.h"
-#include "byteorder.h"
/*******************************************************************
Convert a string to lower case.
#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>
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_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(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;
+}
"UAMS", \
"FCE", \
"ad", \
+ "Spotlight", \
"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);
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 */
options->flags |= OPTION_SHARE_RESERV;
if (iniparser_getboolean(config, INISEC_GLOBAL, "afp read locks", 0))
options->flags |= OPTION_AFP_READ_LOCK;
+ if (iniparser_getboolean(config, INISEC_GLOBAL, "spotlight", 0))
+ options->flags |= OPTION_SPOTLIGHT;
if (!iniparser_getboolean(config, INISEC_GLOBAL, "save password", 1))
options->passwdbits |= PASSWD_NOSAVE;
if (iniparser_getboolean(config, INISEC_GLOBAL, "set password", 0))
AM_CONDITIONAL(DEVELOPER, test x"$enable_dev" = x"yes")
])
+dnl Tracker, for Spotlight
+AC_DEFUN([AC_NETATALK_SPOTLIGHT], [
+ ac_cv_tracker_pkg_default=tracker-sparql-0.12
+ AC_ARG_WITH([tracker-pkg-config],
+ [AS_HELP_STRING([--with-tracker-pkg-config],[name of the Tracker SPARQL pkg in pkg-config])],
+ [ac_cv_tracker_pkg=$withval],
+ [ac_cv_tracker_pkg=$ac_cv_tracker_pkg_default])
+
+ AC_MSG_NOTICE([searching for $ac_cv_tracker_pkg])
+
+ PKG_CHECK_MODULES([TRACKER], [$ac_cv_tracker_pkg >= 0.12], [ac_cv_have_tracker=yes], [ac_cv_have_tracker=no])
+
+ if test x"$ac_cv_have_tracker" = x"no" ; then
+ if test x"$need_tracker" = x"yes" ; then
+ AC_MSG_ERROR([$ac_cv_tracker_pkg not found])
+ fi
+ else
+ AC_DEFINE(HAVE_TRACKER, 1, [Define if Tracker library is available])
+ ac_cv_tracker_prefix=`pkg-config --variable=prefix $ac_cv_tracker_pkg`
+ AC_DEFINE_UNQUOTED(TRACKER_PREFIX, ["$ac_cv_tracker_prefix"], [Path to Tracker])
+ fi
+
+ AC_SUBST(TRACKER_CFLAGS)
+ AC_SUBST(TRACKER_LIBS)
+ AM_CONDITIONAL(HAVE_TRACKER, [test x"$ac_cv_have_tracker" = x"yes"])
+
+ ac_cv_tracker_miner_pkg_default=tracker-miner-0.12
+ AC_ARG_WITH([tracker-miner-pkg-config],
+ [AS_HELP_STRING([--with-tracker-miner-pkg-config],[name of the Tracker miner pkg in pkg-config])],
+ [ac_cv_tracker_miner_pkg=$withval],
+ [ac_cv_tracker_miner_pkg=$ac_cv_tracker_miner_pkg_default])
+
+ AC_MSG_NOTICE([searching for $ac_cv_tracker_miner_pkg])
+
+ PKG_CHECK_MODULES([TRACKER_MINER], [$ac_cv_tracker_miner_pkg >= 0.12], [ac_cv_have_tracker_miner=yes], [ac_cv_have_tracker_miner=no])
+
+ if test x"$ac_cv_have_tracker_miner" = x"yes" ; then
+ AC_DEFINE(HAVE_TRACKER_MINER, 1, [Define if Tracker miner library is available])
+ AC_SUBST(TRACKER_MINER_CFLAGS)
+ AC_SUBST(TRACKER_MINER_LIBS)
+ fi
+])
+
dnl Whether to disable bundled libevent
AC_DEFUN([AC_NETATALK_LIBEVENT], [
AC_MSG_CHECKING([whether to use bundled libevent])
AC_MSG_RESULT([ AFP:])
AC_MSG_RESULT([ Extended Attributes: $neta_cv_eas])
AC_MSG_RESULT([ ACL support: $with_acl_support])
+ AC_MSG_RESULT([ Spotlight: $ac_cv_have_tracker])
AC_MSG_RESULT([ CNID:])
AC_MSG_RESULT([ backends: $compiled_backends])
AC_MSG_RESULT([ UAMS:])
$(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 \