From: Ralph Boehme Date: Mon, 4 Mar 2013 20:50:58 +0000 (+0100) Subject: Merge remote-tracking branch 'origin/develop' into spotlight X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=netatalk.git;a=commitdiff_plain;h=42eb54de3f6373cd394fbd6abfb86d85c8dde935;hp=bb8bc51e54795edffc1d2b70cb232eba79b97ffb Merge remote-tracking branch 'origin/develop' into spotlight Conflicts: include/atalk/globals.h --- diff --git a/.gitignore b/.gitignore index f9ba2b20..cecdf4e2 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ ltconfig ltmain.sh autom4te.cache autoscan.log +ylwrap *.rpm *.deb *.dsc diff --git a/VERSION b/VERSION index 226bf924..d54e8998 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.0.3dev \ No newline at end of file +stp4 \ No newline at end of file diff --git a/bin/ad/ad.h b/bin/ad/ad.h index 3195641f..9155900c 100644 --- a/bin/ad/ad.h +++ b/bin/ad/ad.h @@ -69,7 +69,6 @@ extern int ad_find(int argc, char **argv, AFPObj *obj); /* ad_util.c */ extern int openvol(AFPObj *obj, const char *path, afpvol_t *vol); extern void closevol(afpvol_t *vol); -extern cnid_t cnid_for_path(const afpvol_t *vol, const char *path, cnid_t *did); extern cnid_t cnid_for_paths_parent(const afpvol_t *vol, const char *path, cnid_t *did); extern char *utompath(const struct vol *, const char *); extern int convert_dots_encoding(const afpvol_t *svol, const afpvol_t *dvol, char *path, size_t buflen); diff --git a/bin/ad/ad_cp.c b/bin/ad/ad_cp.c index 239936dc..5cefe964 100644 --- a/bin/ad/ad_cp.c +++ b/bin/ad/ad_cp.c @@ -509,7 +509,7 @@ static int copy(const char *path, /* Get CNID of Parent and add new childir to CNID database */ ppdid = pdid; - if ((did = cnid_for_path(&dvolume, to.p_path, &pdid)) == CNID_INVALID) { + if ((did = cnid_for_path(dvolume.vol->v_cdb, dvolume.vol->v_path, to.p_path, &pdid)) == CNID_INVALID) { SLOG("Error resolving CNID for %s", to.p_path); badcp = rval = 1; return -1; @@ -577,7 +577,7 @@ static int copy(const char *path, /* Get CNID of Parent and add new childir to CNID database */ pdid = did; cnid_t cnid; - if ((cnid = cnid_for_path(&dvolume, to.p_path, &did)) == CNID_INVALID) { + if ((cnid = cnid_for_path(dvolume.vol->v_cdb, dvolume.vol->v_path, to.p_path, &did)) == CNID_INVALID) { SLOG("Error resolving CNID for %s", to.p_path); badcp = rval = 1; return -1; diff --git a/bin/ad/ad_mv.c b/bin/ad/ad_mv.c index 72698f73..3fdf1ea9 100644 --- a/bin/ad/ad_mv.c +++ b/bin/ad/ad_mv.c @@ -278,7 +278,7 @@ static int do_move(const char *from, const char *to) cnid_t cnid = 0; if (!mustcopy) { - if ((cnid = cnid_for_path(&svolume, from, &did)) == CNID_INVALID) { + if ((cnid = cnid_for_path(svolume.vol->v_cdb, svolume.vol->v_path, from, &did)) == CNID_INVALID) { SLOG("Couldn't resolve CNID for %s", from); return -1; } diff --git a/bin/ad/ad_rm.c b/bin/ad/ad_rm.c index 195fee06..b32e659f 100644 --- a/bin/ad/ad_rm.c +++ b/bin/ad/ad_rm.c @@ -211,7 +211,7 @@ static int rm(const char *path, /* Get CNID of Parent and add new childir to CNID database */ pdid = did; - if ((cnid = cnid_for_path(&volume, path, &did)) == CNID_INVALID) { + if ((cnid = cnid_for_path(volume.vol->v_cdb, volume.vol->v_path, path, &did)) == CNID_INVALID) { SLOG("Error resolving CNID for %s", path); return -1; } @@ -247,7 +247,7 @@ static int rm(const char *path, } /* Get CNID of Parent and add new childir to CNID database */ - if ((did = cnid_for_path(&volume, path, &pdid)) == CNID_INVALID) { + if ((did = cnid_for_path(volume.vol->v_cdb, volume.vol->v_path, path, &pdid)) == CNID_INVALID) { SLOG("Error resolving CNID for %s", path); return -1; } @@ -293,7 +293,7 @@ static int rm(const char *path, /* Get CNID of Parent and add new childir to CNID database */ pdid = did; - if ((cnid = cnid_for_path(&volume, path, &did)) == CNID_INVALID) { + if ((cnid = cnid_for_path(volume.vol->v_cdb, volume.vol->v_path, path, &did)) == CNID_INVALID) { SLOG("Error resolving CNID for %s", path); return -1; } diff --git a/bin/ad/ad_util.c b/bin/ad/ad_util.c index 897bdf3b..135bf7ab 100644 --- a/bin/ad/ad_util.c +++ b/bin/ad/ad_util.c @@ -231,73 +231,6 @@ int convert_dots_encoding(const afpvol_t *svol, const afpvol_t *dvol, char *path return 0; } -/*! - * ResolvesCNID of a given paths - * - * path might be: - * (a) relative: - * "dir/subdir" with cwd: "/afp_volume/topdir" - * (b) absolute: - * "/afp_volume/dir/subdir" - * - * path MUST be pointing inside vol, this is usually the case as vol has been build from - * path using loadvolinfo and friends. - * - * @param vol (r) pointer to afpvol_t - * @param path (r) path, see above - * @param did (rw) parent CNID of returned CNID - * - * @returns CNID of path - */ -cnid_t cnid_for_path(const afpvol_t *vol, - const char *path, - cnid_t *did) -{ - EC_INIT; - - cnid_t cnid; - bstring rpath = NULL; - bstring statpath = NULL; - struct bstrList *l = NULL; - struct stat st; - - cnid = htonl(2); - - EC_NULL(rpath = rel_path_in_vol(path, vol->vol->v_path)); - EC_NULL(statpath = bfromcstr(vol->vol->v_path)); - EC_ZERO(bcatcstr(statpath, "/")); - - l = bsplit(rpath, '/'); - for (int i = 0; i < l->qty ; i++) { - *did = cnid; - - EC_ZERO(bconcat(statpath, l->entry[i])); - EC_ZERO_LOGSTR(lstat(cfrombstr(statpath), &st), - "lstat(rpath: %s, elem: %s): %s: %s", - cfrombstr(rpath), cfrombstr(l->entry[i]), - cfrombstr(statpath), strerror(errno)); - - if ((cnid = cnid_add(vol->vol->v_cdb, - &st, - *did, - cfrombstr(l->entry[i]), - blength(l->entry[i]), - 0)) == CNID_INVALID) { - EC_FAIL; - } - EC_ZERO(bcatcstr(statpath, "/")); - } - -EC_CLEANUP: - bdestroy(rpath); - bstrListDestroy(l); - bdestroy(statpath); - if (ret != 0) - return CNID_INVALID; - - return cnid; -} - /*! * Resolves CNID of a given paths parent directory * diff --git a/bin/uniconv/iso8859_1_adapted.c b/bin/uniconv/iso8859_1_adapted.c index 7cdd43c3..96cfcf58 100644 --- a/bin/uniconv/iso8859_1_adapted.c +++ b/bin/uniconv/iso8859_1_adapted.c @@ -27,8 +27,7 @@ #include #include - -#include "../../libatalk/unicode/byteorder.h" +#include 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 *); diff --git a/config/Makefile.am b/config/Makefile.am index e72c38f3..026887b8 100644 --- a/config/Makefile.am +++ b/config/Makefile.am @@ -6,7 +6,7 @@ SUFFIXES = .tmpl . TMPLFILES = afp.conf.tmpl GENFILES = afp.conf CLEANFILES = $(GENFILES) -EXTRA_DIST = afp.conf.tmpl extmap.conf netatalk-dbus.conf +EXTRA_DIST = afp.conf.tmpl extmap.conf dbus-session.conf netatalk-dbus.conf tracker.cfg OVERWRITE_CONFIG = @OVERWRITE_CONFIG@ @@ -41,19 +41,24 @@ install-data-local: install-config-files $(INSTALL_DATA) $(srcdir)/README $(DESTDIR)$(localstatedir)/netatalk/CNID/ uninstall-local: - for f in $(CONFFILES) $(GENFILES); do \ + @for f in $(CONFFILES) $(GENFILES); do \ echo rm -f $(DESTDIR)$(pkgconfdir)/$$f; \ rm -f $(DESTDIR)$(pkgconfdir)/$$f; \ done + rm -f $(DESTDIR)$(pkgconfdir)/dbus-session.conf rm -f $(DESTDIR)$(localstatedir)/netatalk/README rm -f $(DESTDIR)$(localstatedir)/netatalk/CNID/README +if HAVE_TRACKER_RDF + rm -f $(DESTDIR)$(pkgconfdir)/tracker/tracker.cfg +endif if USE_DEBIAN rm -f $(DESTDIR)/etc/default/netatalk endif install-config-files: $(CONFFILES) $(GENFILES) $(mkinstalldirs) $(DESTDIR)$(pkgconfdir) - for f in $(CONFFILES) ; do \ + $(INSTALL_DATA) $(srcdir)/dbus-session.conf $(DESTDIR)$(pkgconfdir) + @for f in $(CONFFILES) ; do \ if test "x$(OVERWRITE_CONFIG)" = "xyes" -o ! -f $(DESTDIR)$(pkgconfdir)/$$f; then \ echo "$(INSTALL_DATA) $$f $(DESTDIR)$(pkgconfdir)"; \ $(INSTALL_DATA) $(srcdir)/$$f $(DESTDIR)$(pkgconfdir); \ @@ -61,7 +66,7 @@ install-config-files: $(CONFFILES) $(GENFILES) echo "not overwriting $$f"; \ fi; \ done - for f in $(GENFILES); do \ + @for f in $(GENFILES); do \ if test "x$(OVERWRITE_CONFIG)" = "xyes" -o ! -f $(DESTDIR)$(pkgconfdir)/$$f; then \ echo "$(INSTALL_DATA) $$f $(DESTDIR)$(pkgconfdir)"; \ $(INSTALL_DATA) $$f $(DESTDIR)$(pkgconfdir); \ @@ -69,3 +74,11 @@ install-config-files: $(CONFFILES) $(GENFILES) echo "not overwriting $$f"; \ fi; \ done +if HAVE_TRACKER_RDF + if test "x$(OVERWRITE_CONFIG)" = "xyes" -o ! -f $(DESTDIR)$(pkgconfdir)/tracker/tracker.cfg ; then \ + if test ! -d $(DESTDIR)$(pkgconfdir)/tracker ; then \ + mkdir $(DESTDIR)$(pkgconfdir)/tracker ; \ + fi ; \ + $(INSTALL_DATA) $(srcdir)/tracker.cfg $(DESTDIR)$(pkgconfdir)/tracker/ ; \ + fi +endif diff --git a/config/dbus-session.conf b/config/dbus-session.conf new file mode 100644 index 00000000..e933380b --- /dev/null +++ b/config/dbus-session.conf @@ -0,0 +1,45 @@ + + + + session + + + + + unix:path=/tmp/spotlight.ipc + + + + + + + + + + + + + + + 1000000000 + 1000000000 + 1000000000 + 120000 + 240000 + 100000 + 10000 + 100000 + 10000 + 50000 + 50000 + 50000 + + diff --git a/config/tracker.cfg b/config/tracker.cfg new file mode 100644 index 00000000..03fc15c6 --- /dev/null +++ b/config/tracker.cfg @@ -0,0 +1,80 @@ + +[General] +# Log Verbosity (0=errors, 1=minimal, 2=detailed, 3=debug) +Verbosity=1 +# Initial sleep time in seconds (0->1000) +InitialSleep=0 +# Minimizes memory use at the expense of indexing speed +LowMemoryMode=false +# Set to TRUE when the home directory is in a NFS filesystem +NFSLocking=false + +[Watches] +# List of directory roots to index and watch (separator=;) +WatchDirectoryRoots= +# List of directory roots to index but NOT watch (separator=;) +CrawlDirectory= +# List of directory roots NOT to index and NOT to watch (separator=;) +NoWatchDirectory= +# Set to false to completely disable any watching +EnableWatching=true + +[Indexing] +# Sets the indexing speed (0->20, where 20=slowest speed) +Throttle=0 +# Set to false to completely disable any indexing +EnableIndexing=true +# Set to false to completely disable file content indexing +EnableFileContentIndexing=true +# Set to false to completely disable thumbnail generation +EnableThumbnails=true +# List of disabled modules (separator=;) +# The modules that are indexed are kept in $prefix/lib/tracker/indexer-modules +DisabledModules=evolution; +# Set to false to NOT hog the disk for extended periods +# FastMerges=false +# List of partial file pattern globs (separator=;) +# This is for files to NOT index +# (basic stat info is only extended for files that match the patterns) +NoIndexFileTypes= +# Set the minimum length of words to index (0->30, default=3) +MinWordLength=3 +# Set the maximum length of words to index (0->200, default=30) +MaxWordLength=30 +# Set the language specific stemmer and stopword list to use +# Values include: +# - en (English) +# - da (Danish) +# - nl (Dutch) +# - fi (Finish) +# - fr (French) +# - de (German) +# - it (Italian) +# - nb (Norwegian) +# - pt (Portugese) +# - ru (Russian) +# - es (Spanish) +# - sv (Swedish) +Language=de +# Set to false to disable language specific stemmer +EnableStemmer=true +# Set to true to disable indexing when running on battery +BatteryIndex=true +# Set to true to disable initial indexing when running on battery +BatteryIndexInitial=false +# Pause indexer when disk space is <= this value +# (0->100, value is in % of $HOME file system, -1=disable pausing) +LowDiskSpaceLimit=1 +# Set to true to enable traversing mounted directories on other file systems +# (this excludes removable devices) +IndexMountedDirectories=true +# Set to true to enable traversing mounted directories for removable devices +IndexRemovableMedia=true + +[Performance] +# Maximum text size in bytes to index from a file's content +MaxTextToIndex=1048576 +# Maximum unique words to index from a file's content +MaxWordsToIndex=10000 +MinBucketCount=65536 +MaxBucketCount=524288 diff --git a/configure.ac b/configure.ac index 566df4f4..d7976608 100644 --- a/configure.ac +++ b/configure.ac @@ -25,6 +25,8 @@ AC_PROG_PS AM_PROG_CC_C_O AC_C_BIGENDIAN AC_C_INLINE +AC_PROG_LEX +AC_PROG_YACC dnl Check if we can use attribute unused (gcc only) from ethereal AC_MSG_CHECKING(to see if we can add '__attribute__((unused))' to CFLAGS) @@ -180,6 +182,9 @@ AC_NETATALK_SENDFILE dnl Check whether bundled libevent shall not be used AC_NETATALK_LIBEVENT +dnl Check for Tracker +AC_NETATALK_SPOTLIGHT + dnl libatalk API checks AC_DEVELOPER @@ -250,6 +255,7 @@ AC_OUTPUT([Makefile etc/afpd/Makefile etc/cnid_dbd/Makefile etc/netatalk/Makefile + etc/spotlight/Makefile etc/uams/Makefile include/Makefile include/atalk/Makefile @@ -265,6 +271,7 @@ AC_OUTPUT([Makefile libatalk/compat/Makefile libatalk/dsi/Makefile libatalk/iniparser/Makefile + libatalk/talloc/Makefile libatalk/tdb/Makefile libatalk/unicode/Makefile libatalk/unicode/charsets/Makefile diff --git a/etc/Makefile.am b/etc/Makefile.am index e5367734..6877a761 100644 --- a/etc/Makefile.am +++ b/etc/Makefile.am @@ -1,3 +1,3 @@ # Makefile.am for etc/ -SUBDIRS = afpd cnid_dbd netatalk uams +SUBDIRS = afpd cnid_dbd netatalk spotlight uams diff --git a/etc/afpd/.gitignore b/etc/afpd/.gitignore index 5d0c03fa..4c401a6e 100644 --- a/etc/afpd/.gitignore +++ b/etc/afpd/.gitignore @@ -3,8 +3,10 @@ Makefile.in afpd fce hash -test_parse_mtab +spot +srp .deps .libs -.gitignore -afpd-afp_asp.o afpd-afp_config.o afpd-afp_dsi.o afpd-afp_options.o afpd-afprun.o afpd-afp_util.o afpd-afs.o afpd-appl.o afpd-auth.o afpd-catsearch.o afpd-desktop.o afpd-directory.o afpd-enumerate.o afpd-extattrs.o afpd-filedir.o afpd-file.o afpd-fork.o afpd-gettok.o afpd-hash.o afpd-main.o afpd-mangle.o afpd-messages.o afpd-nfsquota.o afpd-ofork.o afpd-quota.o afpd-status.o afpd-switch.o afpd-uam.o afpd-uid.o afpd-unix.o afpd-volume.o hash-hash.o +*.o +*.la +*.lo \ No newline at end of file diff --git a/etc/afpd/Makefile.am b/etc/afpd/Makefile.am index 2e242d95..47b8e276 100644 --- a/etc/afpd/Makefile.am +++ b/etc/afpd/Makefile.am @@ -7,7 +7,7 @@ CLEANFILES = DISTCLEANFILES = sbin_PROGRAMS = afpd -noinst_PROGRAMS = hash fce +noinst_PROGRAMS = hash fce spot afpd_SOURCES = \ afp_avahi.c \ @@ -39,6 +39,8 @@ afpd_SOURCES = \ nfsquota.c \ ofork.c \ quota.c \ + spotlight.c \ + spotlight_marshalling.c \ status.c \ switch.c \ uam.c \ @@ -104,3 +106,7 @@ hash_CFLAGS = -DKAZLIB_TEST_MAIN -I$(top_srcdir)/include fce_SOURCES = fce_api.c fce_util.c fce_CFLAGS = -DFCE_TEST_MAIN -I$(top_srcdir)/include fce_LDADD = $(top_builddir)/libatalk/libatalk.la + +spot_SOURCES = spotlight.c spotlight_marshalling.c +spot_CFLAGS = -DSPOT_TEST_MAIN +spot_LDADD = $(top_builddir)/libatalk/libatalk.la diff --git a/etc/afpd/afp_dsi.c b/etc/afpd/afp_dsi.c index 5082ee70..63d2f286 100644 --- a/etc/afpd/afp_dsi.c +++ b/etc/afpd/afp_dsi.c @@ -39,6 +39,7 @@ #include #include #include +#include #include "switch.h" #include "auth.h" @@ -473,6 +474,10 @@ void afp_over_dsi(AFPObj *obj) int flag = 1; setsockopt(dsi->socket, SOL_TCP, TCP_NODELAY, &flag, sizeof(flag)); + /* Initialize Spotlight */ + if ((obj->options.flags & OPTION_SPOTLIGHT) && (obj->options.slmod_path)) + sl_mod_load(obj->options.slmod_path); + ipc_child_state(obj, DSI_RUNNING); /* get stuck here until the end */ diff --git a/etc/afpd/auth.c b/etc/afpd/auth.c index bec624dc..dcb36a2c 100644 --- a/etc/afpd/auth.c +++ b/etc/afpd/auth.c @@ -39,6 +39,7 @@ extern void afp_get_cmdline( int *ac, char ***av ); #include #include #include +#include #include #include "auth.h" @@ -184,7 +185,7 @@ static int set_auth_switch(const AFPObj *obj, int expired) case 31: uam_afpserver_action(AFP_SYNCDIR, UAM_AFPSERVER_POSTAUTH, afp_syncdir, NULL); uam_afpserver_action(AFP_SYNCFORK, UAM_AFPSERVER_POSTAUTH, afp_syncfork, NULL); - uam_afpserver_action(AFP_SPOTLIGHT_PRIVATE, UAM_AFPSERVER_POSTAUTH, afp_null_nolog, NULL); + uam_afpserver_action(AFP_SPOTLIGHT_PRIVATE, UAM_AFPSERVER_POSTAUTH, afp_spotlight_rpc, NULL); uam_afpserver_action(AFP_ENUMERATE_EXT2, UAM_AFPSERVER_POSTAUTH, afp_enumerate_ext2, NULL); case 30: diff --git a/etc/afpd/file.c b/etc/afpd/file.c index bd516087..307f6204 100644 --- a/etc/afpd/file.c +++ b/etc/afpd/file.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "directory.h" #include "dircache.h" @@ -755,9 +756,9 @@ createfile_iderr: ad_flush(&ad); ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF ); fce_register(FCE_FILE_CREATE, fullpathname(upath), NULL, fce_file); + sl_index_file(path); curdir->d_offcnt++; - setvoltime(obj, vol ); return (retvalue); diff --git a/etc/afpd/main.c b/etc/afpd/main.c index 141d420c..516adf16 100644 --- a/etc/afpd/main.c +++ b/etc/afpd/main.c @@ -320,7 +320,7 @@ int main(int ac, char **av) /* Initialize */ cnid_init(); - + /* watch atp, dsi sockets and ipc parent/child file descriptor. */ fd_set_listening_sockets(&obj); diff --git a/etc/afpd/spotlight-packet.bin b/etc/afpd/spotlight-packet.bin new file mode 100644 index 00000000..7ddff550 Binary files /dev/null and b/etc/afpd/spotlight-packet.bin differ diff --git a/etc/afpd/spotlight-packet2.bin b/etc/afpd/spotlight-packet2.bin new file mode 100644 index 00000000..aa3bb07c Binary files /dev/null and b/etc/afpd/spotlight-packet2.bin differ diff --git a/etc/afpd/spotlight.c b/etc/afpd/spotlight.c new file mode 100644 index 00000000..8ea0a459 --- /dev/null +++ b/etc/afpd/spotlight.c @@ -0,0 +1,601 @@ +/* + Copyright (c) 2012 Frank Lahm + + 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 cnid_comp_fn(const void *p1, const void *p2) +{ + const uint64_t *cnid1 = p1, *cnid2 = p2; + if (*cnid1 == *cnid2) + return 0; + if (*cnid1 < *cnid2) + return -1; + else + return 1; +} + +static int sl_createCNIDArray(slq_t *slq, const DALLOC_CTX *p) +{ + EC_INIT; + uint64_t *cnids = NULL; + + EC_NULL( cnids = talloc_array(slq, uint64_t, talloc_array_length(p)) ); + for (int i = 0; i < talloc_array_length(p); i++) + memcpy(&cnids[i], p->dd_talloc_array[i], sizeof(uint64_t)); + qsort(cnids, talloc_array_length(p), sizeof(uint64_t), cnid_comp_fn); + + slq->slq_cnids = cnids; + slq->slq_cnids_num = talloc_array_length(p); + +EC_CLEANUP: + if (ret != 0) { + if (cnids) + talloc_free(cnids); + } + EC_EXIT; +} + +static int sl_rpc_openQuery(AFPObj *obj, const DALLOC_CTX *query, DALLOC_CTX *reply, struct vol *v) +{ + EC_INIT; + char *sl_query; + uint64_t *uint64; + DALLOC_CTX *reqinfo; + sl_array_t *array; + sl_cnids_t *cnids; + slq_t *slq = NULL; + + /* Allocate and initialize query object */ + slq = talloc_zero(sl_ctx, slq_t); + slq->slq_state = SLQ_STATE_NEW; + slq->slq_obj = obj; + slq->slq_vol = v; + 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); + if ((cnids = dalloc_value_for_key(query, "DALLOC_CTX", 0, "DALLOC_CTX", 1, "kMDQueryItemArray"))) { + EC_ZERO_LOG( sl_createCNIDArray(slq, cnids->ca_cnids) ); + } + + LOG(log_maxdebug, logtype_sl, "sl_rpc_openQuery: requested attributes:"); + dd_dump(slq->slq_reqinfo, 0); + + (void)slq_add(slq); + + /* Run the query */ + EC_ZERO( sl_module_export->sl_mod_start_search(slq) ); + + array = talloc_zero(reply, sl_array_t); + uint64_t sl_res = ret == 0 ? 0 : UINT64_MAX; + dalloc_add_copy(array, &sl_res, uint64_t); + dalloc_add(reply, array, sl_array_t); + +EC_CLEANUP: + if (ret != 0) { + slq_error(slq); + } + EC_EXIT; +} + +static int sl_rpc_fetchQueryResultsForContext(const AFPObj *obj, const DALLOC_CTX *query, DALLOC_CTX *reply, const struct vol *v) +{ + EC_INIT; + slq_t *slq = NULL; + uint64_t *uint64, ctx1, ctx2; + + /* Context */ + EC_NULL_LOG (uint64 = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0, "uint64_t", 1) ); + ctx1 = *uint64; + EC_NULL_LOG (uint64 = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0, "uint64_t", 2) ); + ctx2 = *uint64; + + /* Get query for context */ + EC_NULL_LOG( slq = slq_for_ctx(ctx1, ctx2) ); + if (slq->slq_state != SLQ_STATE_RUNNING && slq->slq_state != SLQ_STATE_DONE) { + EC_FAIL_LOG("Spotlight: attempt to fetch results for query that isn't active"); + } + + /* Create and pass reply handle */ + EC_NULL( slq->slq_reply = talloc_zero(reply, sl_array_t) ); + + /* Fetch Tracker results*/ + EC_ZERO_LOG( sl_module_export->sl_mod_fetch_result(slq) ); + + dalloc_add(reply, slq->slq_reply, sl_array_t); + +EC_CLEANUP: + if (ret != 0) { + slq_error(slq); + } + EC_EXIT; +} + +static int sl_rpc_closeQueryForContext(const AFPObj *obj, const DALLOC_CTX *query, DALLOC_CTX *reply, const struct vol *v) +{ + EC_INIT; + slq_t *slq = NULL; + uint64_t *uint64, ctx1, ctx2; + sl_array_t *array; + + /* Context */ + EC_NULL_LOG (uint64 = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0, "uint64_t", 1) ); + ctx1 = *uint64; + EC_NULL_LOG (uint64 = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0, "uint64_t", 2) ); + ctx2 = *uint64; + + /* Get query for context and free it */ + EC_NULL_LOG( slq = slq_for_ctx(ctx1, ctx2) ); + if (slq->slq_state != SLQ_STATE_DONE) + LOG(log_warning, logtype_sl, "Closing active query"); + sl_module_export->sl_mod_end_search(slq); + slq_remove(slq); + talloc_free(slq); + slq = NULL; + + array = talloc_zero(reply, sl_array_t); + uint64_t sl_res = 0; + dalloc_add_copy(array, &sl_res, uint64_t); + dalloc_add(reply, array, sl_array_t); + +EC_CLEANUP: + if (ret != 0) { + slq_error(slq); + } + EC_EXIT; +} + +/************************************************************************************************** + * Spotlight module functions + **************************************************************************************************/ + +int sl_mod_load(const char *path) +{ + EC_INIT; + + sl_ctx = talloc_new(NULL); + + if ((sl_module = mod_open(path)) == NULL) { + LOG(log_error, logtype_sl, "Failed to load module \'%s\': %s", path, mod_error()); + EC_FAIL; + } + + if ((sl_module_export = mod_symbol(sl_module, "sl_mod")) == NULL) { + LOG(log_error, logtype_sl, "sl_mod_load(%s): mod_symbol error for symbol %s", path, "sl_mod"); + EC_FAIL; + } + + sl_module_export->sl_mod_init("test"); + +EC_CLEANUP: + EC_EXIT; +} + +/** + * Index a file + **/ +void sl_index_file(const char *path) +{ + if (sl_module_export && sl_module_export->sl_mod_index_file) + sl_module_export->sl_mod_index_file(path); +} + +/************************************************************************************************** + * AFP functions + **************************************************************************************************/ + +int afp_spotlight_rpc(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen) +{ + EC_INIT; + TALLOC_CTX *tmp_ctx = talloc_new(NULL); + uint16_t vid; + int cmd; + int endianess = SL_ENC_LITTLE_ENDIAN; + struct vol *vol; + DALLOC_CTX *query; + DALLOC_CTX *reply; + char *rpccmd; + int len; + + *rbuflen = 0; + + if (sl_module == NULL) + return AFPERR_NOOP; + + ibuf += 2; + ibuflen -= 2; + + vid = SVAL(ibuf, 0); + LOG(log_debug, logtype_sl, "afp_spotlight_rpc(vid: %" PRIu16 ")", vid); + + if ((vol = getvolbyvid(vid)) == NULL) { + LOG(log_error, logtype_sl, "afp_spotlight_rpc: bad volume id: %" PRIu16 ")", vid); + ret = AFPERR_ACCESS; + goto EC_CLEANUP; + } + + /* IVAL(ibuf, 2): unknown, always 0x00008004, some flags ? */ + + cmd = RIVAL(ibuf, 6); + LOG(log_debug, logtype_sl, "afp_spotlight_rpc(cmd: %d)", cmd); + + /* IVAL(ibuf, 10: unknown, always 0x00000000 */ + + switch (cmd) { + + case SPOTLIGHT_CMD_OPEN: + case SPOTLIGHT_CMD_OPEN2: + RSIVAL(rbuf, 0, ntohs(vid)); + RSIVAL(rbuf, 4, 0); + len = strlen(vol->v_path) + 1; + strncpy(rbuf + 8, vol->v_path, len); + *rbuflen += 8 + len; + break; + + case SPOTLIGHT_CMD_FLAGS: + RSIVAL(rbuf, 0, 0x0100006b); /* Whatever this value means... flags? Helios uses 0x1eefface */ + *rbuflen += 4; + break; + + case SPOTLIGHT_CMD_RPC: + EC_NULL( query = talloc_zero(tmp_ctx, DALLOC_CTX) ); + EC_NULL( reply = talloc_zero(tmp_ctx, DALLOC_CTX) ); + EC_NEG1_LOG( sl_unpack(query, ibuf + 22) ); + + LOG(log_debug, logtype_sl, "afp_spotlight_rpc: Request dump:"); + dd_dump(query, 0); + + EC_NULL_LOG( rpccmd = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0, "char *", 0) ); + + if (STRCMP(rpccmd, ==, "fetchPropertiesForContext:")) { + EC_ZERO_LOG( sl_rpc_fetchPropertiesForContext(obj, query, reply, vol) ); + } else if (STRCMP(rpccmd, ==, "openQueryWithParams:forContext:")) { + EC_ZERO_LOG( sl_rpc_openQuery(obj, query, reply, vol) ); + } else if (STRCMP(rpccmd, ==, "fetchQueryResultsForContext:")) { + EC_ZERO_LOG( sl_rpc_fetchQueryResultsForContext(obj, query, reply, vol) ); + } else if (STRCMP(rpccmd, ==, "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 diff --git a/etc/afpd/spotlight_marshalling.c b/etc/afpd/spotlight_marshalling.c new file mode 100644 index 00000000..f156656a --- /dev/null +++ b/etc/afpd/spotlight_marshalling.c @@ -0,0 +1,812 @@ +/* + Copyright (c) 2012 Frank Lahm + + 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_SLQ_DAT (DSI_DATASIZ - 64) +#define MAX_SLQ_TOC 8192 + +/************************************************************************************************** + * RPC data marshalling and unmarshalling + **************************************************************************************************/ + +/* Spotlight epoch is UNIX epoch minus SPOTLIGHT_TIME_DELTA */ +#define SPOTLIGHT_TIME_DELTA INT64_C(280878921600U) + +#define SQ_TYPE_NULL 0x0000 +#define SQ_TYPE_COMPLEX 0x0200 +#define SQ_TYPE_INT64 0x8400 +#define SQ_TYPE_BOOL 0x0100 +#define SQ_TYPE_FLOAT 0x8500 +#define SQ_TYPE_DATA 0x0700 +#define SQ_TYPE_CNIDS 0x8700 +#define SQ_TYPE_UUID 0x0e00 +#define SQ_TYPE_DATE 0x8600 +#define SQ_TYPE_TOC 0x8800 + +#define SQ_CPX_TYPE_ARRAY 0x0a00 +#define SQ_CPX_TYPE_STRING 0x0c00 +#define SQ_CPX_TYPE_UTF16_STRING 0x1c00 +#define SQ_CPX_TYPE_DICT 0x0d00 +#define SQ_CPX_TYPE_CNIDS 0x1a00 +#define SQ_CPX_TYPE_FILEMETA 0x1b00 + +#define SUBQ_SAFETY_LIM 20 + +/* Forward declarations */ +static int sl_pack_loop(DALLOC_CTX *query, char *buf, int offset, char *toc_buf, int *toc_idx); +static int sl_unpack_loop(DALLOC_CTX *query, const char *buf, int offset, uint count, const uint toc_offset, const uint encoding); + +/************************************************************************************************** + * Wrapper functions for the *VAL macros with bound checking + **************************************************************************************************/ + +static int sivalc(char *buf, off_t off, off_t maxoff, uint32_t val) +{ + if (off + sizeof(val) >= maxoff) { + LOG(log_error, logtype_sl, "sivalc: off: %zd, maxoff: %zd", off, maxoff); + return -1; + } + SIVAL(buf, off, val); + return 0; +} + +static int slvalc(char *buf, off_t off, off_t maxoff, uint64_t val) +{ + if (off + sizeof(val) >= maxoff) { + LOG(log_error, logtype_sl, "slvalc: off: %zd, maxoff: %zd", off, maxoff); + return -1; + } + SLVAL(buf, off, val); + return 0; +} + +/* +* Returns the UTF-16 string encoding, by checking the 2-byte byte order mark. +* If there is no byte order mark, -1 is returned. +*/ +static uint spotlight_get_utf16_string_encoding(const char *buf, int offset, int query_length, uint encoding) { + uint utf16_encoding; + + /* Assumed encoding in absence of a bom is little endian */ + utf16_encoding = SL_ENC_LITTLE_ENDIAN; + + if (query_length >= 2) { + uint8_t le_bom[] = {0xff, 0xfe}; + uint8_t be_bom[] = {0xfe, 0xff}; + if (memcmp(le_bom, buf + offset, sizeof(uint16_t)) == 0) + utf16_encoding = SL_ENC_LITTLE_ENDIAN | SL_ENC_UTF_16; + else if (memcmp(be_bom, buf + offset, sizeof(uint16_t)) == 0) + utf16_encoding = SL_ENC_BIG_ENDIAN | SL_ENC_UTF_16; + } + + return utf16_encoding; +} + +/************************************************************************************************** + * marshalling functions + **************************************************************************************************/ + +#define SL_OFFSET_DELTA 16 + +static uint64_t sl_pack_tag(uint16_t type, uint16_t size_or_count, uint32_t val) +{ + uint64_t tag = ((uint64_t)val << 32) | ((uint64_t)type << 16) | size_or_count; + return tag; +} + +static int sl_pack_float(double d, char *buf, int offset) +{ + EC_INIT; + + union { + double d; + uint64_t w; + } ieee_fp_union; + + EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_FLOAT, 2, 1)) ); + EC_ZERO( slvalc(buf, offset + 8, MAX_SLQ_DAT, ieee_fp_union.w) ); + +EC_CLEANUP: + if (ret != 0) + return -1; + return offset + 2 * sizeof(uint64_t); +} + +static int sl_pack_uint64(uint64_t u, char *buf, int offset) +{ + EC_INIT; + + EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_INT64, 2, 1)) ); + EC_ZERO( slvalc(buf, offset + 8, MAX_SLQ_DAT, u) ); + +EC_CLEANUP: + if (ret != 0) + return -1; + return offset + 2 * sizeof(uint64_t); +} + +static int sl_pack_bool(sl_bool_t bl, char *buf, int offset) +{ + EC_INIT; + + EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_BOOL, 1, bl ? 1 : 0)) ); + +EC_CLEANUP: + if (ret != 0) + return -1; + return offset + sizeof(uint64_t); +} + +static int sl_pack_nil(char *buf, int offset) +{ + EC_INIT; + + EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_NULL, 1, 1)) ); + +EC_CLEANUP: + if (ret != 0) + return -1; + return offset + sizeof(uint64_t); +} + +static int sl_pack_date(sl_time_t t, char *buf, int offset) +{ + EC_INIT; + uint64_t data = 0; + + data = (t.tv_sec + SPOTLIGHT_TIME_DELTA) << 24; + + EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_DATE, 2, 1)) ); + EC_ZERO( slvalc(buf, offset + 8, MAX_SLQ_DAT, data) ); + +EC_CLEANUP: + if (ret != 0) + return -1; + return offset + 2 * sizeof(uint64_t); +} + +static int sl_pack_uuid(sl_uuid_t *uuid, char *buf, int offset) +{ + EC_INIT; + + EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_UUID, 3, 1)) ); + if (offset + 8 + 16 >= MAX_SLQ_DAT) + EC_FAIL; + memcpy(buf + offset + 8, uuid, 16); + +EC_CLEANUP: + if (ret != 0) + return -1; + return offset + sizeof(uint64_t) + 16; +} + +static int sl_pack_CNID(sl_cnids_t *cnids, char *buf, int offset, char *toc_buf, int *toc_idx) +{ + EC_INIT; + int off = 0, len; + int cnid_count = talloc_array_length(cnids->ca_cnids->dd_talloc_array); + uint64_t id; + + EC_ZERO( slvalc(toc_buf, *toc_idx * 8, MAX_SLQ_TOC, sl_pack_tag(SQ_CPX_TYPE_CNIDS, (offset + SL_OFFSET_DELTA) / 8, 0)) ); + EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1)) ); + *toc_idx += 1; + offset += 8; + + len = cnid_count + 1; + if (cnid_count > 0) + len ++; + + EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_CNIDS, len, 8 /* unknown meaning, but always 8 */)) ); + offset += 8; + + if (cnid_count > 0) { + EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(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, *tmp; + int qlen, used_in_last_block, slen; + sl_array_t *sl_array; + sl_dict_t *sl_dict; + sl_filemeta_t *sl_fm; + + switch (cpx_query_type) { + case SQ_CPX_TYPE_ARRAY: + sl_array = talloc_zero(query, sl_array_t); + EC_NEG1_LOG( roffset = sl_unpack_loop(sl_array, buf, offset, cpx_query_count, toc_offset, encoding) ); + dalloc_add(query, sl_array, sl_array_t); + break; + + case SQ_CPX_TYPE_DICT: + sl_dict = talloc_zero(query, sl_dict_t); + EC_NEG1_LOG( roffset = sl_unpack_loop(sl_dict, buf, offset, cpx_query_count, toc_offset, encoding) ); + dalloc_add(query, sl_dict, sl_dict_t); + break; + + case SQ_CPX_TYPE_STRING: + case SQ_CPX_TYPE_UTF16_STRING: + query_data64 = sl_unpack_uint64(buf, offset, encoding); + qlen = (query_data64 & 0xffff) * 8; + used_in_last_block = query_data64 >> 32; + slen = qlen - 16 + used_in_last_block; + + if (cpx_query_type == SQ_CPX_TYPE_STRING) { + p = dalloc_strndup(query, buf + offset + 8, slen); + } else { + unicode_encoding = spotlight_get_utf16_string_encoding(buf, offset + 8, slen, encoding); + mark_exists = (unicode_encoding & SL_ENC_UTF_16); + if (unicode_encoding & SL_ENC_BIG_ENDIAN) + EC_FAIL_LOG("Unsupported big endian UTF16 string"); + slen -= mark_exists ? 2 : 0; + EC_NEG1( convert_string_allocate(CH_UCS2, + CH_UTF8, + buf + offset + (mark_exists ? 10 : 8), + slen, + &tmp) ); + p = dalloc_strndup(query, tmp, strlen(tmp)); + free(tmp); + } + + dalloc_add(query, p, char *); + roffset += qlen; + break; + + case SQ_CPX_TYPE_FILEMETA: + query_data64 = sl_unpack_uint64(buf, offset, encoding); + qlen = (query_data64 & 0xffff) * 8; + if (qlen <= 8) { + EC_FAIL_LOG("SQ_CPX_TYPE_FILEMETA: query_length <= 8: %d", qlen); + } else { + sl_fm = talloc_zero(query, sl_filemeta_t); + EC_NEG1_LOG( sl_unpack(sl_fm, buf + offset + 8) ); + dalloc_add(query, sl_fm, sl_filemeta_t); + } + roffset += qlen; + break; + + case SQ_CPX_TYPE_CNIDS: + query_data64 = sl_unpack_uint64(buf, offset, encoding); + qlen = (query_data64 & 0xffff) * 8; + EC_NEG1_LOG( sl_unpack_CNID(query, buf, offset + 8, qlen, encoding) ); + roffset += qlen; + break; + + default: + EC_FAIL; + } + +EC_CLEANUP: + if (ret != 0) + roffset = -1; + return roffset; +} + +static int sl_unpack_loop(DALLOC_CTX *query, + const char *buf, + int offset, + uint count, + const uint toc_offset, + const uint encoding) +{ + EC_INIT; + int i, toc_index, query_length; + uint subcount; + uint64_t query_data64, query_type; + uint cpx_query_type, cpx_query_count; + sl_nil_t nil; + sl_bool_t b; + + while (count > 0 && (offset < toc_offset)) { + query_data64 = sl_unpack_uint64(buf, offset, encoding); + query_length = (query_data64 & 0xffff) * 8; + query_type = (query_data64 & 0xffff0000) >> 16; + if (query_length == 0) + EC_FAIL; + + switch (query_type) { + case SQ_TYPE_COMPLEX: + toc_index = (query_data64 >> 32) - 1; + query_data64 = sl_unpack_uint64(buf, toc_offset + toc_index * 8, encoding); + cpx_query_type = (query_data64 & 0xffff0000) >> 16; + cpx_query_count = query_data64 >> 32; + + EC_NEG1_LOG( offset = sl_unpack_cpx(query, buf, offset + 8, cpx_query_type, cpx_query_count, toc_offset, encoding)); + count--; + break; + case SQ_TYPE_NULL: + subcount = query_data64 >> 32; + if (subcount > 64) + EC_FAIL; + nil = 0; + for (i = 0; i < subcount; i++) + dalloc_add_copy(query, &nil, sl_nil_t); + offset += query_length; + count -= subcount; + break; + case SQ_TYPE_BOOL: + b = query_data64 >> 32; + dalloc_add_copy(query, &b, sl_bool_t); + offset += query_length; + count--; + break; + case SQ_TYPE_INT64: + EC_NEG1_LOG( subcount = sl_unpack_ints(query, buf, offset, encoding) ); + offset += query_length; + count -= subcount; + break; + case SQ_TYPE_UUID: + EC_NEG1_LOG( subcount = sl_unpack_uuid(query, buf, offset, encoding) ); + offset += query_length; + count -= subcount; + break; + case SQ_TYPE_FLOAT: + EC_NEG1_LOG( subcount = sl_unpack_floats(query, buf, offset, encoding) ); + offset += query_length; + count -= subcount; + break; + case SQ_TYPE_DATE: + EC_NEG1_LOG( subcount = sl_unpack_date(query, buf, offset, encoding) ); + offset += query_length; + count -= subcount; + break; + default: + EC_FAIL; + } + } + +EC_CLEANUP: + if (ret != 0) { + offset = -1; + } + return offset; +} + +/************************************************************************************************** + * Global functions for packing und unpacking + **************************************************************************************************/ + +int sl_pack(DALLOC_CTX *query, char *buf) +{ + EC_INIT; + char toc_buf[MAX_SLQ_TOC]; + int toc_index = 0; + int len = 0; + + memcpy(buf, "432130dm", 8); + EC_NEG1_LOG( len = sl_pack_loop(query, buf + 16, 0, toc_buf + 8, &toc_index) ); + EC_ZERO( sivalc(buf, 8, MAX_SLQ_DAT, len / 8 + 1 + toc_index + 1) ); + EC_ZERO( sivalc(buf, 12, MAX_SLQ_DAT, len / 8 + 1) ); + + EC_ZERO( slvalc(toc_buf, 0, MAX_SLQ_TOC, sl_pack_tag(SQ_TYPE_TOC, toc_index + 1, 0)) ); + if ((16 + len + ((toc_index + 1 ) * 8)) >= MAX_SLQ_DAT) + EC_FAIL; + memcpy(buf + 16 + len, toc_buf, (toc_index + 1 ) * 8); + len += 16 + (toc_index + 1 ) * 8; + +EC_CLEANUP: + if (ret != 0) + len = -1; + return len; +} + +int sl_unpack(DALLOC_CTX *query, const char *buf) +{ + EC_INIT; + int encoding, i, toc_entries; + uint64_t toc_offset, tquerylen, toc_entry; + + if (strncmp(buf, "md031234", 8) == 0) + encoding = SL_ENC_BIG_ENDIAN; + else + encoding = SL_ENC_LITTLE_ENDIAN; + + buf += 8; + + toc_offset = ((sl_unpack_uint64(buf, 0, encoding) >> 32) - 1 ) * 8; + if (toc_offset < 0 || (toc_offset > 65000)) { + EC_FAIL; + } + + buf += 8; + + toc_entries = (int)(sl_unpack_uint64(buf, toc_offset, encoding) & 0xffff); + + EC_NEG1( sl_unpack_loop(query, buf, 0, 1, toc_offset + 8, encoding) ); + +EC_CLEANUP: + EC_EXIT; +} diff --git a/etc/netatalk/Makefile.am b/etc/netatalk/Makefile.am index 06e5a8c3..6cf95eb6 100644 --- a/etc/netatalk/Makefile.am +++ b/etc/netatalk/Makefile.am @@ -7,6 +7,7 @@ sbin_PROGRAMS = netatalk netatalk_SOURCES = netatalk.c netatalk_CFLAGS = \ -D_PATH_CONFDIR=\"$(pkgconfdir)/\" \ + -D_PATH_STATEDIR='"$(localstatedir)/netatalk/"' \ -D_PATH_AFPD=\"$(sbindir)/afpd\" \ -D_PATH_CNID_METAD=\"$(sbindir)/cnid_metad\" diff --git a/etc/netatalk/netatalk.c b/etc/netatalk/netatalk.c index b3ab20a1..8c46e4f5 100644 --- a/etc/netatalk/netatalk.c +++ b/etc/netatalk/netatalk.c @@ -40,6 +40,8 @@ #include #include #include +#include +#include #include @@ -52,11 +54,50 @@ static void kill_childs(int sig, ...); /* static variables */ static AFPObj obj; -static pid_t afpd_pid = -1, cnid_metad_pid = -1; -static uint afpd_restarts, cnid_metad_restarts; +static pid_t afpd_pid = -1, cnid_metad_pid = -1, dbus_pid = -1, trackerd_pid = -1; +static uint afpd_restarts, cnid_metad_restarts, dbus_restarts, trackerd_restarts; static struct event_base *base; struct event *sigterm_ev, *sigquit_ev, *sigchld_ev, *timer_ev; static int in_shutdown; +static const char *dbus_path; +static char *trackerd_loglev; + +/****************************************************************** + * Misc stuff + ******************************************************************/ + +/* Set Tracker Miners to index all our volumes */ +static int set_sl_volumes(void) +{ + EC_INIT; + const struct vol *volumes, *vol; + struct bstrList *vollist = bstrListCreate(); + bstring sep = bfromcstr(", "); + bstring volnamelist = NULL, cmd = NULL; + + EC_NULL_LOG( volumes = getvolumes() ); + + for (vol = volumes; vol; vol = vol->v_next) { + 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 @@ -113,21 +154,27 @@ static void sigterm_cb(evutil_socket_t fd, short what, void *arg) event_del(sigquit_ev); event_del(timer_ev); - kill_childs(SIGTERM, &afpd_pid, &cnid_metad_pid, NULL); +#ifdef HAVE_TRACKER_SPARQL + system("tracker-control -t"); +#endif + kill_childs(SIGTERM, &afpd_pid, &cnid_metad_pid, &dbus_pid, &trackerd_pid, NULL); } /* SIGQUIT callback */ static void sigquit_cb(evutil_socket_t fd, short what, void *arg) { LOG(log_note, logtype_afpd, "Exiting on SIGQUIT"); - kill_childs(SIGQUIT, &afpd_pid, &cnid_metad_pid, NULL); +#ifdef HAVE_TRACKER_SPARQL + system("tracker-control -t"); +#endif + kill_childs(SIGQUIT, &afpd_pid, &cnid_metad_pid, &dbus_pid, &trackerd_pid, NULL); } -/* SIGQUIT callback */ +/* SIGHUP callback */ static void sighup_cb(evutil_socket_t fd, short what, void *arg) { LOG(log_note, logtype_afpd, "Received SIGHUP, sending all processes signal to reload config"); - kill_childs(SIGHUP, &afpd_pid, &cnid_metad_pid, NULL); + kill_childs(SIGHUP, &afpd_pid, &cnid_metad_pid, &trackerd_pid, NULL); } /* SIGCHLD callback */ @@ -136,30 +183,32 @@ static void sigchld_cb(evutil_socket_t fd, short what, void *arg) int status; pid_t pid; - LOG(log_debug, logtype_afpd, "Got SIGCHLD event"); - while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { if (WIFEXITED(status)) { if (WEXITSTATUS(status)) - LOG(log_info, logtype_afpd, "child[%d]: exited %d", pid, WEXITSTATUS(status)); + LOG(log_info, logtype_default, "child[%d]: exited %d", pid, WEXITSTATUS(status)); else - LOG(log_info, logtype_afpd, "child[%d]: done", pid); + LOG(log_info, logtype_default, "child[%d]: done", pid); } else { if (WIFSIGNALED(status)) - LOG(log_info, logtype_afpd, "child[%d]: killed by signal %d", pid, WTERMSIG(status)); + LOG(log_info, logtype_default, "child[%d]: killed by signal %d", pid, WTERMSIG(status)); else - LOG(log_info, logtype_afpd, "child[%d]: died", pid); + LOG(log_info, logtype_default, "child[%d]: died", pid); } if (pid == afpd_pid) afpd_pid = -1; else if (pid == cnid_metad_pid) cnid_metad_pid = -1; + else if (pid == dbus_pid) + dbus_pid = -1; + else if (pid == trackerd_pid) + trackerd_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 && trackerd_pid == -1) event_base_loopbreak(base); } @@ -173,7 +222,7 @@ static void timer_cb(evutil_socket_t fd, short what, void *arg) afpd_restarts++; LOG(log_note, logtype_afpd, "Restarting 'afpd' (restarts: %u)", afpd_restarts); if ((afpd_pid = run_process(_PATH_AFPD, "-d", "-F", obj.options.configfile, NULL)) == -1) { - LOG(log_error, logtype_afpd, "Error starting 'afpd'"); + LOG(log_error, logtype_default, "Error starting 'afpd'"); } } @@ -181,9 +230,27 @@ static void timer_cb(evutil_socket_t fd, short what, void *arg) cnid_metad_restarts++; LOG(log_note, logtype_afpd, "Restarting 'cnid_metad' (restarts: %u)", cnid_metad_restarts); if ((cnid_metad_pid = run_process(_PATH_CNID_METAD, "-d", "-F", obj.options.configfile, NULL)) == -1) { - LOG(log_error, logtype_afpd, "Error starting 'cnid_metad'"); + LOG(log_error, logtype_default, "Error starting 'cnid_metad'"); + } + } + + 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); } } + +#ifdef HAVE_TRACKER_RDF + if (trackerd_pid == -1) { + trackerd_restarts++; + LOG(log_note, logtype_afpd, "Restarting 'trackerd' (restarts: %u)", trackerd_restarts); + if ((trackerd_pid = run_process(TRACKER_RDF_PREFIX "/bin/trackerd", trackerd_loglev, NULL)) == -1) { + LOG(log_error, logtype_default, "Error starting '%s'", "/usr/bin/trackerd"); + } + } +#endif } /****************************************************************** @@ -217,7 +284,8 @@ static void netatalk_exit(int ret) static pid_t run_process(const char *path, ...) { int ret, i = 0; - char *myargv[10]; +#define MYARVSIZE 64 + char *myargv[MYARVSIZE]; va_list args; pid_t pid; @@ -229,8 +297,10 @@ static pid_t run_process(const char *path, ...) if (pid == 0) { myargv[i++] = (char *)path; va_start(args, path); - while ((myargv[i++] = va_arg(args, char *)) != NULL) - ; + while (i < MYARVSIZE) { + if ((myargv[i++] = va_arg(args, char *)) == NULL) + break; + } va_end(args); ret = execv(path, myargv); @@ -285,6 +355,8 @@ int main(int argc, char **argv) if (afp_config_parse(&obj, "netatalk") != 0) netatalk_exit(EXITERR_CONF); + load_volumes(&obj); + event_set_log_callback(libevent_logmsg_cb); event_set_fatal_callback(netatalk_exit); @@ -326,15 +398,54 @@ int main(int argc, char **argv) sigdelset(&blocksigs, SIGHUP); sigprocmask(SIG_SETMASK, &blocksigs, NULL); +#ifdef HAVE_TRACKER + setenv("DBUS_SESSION_BUS_ADDRESS", "unix:path=/tmp/spotlight.ipc", 1); + setenv("XDG_DATA_HOME", _PATH_STATEDIR, 0); + setenv("XDG_CACHE_HOME", _PATH_STATEDIR, 0); + setenv("XDG_CONFIG_HOME", _PATH_CONFDIR, 0); +#endif + +#ifdef HAVE_TRACKER_RDF + /* This assumes Tracker 0.6 with RDF is only used on Solaris and derived platforms */ + dbus_path = iniparser_getstring(obj.iniconfig, INISEC_GLOBAL, "dbus daemon path", "/usr/lib/dbus-daemon"); +#else + dbus_path = iniparser_getstring(obj.iniconfig, INISEC_GLOBAL, "dbus daemon path", "/bin/dbus-daemon"); +#endif + 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); + +#ifdef HAVE_TRACKER_SPARQL + set_sl_volumes(); + system(TRACKER_PREFIX "/bin/tracker-control -s"); +#endif +#ifdef HAVE_TRACKER_RDF + if (asprintf(&trackerd_loglev, "--verbosity=%d", obj.options.tracker_loglevel) == -1) + netatalk_exit(EXITERR_CONF); + if ((trackerd_pid = run_process(TRACKER_RDF_PREFIX "/bin/trackerd", trackerd_loglev, NULL)) == -1) { + LOG(log_error, logtype_default, "Error starting '%s'", TRACKER_RDF_PREFIX "/bin/trackerd"); + netatalk_exit(EXITERR_CONF); + } +#endif + /* run the event loop */ ret = event_base_dispatch(base); - if (afpd_pid != -1 || cnid_metad_pid != -1) { + if (afpd_pid != -1 || cnid_metad_pid != -1 || dbus_pid != -1 || trackerd_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"); + if (trackerd_pid != -1) + LOG(log_error, logtype_afpd, "trackerd still running, killing it"); + kill_childs(SIGKILL, &afpd_pid, &cnid_metad_pid, &dbus_pid, &trackerd_pid, NULL); } LOG(log_note, logtype_afpd, "Netatalk AFP server exiting"); diff --git a/etc/spotlight/.gitignore b/etc/spotlight/.gitignore new file mode 100644 index 00000000..4babbdf7 --- /dev/null +++ b/etc/spotlight/.gitignore @@ -0,0 +1,10 @@ +Makefile +Makefile.in +spot +stp +srp +.deps +.libs +*.o +*.la +*.lo \ No newline at end of file diff --git a/etc/spotlight/Makefile.am b/etc/spotlight/Makefile.am new file mode 100644 index 00000000..e0210b63 --- /dev/null +++ b/etc/spotlight/Makefile.am @@ -0,0 +1,56 @@ +# Makefile.am for etc/spotlight/ + +pkgconfdir = @PKGCONFDIR@ +moduledir = @UAMS_PATH@ +module_LTLIBRARIES = +noinst_PROGRAMS = +noinst_HEADERS = slmod_sparql_map.h slmod_rdf_map.h +BUILT_SOURCES = + +AM_YFLAGS = -d + +if HAVE_TRACKER_SPARQL +BUILT_SOURCES += slmod_sparql_parser.h +noinst_PROGRAMS += srp +module_LTLIBRARIES += slmod_sparql.la + +slmod_sparql_la_SOURCES = \ + slmod_sparql.c \ + slmod_sparql_map.c \ + slmod_sparql_parser.y \ + spotlight_rawquery_lexer.l + +slmod_sparql_la_CFLAGS = -DDBUS_API_SUBJECT_TO_CHANGE @TRACKER_CFLAGS@ @TRACKER_MINER_CFLAGS@ +slmod_sparql_la_LDFLAGS = -module -avoid-version @TRACKER_LIBS@ @TRACKER_MINER_LIBS@ + +srp_SOURCES = \ + slmod_sparql_map.c \ + slmod_sparql_parser.y \ + spotlight_rawquery_lexer.l + +srp_CFLAGS = -DMAIN -I$(top_srcdir)/include @TRACKER_CFLAGS@ +srp_LDADD = $(top_builddir)/libatalk/libatalk.la +endif + +if HAVE_TRACKER_RDF +BUILT_SOURCES += slmod_rdf_parser.h +noinst_PROGRAMS += stp +module_LTLIBRARIES += slmod_rdf.la + +slmod_rdf_la_SOURCES = \ + slmod_rdf.c \ + slmod_rdf_map.c \ + slmod_rdf_parser.y \ + spotlight_rawquery_lexer.l + +slmod_rdf_la_CFLAGS = @TRACKER_CFLAGS@ +slmod_rdf_la_LDFLAGS = -module -avoid-version @TRACKER_LIBS@ + +stp_SOURCES = \ + slmod_rdf_map.c \ + slmod_rdf_parser.y \ + spotlight_rawquery_lexer.l +stp_CFLAGS = -DMAIN @TRACKER_CFLAGS@ +stp_LDADD = $(top_builddir)/libatalk/libatalk.la @TRACKER_LIBS@ + +endif \ No newline at end of file diff --git a/etc/spotlight/slmod_rdf.c b/etc/spotlight/slmod_rdf.c new file mode 100644 index 00000000..69855fe0 --- /dev/null +++ b/etc/spotlight/slmod_rdf.c @@ -0,0 +1,280 @@ +/* + Copyright (c) 2012 Frank Lahm + + 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 +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "slmod_rdf_parser.h" + +#define MAX_SL_RESULTS 20 + +TrackerClient *client; + +static int sl_mod_init(void *p) +{ + EC_INIT; + GError *error = NULL; + const char *msg = p; + + LOG(log_info, logtype_sl, "Initializing Tracker 0.6 RDF Spotlight module"); + + g_type_init(); + setenv("DBUS_SESSION_BUS_ADDRESS", "unix:path=/tmp/spotlight.ipc", 1); + + become_root(); + client = tracker_connect(FALSE); + unbecome_root(); + + if (!client) { + LOG(log_error, logtype_sl, "Failed connecting to Tracker"); + EC_FAIL; + } + +EC_CLEANUP: + EC_EXIT; +} + + +static int sl_mod_start_search(void *p) +{ + EC_INIT; + slq_t *slq = p; + GError *error = NULL; + + EC_ZERO_LOG( map_spotlight_to_rdf_query(slq) ); + + LOG(log_debug, logtype_sl, "sl_mod_start_search: Tracker service: %s, FTS: %s, RDF query:\n%s", + tracker_type_to_service_name(slq->slq_service), + slq->slq_fts, + slq->slq_trackerquery ? slq->slq_trackerquery : "false"); + + if (slq->slq_trackerquery) + slq->slq_state = SLQ_STATE_RUNNING; + else + slq->slq_state = SLQ_STATE_DONE; + +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 cnid_cmp_fn(const void *p1, const void *p2) +{ + const uint64_t *cnid1 = p1, *cnid2 = p2; + if (*cnid1 == *cnid2) + return 0; + if (*cnid1 < *cnid2) + return -1; + else + return 1; +} + +static int sl_mod_fetch_result(void *p) +{ + EC_INIT; + slq_t *slq = p; + GError *error = NULL; + int i = 0; + cnid_t did, id; + sl_cnids_t *cnids; + sl_filemeta_t *fm; + sl_array_t *fm_array; + sl_nil_t nil; + uint64_t uint64; + gboolean qres, firstmatch = true; + GPtrArray *array = NULL; + + /* 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"); + + if (slq->slq_state == SLQ_STATE_RUNNING) { + /* Run the query */ + LOG(log_debug, logtype_sl, "sl_mod_fetch_result: calling tracker"); + become_root(); + array = tracker_search_query(client, + time(NULL), + slq->slq_service, + NULL, /* Fields */ + slq->slq_fts, /* FTS search test */ + NULL, /* Keywords */ + slq->slq_trackerquery, + slq->slq_offset, + MAX_SL_RESULTS, + FALSE, /* Sort by service */ + NULL, + FALSE, + &error); + unbecome_root(); + + if (error) { + slq->slq_state = SLQ_STATE_DONE; + LOG(log_error, logtype_sl, "Couldn't query Tracker: '%s'", error->message); + g_clear_error(&error); + EC_FAIL; + } + + if (!array) { + slq->slq_state = SLQ_STATE_DONE; + LOG(log_debug, logtype_sl, "sl_mod_fetch_result: no results found"); + EC_EXIT_STATUS(0); + } + + while (i < array->len) { + char **resmeta = g_ptr_array_index(array, i); + char *respath = resmeta[0]; + LOG(log_debug, logtype_sl, "sl_mod_fetch_result: result %d: %s", slq->slq_offset, respath); + + 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; + } + + struct stat st; + if (stat(respath, &st) != 0) { + if (errno == ENOENT) + tracker_files_delete(client, respath, NULL); + goto loop_continue; + } + + if ((id = cnid_for_path(slq->slq_vol->v_cdb, slq->slq_vol->v_path, respath, &did)) == CNID_INVALID) { + LOG(log_error, logtype_sl, "sl_mod_fetch_result: cnid_for_path error: %s", respath); + goto loop_continue; + } + LOG(log_debug, logtype_sl, "Result %d: CNID: %" PRIu32 ", path: \"%s\"", i, ntohl(id), respath); + + uint64 = ntohl(id); + if (slq->slq_cnids) { + if (!bsearch(&uint64, slq->slq_cnids, slq->slq_cnids_num, sizeof(uint64_t), cnid_cmp_fn)) + goto loop_continue; + } + + dalloc_add_copy(cnids->ca_cnids, &uint64, uint64_t); + add_filemeta(slq->slq_reqinfo, fm_array, id, respath); + + loop_continue: + i++; + slq->slq_offset++; + } + + g_ptr_array_free(array, TRUE); + array = NULL; + + 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 (array) + g_ptr_array_free(array, TRUE); + EC_EXIT; +} + +/* Free ressources allocated in this module */ +static int sl_mod_close_query(void *p) +{ + EC_INIT; + slq_t *slq = p; + +EC_CLEANUP: + EC_EXIT; +} + +static int sl_mod_error(void *p) +{ + EC_INIT; + slq_t *slq = p; + + if (!slq) + goto EC_CLEANUP; + +EC_CLEANUP: + EC_EXIT; +} + +static int sl_mod_index_file(const void *p) +{ + return 0; +} + +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 +}; diff --git a/etc/spotlight/slmod_rdf_map.c b/etc/spotlight/slmod_rdf_map.c new file mode 100644 index 00000000..c3abd8c6 --- /dev/null +++ b/etc/spotlight/slmod_rdf_map.c @@ -0,0 +1,100 @@ +/* + Copyright (c) 2012 Frank Lahm + + 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 + +#include "slmod_rdf_map.h" + +#define NOTSUPPORTED NULL +#define SPECIAL NULL + +struct spotlight_rdf_map spotlight_rdf_map[] = { + /* srm_spotlight_attr srm_type, srm_rdf_attr */ + {"*", srmt_str, "File:Name"}, + {"kMDItemTextContent", srmt_fts, ""}, + + /* Filesystem metadata */ + {"kMDItemFSLabel", srmt_num, NOTSUPPORTED}, + {"kMDItemDisplayName", srmt_str, "File:Name"}, + {"kMDItemFSName", srmt_str, "File:Name"}, + {"kMDItemFSContentChangeDate", srmt_date, "File:Modified"}, + + /* Common metadata */ + {"kMDItemContentCreationDate", srmt_date, "Doc:Created"}, + {"kMDItemContentModificationDate", srmt_date, "File:Modified"}, + {"kMDItemAttributeChangeDate", srmt_date, "File:Modified"}, + {"kMDItemAuthors", srmt_str, "Doc:Author"}, + {"kMDItemCopyright", srmt_str, "File:Copyright"}, + {"kMDItemCountry", srmt_str, "Image:Country"}, + {"kMDItemCreator", srmt_str, "DC:Creator"}, + {"kMDItemDurationSeconds", srmt_num, "Audio:Duration"}, + {"kMDItemNumberOfPages", srmt_num, "Doc:PageCount"}, + {"kMDItemTitle", srmt_str, "DC:Title"}, + {"_kMDItemGroupId", srmt_type, "File:Mime"}, + {"kMDItemContentTypeTree", srmt_type, "File:Mime"}, + + /* Image metadata */ + {"kMDItemPixelWidth", srmt_num, "Image:Width"}, + {"kMDItemPixelHeight", srmt_num, "Image:Height"}, + {"kMDItemColorSpace", srmt_str, NOTSUPPORTED}, + {"kMDItemBitsPerSample", srmt_num, NOTSUPPORTED}, + {"kMDItemFocalLength", srmt_num, "Image:FocalLength"}, /* RDF: float */ + {"kMDItemISOSpeed", srmt_num, "Image:ISOSpeed"}, + {"kMDItemOrientation", srmt_bool, "Image:Orientation"}, + {"kMDItemResolutionWidthDPI", srmt_num, NOTSUPPORTED}, + {"kMDItemResolutionHeightDPI", srmt_num, NOTSUPPORTED}, + {"kMDItemExposureTimeSeconds", srmt_num, "Image:ExposureTime"}, /* RDF: fload */ + + /* Audio metadata */ + {"kMDItemComposer", srmt_str, NOTSUPPORTED}, + {"kMDItemMusicalGenre", srmt_str, "Audio:Genre"}, + + {NULL, srmt_str, NULL} +}; + +struct MDTypeMap MDTypeMap[] = { + {"1", "equals", "message/rfc822"}, + {"2", "equals", "text/x-vcard"}, + {"3", NOTSUPPORTED, NOTSUPPORTED}, /* PrefPane */ + {"4", NOTSUPPORTED, NOTSUPPORTED}, /* Font. There's no single mime type to match all font formats, ugh! */ + {"5", NOTSUPPORTED, NOTSUPPORTED}, /* Bookmark */ + {"6", "equals", "text/x-vcard"}, + {"7", "startsWith", "video"}, + {"8", "equals", "application/x-executable"}, + {"9", "equals", "Folder"}, + {"10", "startsWith", "audio"}, + {"11", "equals", "application/pdf"}, + {"12", NOTSUPPORTED, NOTSUPPORTED}, /* Presentation */ + {"13", "startsWith", "image"}, + {"public.jpeg", "equals", "image/jpeg"}, + {"public.tiff", "equals", "image/tiff"}, + {"com.compuserve.gif", "equals", "image/gif"}, + {"public.png", "equals", "image/png"}, + {"com.microsoft.bmp", "equals", "image/bmp"}, + {"public.content", "inSet", "application/msword,application/pdf,application/vnd.ms-excel,application/vnd.oasis.opendocument.text,application/vnd.sun.xml.writer"}, + {"public.mp3", "equals", "audio/mpeg"}, + {"public.mpeg-4-audio", "equals", "audio/x-aac"}, + {"com.apple.application", NOTSUPPORTED, NOTSUPPORTED}, + {"public.text", "startsWith", "text"}, + {"public.plain-text", "equals", "text/plain"}, + {"public.rtf", "equals", "text/rtf"}, + {"public.html", "equals", "text/html"}, + {"public.xml", "equals", "text/xml"}, + {"public.source-code", NOTSUPPORTED, NOTSUPPORTED}, + {NULL, NULL} +}; diff --git a/etc/spotlight/slmod_rdf_map.h b/etc/spotlight/slmod_rdf_map.h new file mode 100644 index 00000000..4bfc78a6 --- /dev/null +++ b/etc/spotlight/slmod_rdf_map.h @@ -0,0 +1,46 @@ +/* + Copyright (c) 2012 Frank Lahm + + 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 SLMOD_RDF_MAP_H +#define SLMOD_RDF_MAP_H + +/* Spotlight -> RDF mapping (srm) */ +enum srm_type { + srmt_bool, /* a boolean value */ + srmt_num, /* a numeric value */ + srmt_str, /* a string value */ + srmt_fts, /* a string value for full text search */ + srmt_date, /* date values are handled in a special map function map_daterange() */ + srmt_type /* kMDItemContentType, requires special mapping */ +}; + +struct spotlight_rdf_map { + const char *srm_spotlight_attr; + enum srm_type srm_type; + const char *srm_rdf_attr; +}; + +struct MDTypeMap { + const char *mdtm_value; /* Value of '_kMDItemGroupId' or 'kMDItemContentTypeTree' */ + const char *mdtm_rdfop; /* RDF query operator */ + const char *mdtm_type; /* MIME type */ +}; + +extern struct spotlight_rdf_map spotlight_rdf_map[]; +extern struct MDTypeMap MDTypeMap[]; +#endif diff --git a/etc/spotlight/slmod_rdf_parser.c b/etc/spotlight/slmod_rdf_parser.c new file mode 100644 index 00000000..0256f287 --- /dev/null +++ b/etc/spotlight/slmod_rdf_parser.c @@ -0,0 +1,2079 @@ +/* A Bison parser, made by GNU Bison 2.7. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2012 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "2.7" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + + + +/* Copy the first part of user declarations. */ +/* Line 371 of yacc.c */ +#line 1 "slmod_rdf_parser.y" + + #include + + #include + #include + #include + #include + + #include + #include + + #include + #include + #include + #include + + #include "slmod_rdf_map.h" + + struct yy_buffer_state; + typedef struct yy_buffer_state *YY_BUFFER_STATE; + extern int yylex (void); + extern void yyerror (char const *); + extern void *yyterminate(void); + extern YY_BUFFER_STATE yy_scan_string( const char *str); + extern void yy_delete_buffer ( YY_BUFFER_STATE buffer ); + + /* forward declarations */ + static const char *map_expr(const char *attr, char op, const char *val); + static const char *map_daterange(const char *dateattr, time_t date1, time_t date2); + static time_t isodate2unix(const char *s); + + /* global vars, eg needed by the lexer */ + slq_t *srp_slq; + + /* local vars */ + static gchar *srp_result; + static gchar *srp_fts; + +/* Line 371 of yacc.c */ +#line 107 "slmod_rdf_parser.c" + +# ifndef YY_NULL +# if defined __cplusplus && 201103L <= __cplusplus +# define YY_NULL nullptr +# else +# define YY_NULL 0 +# endif +# endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 1 +#endif + +/* In a future release of Bison, this section will be replaced + by #include "y.tab.h". */ +#ifndef YY_YY_Y_TAB_H_INCLUDED +# define YY_YY_Y_TAB_H_INCLUDED +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int yydebug; +#endif + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + WORD = 258, + BOOL = 259, + FUNC_INRANGE = 260, + DATE_ISO = 261, + OBRACE = 262, + CBRACE = 263, + EQUAL = 264, + UNEQUAL = 265, + GT = 266, + LT = 267, + COMMA = 268, + QUOTE = 269, + AND = 270, + OR = 271 + }; +#endif +/* Tokens. */ +#define WORD 258 +#define BOOL 259 +#define FUNC_INRANGE 260 +#define DATE_ISO 261 +#define OBRACE 262 +#define CBRACE 263 +#define EQUAL 264 +#define UNEQUAL 265 +#define GT 266 +#define LT 267 +#define COMMA 268 +#define QUOTE 269 +#define AND 270 +#define OR 271 + + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +{ +/* Line 387 of yacc.c */ +#line 46 "slmod_rdf_parser.y" + + int ival; + const char *sval; + bool bval; + time_t tval; + + +/* Line 387 of yacc.c */ +#line 190 "slmod_rdf_parser.c" +} YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +#endif + +extern YYSTYPE yylval; + +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (void); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ +/* "%code provides" blocks. */ +/* Line 387 of yacc.c */ +#line 40 "slmod_rdf_parser.y" + + #define SPRAW_TIME_OFFSET 978307200 + extern int map_spotlight_to_rdf_query(slq_t *slq); + extern slq_t *srp_slq; + + +/* Line 387 of yacc.c */ +#line 222 "slmod_rdf_parser.c" + +#endif /* !YY_YY_Y_TAB_H_INCLUDED */ + +/* Copy the second part of user declarations. */ + +/* Line 390 of yacc.c */ +#line 229 "slmod_rdf_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 /* 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 /* 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 /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* 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 /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss_alloc; + YYSTYPE yyvs_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (YYID (0)) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (YYID (0)) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 2 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 51 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 17 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 7 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 21 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 49 + +/* 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, 13, 15, 19, + 23, 27, 33, 39, 45, 51, 58, 65, 72, 79, + 88, 93 +}; + +/* 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, -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, 68, 68, 70, 74, 91, 97, 98, 99, 100, + 101, 126, 127, 128, 129, 130, 131, 132, 133, 137, + 141, 142 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || 1 +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "WORD", "BOOL", "FUNC_INRANGE", + "DATE_ISO", "OBRACE", "CBRACE", "EQUAL", "UNEQUAL", "GT", "LT", "COMMA", + "QUOTE", "AND", "OR", "$accept", "input", "line", "expr", "match", + "function", "date", YY_NULL +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 17, 18, 18, 19, 20, 20, 20, 20, 20, + 20, 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, 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, 6, + 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 8, 9, 10, 0, 0, 0, + 0, 0, 11, 12, 14, 13, 21, 0, 0, 15, + 16, 18, 17, 0, 0, 0, 0, 20, 19 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + -1, 1, 7, 8, 9, 10, 38 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -8 +static const yytype_int8 yypact[] = +{ + -8, 10, -8, 9, -8, -2, -1, -8, 8, -8, + -8, 2, 12, 13, 14, 4, -7, -1, -1, 26, + 27, 28, 29, 20, -8, 18, -8, 21, 22, 23, + 24, 19, 36, 37, 38, 39, -8, 40, 30, -8, + -8, -8, -8, 41, 19, 42, 43, -8, -8 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -8, -8, -8, -6, -8, -8, 1 +}; + +/* 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, 24, 3, 4, 5, 15, 6, 23, 17, 18, + 2, 25, 26, 3, 4, 5, 19, 6, 11, 12, + 13, 14, 36, 17, 18, 37, 20, 21, 22, 27, + 28, 29, 30, 31, 18, 32, 33, 34, 35, 39, + 40, 41, 42, 44, 45, 46, 0, 43, 0, 0, + 47, 48 +}; + +#define yypact_value_is_default(Yystate) \ + (!!((Yystate) == (-8))) + +#define yytable_value_is_error(Yytable_value) \ + YYID (0) + +static const yytype_int8 yycheck[] = +{ + 6, 8, 3, 4, 5, 7, 7, 3, 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, 13, 16, 14, 14, 14, 14, 3, + 3, 3, 3, 13, 3, 44, -1, 7, -1, -1, + 8, 8 +}; + +/* 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, 14, + 14, 14, 14, 3, 8, 20, 20, 3, 3, 3, + 3, 13, 14, 14, 14, 14, 3, 6, 23, 3, + 3, 3, 3, 7, 13, 3, 23, 8, 8 +}; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. However, + YYFAIL appears to be in use. Nevertheless, it is formally deprecated + in Bison 2.4.2's NEWS entry, where a plan to phase it out is + discussed. */ + +#define YYFAIL goto yyerrlab +#if defined YYFAIL + /* This is here to suppress warnings from the GCC cpp's + -Wunused-macros. Normally we don't worry about that warning, but + some users do, and we want to make it easy for users to remove + YYFAIL uses, which will produce warnings from Bison 2.5. */ +#endif + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (YYID (0)) + +/* Error token number */ +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* This macro is provided for backward compatibility. */ +#ifndef YY_LOCATION_PRINT +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +#endif + + +/* YYLEX -- calling `yylex' with the right arguments. */ +#ifdef YYLEX_PARAM +# define YYLEX yylex (YYLEX_PARAM) +#else +# define YYLEX yylex () +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (YYID (0)) + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (YYID (0)) + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_value_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + FILE *yyo = yyoutput; + YYUSE (yyo); + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# else + YYUSE (yyoutput); +# endif + switch (yytype) + { + default: + break; + } +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (yytype < YYNTOKENS) + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + yy_symbol_value_print (yyoutput, yytype, yyvaluep); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) +#else +static void +yy_stack_print (yybottom, yytop) + yytype_int16 *yybottom; + yytype_int16 *yytop; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (YYID (0)) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_reduce_print (YYSTYPE *yyvsp, int yyrule) +#else +static void +yy_reduce_print (yyvsp, yyrule) + YYSTYPE *yyvsp; + int yyrule; +#endif +{ + int yynrhs = yyr2[yyrule]; + int yyi; + unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], + &(yyvsp[(yyi + 1) - (yynrhs)]) + ); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyvsp, Rule); \ +} while (YYID (0)) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static YYSIZE_T +yystrlen (const char *yystr) +#else +static YYSIZE_T +yystrlen (yystr) + const char *yystr; +#endif +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static char * +yystpcpy (char *yydest, const char *yysrc) +#else +static char * +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +#endif +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, + yytype_int16 *yyssp, int yytoken) +{ + YYSIZE_T yysize0 = yytnamerr (YY_NULL, yytname[yytoken]); + YYSIZE_T yysize = yysize0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULL; + /* Arguments of yyformat. */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Number of reported tokens (one for the "unexpected", one per + "expected"). */ + int yycount = 0; + + /* There are many possibilities here to consider: + - Assume YYFAIL is not used. It's too flawed to consider. See + + for details. YYERROR is fine as it does not invoke this + function. + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[*yyssp]; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + { + YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULL, yytname[yyx]); + if (! (yysize <= yysize1 + && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + } + } + } + + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } + + { + YYSIZE_T yysize1 = yysize + yystrlen (yyformat); + if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; + } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + yyp++; + yyformat++; + } + } + return 0; +} +#endif /* YYERROR_VERBOSE */ + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) +#else +static void +yydestruct (yymsg, yytype, yyvaluep) + const char *yymsg; + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + YYUSE (yyvaluep); + + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + switch (yytype) + { + + default: + break; + } +} + + + + +/* The lookahead symbol. */ +int yychar; + + +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +/* The semantic value of the lookahead symbol. */ +YYSTYPE yylval YY_INITIAL_VALUE(yyval_default); + +/* Number of syntax errors so far. */ +int yynerrs; + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void *YYPARSE_PARAM) +#else +int +yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +#endif +#else /* ! YYPARSE_PARAM */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void) +#else +int +yyparse () + +#endif +#endif +{ + int yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + `yyss': related to states. + `yyvs': related to semantic values. + + Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + YYSIZE_T yystacksize; + + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken = 0; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + yyssp = yyss = yyssa; + yyvsp = yyvs = yyvsa; + yystacksize = YYINITDEPTH; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token. */ + yychar = YYEMPTY; + + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 4: +/* Line 1792 of yacc.c */ +#line 74 "slmod_rdf_parser.y" + { + srp_result = talloc_asprintf(srp_slq, + "\n" + " \n" + " \n" + " \n" + " %s\n" + " \n" + " %s\n" + " \n" + "\n", + srp_slq->slq_vol->v_path, (yyvsp[(1) - (1)].sval)); + (yyval.sval) = srp_result; +} + break; + + case 5: +/* Line 1792 of yacc.c */ +#line 91 "slmod_rdf_parser.y" + { + if ((yyvsp[(1) - (1)].bval) == false) + YYACCEPT; + else + YYABORT; +} + break; + + case 6: +/* Line 1792 of yacc.c */ +#line 97 "slmod_rdf_parser.y" + {(yyval.sval) = (yyvsp[(1) - (1)].sval); if ((yyval.sval) == NULL) YYABORT;} + break; + + case 7: +/* Line 1792 of yacc.c */ +#line 98 "slmod_rdf_parser.y" + {(yyval.sval) = (yyvsp[(1) - (1)].sval);} + break; + + case 8: +/* Line 1792 of yacc.c */ +#line 99 "slmod_rdf_parser.y" + {(yyval.sval) = talloc_asprintf(srp_slq, "%s\n", (yyvsp[(2) - (3)].sval));} + break; + + case 9: +/* Line 1792 of yacc.c */ +#line 100 "slmod_rdf_parser.y" + {(yyval.sval) = talloc_asprintf(srp_slq, "\n%s\n%s\n\n", (yyvsp[(1) - (3)].sval), (yyvsp[(3) - (3)].sval));} + break; + + case 10: +/* Line 1792 of yacc.c */ +#line 101 "slmod_rdf_parser.y" + { + if (strcmp((yyvsp[(1) - (3)].sval), "") == 0 || strcmp((yyvsp[(3) - (3)].sval), "") == 0) { + /* + * The default Spotlight search term issued by the Finder (10.8) is: + * '* == "searchterm" || kMDItemTextContent == "searchterm"' + * As it isn't mappable to a single Tracker RDF query, we silently + * map ANY FTS query expression being part of an OR compound + * expression to a simple filename search. + * FTS queries are thus only possible by explicitly requesting + * file content FTS search in the Finder on the client (resulting + * in a 'kMDItemTextContent == "searchterm"' query). + */ + if (strcmp((yyvsp[(1) - (3)].sval), "") == 0) + (yyval.sval) = talloc_asprintf(srp_slq, (yyvsp[(3) - (3)].sval)); + else + (yyval.sval) = talloc_asprintf(srp_slq, (yyvsp[(1) - (3)].sval)); + talloc_free(srp_fts); + srp_fts = NULL; + } else { + (yyval.sval) = talloc_asprintf(srp_slq, "\n%s\n%s\n\n", (yyvsp[(1) - (3)].sval), (yyvsp[(3) - (3)].sval)); + } +} + break; + + case 11: +/* Line 1792 of yacc.c */ +#line 126 "slmod_rdf_parser.y" + {(yyval.sval) = map_expr((yyvsp[(1) - (5)].sval), '=', (yyvsp[(4) - (5)].sval));} + break; + + case 12: +/* Line 1792 of yacc.c */ +#line 127 "slmod_rdf_parser.y" + {(yyval.sval) = map_expr((yyvsp[(1) - (5)].sval), '!', (yyvsp[(4) - (5)].sval));} + break; + + case 13: +/* Line 1792 of yacc.c */ +#line 128 "slmod_rdf_parser.y" + {(yyval.sval) = map_expr((yyvsp[(1) - (5)].sval), '<', (yyvsp[(4) - (5)].sval));} + break; + + case 14: +/* Line 1792 of yacc.c */ +#line 129 "slmod_rdf_parser.y" + {(yyval.sval) = map_expr((yyvsp[(1) - (5)].sval), '>', (yyvsp[(4) - (5)].sval));} + break; + + case 15: +/* Line 1792 of yacc.c */ +#line 130 "slmod_rdf_parser.y" + {(yyval.sval) = map_expr((yyvsp[(1) - (6)].sval), '=', (yyvsp[(4) - (6)].sval));} + break; + + case 16: +/* Line 1792 of yacc.c */ +#line 131 "slmod_rdf_parser.y" + {(yyval.sval) = map_expr((yyvsp[(1) - (6)].sval), '!', (yyvsp[(4) - (6)].sval));} + break; + + case 17: +/* Line 1792 of yacc.c */ +#line 132 "slmod_rdf_parser.y" + {(yyval.sval) = map_expr((yyvsp[(1) - (6)].sval), '<', (yyvsp[(4) - (6)].sval));} + break; + + case 18: +/* Line 1792 of yacc.c */ +#line 133 "slmod_rdf_parser.y" + {(yyval.sval) = map_expr((yyvsp[(1) - (6)].sval), '>', (yyvsp[(4) - (6)].sval));} + break; + + case 19: +/* Line 1792 of yacc.c */ +#line 137 "slmod_rdf_parser.y" + {(yyval.sval) = map_daterange((yyvsp[(3) - (8)].sval), (yyvsp[(5) - (8)].tval), (yyvsp[(7) - (8)].tval));} + break; + + case 20: +/* Line 1792 of yacc.c */ +#line 141 "slmod_rdf_parser.y" + {(yyval.tval) = isodate2unix((yyvsp[(3) - (4)].sval));} + break; + + case 21: +/* Line 1792 of yacc.c */ +#line 142 "slmod_rdf_parser.y" + {(yyval.tval) = atoi((yyvsp[(1) - (1)].sval)) + SPRAW_TIME_OFFSET;} + break; + + +/* Line 1792 of yacc.c */ +#line 1595 "slmod_rdf_parser.c" + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (YY_("syntax error")); +#else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) + { + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; + } +# undef YYSYNTAX_ERROR +#endif + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + /* Do not reclaim the symbols of the rule which action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + yystos[yystate], yyvsp); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#if !defined yyoverflow || YYERROR_VERBOSE +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + } + /* Do not reclaim the symbols of the rule which action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + /* Make sure YYID is used. */ + return YYID (yyresult); +} + + +/* Line 2055 of yacc.c */ +#line 145 "slmod_rdf_parser.y" + + +static time_t isodate2unix(const char *s) +{ + struct tm tm; + + if (strptime(s, "%Y-%m-%dT%H:%M:%SZ", &tm) == NULL) + return (time_t)-1; + return mktime(&tm); +} + +static const char *map_daterange(const char *dateattr, time_t date1, time_t date2) +{ + EC_INIT; + char *result = NULL; + struct spotlight_rdf_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_rdf_map; p->srm_spotlight_attr; p++) { + if (strcmp(dateattr, p->srm_spotlight_attr) == 0) { + result = talloc_asprintf(srp_slq, + "\n" + " \n" + " \n" + " %s\n" + " \n" + " \n" + " \n" + " %s\n" + " \n" + "\n", + p->srm_rdf_attr, buf1, + p->srm_rdf_attr, buf2); + 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; + + for (struct MDTypeMap *p = MDTypeMap; p->mdtm_value; p++) { + if (strcmp(p->mdtm_value, val) == 0) { + if (!p->mdtm_type) + return NULL; + if (val[0] == '9') { + srp_slq->slq_service = SERVICE_FOLDERS; + return ""; + } + result = talloc_asprintf(srp_slq, + "\n" + " \n" + " %s\n" + "\n", + p->mdtm_rdfop, + p->mdtm_type, + p->mdtm_rdfop); + break; + } + } + return result; +} + +static const char *map_expr(const char *attr, char op, const char *val) +{ + EC_INIT; + char *result = NULL; + struct spotlight_rdf_map *p; + time_t t; + struct tm *tmp; + char buf1[64]; + bstring q = NULL, search = NULL, replace = NULL; + char *rdfop; + + for (p = spotlight_rdf_map; p->srm_spotlight_attr; p++) { + if (p->srm_rdf_attr && strcmp(p->srm_spotlight_attr, attr) == 0) { + switch (p->srm_type) { + case srmt_num: + q = bformat("^%s$", val); + search = bfromcstr("*"); + replace = bfromcstr(".*"); + bfindreplace(q, search, replace, 0); + result = talloc_asprintf(srp_slq, + "\n" + " \n" + " %s\n" + "\n", + p->srm_rdf_attr, + bdata(q)); + bdestroy(q); + break; + + case srmt_str: + q = bformat("^%s$", val); + search = bfromcstr("*"); + replace = bfromcstr(".*"); + bfindreplace(q, search, replace, 0); + result = talloc_asprintf(srp_slq, + "\n" + " \n" + " %s\n" + "\n", + p->srm_rdf_attr, + bdata(q)); + bdestroy(q); + break; + + case srmt_fts: + if (srp_fts) { + yyerror("only single fts query allowed"); + EC_FAIL; + } + q = bfromcstr(val); + search = bfromcstr("*"); + replace = bfromcstr(""); + bfindreplace(q, search, replace, 0); + srp_fts = talloc_strdup(srp_slq, bdata(q)); + result = ""; + break; + + case srmt_date: + t = atoi(val) + SPRAW_TIME_OFFSET; + EC_NULL( tmp = localtime(&t) ); + strftime(buf1, sizeof(buf1), "%Y-%m-%dT%H:%M:%SZ", tmp); + + switch (op) { + case '=': + rdfop = "equals"; + case '<': + rdfop = "lessThan"; + case '>': + rdfop = "greaterThan"; + default: + yyerror("unknown date comparison"); + EC_FAIL; + } + result = talloc_asprintf(srp_slq, + "\n" + " \n" + " %s\n" + "\n", + rdfop, + p->srm_rdf_attr, + buf1, + rdfop); + + break; + + case srmt_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 RDF query + * + * @param[in] slq Spotlight query handle + * @return 0 on success, -1 on error + **/ +int map_spotlight_to_rdf_query(slq_t *slq) +{ + EC_INIT; + YY_BUFFER_STATE s = NULL; + srp_result = NULL; + srp_fts = NULL; + slq->slq_service = SERVICE_FILES; + srp_slq = slq; + s = yy_scan_string(slq->slq_qstring); + + EC_ZERO( yyparse() ); + +EC_CLEANUP: + if (s) + yy_delete_buffer(s); + if (ret == 0) { + slq->slq_trackerquery = srp_result; + slq->slq_fts = srp_fts; + } + 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; + } + + srp_slq = talloc_zero(NULL, slq_t); + struct vol *vol = talloc_zero(srp_slq, struct vol); + vol->v_path = "/Volumes/test"; + srp_slq->slq_vol = vol; + + s = yy_scan_string(argv[1]); + + ret = yyparse(); + + yy_delete_buffer(s); + + if (ret == 0) + printf("RDF:\n%s\nFTS: %s\n", + srp_result ? srp_result : "(empty)", + srp_fts ? srp_fts : "(none)"); + return 0; +} +#endif diff --git a/etc/spotlight/slmod_rdf_parser.h b/etc/spotlight/slmod_rdf_parser.h new file mode 100644 index 00000000..3815147e --- /dev/null +++ b/etc/spotlight/slmod_rdf_parser.h @@ -0,0 +1,130 @@ +/* A Bison parser, made by GNU Bison 2.7. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2012 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +#ifndef YY_YY_Y_TAB_H_INCLUDED +# define YY_YY_Y_TAB_H_INCLUDED +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int yydebug; +#endif + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + WORD = 258, + BOOL = 259, + FUNC_INRANGE = 260, + DATE_ISO = 261, + OBRACE = 262, + CBRACE = 263, + EQUAL = 264, + UNEQUAL = 265, + GT = 266, + LT = 267, + COMMA = 268, + QUOTE = 269, + AND = 270, + OR = 271 + }; +#endif +/* Tokens. */ +#define WORD 258 +#define BOOL 259 +#define FUNC_INRANGE 260 +#define DATE_ISO 261 +#define OBRACE 262 +#define CBRACE 263 +#define EQUAL 264 +#define UNEQUAL 265 +#define GT 266 +#define LT 267 +#define COMMA 268 +#define QUOTE 269 +#define AND 270 +#define OR 271 + + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +{ +/* Line 2058 of yacc.c */ +#line 46 "slmod_rdf_parser.y" + + int ival; + const char *sval; + bool bval; + time_t tval; + + +/* Line 2058 of yacc.c */ +#line 97 "slmod_rdf_parser.h" +} YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +#endif + +extern YYSTYPE yylval; + +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (void); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ +/* "%code provides" blocks. */ +/* Line 2058 of yacc.c */ +#line 40 "slmod_rdf_parser.y" + + #define SPRAW_TIME_OFFSET 978307200 + extern int map_spotlight_to_rdf_query(slq_t *slq); + extern slq_t *srp_slq; + + +/* Line 2058 of yacc.c */ +#line 129 "slmod_rdf_parser.h" + +#endif /* !YY_YY_Y_TAB_H_INCLUDED */ diff --git a/etc/spotlight/slmod_rdf_parser.y b/etc/spotlight/slmod_rdf_parser.y new file mode 100644 index 00000000..9352d7a2 --- /dev/null +++ b/etc/spotlight/slmod_rdf_parser.y @@ -0,0 +1,397 @@ +%{ + #include + + #include + #include + #include + #include + + #include + #include + + #include + #include + #include + #include + + #include "slmod_rdf_map.h" + + struct yy_buffer_state; + typedef struct yy_buffer_state *YY_BUFFER_STATE; + extern int yylex (void); + extern void yyerror (char const *); + extern void *yyterminate(void); + extern YY_BUFFER_STATE yy_scan_string( const char *str); + extern void yy_delete_buffer ( YY_BUFFER_STATE buffer ); + + /* forward declarations */ + static const char *map_expr(const char *attr, char op, const char *val); + static const char *map_daterange(const char *dateattr, time_t date1, time_t date2); + static time_t isodate2unix(const char *s); + + /* global vars, eg needed by the lexer */ + slq_t *srp_slq; + + /* local vars */ + static gchar *srp_result; + static gchar *srp_fts; +%} + +%code provides { + #define SPRAW_TIME_OFFSET 978307200 + extern int map_spotlight_to_rdf_query(slq_t *slq); + extern slq_t *srp_slq; +} + +%union { + int ival; + const char *sval; + bool bval; + time_t tval; +} + +%expect 4 +%error-verbose + +%type match expr line function +%type date + +%token WORD +%token 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 { + srp_result = talloc_asprintf(srp_slq, + "\n" + " \n" + " \n" + " \n" + " %s\n" + " \n" + " %s\n" + " \n" + "\n", + srp_slq->slq_vol->v_path, $1); + $$ = srp_result; +} +; + +expr: +BOOL { + if ($1 == false) + YYACCEPT; + else + YYABORT; +} +| match {$$ = $1; if ($$ == NULL) YYABORT;} +| function {$$ = $1;} +| OBRACE expr CBRACE {$$ = talloc_asprintf(srp_slq, "%s\n", $2);} +| expr AND expr {$$ = talloc_asprintf(srp_slq, "\n%s\n%s\n\n", $1, $3);} +| expr OR expr { + if (strcmp($1, "") == 0 || strcmp($3, "") == 0) { + /* + * The default Spotlight search term issued by the Finder (10.8) is: + * '* == "searchterm" || kMDItemTextContent == "searchterm"' + * As it isn't mappable to a single Tracker RDF query, we silently + * map ANY FTS query expression being part of an OR compound + * expression to a simple filename search. + * FTS queries are thus only possible by explicitly requesting + * file content FTS search in the Finder on the client (resulting + * in a 'kMDItemTextContent == "searchterm"' query). + */ + if (strcmp($1, "") == 0) + $$ = talloc_asprintf(srp_slq, $3); + else + $$ = talloc_asprintf(srp_slq, $1); + talloc_free(srp_fts); + srp_fts = NULL; + } else { + $$ = talloc_asprintf(srp_slq, "\n%s\n%s\n\n", $1, $3); + } +} +; + +match: +WORD EQUAL QUOTE WORD QUOTE {$$ = map_expr($1, '=', $4);} +| WORD UNEQUAL QUOTE WORD QUOTE {$$ = map_expr($1, '!', $4);} +| WORD LT QUOTE WORD QUOTE {$$ = map_expr($1, '<', $4);} +| WORD GT QUOTE WORD QUOTE {$$ = map_expr($1, '>', $4);} +| WORD EQUAL QUOTE WORD QUOTE WORD {$$ = map_expr($1, '=', $4);} +| WORD UNEQUAL QUOTE WORD QUOTE WORD {$$ = map_expr($1, '!', $4);} +| WORD LT QUOTE WORD QUOTE WORD {$$ = map_expr($1, '<', $4);} +| WORD GT QUOTE WORD QUOTE WORD {$$ = map_expr($1, '>', $4);} +; + +function: +FUNC_INRANGE OBRACE WORD COMMA date COMMA date CBRACE {$$ = map_daterange($3, $5, $7);} +; + +date: +DATE_ISO OBRACE WORD CBRACE {$$ = isodate2unix($3);} +| WORD {$$ = atoi($1) + SPRAW_TIME_OFFSET;} +; + +%% + +static time_t isodate2unix(const char *s) +{ + struct tm tm; + + if (strptime(s, "%Y-%m-%dT%H:%M:%SZ", &tm) == NULL) + return (time_t)-1; + return mktime(&tm); +} + +static const char *map_daterange(const char *dateattr, time_t date1, time_t date2) +{ + EC_INIT; + char *result = NULL; + struct spotlight_rdf_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_rdf_map; p->srm_spotlight_attr; p++) { + if (strcmp(dateattr, p->srm_spotlight_attr) == 0) { + result = talloc_asprintf(srp_slq, + "\n" + " \n" + " \n" + " %s\n" + " \n" + " \n" + " \n" + " %s\n" + " \n" + "\n", + p->srm_rdf_attr, buf1, + p->srm_rdf_attr, buf2); + 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; + + for (struct MDTypeMap *p = MDTypeMap; p->mdtm_value; p++) { + if (strcmp(p->mdtm_value, val) == 0) { + if (!p->mdtm_type) + return NULL; + if (val[0] == '9') { + srp_slq->slq_service = SERVICE_FOLDERS; + return ""; + } + result = talloc_asprintf(srp_slq, + "\n" + " \n" + " %s\n" + "\n", + p->mdtm_rdfop, + p->mdtm_type, + p->mdtm_rdfop); + break; + } + } + return result; +} + +static const char *map_expr(const char *attr, char op, const char *val) +{ + EC_INIT; + char *result = NULL; + struct spotlight_rdf_map *p; + time_t t; + struct tm *tmp; + char buf1[64]; + bstring q = NULL, search = NULL, replace = NULL; + char *rdfop; + + for (p = spotlight_rdf_map; p->srm_spotlight_attr; p++) { + if (p->srm_rdf_attr && strcmp(p->srm_spotlight_attr, attr) == 0) { + switch (p->srm_type) { + case srmt_num: + q = bformat("^%s$", val); + search = bfromcstr("*"); + replace = bfromcstr(".*"); + bfindreplace(q, search, replace, 0); + result = talloc_asprintf(srp_slq, + "\n" + " \n" + " %s\n" + "\n", + p->srm_rdf_attr, + bdata(q)); + bdestroy(q); + break; + + case srmt_str: + q = bformat("^%s$", val); + search = bfromcstr("*"); + replace = bfromcstr(".*"); + bfindreplace(q, search, replace, 0); + result = talloc_asprintf(srp_slq, + "\n" + " \n" + " %s\n" + "\n", + p->srm_rdf_attr, + bdata(q)); + bdestroy(q); + break; + + case srmt_fts: + if (srp_fts) { + yyerror("only single fts query allowed"); + EC_FAIL; + } + q = bfromcstr(val); + search = bfromcstr("*"); + replace = bfromcstr(""); + bfindreplace(q, search, replace, 0); + srp_fts = talloc_strdup(srp_slq, bdata(q)); + result = ""; + break; + + case srmt_date: + t = atoi(val) + SPRAW_TIME_OFFSET; + EC_NULL( tmp = localtime(&t) ); + strftime(buf1, sizeof(buf1), "%Y-%m-%dT%H:%M:%SZ", tmp); + + switch (op) { + case '=': + rdfop = "equals"; + case '<': + rdfop = "lessThan"; + case '>': + rdfop = "greaterThan"; + default: + yyerror("unknown date comparison"); + EC_FAIL; + } + result = talloc_asprintf(srp_slq, + "\n" + " \n" + " %s\n" + "\n", + rdfop, + p->srm_rdf_attr, + buf1, + rdfop); + + break; + + case srmt_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 RDF query + * + * @param[in] slq Spotlight query handle + * @return 0 on success, -1 on error + **/ +int map_spotlight_to_rdf_query(slq_t *slq) +{ + EC_INIT; + YY_BUFFER_STATE s = NULL; + srp_result = NULL; + srp_fts = NULL; + slq->slq_service = SERVICE_FILES; + srp_slq = slq; + s = yy_scan_string(slq->slq_qstring); + + EC_ZERO( yyparse() ); + +EC_CLEANUP: + if (s) + yy_delete_buffer(s); + if (ret == 0) { + slq->slq_trackerquery = srp_result; + slq->slq_fts = srp_fts; + } + 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; + } + + srp_slq = talloc_zero(NULL, slq_t); + struct vol *vol = talloc_zero(srp_slq, struct vol); + vol->v_path = "/Volumes/test"; + srp_slq->slq_vol = vol; + + s = yy_scan_string(argv[1]); + + ret = yyparse(); + + yy_delete_buffer(s); + + if (ret == 0) + printf("RDF:\n%s\nFTS: %s\n", + srp_result ? srp_result : "(empty)", + srp_fts ? srp_fts : "(none)"); + return 0; +} +#endif diff --git a/etc/spotlight/slmod_sparql.c b/etc/spotlight/slmod_sparql.c new file mode 100644 index 00000000..9edad460 --- /dev/null +++ b/etc/spotlight/slmod_sparql.c @@ -0,0 +1,376 @@ +/* + Copyright (c) 2012 Frank Lahm + + 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 +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "slmod_sparql_parser.h" + +#define MAX_SL_RESULTS 20 + +static TrackerSparqlConnection *connection; +static TrackerMinerManager *manager; + +static char *tracker_to_unix_path(const char *uri) +{ + EC_INIT; + GFile *f = NULL; + char *path = NULL; + + EC_NULL_LOG( f = g_file_new_for_uri(uri) ); + EC_NULL_LOG( path = g_file_get_path(f) ); + +EC_CLEANUP: + if (f) + g_object_unref(f); + if (ret != 0) + return NULL; + return path; +} + +static int sl_mod_init(void *p) +{ + EC_INIT; + GError *error = NULL; + const char *msg = p; + + LOG(log_info, logtype_sl, "Initializing Spotlight module"); + + g_type_init(); + setenv("DBUS_SESSION_BUS_ADDRESS", "unix:path=/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); +#if 0 + /* + * Disabled for now, hangs in tracker_miner_manager_new_full() since adding + * the dbus afpstats stuff. + */ + manager = tracker_miner_manager_new_full(FALSE, &error); +#endif + 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 0 + if (!manager) { + LOG(log_error, logtype_sl, "Couldn't connect to Tracker miner"); + g_clear_error(&error); + EC_FAIL; + } +#endif + +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 cnid_cmp_fn(const void *p1, const void *p2) +{ + const uint64_t *cnid1 = p1, *cnid2 = p2; + if (*cnid1 == *cnid2) + return 0; + if (*cnid1 < *cnid2) + return -1; + else + return 1; +} + +static int sl_mod_fetch_result(void *p) +{ + EC_INIT; + slq_t *slq = p; + GError *error = NULL; + int i = 0; + cnid_t did, id; + const gchar *uri; + char *path; + sl_cnids_t *cnids; + sl_filemeta_t *fm; + sl_array_t *fm_array; + sl_nil_t nil; + uint64_t uint64; + gboolean qres, firstmatch = true; + + if (!slq->slq_tracker_cursor) { + LOG(log_debug, logtype_sl, "sl_mod_fetch_result: no results found"); + goto EC_CLEANUP; + } + + /* Prepare CNIDs */ + cnids = talloc_zero(slq->slq_reply, sl_cnids_t); + cnids->ca_cnids = talloc_zero(cnids, DALLOC_CTX); + cnids->ca_unkn1 = 0xadd; + cnids->ca_context = slq->slq_ctx2; + + /* Prepare FileMeta */ + fm = talloc_zero(slq->slq_reply, sl_filemeta_t); + fm_array = talloc_zero(fm, sl_array_t); + 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); + if (slq->slq_cnids) { + if (!bsearch(&uint64, slq->slq_cnids, slq->slq_cnids_num, sizeof(uint64_t), cnid_cmp_fn)) + goto loop_cleanup; + } + + 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) +{ + return 0; +#ifdef HAVE_TRACKER_MINER + EC_INIT; + const char *f = p; + + if (!f) + goto EC_CLEANUP; + + GError *error = NULL; + GFile *file = NULL; + + file = g_file_new_for_commandline_arg(f); + + become_root(); + tracker_miner_manager_index_file(manager, file, &error); + unbecome_root(); + + if (error) + LOG(log_error, logtype_sl, "sl_mod_index_file(\"%s\"): indexing failed", f); + else + LOG(log_debug, logtype_sl, "sl_mod_index_file(\"%s\"): indexing file was successful", f); + +EC_CLEANUP: + if (file) + g_object_unref(file); + EC_EXIT; +#else + return 0; +#endif +} + +struct sl_module_export sl_mod = { + SL_MODULE_VERSION, + sl_mod_init, + sl_mod_start_search, + sl_mod_fetch_result, + sl_mod_close_query, + sl_mod_error, + sl_mod_index_file +}; diff --git a/etc/spotlight/slmod_sparql_map.c b/etc/spotlight/slmod_sparql_map.c new file mode 100644 index 00000000..49296d79 --- /dev/null +++ b/etc/spotlight/slmod_sparql_map.c @@ -0,0 +1,100 @@ +/* + Copyright (c) 2012 Frank Lahm + + 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 + +#include "slmod_sparql_map.h" + +#define NOTSUPPORTED NULL +#define SPECIAL NULL + +struct spotlight_sparql_map spotlight_sparql_map[] = { + /* ssm_spotlight_attr ssm_type, ssm_sparql_attr */ + {"*", ssmt_fts, "fts:match"}, + + /* Filesystem metadata */ + {"kMDItemFSLabel", ssmt_num, NOTSUPPORTED}, + {"kMDItemDisplayName", ssmt_str, "nfo:fileName"}, + {"kMDItemFSName", ssmt_str, "nfo:fileName"}, + {"kMDItemFSContentChangeDate", ssmt_date, "nfo:fileLastModified"}, + + /* Common metadata */ + {"kMDItemTextContent", ssmt_fts, "fts:match"}, + {"kMDItemContentCreationDate", ssmt_date, "nie:contentCreated"}, + {"kMDItemContentModificationDate", ssmt_date, "nfo:fileLastModified"}, + {"kMDItemAttributeChangeDate", ssmt_date, "nfo:fileLastModified"}, + {"kMDItemAuthors", ssmt_str, "dc:creator"}, + {"kMDItemCopyright", ssmt_str, "nie:copyright"}, + {"kMDItemCountry", ssmt_str, "nco:country"}, + {"kMDItemCreator", ssmt_str, "dc:creator"}, + {"kMDItemDurationSeconds", ssmt_num, "nfo:duration"}, + {"kMDItemNumberOfPages", ssmt_num, "nfo:pageCount"}, + {"kMDItemTitle", ssmt_str, "nie:title"}, + {"_kMDItemGroupId", ssmt_type, SPECIAL}, + {"kMDItemContentTypeTree", ssmt_type, SPECIAL}, + + /* Image metadata */ + {"kMDItemPixelWidth", ssmt_num, "nfo:width"}, + {"kMDItemPixelHeight", ssmt_num, "nfo:height"}, + {"kMDItemColorSpace", ssmt_str, "nexif:colorSpace"}, + {"kMDItemBitsPerSample", ssmt_num, "nfo:colorDepth"}, + {"kMDItemFocalLength", ssmt_num, "nmm:focalLength"}, + {"kMDItemISOSpeed", ssmt_num, "nmm:isoSpeed"}, + {"kMDItemOrientation", ssmt_bool, "nfo:orientation"}, + {"kMDItemResolutionWidthDPI", ssmt_num, "nfo:horizontalResolution"}, + {"kMDItemResolutionHeightDPI", ssmt_num, "nfo:verticalResolution"}, + {"kMDItemExposureTimeSeconds", ssmt_num, "nmm:exposureTime"}, + + /* Audio metadata */ + {"kMDItemComposer", ssmt_str, "nmm:composer"}, + {"kMDItemMusicalGenre", ssmt_str, "nfo:genre"}, + + {NULL, ssmt_str, NULL} +}; + +struct MDTypeMap MDTypeMap[] = { + {"1", kMDTypeMapRDF, "http://www.semanticdesktop.org/ontologies/2007/03/22/nmo#Email"}, + {"2", kMDTypeMapRDF, "http://www.semanticdesktop.org/ontologies/2007/03/22/nco#Contact"}, + {"3", kMDTypeMapNotSup, NULL}, /* PrefPane */ + {"4", kMDTypeMapRDF, "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Font"}, + {"5", kMDTypeMapRDF, "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Bookmark"}, + {"6", kMDTypeMapRDF, "http://www.semanticdesktop.org/ontologies/2007/03/22/nco#Contact"}, + {"7", kMDTypeMapRDF, "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Video"}, + {"8", kMDTypeMapRDF, "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Executable"}, + {"9", kMDTypeMapRDF, "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Folder"}, + {"10", kMDTypeMapRDF, "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Audio"}, + {"11", kMDTypeMapMime, "application/pdf"}, + {"12", kMDTypeMapRDF, "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Presentation"}, + {"13", kMDTypeMapRDF, "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Image"}, + {"public.jpeg", kMDTypeMapMime, "image/jpeg"}, + {"public.tiff", kMDTypeMapMime, "image/tiff"}, + {"com.compuserve.gif", kMDTypeMapMime, "image/gif"}, + {"public.png", kMDTypeMapMime, "image/png"}, + {"com.microsoft.bmp", kMDTypeMapMime, "image/bmp"}, + {"public.content", kMDTypeMapRDF, "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Document"}, + {"public.mp3", kMDTypeMapMime, "audio/mpeg"}, + {"public.mpeg-4-audio", kMDTypeMapMime, "audio/x-aac"}, + {"com.apple.application", kMDTypeMapRDF, "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Software"}, + {"public.text", kMDTypeMapRDF, "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#TextDocument"}, + {"public.plain-text", kMDTypeMapMime, "text/plain"}, + {"public.rtf", kMDTypeMapMime, "text/rtf"}, + {"public.html", kMDTypeMapMime, "text/html"}, + {"public.xml", kMDTypeMapMime, "text/xml"}, + {"public.source-code", kMDTypeMapRDF, "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#SourceCode"}, + {NULL, kMDTypeMapNotSup, NULL} +}; diff --git a/etc/spotlight/slmod_sparql_map.h b/etc/spotlight/slmod_sparql_map.h new file mode 100644 index 00000000..250894b1 --- /dev/null +++ b/etc/spotlight/slmod_sparql_map.h @@ -0,0 +1,52 @@ +/* + Copyright (c) 2012 Frank Lahm + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#ifndef SPOTLIGHT_SPARQL_MAP_H +#define SPOTLIGHT_SPARQL_MAP_H + +enum ssm_type { + ssmt_bool, /* a boolean value that doesn't requires a SPARQL FILTER */ + ssmt_num, /* a numeric value that requires a SPARQL FILTER */ + ssmt_str, /* a string value that requieres a SPARQL FILTER */ + ssmt_fts, /* a string value that will be queried with SPARQL 'fts:match' */ + ssmt_date, /* date values are handled in a special map function map_daterange() */ + ssmt_type /* kMDItemContentType, requires special mapping */ +}; + +enum kMDTypeMap { + kMDTypeMapNotSup, /* not supported */ + kMDTypeMapRDF, /* query with rdf:type */ + kMDTypeMapMime /* query with nie:mimeType */ +}; + +struct spotlight_sparql_map { + const char *ssm_spotlight_attr; + enum ssm_type ssm_type; + const char *ssm_sparql_attr; +}; + +struct MDTypeMap { + const char *mdtm_value; /* MD query value of attributes '_kMDItemGroupId' and 'kMDItemContentTypeTree' */ + enum kMDTypeMap mdtm_type; /* whether SPARQL query must search attribute rdf:type or nie:mime_Type */ + const char *mdtm_sparql; /* the SPARQL query match string */ +}; + +extern struct spotlight_sparql_map spotlight_sparql_map[]; +extern struct spotlight_sparql_map spotlight_sparql_date_map[]; +extern struct MDTypeMap MDTypeMap[]; +#endif diff --git a/etc/spotlight/slmod_sparql_parser.c b/etc/spotlight/slmod_sparql_parser.c new file mode 100644 index 00000000..9772b9ee --- /dev/null +++ b/etc/spotlight/slmod_sparql_parser.c @@ -0,0 +1,2074 @@ +/* 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 . */ + +/* 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 "slmod_sparql_parser.y" + + #include + + #include + #include + #include + #include + + #include + + #include + #include + #include + #include + + #include "slmod_sparql_map.h" + + struct yy_buffer_state; + typedef struct yy_buffer_state *YY_BUFFER_STATE; + extern int yylex (void); + extern void yyerror (char const *); + extern void *yyterminate(void); + extern YY_BUFFER_STATE yy_scan_string( const char *str); + extern void yy_delete_buffer ( YY_BUFFER_STATE buffer ); + + /* forward declarations */ + static const char *map_expr(const char *attr, char op, const char *val); + static const char *map_daterange(const char *dateattr, time_t date1, time_t date2); + static time_t isodate2unix(const char *s); + + /* global vars, eg needed by the lexer */ + slq_t *ssp_slq; + + /* local vars */ + static gchar *ssp_result; + static char sparqlvar; + + +/* Line 268 of yacc.c */ +#line 110 "slmod_sparql_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 "slmod_sparql_parser.y" + + int ival; + const char *sval; + bool bval; + time_t tval; + + + +/* Line 293 of yacc.c */ +#line 187 "slmod_sparql_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 "slmod_sparql_parser.y" + + #define SPRAW_TIME_OFFSET 978307200 + extern int map_spotlight_to_sparql_query(slq_t *slq, gchar **sparql_result); + extern slq_t *ssp_slq; + + + +/* Line 340 of yacc.c */ +#line 206 "slmod_sparql_parser.c" + +/* Copy the second part of user declarations. */ + + +/* Line 343 of yacc.c */ +#line 212 "slmod_sparql_parser.c" + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#elif (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +typedef signed char yytype_int8; +#else +typedef short int yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* 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 /* 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 /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* 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 /* 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 /* 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 /* 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 + + 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 "slmod_sparql_parser.y" + { + ssp_result = talloc_asprintf(ssp_slq, + "SELECT DISTINCT ?url WHERE " + "{ ?obj nie:url ?url FILTER(regex(?url, '^file://%s/')) . %s}", + ssp_slq->slq_vol->v_path, (yyvsp[(1) - (1)].sval)); + (yyval.sval) = ssp_result; +} + break; + + case 5: + +/* Line 1806 of yacc.c */ +#line 83 "slmod_sparql_parser.y" + { + if ((yyvsp[(1) - (1)].bval) == false) + YYACCEPT; + else + YYABORT; +} + break; + + case 6: + +/* Line 1806 of yacc.c */ +#line 89 "slmod_sparql_parser.y" + { + if (strcmp((yyvsp[(1) - (3)].sval), (yyvsp[(3) - (3)].sval)) != 0) + (yyval.sval) = talloc_asprintf(ssp_slq, "{ %s } UNION { %s }", (yyvsp[(1) - (3)].sval), (yyvsp[(3) - (3)].sval)); + else + (yyval.sval) = talloc_asprintf(ssp_slq, "%s", (yyvsp[(1) - (3)].sval)); +} + break; + + case 7: + +/* Line 1806 of yacc.c */ +#line 95 "slmod_sparql_parser.y" + {(yyval.sval) = (yyvsp[(1) - (1)].sval); if ((yyval.sval) == NULL) YYABORT;} + break; + + case 8: + +/* Line 1806 of yacc.c */ +#line 96 "slmod_sparql_parser.y" + {(yyval.sval) = (yyvsp[(1) - (1)].sval);} + break; + + case 9: + +/* Line 1806 of yacc.c */ +#line 97 "slmod_sparql_parser.y" + {(yyval.sval) = talloc_asprintf(ssp_slq, "%s", (yyvsp[(2) - (3)].sval));} + break; + + case 10: + +/* Line 1806 of yacc.c */ +#line 98 "slmod_sparql_parser.y" + {(yyval.sval) = talloc_asprintf(ssp_slq, "%s . %s", (yyvsp[(1) - (3)].sval), (yyvsp[(3) - (3)].sval));} + break; + + case 11: + +/* Line 1806 of yacc.c */ +#line 99 "slmod_sparql_parser.y" + { + if (strcmp((yyvsp[(1) - (3)].sval), (yyvsp[(3) - (3)].sval)) != 0) + (yyval.sval) = talloc_asprintf(ssp_slq, "{ %s } UNION { %s }", (yyvsp[(1) - (3)].sval), (yyvsp[(3) - (3)].sval)); + else + (yyval.sval) = talloc_asprintf(ssp_slq, "%s", (yyvsp[(1) - (3)].sval)); +} + break; + + case 12: + +/* Line 1806 of yacc.c */ +#line 108 "slmod_sparql_parser.y" + {(yyval.sval) = map_expr((yyvsp[(1) - (5)].sval), '=', (yyvsp[(4) - (5)].sval));} + break; + + case 13: + +/* Line 1806 of yacc.c */ +#line 109 "slmod_sparql_parser.y" + {(yyval.sval) = map_expr((yyvsp[(1) - (5)].sval), '!', (yyvsp[(4) - (5)].sval));} + break; + + case 14: + +/* Line 1806 of yacc.c */ +#line 110 "slmod_sparql_parser.y" + {(yyval.sval) = map_expr((yyvsp[(1) - (5)].sval), '<', (yyvsp[(4) - (5)].sval));} + break; + + case 15: + +/* Line 1806 of yacc.c */ +#line 111 "slmod_sparql_parser.y" + {(yyval.sval) = map_expr((yyvsp[(1) - (5)].sval), '>', (yyvsp[(4) - (5)].sval));} + break; + + case 16: + +/* Line 1806 of yacc.c */ +#line 112 "slmod_sparql_parser.y" + {(yyval.sval) = map_expr((yyvsp[(1) - (6)].sval), '=', (yyvsp[(4) - (6)].sval));} + break; + + case 17: + +/* Line 1806 of yacc.c */ +#line 113 "slmod_sparql_parser.y" + {(yyval.sval) = map_expr((yyvsp[(1) - (6)].sval), '!', (yyvsp[(4) - (6)].sval));} + break; + + case 18: + +/* Line 1806 of yacc.c */ +#line 114 "slmod_sparql_parser.y" + {(yyval.sval) = map_expr((yyvsp[(1) - (6)].sval), '<', (yyvsp[(4) - (6)].sval));} + break; + + case 19: + +/* Line 1806 of yacc.c */ +#line 115 "slmod_sparql_parser.y" + {(yyval.sval) = map_expr((yyvsp[(1) - (6)].sval), '>', (yyvsp[(4) - (6)].sval));} + break; + + case 20: + +/* Line 1806 of yacc.c */ +#line 119 "slmod_sparql_parser.y" + {(yyval.sval) = map_daterange((yyvsp[(3) - (8)].sval), (yyvsp[(5) - (8)].tval), (yyvsp[(7) - (8)].tval));} + break; + + case 21: + +/* Line 1806 of yacc.c */ +#line 123 "slmod_sparql_parser.y" + {(yyval.tval) = isodate2unix((yyvsp[(3) - (4)].sval));} + break; + + case 22: + +/* Line 1806 of yacc.c */ +#line 124 "slmod_sparql_parser.y" + {(yyval.tval) = atoi((yyvsp[(1) - (1)].sval)) + SPRAW_TIME_OFFSET;} + break; + + + +/* Line 1806 of yacc.c */ +#line 1622 "slmod_sparql_parser.c" + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (YY_("syntax error")); +#else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) + { + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; + } +# undef YYSYNTAX_ERROR +#endif + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + /* Do not reclaim the symbols of the rule which action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + yystos[yystate], yyvsp); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + *++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 "slmod_sparql_parser.y" + + +static time_t isodate2unix(const char *s) +{ + struct tm tm; + + if (strptime(s, "%Y-%m-%dT%H:%M:%SZ", &tm) == NULL) + return (time_t)-1; + return mktime(&tm); +} + +static const char *map_daterange(const char *dateattr, time_t date1, time_t date2) +{ + EC_INIT; + char *result = NULL; + struct spotlight_sparql_map *p; + struct tm *tmp; + char buf1[64], buf2[64]; + + EC_NULL_LOG( tmp = localtime(&date1) ); + strftime(buf1, sizeof(buf1), "%Y-%m-%dT%H:%M:%SZ", tmp); + EC_NULL_LOG( tmp = localtime(&date2) ); + strftime(buf2, sizeof(buf2), "%Y-%m-%dT%H:%M:%SZ", tmp); + + for (p = spotlight_sparql_map; p->ssm_spotlight_attr; p++) { + if (strcmp(dateattr, p->ssm_spotlight_attr) == 0) { + result = talloc_asprintf(ssp_slq, + "?obj %s ?%c FILTER (?%c > '%s' && ?%c < '%s')", + p->ssm_sparql_attr, + sparqlvar, + sparqlvar, + buf1, + sparqlvar, + buf2); + sparqlvar++; + break; + } + } + +EC_CLEANUP: + if (ret != 0) + return NULL; + return result; +} + +static char *map_type_search(const char *attr, char op, const char *val) +{ + char *result = NULL; + const char *sparqlAttr; + + for (struct MDTypeMap *p = MDTypeMap; p->mdtm_value; p++) { + if (strcmp(p->mdtm_value, val) == 0) { + switch (p->mdtm_type) { + case kMDTypeMapRDF: + sparqlAttr = "rdf:type"; + break; + case kMDTypeMapMime: + sparqlAttr = "nie:mimeType"; + break; + default: + return NULL; + } + result = talloc_asprintf(ssp_slq, "?obj %s '%s'", + sparqlAttr, + p->mdtm_sparql); + break; + } + } + return result; +} + +static const char *map_expr(const char *attr, char op, const char *val) +{ + EC_INIT; + char *result = NULL; + struct spotlight_sparql_map *p; + time_t t; + struct tm *tmp; + char buf1[64]; + bstring q = NULL, search = NULL, replace = NULL; + + for (p = spotlight_sparql_map; p->ssm_spotlight_attr; p++) { + if (strcmp(p->ssm_spotlight_attr, attr) == 0) { + if (p->ssm_type != ssmt_type && p->ssm_sparql_attr == NULL) { + yyerror("unsupported Spotlight attribute"); + EC_FAIL; + } + switch (p->ssm_type) { + case ssmt_bool: + result = talloc_asprintf(ssp_slq, "?obj %s '%s'", p->ssm_sparql_attr, val); + break; + case ssmt_num: + result = talloc_asprintf(ssp_slq, "?obj %s ?%c FILTER(?%c %c%c '%s')", + p->ssm_sparql_attr, + sparqlvar, + sparqlvar, + op, + op == '!' ? '=' : ' ', /* append '=' to '!' */ + val); + sparqlvar++; + break; + case ssmt_str: + q = bformat("^%s$", val); + search = bfromcstr("*"); + replace = bfromcstr(".*"); + bfindreplace(q, search, replace, 0); + result = talloc_asprintf(ssp_slq, "?obj %s ?%c FILTER(regex(?%c, '%s'))", + p->ssm_sparql_attr, + sparqlvar, + sparqlvar, + bdata(q)); + sparqlvar++; + break; + case ssmt_fts: + result = talloc_asprintf(ssp_slq, "?obj %s '%s'", p->ssm_sparql_attr, val); + break; + case ssmt_date: + t = atoi(val) + SPRAW_TIME_OFFSET; + EC_NULL( tmp = localtime(&t) ); + strftime(buf1, sizeof(buf1), "%Y-%m-%dT%H:%M:%SZ", tmp); + result = talloc_asprintf(ssp_slq, "?obj %s ?%c FILTER(?%c %c '%s')", + p->ssm_sparql_attr, + sparqlvar, + sparqlvar, + op, + buf1); + sparqlvar++; + break; + case ssmt_type: + result = map_type_search(attr, op, val); + break; + default: + EC_FAIL; + } + break; + } + } + +EC_CLEANUP: + if (q) + bdestroy(q); + if (search) + bdestroy(search); + if (replace) + bdestroy(replace); + return result; +} + +void yyerror(const char *str) +{ +#ifdef MAIN + printf("yyerror: %s\n", str); +#else + LOG(log_error, logtype_sl, "yyerror: %s", str); +#endif +} + +int yywrap() +{ + return 1; +} + +/** + * Map a Spotlight RAW query string to a SPARQL query string + * + * @param[in] slq Spotlight query handle + * @param[out] sparql_result Mapped SPARQL query, string is allocated in + * talloc context of slq + * @return 0 on success, -1 on error + **/ +int map_spotlight_to_sparql_query(slq_t *slq, gchar **sparql_result) +{ + EC_INIT; + YY_BUFFER_STATE s = NULL; + ssp_result = NULL; + + ssp_slq = slq; + s = yy_scan_string(slq->slq_qstring); + sparqlvar = 'a'; + + EC_ZERO( yyparse() ); + +EC_CLEANUP: + if (s) + yy_delete_buffer(s); + if (ret == 0) + *sparql_result = ssp_result; + else + *sparql_result = NULL; + EC_EXIT; +} + +#ifdef MAIN +int main(int argc, char **argv) +{ + int ret; + YY_BUFFER_STATE s; + + if (argc != 2) { + printf("usage: %s QUERY\n", argv[0]); + return 1; + } + + ssp_slq = talloc_zero(NULL, slq_t); + struct vol *vol = talloc_zero(ssp_slq, struct vol); + vol->v_path = "/Volumes/test"; + ssp_slq->slq_vol = vol; + sparqlvar = 'a'; + + s = yy_scan_string(argv[1]); + + ret = yyparse(); + + yy_delete_buffer(s); + + if (ret == 0) + printf("SPARQL: %s\n", ssp_result ? ssp_result : "(empty)"); + + return 0; +} +#endif + diff --git a/etc/spotlight/slmod_sparql_parser.h b/etc/spotlight/slmod_sparql_parser.h new file mode 100644 index 00000000..05802801 --- /dev/null +++ b/etc/spotlight/slmod_sparql_parser.h @@ -0,0 +1,112 @@ +/* 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 . */ + +/* 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 "slmod_sparql_parser.y" + + int ival; + const char *sval; + bool bval; + time_t tval; + + + +/* Line 2068 of yacc.c */ +#line 91 "slmod_sparql_parser.h" +} YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +#endif + +extern YYSTYPE yylval; + + +/* "%code provides" blocks. */ + +/* Line 2068 of yacc.c */ +#line 39 "slmod_sparql_parser.y" + + #define SPRAW_TIME_OFFSET 978307200 + extern int map_spotlight_to_sparql_query(slq_t *slq, gchar **sparql_result); + extern slq_t *ssp_slq; + + + +/* Line 2068 of yacc.c */ +#line 113 "slmod_sparql_parser.h" diff --git a/etc/spotlight/slmod_sparql_parser.y b/etc/spotlight/slmod_sparql_parser.y new file mode 100644 index 00000000..68d3d101 --- /dev/null +++ b/etc/spotlight/slmod_sparql_parser.y @@ -0,0 +1,347 @@ +%{ + #include + + #include + #include + #include + #include + + #include + + #include + #include + #include + #include + + #include "slmod_sparql_map.h" + + struct yy_buffer_state; + typedef struct yy_buffer_state *YY_BUFFER_STATE; + extern int yylex (void); + extern void yyerror (char const *); + extern void *yyterminate(void); + extern YY_BUFFER_STATE yy_scan_string( const char *str); + extern void yy_delete_buffer ( YY_BUFFER_STATE buffer ); + + /* forward declarations */ + static const char *map_expr(const char *attr, char op, const char *val); + static const char *map_daterange(const char *dateattr, time_t date1, time_t date2); + static time_t isodate2unix(const char *s); + + /* global vars, eg needed by the lexer */ + slq_t *ssp_slq; + + /* local vars */ + static gchar *ssp_result; + static char sparqlvar; +%} + +%code provides { + #define SPRAW_TIME_OFFSET 978307200 + extern int map_spotlight_to_sparql_query(slq_t *slq, gchar **sparql_result); + extern slq_t *ssp_slq; +} + +%union { + int ival; + const char *sval; + bool bval; + time_t tval; +} + +%expect 5 +%error-verbose + +%type match expr line function +%type date + +%token WORD +%token BOOL +%token FUNC_INRANGE +%token DATE_ISO +%token OBRACE CBRACE EQUAL UNEQUAL GT LT COMMA QUOTE +%left AND +%left OR +%% + +input: +/* empty */ +| input line +; + +line: +expr { + ssp_result = talloc_asprintf(ssp_slq, + "SELECT DISTINCT ?url WHERE " + "{ ?obj nie:url ?url FILTER(regex(?url, '^file://%s/')) . %s}", + ssp_slq->slq_vol->v_path, $1); + $$ = ssp_result; +} +; + +expr: +BOOL { + if ($1 == false) + YYACCEPT; + else + YYABORT; +} +| match OR match { + if (strcmp($1, $3) != 0) + $$ = talloc_asprintf(ssp_slq, "{ %s } UNION { %s }", $1, $3); + else + $$ = talloc_asprintf(ssp_slq, "%s", $1); +} +| match {$$ = $1; if ($$ == NULL) YYABORT;} +| function {$$ = $1;} +| OBRACE expr CBRACE {$$ = talloc_asprintf(ssp_slq, "%s", $2);} +| expr AND expr {$$ = talloc_asprintf(ssp_slq, "%s . %s", $1, $3);} +| expr OR expr { + if (strcmp($1, $3) != 0) + $$ = talloc_asprintf(ssp_slq, "{ %s } UNION { %s }", $1, $3); + else + $$ = talloc_asprintf(ssp_slq, "%s", $1); +} +; + +match: +WORD EQUAL QUOTE WORD QUOTE {$$ = map_expr($1, '=', $4);} +| WORD UNEQUAL QUOTE WORD QUOTE {$$ = map_expr($1, '!', $4);} +| WORD LT QUOTE WORD QUOTE {$$ = map_expr($1, '<', $4);} +| WORD GT QUOTE WORD QUOTE {$$ = map_expr($1, '>', $4);} +| WORD EQUAL QUOTE WORD QUOTE WORD {$$ = map_expr($1, '=', $4);} +| WORD UNEQUAL QUOTE WORD QUOTE WORD {$$ = map_expr($1, '!', $4);} +| WORD LT QUOTE WORD QUOTE WORD {$$ = map_expr($1, '<', $4);} +| WORD GT QUOTE WORD QUOTE WORD {$$ = map_expr($1, '>', $4);} +; + +function: +FUNC_INRANGE OBRACE WORD COMMA date COMMA date CBRACE {$$ = map_daterange($3, $5, $7);} +; + +date: +DATE_ISO OBRACE WORD CBRACE {$$ = isodate2unix($3);} +| WORD {$$ = atoi($1) + SPRAW_TIME_OFFSET;} +; + +%% + +static time_t isodate2unix(const char *s) +{ + struct tm tm; + + if (strptime(s, "%Y-%m-%dT%H:%M:%SZ", &tm) == NULL) + return (time_t)-1; + return mktime(&tm); +} + +static const char *map_daterange(const char *dateattr, time_t date1, time_t date2) +{ + EC_INIT; + char *result = NULL; + struct spotlight_sparql_map *p; + struct tm *tmp; + char buf1[64], buf2[64]; + + EC_NULL_LOG( tmp = localtime(&date1) ); + strftime(buf1, sizeof(buf1), "%Y-%m-%dT%H:%M:%SZ", tmp); + EC_NULL_LOG( tmp = localtime(&date2) ); + strftime(buf2, sizeof(buf2), "%Y-%m-%dT%H:%M:%SZ", tmp); + + for (p = spotlight_sparql_map; p->ssm_spotlight_attr; p++) { + if (strcmp(dateattr, p->ssm_spotlight_attr) == 0) { + result = talloc_asprintf(ssp_slq, + "?obj %s ?%c FILTER (?%c > '%s' && ?%c < '%s')", + p->ssm_sparql_attr, + sparqlvar, + sparqlvar, + buf1, + sparqlvar, + buf2); + sparqlvar++; + break; + } + } + +EC_CLEANUP: + if (ret != 0) + return NULL; + return result; +} + +static char *map_type_search(const char *attr, char op, const char *val) +{ + char *result = NULL; + const char *sparqlAttr; + + for (struct MDTypeMap *p = MDTypeMap; p->mdtm_value; p++) { + if (strcmp(p->mdtm_value, val) == 0) { + switch (p->mdtm_type) { + case kMDTypeMapRDF: + sparqlAttr = "rdf:type"; + break; + case kMDTypeMapMime: + sparqlAttr = "nie:mimeType"; + break; + default: + return NULL; + } + result = talloc_asprintf(ssp_slq, "?obj %s '%s'", + sparqlAttr, + p->mdtm_sparql); + break; + } + } + return result; +} + +static const char *map_expr(const char *attr, char op, const char *val) +{ + EC_INIT; + char *result = NULL; + struct spotlight_sparql_map *p; + time_t t; + struct tm *tmp; + char buf1[64]; + bstring q = NULL, search = NULL, replace = NULL; + + for (p = spotlight_sparql_map; p->ssm_spotlight_attr; p++) { + if (strcmp(p->ssm_spotlight_attr, attr) == 0) { + if (p->ssm_type != ssmt_type && p->ssm_sparql_attr == NULL) { + yyerror("unsupported Spotlight attribute"); + EC_FAIL; + } + switch (p->ssm_type) { + case ssmt_bool: + result = talloc_asprintf(ssp_slq, "?obj %s '%s'", p->ssm_sparql_attr, val); + break; + case ssmt_num: + result = talloc_asprintf(ssp_slq, "?obj %s ?%c FILTER(?%c %c%c '%s')", + p->ssm_sparql_attr, + sparqlvar, + sparqlvar, + op, + op == '!' ? '=' : ' ', /* append '=' to '!' */ + val); + sparqlvar++; + break; + case ssmt_str: + q = bformat("^%s$", val); + search = bfromcstr("*"); + replace = bfromcstr(".*"); + bfindreplace(q, search, replace, 0); + result = talloc_asprintf(ssp_slq, "?obj %s ?%c FILTER(regex(?%c, '%s'))", + p->ssm_sparql_attr, + sparqlvar, + sparqlvar, + bdata(q)); + sparqlvar++; + break; + case ssmt_fts: + result = talloc_asprintf(ssp_slq, "?obj %s '%s'", p->ssm_sparql_attr, val); + break; + case ssmt_date: + t = atoi(val) + SPRAW_TIME_OFFSET; + EC_NULL( tmp = localtime(&t) ); + strftime(buf1, sizeof(buf1), "%Y-%m-%dT%H:%M:%SZ", tmp); + result = talloc_asprintf(ssp_slq, "?obj %s ?%c FILTER(?%c %c '%s')", + p->ssm_sparql_attr, + sparqlvar, + sparqlvar, + op, + buf1); + sparqlvar++; + break; + case ssmt_type: + result = map_type_search(attr, op, val); + break; + default: + EC_FAIL; + } + break; + } + } + +EC_CLEANUP: + if (q) + bdestroy(q); + if (search) + bdestroy(search); + if (replace) + bdestroy(replace); + return result; +} + +void yyerror(const char *str) +{ +#ifdef MAIN + printf("yyerror: %s\n", str); +#else + LOG(log_error, logtype_sl, "yyerror: %s", str); +#endif +} + +int yywrap() +{ + return 1; +} + +/** + * Map a Spotlight RAW query string to a SPARQL query string + * + * @param[in] slq Spotlight query handle + * @param[out] sparql_result Mapped SPARQL query, string is allocated in + * talloc context of slq + * @return 0 on success, -1 on error + **/ +int map_spotlight_to_sparql_query(slq_t *slq, gchar **sparql_result) +{ + EC_INIT; + YY_BUFFER_STATE s = NULL; + ssp_result = NULL; + + ssp_slq = slq; + s = yy_scan_string(slq->slq_qstring); + sparqlvar = 'a'; + + EC_ZERO( yyparse() ); + +EC_CLEANUP: + if (s) + yy_delete_buffer(s); + if (ret == 0) + *sparql_result = ssp_result; + else + *sparql_result = NULL; + EC_EXIT; +} + +#ifdef MAIN +int main(int argc, char **argv) +{ + int ret; + YY_BUFFER_STATE s; + + if (argc != 2) { + printf("usage: %s QUERY\n", argv[0]); + return 1; + } + + ssp_slq = talloc_zero(NULL, slq_t); + struct vol *vol = talloc_zero(ssp_slq, struct vol); + vol->v_path = "/Volumes/test"; + ssp_slq->slq_vol = vol; + sparqlvar = 'a'; + + s = yy_scan_string(argv[1]); + + ret = yyparse(); + + yy_delete_buffer(s); + + if (ret == 0) + printf("SPARQL: %s\n", ssp_result ? ssp_result : "(empty)"); + + return 0; +} +#endif diff --git a/etc/spotlight/spotlight_rawquery_lexer.c b/etc/spotlight/spotlight_rawquery_lexer.c new file mode 100644 index 00000000..84c99367 --- /dev/null +++ b/etc/spotlight/spotlight_rawquery_lexer.c @@ -0,0 +1,1867 @@ +#line 2 "spotlight_rawquery_lexer.l" +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + + + +#line 9 "spotlight_rawquery_lexer.c" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 +#define YY_FLEX_SUBMINOR_VERSION 35 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . 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 +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; +#endif /* ! C99 */ + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#endif /* ! FLEXINT_H */ + +#ifdef __cplusplus + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +/* C99 requires __STDC__ to be defined as 1. */ +#if defined (__STDC__) + +#define YY_USE_CONST + +#endif /* defined (__STDC__) */ +#endif /* ! __cplusplus */ + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN (yy_start) = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START (((yy_start) - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart(yyin ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#define YY_BUF_SIZE 16384 +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +extern int yyleng; + +extern FILE *yyin, *yyout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = (yy_hold_char); \ + YY_RESTORE_YY_MORE_OFFSET \ + (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, (yytext_ptr) ) + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* Stack of input buffers. */ +static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ +static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ +static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ + ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ + : NULL) + +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] + +/* yy_hold_char holds the character lost when yytext is formed. */ +static char yy_hold_char; +static int yy_n_chars; /* number of characters read into yy_ch_buf */ +int yyleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = (char *) 0; +static int yy_init = 0; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void yyrestart (FILE *input_file ); +void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ); +YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ); +void yy_delete_buffer (YY_BUFFER_STATE b ); +void yy_flush_buffer (YY_BUFFER_STATE b ); +void yypush_buffer_state (YY_BUFFER_STATE new_buffer ); +void yypop_buffer_state (void ); + +static void yyensure_buffer_stack (void ); +static void yy_load_buffer_state (void ); +static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file ); + +#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER ) + +YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ); +YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ); +YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len ); + +void *yyalloc (yy_size_t ); +void *yyrealloc (void *,yy_size_t ); +void yyfree (void * ); + +#define yy_new_buffer yy_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer(yyin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + yyensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer(yyin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +typedef unsigned char YY_CHAR; + +FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; + +typedef int yy_state_type; + +extern int yylineno; + +int yylineno = 1; + +extern char *yytext; +#define yytext_ptr yytext + +static yy_state_type yy_get_previous_state (void ); +static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); +static int yy_get_next_buffer (void ); +static void yy_fatal_error (yyconst char msg[] ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + (yytext_ptr) = yy_bp; \ + yyleng = (size_t) (yy_cp - yy_bp); \ + (yy_hold_char) = *yy_cp; \ + *yy_cp = '\0'; \ + (yy_c_buf_p) = yy_cp; + +#define YY_NUM_RULES 17 +#define YY_END_OF_BUFFER 18 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static yyconst flex_int16_t yy_accept[57] = + { 0, + 0, 0, 18, 17, 16, 17, 5, 17, 17, 6, + 7, 15, 14, 12, 17, 13, 15, 15, 15, 17, + 17, 17, 17, 11, 0, 8, 15, 0, 0, 0, + 10, 15, 15, 15, 9, 0, 0, 0, 15, 15, + 15, 0, 0, 15, 15, 4, 0, 15, 3, 0, + 15, 0, 1, 0, 2, 0 + } ; + +static yyconst flex_int32_t yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 3, 4, 1, 5, 1, 6, 1, 7, + 8, 9, 1, 10, 9, 11, 1, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 1, 12, + 13, 14, 1, 1, 9, 9, 9, 9, 9, 9, + 9, 9, 15, 9, 9, 9, 9, 9, 9, 9, + 9, 16, 9, 9, 9, 9, 9, 9, 9, 9, + 1, 1, 1, 1, 9, 1, 17, 9, 9, 9, + + 18, 19, 20, 9, 21, 9, 9, 22, 23, 24, + 25, 9, 9, 26, 27, 28, 29, 9, 9, 9, + 9, 9, 1, 30, 1, 1, 1, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 1, 1, 32, 32, 32, 32, 32, 32, 32, + + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, + 34, 34, 34, 34, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static yyconst flex_int32_t yy_meta[35] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, + 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, + 1, 2, 2, 2 + } ; + +static yyconst flex_int16_t yy_base[58] = + { 0, + 0, 0, 91, 92, 92, 77, 92, 61, 82, 92, + 92, 3, 92, 92, 74, 92, 14, 25, 15, 56, + 54, 53, 52, 92, 61, 92, 57, 49, 48, 47, + 92, 27, 28, 16, 92, 46, 45, 52, 36, 30, + 37, 43, 55, 48, 38, 47, 59, 39, 45, 47, + 40, 40, 42, 40, 92, 92, 42 + } ; + +static yyconst flex_int16_t yy_def[58] = + { 0, + 56, 1, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 57, 56, 56, 56, 56, 57, 17, 17, 56, + 56, 56, 56, 56, 56, 56, 17, 56, 56, 56, + 56, 17, 17, 17, 56, 56, 56, 56, 17, 17, + 17, 56, 56, 17, 17, 17, 56, 17, 17, 56, + 17, 56, 17, 56, 56, 0, 56 + } ; + +static yyconst flex_int16_t yy_nxt[127] = + { 0, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 12, 14, 15, 16, 17, 12, 12, 12, 18, 12, + 12, 12, 12, 12, 12, 12, 12, 19, 12, 20, + 4, 21, 22, 23, 28, 29, 30, 32, 27, 27, + 34, 33, 39, 27, 41, 28, 29, 30, 27, 40, + 27, 27, 44, 27, 46, 49, 45, 53, 51, 27, + 27, 27, 27, 27, 55, 27, 54, 52, 27, 50, + 27, 48, 47, 27, 43, 42, 27, 37, 36, 27, + 27, 38, 37, 36, 27, 35, 31, 26, 25, 24, + 56, 3, 56, 56, 56, 56, 56, 56, 56, 56, + + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56 + } ; + +static yyconst flex_int16_t yy_chk[127] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 12, 12, 12, 17, 19, 34, + 19, 18, 32, 57, 34, 17, 17, 17, 18, 33, + 32, 33, 39, 40, 41, 45, 40, 51, 48, 39, + 41, 45, 48, 51, 54, 53, 52, 50, 49, 47, + 46, 44, 43, 42, 38, 37, 36, 30, 29, 28, + 27, 25, 23, 22, 21, 20, 15, 9, 8, 6, + 3, 56, 56, 56, 56, 56, 56, 56, 56, 56, + + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56 + } ; + +static yy_state_type yy_last_accepting_state; +static char *yy_last_accepting_cpos; + +extern int yy_flex_debug; +int yy_flex_debug = 0; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +char *yytext; +#line 1 "spotlight_rawquery_lexer.l" + +#line 8 "spotlight_rawquery_lexer.l" +#include +#include +#include +#include +#ifdef HAVE_TRACKER_SPARQL +#include "slmod_sparql_parser.h" +#define SLQ_VAR ssp_slq +#endif +#ifdef HAVE_TRACKER_RDF +#include +#include "slmod_rdf_parser.h" +#define SLQ_VAR srp_slq +#endif +#line 514 "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 +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +static int yy_init_globals (void ); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy (void ); + +int yyget_debug (void ); + +void yyset_debug (int debug_flag ); + +YY_EXTRA_TYPE yyget_extra (void ); + +void yyset_extra (YY_EXTRA_TYPE user_defined ); + +FILE *yyget_in (void ); + +void yyset_in (FILE * in_str ); + +FILE *yyget_out (void ); + +void yyset_out (FILE * out_str ); + +int yyget_leng (void ); + +char *yyget_text (void ); + +int yyget_lineno (void ); + +void yyset_lineno (int line_number ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap (void ); +#else +extern int yywrap (void ); +#endif +#endif + + static void yyunput (int c,char *buf_ptr ); + +#ifndef yytext_ptr +static void yy_flex_strncpy (char *,yyconst char *,int ); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * ); +#endif + +#ifndef YY_NO_INPUT + +#ifdef __cplusplus +static int yyinput (void ); +#else +static int input (void ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO fwrite( yytext, yyleng, 1, yyout ) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + int n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex (void); + +#define YY_DECL int yylex (void) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + +#line 32 "spotlight_rawquery_lexer.l" + +#line 698 "spotlight_rawquery_lexer.c" + + if ( !(yy_init) ) + { + (yy_init) = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! (yy_start) ) + (yy_start) = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer(yyin,YY_BUF_SIZE ); + } + + yy_load_buffer_state( ); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = (yy_c_buf_p); + + /* Support of yytext. */ + *yy_cp = (yy_hold_char); + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = (yy_start); +yy_match: + do + { + register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 57 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 92 ); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + if ( yy_act == 0 ) + { /* have to back up */ + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + yy_act = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = (yy_hold_char); + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + goto yy_find_action; + +case 1: +YY_RULE_SETUP +#line 33 "spotlight_rawquery_lexer.l" +return FUNC_INRANGE; + YY_BREAK +case 2: +YY_RULE_SETUP +#line 34 "spotlight_rawquery_lexer.l" +return DATE_ISO; + YY_BREAK +case 3: +YY_RULE_SETUP +#line 35 "spotlight_rawquery_lexer.l" +{yylval.bval = false; return BOOL;} + YY_BREAK +case 4: +YY_RULE_SETUP +#line 36 "spotlight_rawquery_lexer.l" +{yylval.bval = true; return BOOL;} + YY_BREAK +case 5: +YY_RULE_SETUP +#line 37 "spotlight_rawquery_lexer.l" +return QUOTE; + YY_BREAK +case 6: +YY_RULE_SETUP +#line 38 "spotlight_rawquery_lexer.l" +return OBRACE; + YY_BREAK +case 7: +YY_RULE_SETUP +#line 39 "spotlight_rawquery_lexer.l" +return CBRACE; + YY_BREAK +case 8: +YY_RULE_SETUP +#line 40 "spotlight_rawquery_lexer.l" +return AND; + YY_BREAK +case 9: +YY_RULE_SETUP +#line 41 "spotlight_rawquery_lexer.l" +return OR; + YY_BREAK +case 10: +YY_RULE_SETUP +#line 42 "spotlight_rawquery_lexer.l" +return EQUAL; + YY_BREAK +case 11: +YY_RULE_SETUP +#line 43 "spotlight_rawquery_lexer.l" +return UNEQUAL; + YY_BREAK +case 12: +YY_RULE_SETUP +#line 44 "spotlight_rawquery_lexer.l" +return LT; + YY_BREAK +case 13: +YY_RULE_SETUP +#line 45 "spotlight_rawquery_lexer.l" +return GT; + YY_BREAK +case 14: +YY_RULE_SETUP +#line 46 "spotlight_rawquery_lexer.l" +return COMMA; + YY_BREAK +case 15: +YY_RULE_SETUP +#line 47 "spotlight_rawquery_lexer.l" +{yylval.sval = talloc_strdup(SLQ_VAR, yytext); return WORD;} + YY_BREAK +case 16: +/* rule 16 can match eol */ +YY_RULE_SETUP +#line 48 "spotlight_rawquery_lexer.l" +/* ignore */ + YY_BREAK +case 17: +YY_RULE_SETUP +#line 49 "spotlight_rawquery_lexer.l" +ECHO; + YY_BREAK +#line 867 "spotlight_rawquery_lexer.c" +case YY_STATE_EOF(INITIAL): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = (yy_hold_char); + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++(yy_c_buf_p); + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = (yy_c_buf_p); + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_END_OF_FILE: + { + (yy_did_buffer_switch_on_eof) = 0; + + if ( yywrap( ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = + (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + (yy_c_buf_p) = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ +} /* end of yylex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (void) +{ + register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + register char *source = (yytext_ptr); + register int number_to_move, i; + int ret_val; + + if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER; + + int yy_c_buf_p_offset = + (int) ((yy_c_buf_p) - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = 0; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + (yy_n_chars), (size_t) num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + if ( (yy_n_chars) == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart(yyin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + } + + (yy_n_chars) += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; + + (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (void) +{ + register yy_state_type yy_current_state; + register char *yy_cp; + + yy_current_state = (yy_start); + + for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) + { + register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 57 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) +{ + register int yy_is_jam; + register char *yy_cp = (yy_c_buf_p); + + register YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 57 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + yy_is_jam = (yy_current_state == 56); + + return yy_is_jam ? 0 : yy_current_state; +} + + static void yyunput (int c, register char * yy_bp ) +{ + register char *yy_cp; + + yy_cp = (yy_c_buf_p); + + /* undo effects of setting up yytext */ + *yy_cp = (yy_hold_char); + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + register int number_to_move = (yy_n_chars) + 2; + register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; + register char *source = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; + + while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size; + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--yy_cp = (char) c; + + (yytext_ptr) = yy_bp; + (yy_hold_char) = *yy_cp; + (yy_c_buf_p) = yy_cp; +} + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (void) +#else + static int input (void) +#endif + +{ + int c; + + *(yy_c_buf_p) = (yy_hold_char); + + if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + /* This was really a NUL. */ + *(yy_c_buf_p) = '\0'; + + else + { /* need more input */ + int offset = (yy_c_buf_p) - (yytext_ptr); + ++(yy_c_buf_p); + + switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart(yyin ); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap( ) ) + return EOF; + + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = (yytext_ptr) + offset; + break; + } + } + } + + c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ + *(yy_c_buf_p) = '\0'; /* preserve yytext */ + (yy_hold_char) = *++(yy_c_buf_p); + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * + * @note This function does not reset the start condition to @c INITIAL . + */ + void yyrestart (FILE * input_file ) +{ + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer(yyin,YY_BUF_SIZE ); + } + + yy_init_buffer(YY_CURRENT_BUFFER,input_file ); + yy_load_buffer_state( ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * + */ + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ) +{ + + /* TODO. We should be able to replace this entire function body + * with + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + yyensure_buffer_stack (); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + yy_load_buffer_state( ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + (yy_did_buffer_switch_on_eof) = 1; +} + +static void yy_load_buffer_state (void) +{ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + (yy_hold_char) = *(yy_c_buf_p); +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * + * @return the allocated buffer state. + */ + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size ) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2 ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer(b,file ); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * + */ + void yy_delete_buffer (YY_BUFFER_STATE b ) +{ + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yyfree((void *) b->yy_ch_buf ); + + yyfree((void *) b ); +} + +#ifndef __cplusplus +extern int isatty (int ); +#endif /* __cplusplus */ + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file ) + +{ + int oerrno = errno; + + yy_flush_buffer(b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * + */ + void yy_flush_buffer (YY_BUFFER_STATE b ) +{ + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + yy_load_buffer_state( ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * + */ +void yypush_buffer_state (YY_BUFFER_STATE new_buffer ) +{ + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(); + + /* This block is copied from yy_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + (yy_buffer_stack_top)++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * + */ +void yypop_buffer_state (void) +{ + if (!YY_CURRENT_BUFFER) + return; + + yy_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + if ((yy_buffer_stack_top) > 0) + --(yy_buffer_stack_top); + + if (YY_CURRENT_BUFFER) { + yy_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void yyensure_buffer_stack (void) +{ + int num_to_alloc; + + if (!(yy_buffer_stack)) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; + (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + (yy_buffer_stack_max) = num_to_alloc; + (yy_buffer_stack_top) = 0; + return; + } + + if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + int grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = (yy_buffer_stack_max) + grow_size; + (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc + ((yy_buffer_stack), + num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); + (yy_buffer_stack_max) = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size ) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = 0; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer(b ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to yylex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (yyconst char * yystr ) +{ + + return yy_scan_bytes(yystr,strlen(yystr) ); +} + +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param bytes the byte buffer to scan + * @param len the number of bytes in the buffer pointed to by @a bytes. + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, int _yybytes_len ) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = _yybytes_len + 2; + buf = (char *) yyalloc(n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer(buf,n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yy_fatal_error (yyconst char* msg ) +{ + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + yytext[yyleng] = (yy_hold_char); \ + (yy_c_buf_p) = yytext + yyless_macro_arg; \ + (yy_hold_char) = *(yy_c_buf_p); \ + *(yy_c_buf_p) = '\0'; \ + yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the current line number. + * + */ +int yyget_lineno (void) +{ + + return yylineno; +} + +/** Get the input stream. + * + */ +FILE *yyget_in (void) +{ + return yyin; +} + +/** Get the output stream. + * + */ +FILE *yyget_out (void) +{ + return yyout; +} + +/** Get the length of the current token. + * + */ +int yyget_leng (void) +{ + return yyleng; +} + +/** Get the current token. + * + */ + +char *yyget_text (void) +{ + return yytext; +} + +/** Set the current line number. + * @param line_number + * + */ +void yyset_lineno (int line_number ) +{ + + yylineno = line_number; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param in_str A readable stream. + * + * @see yy_switch_to_buffer + */ +void yyset_in (FILE * in_str ) +{ + yyin = in_str ; +} + +void yyset_out (FILE * out_str ) +{ + yyout = out_str ; +} + +int yyget_debug (void) +{ + return yy_flex_debug; +} + +void yyset_debug (int bdebug ) +{ + yy_flex_debug = bdebug ; +} + +static int yy_init_globals (void) +{ + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from yylex_destroy(), so don't allocate here. + */ + + (yy_buffer_stack) = 0; + (yy_buffer_stack_top) = 0; + (yy_buffer_stack_max) = 0; + (yy_c_buf_p) = (char *) 0; + (yy_init) = 0; + (yy_start) = 0; + +/* Defined in main.c */ +#ifdef YY_STDINIT + yyin = stdin; + yyout = stdout; +#else + yyin = (FILE *) 0; + yyout = (FILE *) 0; +#endif + + /* For future reference: Set errno on error, since we are called by + * yylex_init() + */ + return 0; +} + +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (void) +{ + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(); + } + + /* Destroy the stack itself. */ + yyfree((yy_buffer_stack) ); + (yy_buffer_stack) = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * yylex() is called, initialization will occur. */ + yy_init_globals( ); + + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) +{ + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * s ) +{ + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *yyalloc (yy_size_t size ) +{ + return (void *) malloc( size ); +} + +void *yyrealloc (void * ptr, yy_size_t size ) +{ + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); +} + +void yyfree (void * ptr ) +{ + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 49 "spotlight_rawquery_lexer.l" + + + diff --git a/etc/spotlight/spotlight_rawquery_lexer.l b/etc/spotlight/spotlight_rawquery_lexer.l new file mode 100644 index 00000000..1e903857 --- /dev/null +++ b/etc/spotlight/spotlight_rawquery_lexer.l @@ -0,0 +1,49 @@ +%top{ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ +} + +%{ +#include +#include +#include +#include +#ifdef HAVE_TRACKER_SPARQL +#include "slmod_sparql_parser.h" +#define SLQ_VAR ssp_slq +#endif +#ifdef HAVE_TRACKER_RDF +#include +#include "slmod_rdf_parser.h" +#define SLQ_VAR srp_slq +#endif +%} + +ASC [a-zA-Z0-9_\*\:\-\.] +U [\x80-\xbf] +U2 [\xc2-\xdf] +U3 [\xe0-\xef] +U4 [\xf0-\xf4] + +UANY {ASC}|{U2}{U}|{U3}{U}{U}|{U4}{U}{U}{U} +UONLY {U2}{U}|{U3}{U}{U}|{U4}{U}{U}{U} + +%% +InRange return FUNC_INRANGE; +\$time\.iso return DATE_ISO; +false {yylval.bval = false; return BOOL;} +true {yylval.bval = true; return BOOL;} +\" return QUOTE; +\( return OBRACE; +\) return CBRACE; +\&\& return AND; +\|\| return OR; +\=\= return EQUAL; +\!\= return UNEQUAL; +\< return LT; +\> return GT; +\, return COMMA; +{UANY}+ {yylval.sval = talloc_strdup(SLQ_VAR, yytext); return WORD;} +[ \t\n] /* ignore */ +%% diff --git a/etc/uams/uams_dhx2_pam.c b/etc/uams/uams_dhx2_pam.c index 76e9143f..5229d372 100644 --- a/etc/uams/uams_dhx2_pam.c +++ b/etc/uams/uams_dhx2_pam.c @@ -952,7 +952,6 @@ static void uam_cleanup(void) gcry_mpi_release(g); } - UAM_MODULE_EXPORT struct uam_export uams_dhx2 = { UAM_MODULE_SERVER, UAM_MODULE_VERSION, diff --git a/include/atalk/Makefile.am b/include/atalk/Makefile.am index 42085187..2f63e0f6 100644 --- a/include/atalk/Makefile.am +++ b/include/atalk/Makefile.am @@ -42,7 +42,11 @@ noinst_HEADERS = \ ftw.h \ dsi.h \ ldapconfig.h \ - fce_api.h + talloc.h \ + dalloc.h \ + byteorder.h \ + fce_api.h \ + spotlight.h EXTRA_DIST = afp_dtrace.d diff --git a/include/atalk/byteorder.h b/include/atalk/byteorder.h new file mode 100644 index 00000000..0ea3972f --- /dev/null +++ b/include/atalk/byteorder.h @@ -0,0 +1,217 @@ +/* + Unix SMB/CIFS implementation. + SMB Byte handling + Copyright (C) Andrew Tridgell 1992-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _BYTEORDER_H +#define _BYTEORDER_H +#include + +/* + This file implements macros for machine independent short and + int manipulation + +Here is a description of this file that I emailed to the samba list once: + +> I am confused about the way that byteorder.h works in Samba. I have +> looked at it, and I would have thought that you might make a distinction +> between LE and BE machines, but you only seem to distinguish between 386 +> and all other architectures. +> +> Can you give me a clue? + +sure. + +The distinction between 386 and other architectures is only there as +an optimisation. You can take it out completely and it will make no +difference. The routines (macros) in byteorder.h are totally byteorder +independent. The 386 optimsation just takes advantage of the fact that +the x86 processors don't care about alignment, so we don't have to +align ints on int boundaries etc. If there are other processors out +there that aren't alignment sensitive then you could also define +CAREFUL_ALIGNMENT=0 on those processors as well. + +Ok, now to the macros themselves. I'll take a simple example, say we +want to extract a 2 byte integer from a SMB packet and put it into a +type called uint16 that is in the local machines byte order, and you +want to do it with only the assumption that uint16 is _at_least_ 16 +bits long (this last condition is very important for architectures +that don't have any int types that are 2 bytes long) + +You do this: + +#define CVAL(buf,pos) (((unsigned char *)(buf))[pos]) +#define PVAL(buf,pos) ((unsigned)CVAL(buf,pos)) +#define SVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+1)<<8) + +then to extract a uint16 value at offset 25 in a buffer you do this: + +char *buffer = foo_bar(); +uint16 xx = SVAL(buffer,25); + +We are using the byteoder independence of the ANSI C bitshifts to do +the work. A good optimising compiler should turn this into efficient +code, especially if it happens to have the right byteorder :-) + +I know these macros can be made a bit tidier by removing some of the +casts, but you need to look at byteorder.h as a whole to see the +reasoning behind them. byteorder.h defines the following macros: + +SVAL(buf,pos) - extract a 2 byte SMB value +IVAL(buf,pos) - extract a 4 byte SMB value +LVAL(buf,pos) - extract a 8 byte SMB value +SVALS(buf,pos) signed version of SVAL() +IVALS(buf,pos) signed version of IVAL() + +SSVAL(buf,pos,val) - put a 2 byte SMB value into a buffer +SIVAL(buf,pos,val) - put a 4 byte SMB value into a buffer +SSVALS(buf,pos,val) - signed version of SSVAL() +SIVALS(buf,pos,val) - signed version of SIVAL() + +RSVAL(buf,pos) - like SVAL() but for NMB byte ordering +RSVALS(buf,pos) - like SVALS() but for NMB byte ordering +RIVAL(buf,pos) - like IVAL() but for NMB byte ordering +RIVALS(buf,pos) - like IVALS() but for NMB byte ordering +RSSVAL(buf,pos,val) - like SSVAL() but for NMB ordering +RSIVAL(buf,pos,val) - like SIVAL() but for NMB ordering +RSIVALS(buf,pos,val) - like SIVALS() but for NMB ordering + +it also defines lots of intermediate macros, just ignore those :-) + +*/ + +#undef CAREFUL_ALIGNMENT + +/* we know that the 386 can handle misalignment and has the "right" + byteorder */ +#ifdef __i386__ +#define CAREFUL_ALIGNMENT 0 +#endif + +#ifndef CAREFUL_ALIGNMENT +#define CAREFUL_ALIGNMENT 1 +#endif + +#define CVAL(buf,pos) ((unsigned)(((const unsigned char *)(buf))[pos])) +#define CVAL_NC(buf,pos) (((unsigned char *)(buf))[pos]) /* Non-const version of CVAL */ +#define PVAL(buf,pos) (CVAL(buf,pos)) +#define SCVAL(buf,pos,val) (CVAL_NC(buf,pos) = (val)) + + +#if CAREFUL_ALIGNMENT + +#ifdef WORDS_BIGENDIAN + +#define SVAL(buf,pos) (PVAL(buf,(pos)+1)|PVAL(buf,pos)<<8) +#define SVALS(buf,pos) ((int16)SVAL(buf,pos)) +#define IVAL(buf,pos) (SVAL(buf,pos)|SVAL(buf,(pos)+2)<<16) +#define IVALS(buf,pos) ((int32_t)IVAL(buf,pos)) +#define LVAL(buf,pos) (IVAL(buf,pos)|IVAL(buf,(pos)+4)<<32) +#define LVALS(buf,pos) ((int64_t)LVAL(buf,pos)) + +#define SSVALX(buf,pos,val) (CVAL_NC(buf,pos+1)=(unsigned char)((val)&0xFF),CVAL_NC(buf,pos)=(unsigned char)((val)>>8)) +#define SIVALX(buf,pos,val) (SSVALX(buf,pos,((val)&0xFFFF)),SSVALX(buf,pos+2,(val)>>16)) +#define SLVALX(buf,pos,val) (SIVALX(buf,pos,((val)&0xFFFFFFFF)),SIVALX(buf,pos+4,(val)>>32)) + +#define SSVAL(buf,pos,val) SSVALX((buf),(pos),((uint16_t)(val))) +#define SSVALS(buf,pos,val) SSVALX((buf),(pos),((int16)(val))) +#define SIVAL(buf,pos,val) SIVALX((buf),(pos),((uint32_t)(val))) +#define SIVALS(buf,pos,val) SIVALX((buf),(pos),((int32_t)(val))) +#define SLVAL(buf,pos,val) SLVALX((buf),(pos),((uint64_t)(val))) +#define SLVALS(buf,pos,val) SLVALX((buf),(pos),((int64_t)(val))) + +#else + +#define SVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+1)<<8) +#define SVALS(buf,pos) ((int16)SVAL(buf,pos)) +#define IVAL(buf,pos) (SVAL(buf,pos)|SVAL(buf,(pos)+2)<<16) +#define IVALS(buf,pos) ((int32_t)IVAL(buf,pos)) +#define LVAL(buf,pos) (IVAL(buf,pos)|((uint64_t)IVAL(buf,(pos)+4))<<32) +#define LVALS(buf,pos) ((int64_t)LVAL(buf,pos)) + +#define SSVALX(buf,pos,val) (CVAL_NC(buf,pos)=(unsigned char)((val)&0xFF),CVAL_NC(buf,pos+1)=(unsigned char)((val)>>8)) +#define SIVALX(buf,pos,val) (SSVALX(buf,pos,((val)&0xFFFF)),SSVALX(buf,pos+2,(val)>>16)) +#define SLVALX(buf,pos,val) (SIVALX(buf,pos,((val)&0xFFFFFFFF)),SIVALX(buf,pos+4,(val)>>32)) + +#define SSVAL(buf,pos,val) SSVALX((buf),(pos),((uint16_t)(val))) +#define SSVALS(buf,pos,val) SSVALX((buf),(pos),((int16)(val))) +#define SIVAL(buf,pos,val) SIVALX((buf),(pos),((uint32_t)(val))) +#define SIVALS(buf,pos,val) SIVALX((buf),(pos),((int32_t)(val))) +#define SLVAL(buf,pos,val) SLVALX((buf),(pos),((uint64_t)(val))) +#define SLVALS(buf,pos,val) SLVALX((buf),(pos),((int64_t)(val))) + +#endif + +#else /* CAREFUL_ALIGNMENT */ + +/* this handles things for architectures like the 386 that can handle + alignment errors */ +/* + WARNING: This section is dependent on the length of int16 and int32 + being correct +*/ + +/* get single value from an SMB buffer */ +#define SVAL(buf,pos) (*(const uint16_t *)((const char *)(buf) + (pos))) +#define SVAL_NC(buf,pos) (*(uint16_t *)((char *)(buf) + (pos))) /* Non const version of above. */ +#define IVAL(buf,pos) (*(const uint32_t *)((const char *)(buf) + (pos))) +#define IVAL_NC(buf,pos) (*(uint32_t *)((char *)(buf) + (pos))) /* Non const version of above. */ +#define LVAL(buf,pos) (*(const uint64_t *)((const char *)(buf) + (pos))) +#define LVAL_NC(buf,pos) (*(uint64_t *)((char *)(buf) + (pos))) +#define SVALS(buf,pos) (*(const int16_t *)((const char *)(buf) + (pos))) +#define SVALS_NC(buf,pos) (*(int16 *)((char *)(buf) + (pos))) /* Non const version of above. */ +#define IVALS(buf,pos) (*(const int32_t *)((const char *)(buf) + (pos))) +#define IVALS_NC(buf,pos) (*(int32_t *)((char *)(buf) + (pos))) /* Non const version of above. */ +#define LVALS(buf,pos) (*(const int64_t *)((const char *)(buf) + (pos))) +#define LVALS_NC(buf,pos) (*(int64_t *)((char *)(buf) + (pos))) + +/* store single value in an SMB buffer */ +#define SSVAL(buf,pos,val) SVAL_NC(buf,pos)=((uint16_t)(val)) +#define SIVAL(buf,pos,val) IVAL_NC(buf,pos)=((uint32_t)(val)) +#define SLVAL(buf,pos,val) LVAL_NC(buf,pos)=((uint64_t)(val)) +#define SSVALS(buf,pos,val) SVALS_NC(buf,pos)=((int16)(val)) +#define SIVALS(buf,pos,val) IVALS_NC(buf,pos)=((int32_t)(val)) +#define SLVALS(buf,pos,val) LVALS_NC(buf,pos)=((int64_t)(val)) + +#endif /* CAREFUL_ALIGNMENT */ + +/* now the reverse routines - these are used in nmb packets (mostly) */ +#define SREV(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF)) +#define IREV(x) ((SREV(x)<<16) | (SREV((x)>>16))) +#define LREV(x) ((IREV(x)<<32) | (IREV((x)>>32))) + +#define RSVAL(buf,pos) SREV(SVAL(buf,pos)) +#define RSVALS(buf,pos) SREV(SVALS(buf,pos)) +#define RIVAL(buf,pos) IREV(IVAL(buf,pos)) +#define RIVALS(buf,pos) IREV(IVALS(buf,pos)) +#define RLVAL(buf,pos) LREV(LVAL(buf,pos)) +#define RLVALS(buf,pos) LREV(LVALS(buf,pos)) + +#define RSSVAL(buf,pos,val) SSVAL(buf,pos,SREV(val)) +#define RSSVALS(buf,pos,val) SSVALS(buf,pos,SREV(val)) +#define RSIVAL(buf,pos,val) SIVAL(buf,pos,IREV(val)) +#define RSIVALS(buf,pos,val) SIVALS(buf,pos,IREV(val)) + +#define RSLVAL(buf,pos,val) SLVAL(buf,pos,LREV(val)) +#define RSLVALS(buf,pos,val) SLVALS(buf,pos,LREV(val)) + +/* Alignment macros. */ +#define ALIGN4(p,base) ((p) + ((4 - (PTR_DIFF((p), (base)) & 3)) & 3)) +#define ALIGN2(p,base) ((p) + ((2 - (PTR_DIFF((p), (base)) & 1)) & 1)) + +#endif /* _BYTEORDER_H */ diff --git a/include/atalk/dalloc.h b/include/atalk/dalloc.h new file mode 100644 index 00000000..4e963cd6 --- /dev/null +++ b/include/atalk/dalloc.h @@ -0,0 +1,39 @@ +/* + Copyright (c) 2012 Frank Lahm + + 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 + +/* 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 */ diff --git a/include/atalk/dsi.h b/include/atalk/dsi.h index fc1af468..11452550 100644 --- a/include/atalk/dsi.h +++ b/include/atalk/dsi.h @@ -53,7 +53,7 @@ struct dsi_block { uint32_t dsi_reserved; /* reserved field */ }; -#define DSI_DATASIZ 8192 +#define DSI_DATASIZ 65536 /* child and parent processes might interpret a couple of these * differently. */ diff --git a/include/atalk/errchk.h b/include/atalk/errchk.h index ccc66eb6..47576bec 100644 --- a/include/atalk/errchk.h +++ b/include/atalk/errchk.h @@ -19,9 +19,9 @@ #define EC_STATUS(a) ret = (a) #define EC_EXIT_STATUS(a) do { ret = (a); goto cleanup; } while (0) #define EC_FAIL do { ret = -1; goto cleanup; } while (0) -#define EC_FAIL_LOG(a, ...) \ +#define EC_FAIL_LOG(...) \ do { \ - LOG(log_error, logtype_default, a, __VA_ARGS__); \ + LOG(log_error, logtype_default, __VA_ARGS__); \ ret = -1; \ goto cleanup; \ } while (0) diff --git a/include/atalk/globals.h b/include/atalk/globals.h index 96f062a0..6f8ff68f 100644 --- a/include/atalk/globals.h +++ b/include/atalk/globals.h @@ -56,6 +56,7 @@ #define OPTION_NOZEROCONF (1 << 9) #define OPTION_SHARE_RESERV (1 << 11) /* whether to use Solaris fcntl F_SHARE locks */ #define OPTION_DBUS_AFPSTATS (1 << 12) /* whether to run dbus thread for afpstats */ +#define OPTION_SPOTLIGHT (1 << 13) /* whether to enable Spotlight support */ #define PASSWD_NONE 0 #define PASSWD_SET (1 << 0) @@ -116,6 +117,8 @@ struct afp_options { char *logfile; char *mimicmodel; char *adminauthuser; + char *slmod_path; + int tracker_loglevel; struct afp_volume_name volfile; }; diff --git a/include/atalk/logger.h b/include/atalk/logger.h index 1847c0d5..3f139655 100644 --- a/include/atalk/logger.h +++ b/include/atalk/logger.h @@ -93,6 +93,7 @@ enum logtypes { logtype_uams, logtype_fce, logtype_ad, + logtype_sl, logtype_end_of_list_marker /* don't put any logtypes after this */ }; diff --git a/include/atalk/spotlight.h b/include/atalk/spotlight.h new file mode 100644 index 00000000..56e09306 --- /dev/null +++ b/include/atalk/spotlight.h @@ -0,0 +1,123 @@ +/* + Copyright (c) 2012 Frank Lahm + + 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 +#include + +#include +#include +#include + +/************************************************************************************************** + * 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 */ + sl_array_t *slq_reqinfo; /* array with requested metadata */ + const char *slq_qstring; /* the Spotlight query string */ + uint64_t *slq_cnids; /* Pointer to array with CNIDs to which a query applies */ + size_t slq_cnids_num; /* Size of slq_cnids array */ +#ifdef HAVE_TRACKER_SPARQL + void *slq_tracker_cursor; /* Tracker SPARQL query result cursor */ +#endif +#ifdef HAVE_TRACKER_RDF + char *slq_trackerquery; /* RDF query string*/ + char *slq_fts; /* FTS search string */ + int slq_service; /* Tracker service */ + int slq_offset; /* search offset */ +#endif +} slq_t; + +/************************************************************************************************** + * Function declarations + **************************************************************************************************/ + +extern int afp_spotlight_rpc(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen); +extern int sl_pack(DALLOC_CTX *query, char *buf); +extern int sl_unpack(DALLOC_CTX *query, const char *buf); + +#endif /* SPOTLIGHT_H */ diff --git a/include/atalk/talloc.h b/include/atalk/talloc.h new file mode 100644 index 00000000..96c7e246 --- /dev/null +++ b/include/atalk/talloc.h @@ -0,0 +1,1711 @@ +#ifndef _TALLOC_H_ +#define _TALLOC_H_ +/* + Unix SMB/CIFS implementation. + Samba temporary memory allocation functions + + Copyright (C) Andrew Tridgell 2004-2005 + Copyright (C) Stefan Metzmacher 2006 + + ** NOTE! The following LGPL license applies to the talloc + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . +*/ + +#include +#include +#include + +#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 diff --git a/include/atalk/util.h b/include/atalk/util.h index facc7763..12aaba90 100644 --- a/include/atalk/util.h +++ b/include/atalk/util.h @@ -19,6 +19,7 @@ #include #include +#include /* exit error codes */ #define EXITERR_CLNT 1 /* client related error */ @@ -197,6 +198,7 @@ extern int ochmod(char *path, mode_t mode, const struct stat *st, int options); *****************************************************************/ extern bstring rel_path_in_vol(const char *path, const char *volpath); +extern cnid_t cnid_for_path(struct _cnid_db *cdb, const char *volpath, const char *path, cnid_t *did); /****************************************************************** * cnid.c diff --git a/libatalk/Makefile.am b/libatalk/Makefile.am index 732aeea8..4ad799bf 100644 --- a/libatalk/Makefile.am +++ b/libatalk/Makefile.am @@ -31,7 +31,7 @@ VERSION_INFO = 3:0:0 # 3.0.1 2:0:0 # 3.0.2 3:0:0 -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 @@ -49,6 +49,7 @@ libatalk_la_LIBADD = \ compat/libcompat.la \ dsi/libdsi.la \ iniparser/libiniparser.la \ + talloc/libtalloc.la \ tdb/libtdb.la \ unicode/libunicode.la \ util/libutil.la \ @@ -61,6 +62,7 @@ libatalk_la_DEPENDENCIES = \ cnid/libcnid.la \ compat/libcompat.la \ dsi/libdsi.la \ + talloc/libtalloc.la \ iniparser/libiniparser.la \ tdb/libtdb.la \ unicode/libunicode.la \ diff --git a/libatalk/talloc/Makefile.am b/libatalk/talloc/Makefile.am new file mode 100644 index 00000000..8616eac0 --- /dev/null +++ b/libatalk/talloc/Makefile.am @@ -0,0 +1,4 @@ +# Makefile.am for libatalk/talloc/ + +noinst_LTLIBRARIES = libtalloc.la +libtalloc_la_SOURCES = talloc.c dalloc.c diff --git a/libatalk/talloc/dalloc.c b/libatalk/talloc/dalloc.c new file mode 100644 index 00000000..4c06f065 --- /dev/null +++ b/libatalk/talloc/dalloc.c @@ -0,0 +1,174 @@ +/* + Copyright (c) 2012 Frank Lahm + + 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* 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; +} diff --git a/libatalk/talloc/talloc.c b/libatalk/talloc/talloc.c new file mode 100644 index 00000000..78dffa8e --- /dev/null +++ b/libatalk/talloc/talloc.c @@ -0,0 +1,2379 @@ +/* + Samba Unix SMB/CIFS implementation. + + Samba trivial allocation library - new interface + + NOTE: Please read talloc_guide.txt for full documentation + + Copyright (C) Andrew Tridgell 2004 + Copyright (C) Stefan Metzmacher 2006 + + ** NOTE! The following LGPL license applies to the talloc + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . +*/ + +/* + inspired by http://swapped.cc/halloc/ +*/ + +#include "config.h" + +#include +#include +#include +#include + +#include +#include +#include + +#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 +#elif defined(HAVE_VALGRIND_H) +#include +#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); +} diff --git a/libatalk/unicode/Makefile.am b/libatalk/unicode/Makefile.am index b56f9f35..abf6f326 100644 --- a/libatalk/unicode/Makefile.am +++ b/libatalk/unicode/Makefile.am @@ -17,6 +17,6 @@ libunicode_la_SOURCES = \ libunicode_la_LIBADD = $(LIBUNICODE_DEPS) -noinst_HEADERS = utf16_casetable.h precompose.h byteorder.h +noinst_HEADERS = utf16_casetable.h precompose.h LIBS=@ICONV_LIBS@ diff --git a/libatalk/unicode/byteorder.h b/libatalk/unicode/byteorder.h deleted file mode 100644 index cc9a7f02..00000000 --- a/libatalk/unicode/byteorder.h +++ /dev/null @@ -1,189 +0,0 @@ -/* - Unix SMB/CIFS implementation. - SMB Byte handling - Copyright (C) Andrew Tridgell 1992-1998 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#ifndef _BYTEORDER_H -#define _BYTEORDER_H -#include - -/* - 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 */ diff --git a/libatalk/unicode/charcnv.c b/libatalk/unicode/charcnv.c index 4e772e16..7242b576 100644 --- a/libatalk/unicode/charcnv.c +++ b/libatalk/unicode/charcnv.c @@ -42,8 +42,7 @@ #include #include #include - -#include "byteorder.h" +#include /** diff --git a/libatalk/unicode/charsets/generic_cjk.h b/libatalk/unicode/charsets/generic_cjk.h index 5dab15a6..3c4a48c7 100644 --- a/libatalk/unicode/charsets/generic_cjk.h +++ b/libatalk/unicode/charsets/generic_cjk.h @@ -19,7 +19,7 @@ #include #include -#include "../byteorder.h" +#include #define CJK_PUSH_BUFFER 4 #define CJK_PULL_BUFFER 8 diff --git a/libatalk/unicode/charsets/generic_mb.c b/libatalk/unicode/charsets/generic_mb.c index 7b36997d..0347c3e7 100644 --- a/libatalk/unicode/charsets/generic_mb.c +++ b/libatalk/unicode/charsets/generic_mb.c @@ -35,9 +35,10 @@ #include #include +#include #include "generic_mb.h" -#include "../byteorder.h" + /* ------------------------ */ diff --git a/libatalk/unicode/charsets/mac_hebrew.c b/libatalk/unicode/charsets/mac_hebrew.c index 91a022bd..34d96902 100644 --- a/libatalk/unicode/charsets/mac_hebrew.c +++ b/libatalk/unicode/charsets/mac_hebrew.c @@ -34,8 +34,8 @@ #include #include +#include -#include "../byteorder.h" #include "mac_hebrew.h" static size_t mac_hebrew_pull(void *,char **, size_t *, char **, size_t *); diff --git a/libatalk/unicode/iconv.c b/libatalk/unicode/iconv.c index 7c5eb67e..c44df315 100644 --- a/libatalk/unicode/iconv.c +++ b/libatalk/unicode/iconv.c @@ -43,7 +43,7 @@ #include #include -#include "byteorder.h" +#include /** diff --git a/libatalk/unicode/utf8.c b/libatalk/unicode/utf8.c index 896a2611..4a300398 100644 --- a/libatalk/unicode/utf8.c +++ b/libatalk/unicode/utf8.c @@ -34,7 +34,7 @@ #include #include #include -#include "byteorder.h" +#include /* Given a trailing UTF-8 byte, get the contribution from it to * the Unicode scalar value for a particular bit shift amount diff --git a/libatalk/unicode/util_unistr.c b/libatalk/unicode/util_unistr.c index 0eba9a3c..5108a4c8 100644 --- a/libatalk/unicode/util_unistr.c +++ b/libatalk/unicode/util_unistr.c @@ -22,8 +22,9 @@ #include #include +#include + #include "precompose.h" -#include "byteorder.h" /******************************************************************* Convert a string to lower case. diff --git a/libatalk/util/cnid.c b/libatalk/util/cnid.c index 0cc56d49..54dfb55c 100644 --- a/libatalk/util/cnid.c +++ b/libatalk/util/cnid.c @@ -35,7 +35,7 @@ #include #include #include - +#include #include #include #include @@ -134,3 +134,72 @@ EC_CLEANUP: return NULL; return fpath; } + +/*! + * Resolves CNID of a given path + * + * path might be: + * (a) relative: + * "dir/subdir" with cwd: "/afp_volume/topdir" + * (b) absolute: + * "/afp_volume/dir/subdir" + * + * path MUST be pointing inside vol, this is usually the case as vol has been build from + * path using loadvolinfo and friends. + * + * @param cdb (r) CNID db handle + * @param volpath (r) UNIX path of volume + * @param path (r) path, see above + * @param did (w) parent CNID of returned CNID + * + * @returns CNID of path + */ +cnid_t cnid_for_path(struct _cnid_db *cdb, + const char *volpath, + const char *path, + cnid_t *did) +{ + EC_INIT; + + cnid_t cnid; + bstring rpath = NULL; + bstring statpath = NULL; + struct bstrList *l = NULL; + struct stat st; + + cnid = htonl(2); + + EC_NULL(rpath = rel_path_in_vol(path, volpath)); + EC_NULL(statpath = bfromcstr(volpath)); + EC_ZERO(bcatcstr(statpath, "/")); + + l = bsplit(rpath, '/'); + for (int i = 0; i < l->qty ; i++) { + *did = cnid; + + EC_ZERO(bconcat(statpath, l->entry[i])); + EC_ZERO_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; +} diff --git a/libatalk/util/logger.c b/libatalk/util/logger.c index 94423426..4bf0e338 100644 --- a/libatalk/util/logger.c +++ b/libatalk/util/logger.c @@ -62,6 +62,7 @@ Netatalk 2001 (c) "UAMS", \ "FCE", \ "ad", \ + "Spotlight", \ "end_of_list_marker"} /* ========================================================================= @@ -86,8 +87,9 @@ UAM_MODULE_EXPORT logtype_conf_t type_configs[logtype_end_of_list_marker] = { DEFAULT_LOG_CONFIG, /* logtype_afpd */ DEFAULT_LOG_CONFIG, /* logtype_dsi */ DEFAULT_LOG_CONFIG, /* logtype_uams */ - DEFAULT_LOG_CONFIG, /* logtype_fce */ - DEFAULT_LOG_CONFIG /* logtype_ad */ + DEFAULT_LOG_CONFIG, /* logtype_fce */ + DEFAULT_LOG_CONFIG, /* logtype_ad */ + DEFAULT_LOG_CONFIG /* logtype_sl */ }; static void syslog_setup(int loglevel, enum logtypes logtype, int display_options, int facility); diff --git a/libatalk/util/netatalk_conf.c b/libatalk/util/netatalk_conf.c index fdc7e556..53c29e28 100644 --- a/libatalk/util/netatalk_conf.c +++ b/libatalk/util/netatalk_conf.c @@ -916,16 +916,16 @@ static struct vol *creatvol(AFPObj *obj, initvol_vfs(volume); /* get/store uuid from file in afpd master*/ - if (!(pwd) && (volume->v_flags & AFPVOL_TM)) { - char *uuid = get_vol_uuid(obj, volume->v_localname); - if (!uuid) { - LOG(log_error, logtype_afpd, "Volume '%s': couldn't get UUID", - volume->v_localname); - } else { - volume->v_uuid = uuid; - LOG(log_debug, logtype_afpd, "Volume '%s': UUID '%s'", - volume->v_localname, volume->v_uuid); - } + become_root(); + char *uuid = get_vol_uuid(obj, volume->v_localname); + unbecome_root(); + if (!uuid) { + LOG(log_error, logtype_afpd, "Volume '%s': couldn't get UUID", + volume->v_localname); + } else { + volume->v_uuid = uuid; + LOG(log_debug, logtype_afpd, "Volume '%s': UUID '%s'", + volume->v_localname, volume->v_uuid); } /* no errors shall happen beyond this point because the cleanup would mess the volume chain up */ @@ -1692,6 +1692,12 @@ int afp_config_parse(AFPObj *AFPObj, char *processname) options->configfile = AFPObj->cmdlineconfigfile ? strdup(AFPObj->cmdlineconfigfile) : strdup(_PATH_CONFDIR "afp.conf"); options->sigconffile = strdup(_PATH_STATEDIR "afp_signature.conf"); options->uuidconf = strdup(_PATH_STATEDIR "afp_voluuid.conf"); +#ifdef HAVE_TRACKER_SPARQL + options->slmod_path = strdup(_PATH_AFPDUAMPATH "slmod_sparql.so"); +#endif +#ifdef HAVE_TRACKER_RDF + options->slmod_path = strdup(_PATH_AFPDUAMPATH "slmod_rdf.so"); +#endif options->flags = OPTION_UUID | AFPObj->cmdlineflags; if ((config = iniparser_load(AFPObj->options.configfile)) == NULL) @@ -1723,6 +1729,8 @@ int afp_config_parse(AFPObj *AFPObj, char *processname) options->flags |= OPTION_DBUS_AFPSTATS; 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)) @@ -1758,6 +1766,7 @@ int afp_config_parse(AFPObj *AFPObj, char *processname) options->fce_fmodwait = iniparser_getint (config, INISEC_GLOBAL, "fce holdfmod", 60); options->sleep = iniparser_getint (config, INISEC_GLOBAL, "sleep time", 10); options->disconnected = iniparser_getint (config, INISEC_GLOBAL, "disconnect time",24); + options->tracker_loglevel = iniparser_getint (config, INISEC_GLOBAL, "tracker loglevel", 1); if ((p = iniparser_getstring(config, INISEC_GLOBAL, "hostname", NULL))) { EC_NULL_LOG( options->hostname = strdup(p) ); @@ -1949,6 +1958,8 @@ void afp_config_free(AFPObj *obj) CONFIG_ARG_FREE(obj->options.Cnid_port); if (obj->options.fqdn) CONFIG_ARG_FREE(obj->options.fqdn); + if (obj->options.slmod_path) + CONFIG_ARG_FREE(obj->options.slmod_path); if (obj->options.unixcodepage) CONFIG_ARG_FREE(obj->options.unixcodepage); diff --git a/macros/netatalk.m4 b/macros/netatalk.m4 index c190405f..b5096e37 100644 --- a/macros/netatalk.m4 +++ b/macros/netatalk.m4 @@ -77,6 +77,63 @@ AC_DEFUN([AC_DEVELOPER], [ AM_CONDITIONAL(DEVELOPER, test x"$enable_dev" = x"yes") ]) +dnl Tracker, for Spotlight +AC_DEFUN([AC_NETATALK_SPOTLIGHT], [ + ac_cv_have_tracker=no + dnl Tracker SPARQL + 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]) + + PKG_CHECK_MODULES([TRACKER], [$ac_cv_tracker_pkg >= 0.12], [ac_cv_have_tracker_sparql=yes], [ac_cv_have_tracker_sparql=no]) + + if test x"$ac_cv_have_tracker_sparql" = 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 is available]) + AC_DEFINE(HAVE_TRACKER_SPARQL, 1, [Define if Tracker SPARQL 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_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]) + + 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 Test for Tracker 0.6 on Solaris and derived platforms + if test x"$this_os" = x"solaris" ; then + PKG_CHECK_MODULES([TRACKER], [tracker >= 0.6], [ac_cv_have_tracker_rdf=yes], [ac_cv_have_tracker_rdf=no]) + if test x"$ac_cv_have_tracker_rdf" = x"yes" ; then + AC_DEFINE(HAVE_TRACKER, 1, [Define if Tracker is available]) + AC_DEFINE(HAVE_TRACKER_RDF, 1, [Define if Tracker 0.6 with support for RDF queries is available]) + ac_cv_tracker_prefix=`pkg-config --variable=prefix tracker` + AC_DEFINE_UNQUOTED(TRACKER_RDF_PREFIX, ["$ac_cv_tracker_prefix"], [Path to Tracker]) + fi + fi + + if test x"$ac_cv_have_tracker_sparql" = x"yes" -o x"$ac_cv_have_tracker_rdf" = x"yes" ; then + ac_cv_have_tracker=yes + fi + AC_SUBST(TRACKER_CFLAGS) + AC_SUBST(TRACKER_LIBS) + AM_CONDITIONAL(HAVE_TRACKER_SPARQL, [test x"$ac_cv_have_tracker_sparql" = x"yes"]) + AM_CONDITIONAL(HAVE_TRACKER_RDF, [test x"$ac_cv_have_tracker_rdf" = x"yes"]) +]) + dnl Whether to disable bundled libevent AC_DEFUN([AC_NETATALK_LIBEVENT], [ AC_MSG_CHECKING([whether to use bundled libevent]) diff --git a/macros/summary.m4 b/macros/summary.m4 index 7f5e6499..b5093a04 100644 --- a/macros/summary.m4 +++ b/macros/summary.m4 @@ -12,6 +12,7 @@ AC_DEFUN([AC_NETATALK_CONFIG_SUMMARY], [ AC_MSG_RESULT([ AFP:]) AC_MSG_RESULT([ Extended Attributes: $neta_cv_eas]) AC_MSG_RESULT([ ACL support: $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:]) diff --git a/test/afpd/Makefile.am b/test/afpd/Makefile.am index 34e28f48..6e7c2f46 100644 --- a/test/afpd/Makefile.am +++ b/test/afpd/Makefile.am @@ -38,6 +38,8 @@ test_SOURCES = test.c subtests.c afpfunc_helpers.c \ $(top_srcdir)/etc/afpd/ofork.c \ $(top_srcdir)/etc/afpd/quota.c \ $(top_srcdir)/etc/afpd/status.c \ + $(top_srcdir)/etc/afpd/spotlight.c \ + $(top_srcdir)/etc/afpd/spotlight_marshalling.c \ $(top_srcdir)/etc/afpd/switch.c \ $(top_srcdir)/etc/afpd/uam.c \ $(top_srcdir)/etc/afpd/unix.c \