From 3a93dc5da82a9d2c08ee584c487582651dbb3dce Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Mon, 10 Jan 2022 20:00:29 +0100 Subject: [PATCH] [daap/rsp/smartpl] Drop ANTLR parsers Replacing the antlr parsers solves multiple issues: - Build warnings (issue #307) - Build hacks: "-Xconversiontimeout 30000" and other Makefile magic - Incorrect parsing of daap queries with sql wildcards (like 'tag:*tes%t_ng*') - Infinite recursion/memory/CPU (issue #570 and #1248) - systemd service file workarounds due to memory/CPU issue - ANTLR3 being replaced with ANTLR4 (that doesn't support C file generation) - Runtime dependency on antlr library - Difficult installs of ANTLR3 on some systems (special install script) --- INSTALL.md | 54 +---- configure.ac | 24 +- owntone.spec.in | 5 +- scripts/antlr35_install.sh | 255 -------------------- scripts/freebsd_install.sh | 24 -- src/.gitignore | 6 - src/DAAP.g | 63 ----- src/DAAP2SQL.g | 383 ------------------------------ src/Makefile.am | 54 +---- src/RSP.g | 148 ------------ src/RSP2SQL.g | 474 ------------------------------------- src/SMARTPL.g | 240 ------------------- src/SMARTPL2SQL.g | 427 --------------------------------- src/daap_query.c | 122 +--------- src/rsp_query.c | 116 +-------- src/smartpl_query.c | 170 +------------ 16 files changed, 15 insertions(+), 2550 deletions(-) delete mode 100755 scripts/antlr35_install.sh delete mode 100644 src/DAAP.g delete mode 100644 src/DAAP2SQL.g delete mode 100644 src/RSP.g delete mode 100644 src/RSP2SQL.g delete mode 100644 src/SMARTPL.g delete mode 100644 src/SMARTPL2SQL.g diff --git a/INSTALL.md b/INSTALL.md index bf60685e..88eacac0 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -19,11 +19,11 @@ libraries: ```bash sudo apt-get install \ build-essential git autotools-dev autoconf automake libtool gettext gawk \ - gperf antlr3 libantlr3c-dev libconfuse-dev libunistring-dev libsqlite3-dev \ + gperf libconfuse-dev libunistring-dev libsqlite3-dev libprotobuf-c-dev \ libavcodec-dev libavformat-dev libavfilter-dev libswscale-dev libavutil-dev \ libasound2-dev libmxml-dev libgcrypt20-dev libavahi-client-dev zlib1g-dev \ libevent-dev libplist-dev libsodium-dev libjson-c-dev libwebsockets-dev \ - libcurl4-openssl-dev libprotobuf-c-dev + libcurl4-openssl-dev ``` Note that OwnTone will also work with other versions and flavours of @@ -83,7 +83,7 @@ sudo yum install \ sqlite-devel libconfuse-devel libunistring-devel mxml-devel libevent-devel \ avahi-devel libgcrypt-devel zlib-devel alsa-lib-devel ffmpeg-devel \ libplist-devel libsodium-devel json-c-devel libwebsockets-devel \ - libcurl-devel protobuf-c-devel antlr3 antlr3-C-devel + libcurl-devel protobuf-c-devel ``` Clone the OwnTone repo: @@ -184,12 +184,6 @@ git clone https://github.com/owntone/owntone-server.git cd owntone-server ``` -Install antlr3 and library using the included script: - -```bash -scripts/antlr35_install.sh -p /usr/local -``` - Finally, configure, build and install, adding configure arguments for optional features: @@ -229,11 +223,6 @@ dns-sd -B _daap._tcp Required tools: -- ANTLR v3 is required to build OwnTone, along with its C runtime - (libantlr3c). Use a version between 3.1.3 and 3.5 of ANTLR v3 and the - matching C runtime version. Get it from -- Java runtime: ANTLR is written in Java and as such a JRE is required to - run the tool. The JRE is enough, you don't need a full JDK. - autotools: autoconf 2.63+, automake 1.10+, libtool 2.2. Run `autoreconf -i` at the top of the source tree to generate the build system. - gettext: libunistring requires iconv and gettext provides the autotools @@ -242,8 +231,6 @@ Required tools: Libraries: -- libantlr3c (ANTLR3 C runtime, use the same version as antlr3) - from - Avahi client libraries (avahi-client), 0.6.24 minimum from - sqlite3 3.5.0+ with unlock notify API enabled (read below) @@ -297,22 +284,6 @@ documentation, look for `SQLITE_ENABLE_UNLOCK_NOTIFY`. Start by generating the build system by running `autoreconf -i`. This will generate the configure script and `Makefile.in`. -The configure script will look for a wrapper called antlr3 in the PATH to -invoke ANTLR3. If your installation of ANTLR3 does not come with such a -wrapper, create one as follows: - -```bash -#!/bin/sh -CLASSPATH=... -exec /path/to/java -cp $CLASSPATH org.antlr.Tool "$@" -``` - -Adjust the `CLASSPATH` as needed so that Java will find all the jars needed -by ANTLR3. - -The parsers will be generated during the build, no manual intervention is -needed. - To display the configure options `run ./configure --help`. Support for Spotify is optional. Use `--disable-spotify` to disable this feature. @@ -371,25 +342,6 @@ Use `--disable-install-systemd` if you don't want that. Using `--enable-install-user` means that `make install` will also add a system user and group for owntone. -You may see two kinds of warnings during make. -First, `/usr/bin/antlr3` may generate a long series of warnings that -begin like this: - -```log -warning(24): template error: context ... -``` - -Second, you may see compiler warnings that look like this: - -```log -RSPLexer.c: In function `mESCAPED': -RSPLexer.c:2674:16: warning: unused variable `_type' [-Wunused-variable] - ANTLR3_UINT32 _type; - ^~~~~ -``` - -You can safely ignore all of these warnings. - After installation: - edit the configuration file, `/etc/owntone.conf` diff --git a/configure.ac b/configure.ac index 4148e6bb..9aca3b9f 100644 --- a/configure.ac +++ b/configure.ac @@ -27,22 +27,10 @@ AS_IF([[test -z "$GPERF"]], AC_MSG_NOTICE([[ GNU gperf not found, but it's output appears to be present. -If you modify any gperf or ANTLR grammar files (.g), you will need -to install it.]])], +If you modify any .gperf files, you will need to install it.]])], [AC_MSG_ERROR([[GNU gperf required, please install it.]])]) ]) -AC_PATH_PROG([ANTLR], [[antlr3]]) -AS_IF([[test -z "$ANTLR"]], - [AS_IF([[test -f "$srcdir/src/SMARTPLLexer.h"]], - [AM_MISSING_PROG([ANTLR], [[antlr3]]) - AC_MSG_NOTICE([[ - -antlr3 not found, but it's output appears to be present. -If you modify any ANTLR grammar files (.g), you will need to install it.]])], - [AC_MSG_ERROR([[antlr3 wrapper required, please install it.]])]) - ]) - dnl Enable all warnings by default. AM_CPPFLAGS="-Wall" AC_SUBST([AM_CPPFLAGS]) @@ -174,16 +162,6 @@ PKG_CHECK_EXISTS([libplist], [OWNTONE_MODULES_CHECK([OWNTONE], [LIBPLIST], [libplist-2.0], [plist_dict_get_item], [plist/plist.h])]) - -OWNTONE_FUNC_REQUIRE([OWNTONE], [ANTLR3 C runtime], [ANTLR3C], [antlr3c], - [antlr3BaseRecognizerNew], [antlr3.h], - [AC_CHECK_FUNC([[antlr3NewAsciiStringInPlaceStream]], - [AC_DEFINE([ANTLR3C_NEW_INPUT], 0, - [define if antlr3 C runtime uses new input routines])], - [AC_DEFINE([ANTLR3C_NEW_INPUT], 1, - [define if antlr3 C runtime uses new input routines])]) - ]) - AM_PATH_LIBGCRYPT([1:1.2.0]) OWNTONE_FUNC_REQUIRE([OWNTONE], [GNU Crypt Library], [LIBGCRYPT], [gcrypt], [gcry_control], [gcrypt.h]) diff --git a/owntone.spec.in b/owntone.spec.in index 02910919..5e0d9053 100644 --- a/owntone.spec.in +++ b/owntone.spec.in @@ -21,7 +21,7 @@ Source0: https://github.com/owntone/%{name}/archive/%{version}/%{name}-%{version BuildRequires: gcc, make, systemd, pkgconfig, libunistring-devel BuildRequires: pkgconfig(zlib), pkgconfig(libconfuse), pkgconfig(mxml) BuildRequires: pkgconfig(sqlite3) >= 3.5.0, pkgconfig(libevent) >= 2.0.0 -BuildRequires: pkgconfig(json-c), antlr3-C-devel, libgcrypt-devel >= 1.2.0 +BuildRequires: pkgconfig(json-c), libgcrypt-devel >= 1.2.0 BuildRequires: libgpg-error-devel >= 1.6 BuildRequires: pkgconfig(libavformat), pkgconfig(libavcodec) BuildRequires: pkgconfig(libswscale), pkgconfig(libavutil) @@ -109,6 +109,9 @@ exit 0 %{_mandir}/man?/* %changelog +* Mon Jan 10 2022 Espen Jürgensen - 28.3-1 + - Remove antlr dependency + * Mon Nov 22 2021 Derek Atkins - 28.2-1 - Release tarball is a XZ not GZ file - Configure always needs protobuf-c, not just for chromecast diff --git a/scripts/antlr35_install.sh b/scripts/antlr35_install.sh deleted file mode 100755 index 00b0b839..00000000 --- a/scripts/antlr35_install.sh +++ /dev/null @@ -1,255 +0,0 @@ -#!/bin/sh - -WORKDIR=~/antlr35.tmp - -# programs -MAKE=${MAKE-make} -DOWNLOAD="wget --no-check-certificate" -ALTDOWNLOAD="curl -LO" -SUDO=sudo - -# source -ANTLR_VERSION=3.5 - -ANTLR3_SOURCE="https://github.com/antlr/website-antlr3/raw/gh-pages/download" -ANTLR3_JAR="antlr-3.5.2-complete.jar" -ANTLR3_URL="$ANTLR3_SOURCE/$ANTLR3_JAR" - -LIBANTLR3C="libantlr3c-3.4" -LIBANTLR3C_SOURCE="https://github.com/antlr/website-antlr3/raw/gh-pages/download/C" -LIBANTLR3C_TAR="${LIBANTLR3C}.tar.gz" -LIBANTLR3C_URL="$LIBANTLR3C_SOURCE/$LIBANTLR3C_TAR" - -usage() { - echo - echo "This script will download, build and install antlr $ANTLR_VERSION" - echo " (and matching libantlrc) on your computer." - echo - echo "Usage: ${0##*/} -h | [ -p ] [ -y ] [ ]" - echo - echo "Parameters:" - echo " -h Show this help" - echo " -p Install to prefix (default: choose /usr or /usr/local)" - echo " -y Automatic yes to prompts (run non-interactively with -p)" - echo " Build directory (default: $WORKDIR)" - exit 0 -} - -GIVEN_PREFIX= -ALWAYS_YES= -while [ "$1" != "" ]; do - case $1 in - -p | --prefix ) - shift - GIVEN_PREFIX=$1 - ;; - -y | --yes ) - ALWAYS_YES=1 - ;; - -h | --help ) - usage - exit - ;; - * ) - echo "Unrecognized option $1 (try -h for usage)" - exit 1 - ;; - esac - shift -done - -# override build directory? (support ~ expansion) -[ -n "$1" ] && WORKDIR=$1 -ORIG_DIR=`pwd` - -err() { - echo "$*" - if [ -n "$FILES_EXIST" ]; then - echo "Files remain in $WORKDIR..." - else - cd "$ORIG_DIR" - rmdir "$WORKDIR" - fi - exit 1 -} - -is_yes() { - case "$1" in - [N]*|[n]*) return 1;; - *) ;; - esac - return 0 -} - -ask_yn() { - if [ "$ALWAYS_YES" = "1" ]; then - yn="y" - else - read -p "$1" yn - fi -} - -prog_install() { - ask_yn "Would you like to install into $PREFIX now? [Y/n] " - if ! is_yes "$yn"; then - echo "Build left ready to install from $WORKDIR" - echo "You can re-run the script (eg. as root) to install into" - echo " $PREFIX later." - exit - fi - if [ `id -u` -ne 0 ]; then - ask_yn "Would you like to install with sudo? NOTE: You WILL be asked for your password! [Y/n] " - if ! is_yes "$yn"; then - SUDO= - ask_yn "Continue to install as non-root user? [Y/n] " - is_yes "$yn" || err "Install cancelled" - fi - else - SUDO= - fi - cd $LIBANTLR3C || err "Unable to cd to build libantlr3c build directory!" - echo "Installing libantlr3c to $PREFIX" - $SUDO $MAKE install || err "Install of libantlr3c to $PREFIX failed!" - - cd "$ORIG_DIR" - cd $WORKDIR - echo "Installing antlr3 to $PREFIX" - $SUDO mkdir -p "$PREFIX_JAVA" || err "Unable to create $PREFIX_JAVA" - $SUDO install "$ANTLR3_JAR" "$PREFIX_JAVA" || \ - err "Failed to install antlr3 jar to $PREFIX_JAVA" - $SUDO mkdir -p "$PREFIX/bin" || err "Unable to create $PREFIX/bin" - $SUDO install -m 755 antlr3 "$PREFIX/bin" || \ - err "Failed to install antlr3 to $PREFIX/bin" - echo "Install complete (build remains in $WORKDIR)" -} - -echo "This script will download, build and install antlr $ANTLR_VERSION" -echo " (and matching libantlrc) on your computer." -echo - -# check if make works -ISGNU=`$MAKE --version 2>/dev/null | grep "GNU Make"` -if [ -z "$ISGNU" ]; then - MAKE=gmake - ISGNU=`$MAKE --version 2>/dev/null | grep "GNU Make"` -fi -[ -z "$ISGNU" ] && err "Unable to locate GNU Make, set \$MAKE to it's location and re-run" - -if [ -f "$WORKDIR/install_env" ]; then - echo "Existing build found in $WORKDIR" - FILES_EXIST=1 - cd $WORKDIR || err "Unable to cd to '$WORKDIR'" - . install_env - [ -n "$PREFIX" ] || err "PREFIX is missing in file 'install_env'" - if [ -n "$GIVEN_PREFIX" ] && [ "$GIVEN_PREFIX" != "$PREFIX" ]; then - echo "You must rebuild to install into $GIVEN_PREFIX (current build for $PREFIX)" - ask_yn "Would you like to rebuild for ${GIVEN_PREFIX}? [Y/n] " - if is_yes "$yn"; then - rm -f install_env - PREFIX= - else - ask_yn "Would you like to install to ${PREFIX}? [Y/n] " - ! is_yes "$yn" && err "Install cancelled" - fi - fi - if [ -n "$PREFIX" ]; then - PREFIX_JAVA=$PREFIX/share/java - prog_install - exit 0 - fi -fi - -if [ ! -d "$WORKDIR" ]; then - ask_yn "Should the script create $WORKDIR and use it for building? [Y/n] " - is_yes "$yn" || exit -fi - -if [ -n "$GIVEN_PREFIX" ]; then - PREFIX=$GIVEN_PREFIX -else - read -p "Should the script install with prefix /usr or /usr/local? [U/l] " yn - if [ "$yn" = "l" ]; then - PREFIX=/usr/local - else - PREFIX=/usr - fi -fi -PREFIX_JAVA=$PREFIX/share/java - -MACHBITS=`getconf LONG_BIT 2>/dev/null` -[ "$MACHBITS" = "64" ] && DEF_AN="[Y/n]" || DEF_AN="[y/N]" -ask_yn "Should the script build libantlr3c for 64 bit? $DEF_AN " -[ -z "$yn" -a "$MACHBITS" != "64" ] && yn=n -is_yes "$yn" && ENABLE64BIT="--enable-64bit" - -mkdir -p "$WORKDIR" || err "Error creating $WORKDIR" -# don't quote WORKDIR to catch a WORKDIR that will break the build (eg spaces) -cd $WORKDIR || err "Unable to cd to '$WORKDIR' (does it include spaces?)" - -REMOVE_ON_CANCEL= -cancel_download() { - echo "removing $REMOVE_ON_CANCEL" - [ -n "$REMOVE_ON_CANCEL" ] && rm -f "$REMOVE_ON_CANCEL" - err "Cancelling download..." -} - -antlr_download() { - trap cancel_download SIGINT - $DOWNLOAD --help >/dev/null 2>&1 || DOWNLOAD=$ALTDOWNLOAD - $DOWNLOAD --help >/dev/null 2>&1 || { - echo "Unable to find wget or curl commands to download source," - echo " please install either one and re-try." - exit 1 - } - [ "x$1" = "xreset" ] && rm "$ANTLR3_JAR" "$LIBANTLR3C_TAR" - if [ ! -f "$ANTLR3_JAR" ]; then - echo - echo "Downloading antlr from $ANTLR3_URL" - echo "Ctrl-C to abort..." - REMOVE_ON_CANCEL=$ANTLR3_JAR - $DOWNLOAD "$ANTLR3_URL" || err "Download of $ANTLR3_JAR failed!" - FILES_EXIST=1 - fi - if [ ! -f "$LIBANTLR3C_TAR" ]; then - echo - echo "Downloading libantlr3c from $LIBANTLR3C_URL" - echo "Ctrl-C to abort..." - REMOVE_ON_CANCEL=$LIBANTLR3C_TAR - $DOWNLOAD "$LIBANTLR3C_URL" || err "Download of $LIBANTLR3C_TAR failed!" - FILES_EXIST=1 - fi - trap - SIGINT -} - -# retrieve the source -if [ -f "$ANTLR3_JAR" -a -f "$LIBANTLR3C_TAR" ]; then - FILES_EXIST=1 - ask_yn "Files appear to already be downloaded, use them? [Y/n] " - ! is_yes "$yn" && antlr_download reset -else - ask_yn "Should the script download and build antlr and libantlr3c? [Y/n] " - is_yes "$yn" || exit - antlr_download -fi - -# build/install libantlr3c -[ -d "$LIBANTLR3C" ] && rm -rf "$LIBANTLR3C" -tar xzf "$LIBANTLR3C_TAR" || err "Uncompress of $LIBANTLR3C_TAR failed!" -cd $LIBANTLR3C || err "Unable to cd to build $LIBANTLR3C build directory!" -./configure $ENABLE64BIT --prefix=$PREFIX && $MAKE -[ $? -ne 0 ] && err "Build of libantlr3c failed!" - -# install antlr3 jar and wrapper -cd "$ORIG_DIR" -cd $WORKDIR -printf "#!/bin/sh -export CLASSPATH -CLASSPATH=\$CLASSPATH:$PREFIX_JAVA/${ANTLR3_JAR}:$PREFIX_JAVA -/usr/bin/java org.antlr.Tool \$* -" > antlr3 - -# save for later install attempts -echo "PREFIX=$PREFIX" > install_env -echo - -prog_install diff --git a/scripts/freebsd_install.sh b/scripts/freebsd_install.sh index f569b692..2e79ed49 100644 --- a/scripts/freebsd_install.sh +++ b/scripts/freebsd_install.sh @@ -46,30 +46,6 @@ if [ ! -d $WORKDIR ]; then fi cd $WORKDIR -read -p "Should the script install antlr and libantlr3c? [y/N] " yn -if [ "$yn" = "y" ]; then - read -p "Should the script build libantlr3c for 64 bit? [Y/n] " yn - if [ "$yn" != "n" ]; then - ENABLE64BIT="--enable-64bit" - fi - - wget --no-check-certificate https://github.com/antlr/website-antlr3/raw/gh-pages/download/antlr-3.4-complete.jar - wget --no-check-certificate https://github.com/antlr/website-antlr3/raw/gh-pages/download/C/libantlr3c-3.4.tar.gz - - sudo install antlr-3.4-complete.jar /usr/local/share/java - printf "#!/bin/sh -export CLASSPATH -CLASSPATH=\$CLASSPATH:/usr/local/share/java/antlr-3.4-complete.jar:/usr/local/share/java -/usr/local/bin/java org.antlr.Tool \$* -" > antlr3 - sudo install -m 755 antlr3 /usr/local/bin - - tar xzf libantlr3c-3.4.tar.gz - cd libantlr3c-3.4 - ./configure $ENABLE64BIT && gmake && sudo gmake install - cd $WORKDIR -fi - read -p "Should the script build owntone? [y/N] " yn if [ "$yn" = "y" ]; then git clone https://github.com/owntone/owntone-server.git diff --git a/src/.gitignore b/src/.gitignore index 2e610b18..4580a8b5 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -1,11 +1,5 @@ owntone -*.tokens -*Lexer.[ch] -*Parser.[ch] -*2SQL.[ch] -*.u - daap_query_hash.h rsp_query_hash.h dacp_prop_hash.h diff --git a/src/DAAP.g b/src/DAAP.g deleted file mode 100644 index a6013d89..00000000 --- a/src/DAAP.g +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2009-2010 Julien BLACHE - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -grammar DAAP; - -options { - output = AST; - ASTLabelType = pANTLR3_BASE_TREE; - language = C; -} - -query : expr NEWLINE? EOF -> expr - ; - -expr : aexpr (OPOR^ aexpr)* - ; - -aexpr : crit (OPAND^ crit)* - ; - -crit : LPAR expr RPAR -> expr - | STR - ; - -QUOTE : '\''; -LPAR : '('; -RPAR : ')'; - -OPAND : '+' | ' '; -OPOR : ','; - -NEWLINE : '\r'? '\n'; - -/* -Unescaping adapted from (ported to the C runtime) - -*/ -STR -@init{ pANTLR3_STRING unesc = GETTEXT()->factory->newRaw(GETTEXT()->factory); } - : QUOTE ( reg = ~('\\' | '\'') { unesc->addc(unesc, reg); } - | esc = ESCAPED { unesc->appendS(unesc, GETTEXT()); } )+ QUOTE { SETTEXT(unesc); }; - -fragment -ESCAPED : '\\' - ( '\\' { SETTEXT(GETTEXT()->factory->newStr8(GETTEXT()->factory, (pANTLR3_UINT8)"\\")); } - | '\'' { SETTEXT(GETTEXT()->factory->newStr8(GETTEXT()->factory, (pANTLR3_UINT8)"\'")); } - ) - ; diff --git a/src/DAAP2SQL.g b/src/DAAP2SQL.g deleted file mode 100644 index 55364a1f..00000000 --- a/src/DAAP2SQL.g +++ /dev/null @@ -1,383 +0,0 @@ -/* - * Copyright (C) 2009-2011 Julien BLACHE - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -tree grammar DAAP2SQL; - -options { - tokenVocab = DAAP; - ASTLabelType = pANTLR3_BASE_TREE; - language = C; -} - -@header { - #include - #include - #include - #include - #include - - #include "logger.h" - #include "db.h" - #include "daap_query.h" -} - -@members { - struct dmap_query_field_map { - char *dmap_field; - char *db_col; - int as_int; - }; - - /* gperf static hash, daap_query.gperf */ - #include "daap_query_hash.h" -} - -query returns [ pANTLR3_STRING result ] -@init { $result = NULL; } - : e = expr - { - if (!$e.valid) - { - $result = NULL; - } - else - { - $result = $e.result->factory->newRaw($e.result->factory); - $result->append8($result, "("); - $result->appendS($result, $e.result); - $result->append8($result, ")"); - } - } - ; - -expr returns [ pANTLR3_STRING result, int valid ] -@init { $result = NULL; $valid = 1; } - : ^(OPAND a = expr b = expr) - { - if ($a.valid && $b.valid) - { - $result = $a.result->factory->newRaw($a.result->factory); - $result->append8($result, "("); - $result->appendS($result, $a.result); - $result->append8($result, " AND "); - $result->appendS($result, $b.result); - $result->append8($result, ")"); - } - else if ($a.valid) - { - $result = $a.result->factory->newRaw($a.result->factory); - $result->appendS($result, $a.result); - } - else if ($b.valid) - { - $result = $b.result->factory->newRaw($b.result->factory); - $result->appendS($result, $b.result); - } - else - { - $valid = 0; - } - } - | ^(OPOR a = expr b = expr) - { - if ($a.valid && $b.valid) - { - $result = $a.result->factory->newRaw($a.result->factory); - $result->append8($result, "("); - $result->appendS($result, $a.result); - $result->append8($result, " OR "); - $result->appendS($result, $b.result); - $result->append8($result, ")"); - } - else if ($a.valid) - { - $result = $a.result->factory->newRaw($a.result->factory); - $result->appendS($result, $a.result); - } - else if ($b.valid) - { - $result = $b.result->factory->newRaw($b.result->factory); - $result->appendS($result, $b.result); - } - else - { - $valid = 0; - } - } - | STR - { - pANTLR3_STRING str; - pANTLR3_UINT8 field; - pANTLR3_UINT8 val; - pANTLR3_UINT8 escaped; - ANTLR3_UINT8 op; - int neg_op; - const struct dmap_query_field_map *dqfm; - char *end; - long long llval; - - escaped = NULL; - - $result = $STR.text->factory->newRaw($STR.text->factory); - - str = $STR.text->toUTF8($STR.text); - - /* NOTE: the lexer delivers the string without quotes - which may not be obvious from the grammar due to embedded code - */ - - /* Make daap.songalbumid:0 a no-op */ - if (strcmp((char *)str->chars, "daap.songalbumid:0") == 0) - { - $result->append8($result, "1 = 1"); - - goto STR_out; - } - - field = str->chars; - - val = field; - while ((*val != '\0') && ((*val == '.') - || (*val == '-') - || ((*val >= 'a') && (*val <= 'z')) - || ((*val >= 'A') && (*val <= 'Z')) - || ((*val >= '0') && (*val <= '9')))) - { - val++; - } - - if (*field == '\0') - { - DPRINTF(E_LOG, L_DAAP, "No field name found in clause '\%s'\n", field); - $valid = 0; - goto STR_result_valid_0; /* ABORT */ - } - - if (*val == '\0') - { - DPRINTF(E_LOG, L_DAAP, "No operator found in clause '\%s'\n", field); - $valid = 0; - goto STR_result_valid_0; /* ABORT */ - } - - op = *val; - *val = '\0'; - val++; - - if (op == '!') - { - if (*val == '\0') - { - DPRINTF(E_LOG, L_DAAP, "Negation found but operator missing in clause '\%s\%c'\n", field, op); - $valid = 0; - goto STR_result_valid_0; /* ABORT */ - } - - neg_op = 1; - op = *val; - val++; - } - else - neg_op = 0; - - /* Lookup DMAP field in the query field map */ - dqfm = daap_query_field_lookup((char *)field, strlen((char *)field)); - if (!dqfm) - { - DPRINTF(E_LOG, L_DAAP, "DMAP field '\%s' is not a valid field in queries\n", field); - $valid = 0; - goto STR_result_valid_0; /* ABORT */ - } - - /* Empty values OK for string fields, NOK for integer */ - if (*val == '\0') - { - if (dqfm->as_int) - { - DPRINTF(E_LOG, L_DAAP, "No value given in clause '\%s\%s\%c'\n", field, (neg_op) ? "!" : "", op); - $valid = 0; - goto STR_result_valid_0; /* ABORT */ - } - - /* No need to exclude empty artist and album, as the server makes sure there always exists an artist/album. */ - if (neg_op && (op == ':' || op == '@') - && (strcmp((char *)field, "daap.songalbumartist") == 0 - || strcmp((char *)field, "daap.songartist") == 0 - || strcmp((char *)field, "daap.songalbum") == 0)) - { - DPRINTF(E_DBG, L_DAAP, "Ignoring clause '\%s\%s\%c'\n", field, (neg_op) ? "!" : "", op); - $valid = 0; - goto STR_result_valid_0; - } - - /* Need to check against NULL too */ - if (op == ':' || op == '@') - $result->append8($result, "("); - } - - /* Int field: check integer conversion */ - if (dqfm->as_int) - { - errno = 0; - llval = strtoll((const char *)val, &end, 10); - - if (((errno == ERANGE) && ((llval == LLONG_MAX) || (llval == LLONG_MIN))) - || ((errno != 0) && (llval == 0))) - { - DPRINTF(E_LOG, L_DAAP, "Value '\%s' in clause '\%s\%s\%c\%s' does not convert to an integer type\n", - val, field, (neg_op) ? "!" : "", op, val); - $valid = 0; - goto STR_result_valid_0; /* ABORT */ - } - - if (end == (char *)val) - { - DPRINTF(E_LOG, L_DAAP, "Value '\%s' in clause '\%s\%s\%c\%s' does not represent an integer value\n", - val, field, (neg_op) ? "!" : "", op, val); - $valid = 0; - goto STR_result_valid_0; /* ABORT */ - } - - *end = '\0'; /* Cut out potential garbage - we're being kind */ - - /* The server only has media_kind = 1 for music - so remove media_kind = 32 to imporve select query performance. */ - if (llval == 32 - && (strcmp((char *)field, "com.apple.itunes.mediakind") == 0 - || strcmp((char *)field, "com.apple.itunes.extended-media-kind") == 0)) - { - DPRINTF(E_DBG, L_DAAP, "Ignoring clause '\%s\%s\%c\%s'\n", field, (neg_op) ? "!" : "", op, val); - - if (neg_op) - $result->append8($result, "1 = 1"); - else - $result->append8($result, "1 = 0"); - - goto STR_out; - } - } - /* String field: escape string, check for '*' */ - else - { - /* With Apple Music 1.0.1.37, we observed some ':' queries using '@' instead, resulting in 'Unknown operator' errors */ - /* Ex: '/databases/1/containers?query=('com.apple.itunes.extended-media-kind@1', ...) */ - if (op != ':' && op != '@') - { - DPRINTF(E_LOG, L_DAAP, "Operation '\%c' not valid for string values\n", op); - $valid = 0; - goto STR_result_valid_0; /* ABORT */ - } - - escaped = (pANTLR3_UINT8)db_escape_string((char *)val); - if (!escaped) - { - DPRINTF(E_LOG, L_DAAP, "Could not escape value\n"); - $valid = 0; - goto STR_result_valid_0; /* ABORT */ - } - - val = escaped; - - if (val[0] == '*') - { - op = '\%'; - val[0] = '\%'; - } - - if (val[0] && val[1] && val[strlen((char *)val) - 1] == '*') - { - op = '\%'; - val[strlen((char *)val) - 1] = '\%'; - } - } - - $result->append8($result, dqfm->db_col); - - switch(op) - { - case '@': - case ':': - if (neg_op) - $result->append8($result, " <> "); - else - $result->append8($result, " = "); - break; - - case '+': - if (neg_op) - $result->append8($result, " <= "); - else - $result->append8($result, " > "); - break; - - case '-': - if (neg_op) - $result->append8($result, " >= "); - else - $result->append8($result, " < "); - break; - - case '\%': - $result->append8($result, " LIKE "); - break; - - default: - if (neg_op) - DPRINTF(E_LOG, L_DAAP, "Missing or unknown operator '\%c' in clause '\%s!\%c\%s'\n", op, field, op, val); - else - DPRINTF(E_LOG, L_DAAP, "Unknown operator '\%c' in clause '\%s\%c\%s'\n", op, field, op, val); - $valid = 0; - goto STR_result_valid_0; /* ABORT */ - break; - } - - if (!dqfm->as_int) - $result->append8($result, "'"); - - $result->append8($result, (const char *)val); - - if (!dqfm->as_int) - $result->append8($result, "'"); - - /* For empty string value, we need to check against NULL too */ - if ((*val == '\0') && (op == ':' || op == '@')) - { - if (neg_op) - $result->append8($result, " AND "); - else - $result->append8($result, " OR "); - - $result->append8($result, dqfm->db_col); - - if (neg_op) - $result->append8($result, " IS NOT NULL"); - else - $result->append8($result, " IS NULL"); - - $result->append8($result, ")"); - } - - STR_result_valid_0: /* bail out label */ - ; - - if (escaped) - free(escaped); - - STR_out: /* get out of here */ - ; - } - ; diff --git a/src/Makefile.am b/src/Makefile.am index 10afdffa..bc1962ef 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -62,28 +62,6 @@ GPERF_FILES = \ GPERF_SRC = $(GPERF_FILES:.gperf=_hash.h) -ANTLR_GRAMMARS = \ - RSP.g RSP2SQL.g \ - DAAP.g DAAP2SQL.g \ - SMARTPL.g SMARTPL2SQL.g - -ANTLR_TOKENS = $(ANTLR_GRAMMARS:.g=.tokens) - -ANTLR_DEPS = $(ANTLR_GRAMMARS:%.g=$(srcdir)/%.u) - -ANTLR_SRC = \ - RSPLexer.c RSPLexer.h RSPParser.c RSPParser.h \ - RSP2SQL.c RSP2SQL.h \ - DAAPLexer.c DAAPLexer.h DAAPParser.c DAAPParser.h \ - DAAP2SQL.c DAAP2SQL.h \ - SMARTPLLexer.c SMARTPLLexer.h SMARTPLParser.c SMARTPLParser.h \ - SMARTPL2SQL.c SMARTPL2SQL.h - -ANTLR_OBJECTS = \ - RSPLexer.$(OBJEXT) RSPParser.$(OBJEXT) RSP2SQL.$(OBJEXT) \ - DAAPLexer.$(OBJEXT) DAAPParser.$(OBJEXT) DAAP2SQL.$(OBJEXT) \ - SMARTPLLexer.$(OBJEXT) SMARTPLParser.$(OBJEXT) SMARTPL2SQL.$(OBJEXT) - AM_CPPFLAGS += \ $(OWNTONE_CPPFLAGS) \ $(OWNTONE_OPTS_CPPFLAGS) \ @@ -152,42 +130,16 @@ owntone_SOURCES = main.c \ mxml-compat.h \ outputs/plist_wrap.h \ $(LIBWEBSOCKETS_SRC) \ - $(GPERF_SRC) \ - $(ANTLR_SRC) + $(GPERF_SRC) # built by maintainers, and distributed. Clean with maintainer-clean BUILT_SOURCES = \ - $(GPERF_SRC) \ - $(ANTLR_SRC) \ - $(ANTLR_TOKENS) \ - $(ANTLR_DEPS) + $(GPERF_SRC) EXTRA_DIST = \ - $(GPERF_FILES) \ - $(ANTLR_GRAMMARS) \ - $(ANTLR_TOKENS) \ - $(ANTLR_DEPS) - -# silence unused warnings from antlr generated files -$(ANTLR_OBJECTS): AM_CPPFLAGS += -Wno-unused + $(GPERF_FILES) # gperf construction rules %_hash.h: %.gperf $(AM_V_GEN)$(GPERF) --output-file=$@ $< -# silent rules for antlr -antlr_verbose = $(antlr_verbose_@AM_V@) -antlr_verbose_ = $(antlr_verbose_@AM_DEFAULT_V@) -antlr_verbose_0 = @echo " GEN " $< "products"; - -# ANTLR grammar products -%.tokens %.c %Lexer.c %Parser.c %Lexer.h %Parser.h %.h: %.g - $(antlr_verbose)$(ANTLR) -Xconversiontimeout 30000 $(ANTLR_OPTIONS) -fo . $< - -# ANTLR dependency files (bypass circular dependency of .g on .tokens) -%.u: %.g - $(AM_V_GEN)$(ANTLR) -depend -fo . $< > $@ - $(AM_V_at)$(SED) -n -e '/^.*\.g[ ]*:\(.*\)/ { s//\1/;h;d; }' -e '/\.tokens.*:/ { p;d; }' -e '/:/ { G;s/\n/ /;p; }' $@ > $@-t - $(AM_V_at)mv $@-t $@ - --include $(ANTLR_DEPS) diff --git a/src/RSP.g b/src/RSP.g deleted file mode 100644 index 6a27536c..00000000 --- a/src/RSP.g +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (C) 2009-2010 Julien BLACHE - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -grammar RSP; - -options { - output = AST; - ASTLabelType = pANTLR3_BASE_TREE; - language = C; -} - -query : expr NEWLINE? EOF -> expr - ; - -expr : aexpr (OR^ aexpr)* - ; - -aexpr : crit (AND^ crit)* - ; - -crit : LPAR expr RPAR -> expr - | strcrit - | intcrit - | datecrit - ; - -strcrit : FIELD strop STR -> ^(strop FIELD STR) - | FIELD NOT strop STR -> ^(NOT ^(strop FIELD STR)) - ; - -strop : equal=EQUAL - | includes=INCLUDES - | startsw=STARTSW - | endsw=ENDSW - ; - -intcrit : FIELD intop INT -> ^(intop FIELD INT) - | FIELD NOT intop INT -> ^(NOT ^(intop FIELD INT)) - ; - -intop : equal=EQUAL - | less=LESS - | greater=GREATER - | lte=LTE - | gte=GTE - ; - -datecrit: FIELD dateop datespec -> ^(dateop FIELD datespec) - ; - -dateop : before=BEFORE - | after=AFTER - ; - -datespec: dateref - | INT dateintval dateop dateref -> ^(dateop dateref INT dateintval) - ; - -dateref : date=DATE - | today=TODAY - ; - -dateintval - : day=DAY - | week=WEEK - | month=MONTH - | year=YEAR - ; - -QUOTE : '"'; -LPAR : '('; -RPAR : ')'; - -AND : 'and'; -OR : 'or'; -NOT : '!'; - -/* Both string & int */ -EQUAL : '='; - -/* String */ -INCLUDES: 'includes'; -STARTSW : 'startswith'; -ENDSW : 'endswith'; - -/* Int */ -GREATER : '>'; -LESS : '<'; -GTE : '>='; -LTE : '<='; - -/* Date */ -BEFORE : 'before'; -AFTER : 'after'; -DAY : 'day' | 'days'; -WEEK : 'week' | 'weeks'; -MONTH : 'month' | 'months'; -YEAR : 'year' | 'years'; -TODAY : 'today'; - -NEWLINE : '\r'? '\n'; - -WS : (' ' | '\t') { $channel = HIDDEN; }; - -FIELD : 'a'..'z' ('a'..'z' | '_')* 'a'..'z'; - -INT : DIGIT19 DIGIT09*; - -/* YYYY-MM-DD */ -DATE : DIGIT19 DIGIT09 DIGIT09 DIGIT09 '-' ('0' DIGIT19 | '1' '0'..'2') '-' ('0' DIGIT19 | '1'..'2' DIGIT09 | '3' '0'..'1'); - -/* -Unescaping adapted from (ported to the C runtime) - -*/ -STR -@init{ pANTLR3_STRING unesc = GETTEXT()->factory->newRaw(GETTEXT()->factory); } - : QUOTE ( reg = ~('\\' | '"') { unesc->addc(unesc, reg); } - | esc = ESCAPED { unesc->appendS(unesc, GETTEXT()); } )+ QUOTE { SETTEXT(unesc); } - ; - -fragment -ESCAPED : '\\' - ( '\\' { SETTEXT(GETTEXT()->factory->newStr8(GETTEXT()->factory, (pANTLR3_UINT8)"\\")); } - | '"' { SETTEXT(GETTEXT()->factory->newStr8(GETTEXT()->factory, (pANTLR3_UINT8)"\"")); } - ) - ; - -fragment -DIGIT09 : '0'..'9'; - -fragment -DIGIT19 : '1'..'9'; diff --git a/src/RSP2SQL.g b/src/RSP2SQL.g deleted file mode 100644 index 78c67593..00000000 --- a/src/RSP2SQL.g +++ /dev/null @@ -1,474 +0,0 @@ -/* - * Copyright (C) 2009-2011 Julien BLACHE - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -tree grammar RSP2SQL; - -options { - tokenVocab = RSP; - ASTLabelType = pANTLR3_BASE_TREE; - language = C; -} - -@header { - /* Needs #define _GNU_SOURCE for strptime() */ - - #include - #include - #include - #include - - #include "logger.h" - #include "db.h" - #include "misc.h" - #include "rsp_query.h" -} - -@members { - #define RSP_TYPE_STRING 0 - #define RSP_TYPE_INT 1 - #define RSP_TYPE_DATE 2 - - struct rsp_query_field_map { - char *rsp_field; - int field_type; - /* RSP fields are named after the DB columns - or vice versa */ - }; - - /* gperf static hash, rsp_query.gperf */ - #include "rsp_query_hash.h" -} - -query returns [ pANTLR3_STRING result ] -@init { $result = NULL; } - : e = expr - { - if (!$e.valid) - { - $result = NULL; - } - else - { - $result = $e.result->factory->newRaw($e.result->factory); - $result->append8($result, "("); - $result->appendS($result, $e.result); - $result->append8($result, ")"); - } - } - ; - -expr returns [ pANTLR3_STRING result, int valid ] -@init { $result = NULL; $valid = 1; } - : ^(AND a = expr b = expr) - { - if (!$a.valid || !$b.valid) - { - $valid = 0; - } - else - { - $result = $a.result->factory->newRaw($a.result->factory); - $result->append8($result, "("); - $result->appendS($result, $a.result); - $result->append8($result, " AND "); - $result->appendS($result, $b.result); - $result->append8($result, ")"); - } - } - | ^(OR a = expr b = expr) - { - if (!$a.valid || !$b.valid) - { - $valid = 0; - } - else - { - $result = $a.result->factory->newRaw($a.result->factory); - $result->append8($result, "("); - $result->appendS($result, $a.result); - $result->append8($result, " OR "); - $result->appendS($result, $b.result); - $result->append8($result, ")"); - } - } - | c = strcrit - { - $valid = $c.valid; - $result = $c.result; - } - | ^(NOT c = strcrit) - { - if (!$c.valid || !$c.result) - { - $valid = 0; - } - else - { - $result = $c.result->factory->newRaw($c.result->factory); - $result->append8($result, "(NOT "); - $result->appendS($result, $c.result); - $result->append8($result, ")"); - } - } - | i = intcrit - { - $valid = $i.valid; - $result = $i.result; - } - | ^(NOT i = intcrit) - { - if (!$i.valid || !$i.result) - { - $valid = 0; - } - else - { - $result = $i.result->factory->newRaw($i.result->factory); - $result->append8($result, "(NOT "); - $result->appendS($result, $i.result); - $result->append8($result, ")"); - } - } - | d = datecrit - { - $valid = $d.valid; - $result = $d.result; - } - ; - -strcrit returns [ pANTLR3_STRING result, int valid ] -@init { $result = NULL; $valid = 1; } - : ^(o = strop f = FIELD s = STR) - { - char *op; - const struct rsp_query_field_map *rqfp; - pANTLR3_STRING field; - char *escaped; - ANTLR3_UINT32 optok; - - escaped = NULL; - - op = NULL; - optok = $o.op->getType($o.op); - switch (optok) - { - case EQUAL: - op = " = "; - break; - - case INCLUDES: - case STARTSW: - case ENDSW: - op = " LIKE "; - break; - } - - field = $f->getText($f); - - /* Field lookup */ - rqfp = rsp_query_field_lookup((char *)field->chars, strlen((char *)field->chars)); - if (!rqfp) - { - DPRINTF(E_LOG, L_RSP, "Field '\%s' is not a valid field in queries\n", field->chars); - $valid = 0; - goto strcrit_valid_0; /* ABORT */ - } - - /* Check field type */ - if (rqfp->field_type != RSP_TYPE_STRING) - { - DPRINTF(E_LOG, L_RSP, "Field '\%s' is not a string field\n", field->chars); - $valid = 0; - goto strcrit_valid_0; /* ABORT */ - } - - escaped = db_escape_string((char *)$s->getText($s)->chars); - if (!escaped) - { - DPRINTF(E_LOG, L_RSP, "Could not escape value\n"); - $valid = 0; - goto strcrit_valid_0; /* ABORT */ - } - - $result = field->factory->newRaw(field->factory); - $result->append8($result, "f."); - $result->appendS($result, field); - $result->append8($result, op); - $result->append8($result, "'"); - if ((optok == INCLUDES) || (optok == STARTSW)) - $result->append8($result, "\%"); - - $result->append8($result, escaped); - - if ((optok == INCLUDES) || (optok == ENDSW)) - $result->append8($result, "\%"); - $result->append8($result, "'"); - - strcrit_valid_0: - ; - - if (escaped) - free(escaped); - } - ; - -strop returns [ pANTLR3_COMMON_TOKEN op ] -@init { $op = NULL; } - : n = EQUAL - { $op = $n->getToken($n); } - | n = INCLUDES - { $op = $n->getToken($n); } - | n = STARTSW - { $op = $n->getToken($n); } - | n = ENDSW - { $op = $n->getToken($n); } - ; - -intcrit returns [ pANTLR3_STRING result, int valid ] -@init { $result = NULL; $valid = 1; } - : ^(o = intop f = FIELD i = INT) - { - char *op; - const struct rsp_query_field_map *rqfp; - pANTLR3_STRING field; - - op = NULL; - switch ($o.op->getType($o.op)) - { - case EQUAL: - op = " = "; - break; - - case LESS: - op = " < "; - break; - - case GREATER: - op = " > "; - break; - - case LTE: - op = " <= "; - break; - - case GTE: - op = " >= "; - break; - } - - field = $f->getText($f); - - /* Field lookup */ - rqfp = rsp_query_field_lookup((char *)field->chars, strlen((char *)field->chars)); - if (!rqfp) - { - DPRINTF(E_LOG, L_RSP, "Field '\%s' is not a valid field in queries\n", field->chars); - $valid = 0; - goto intcrit_valid_0; /* ABORT */ - } - - /* Check field type */ - if (rqfp->field_type != RSP_TYPE_INT) - { - DPRINTF(E_LOG, L_RSP, "Field '\%s' is not an integer field\n", field->chars); - $valid = 0; - goto intcrit_valid_0; /* ABORT */ - } - - $result = field->factory->newRaw(field->factory); - $result->append8($result, "f."); - $result->appendS($result, field); - $result->append8($result, op); - $result->appendS($result, $i->getText($i)); - - intcrit_valid_0: - ; - } - ; - -intop returns [ pANTLR3_COMMON_TOKEN op ] -@init { $op = NULL; } - : n = EQUAL - { $op = $n->getToken($n); } - | n = LESS - { $op = $n->getToken($n); } - | n = GREATER - { $op = $n->getToken($n); } - | n = LTE - { $op = $n->getToken($n); } - | n = GTE - { $op = $n->getToken($n); } - ; - -datecrit returns [ pANTLR3_STRING result, int valid ] -@init { $result = NULL; $valid = 1; } - : ^(o = dateop f = FIELD d = datespec) - { - char *op; - const struct rsp_query_field_map *rqfp; - pANTLR3_STRING field; - char buf[32]; - int ret; - - op = NULL; - switch ($o.op->getType($o.op)) - { - case BEFORE: - op = " < "; - break; - - case AFTER: - op = " > "; - break; - } - - field = $f->getText($f); - - /* Field lookup */ - rqfp = rsp_query_field_lookup((char *)field->chars, strlen((char *)field->chars)); - if (!rqfp) - { - DPRINTF(E_LOG, L_RSP, "Field '\%s' is not a valid field in queries\n", field->chars); - $valid = 0; - goto datecrit_valid_0; /* ABORT */ - } - - /* Check field type */ - if (rqfp->field_type != RSP_TYPE_DATE) - { - DPRINTF(E_LOG, L_RSP, "Field '\%s' is not a date field\n", field->chars); - $valid = 0; - goto datecrit_valid_0; /* ABORT */ - } - - ret = snprintf(buf, sizeof(buf), "\%ld", $d.date); - if ((ret < 0) || (ret >= sizeof(buf))) - { - DPRINTF(E_LOG, L_RSP, "Date \%ld too large for buffer, oops!\n", $d.date); - $valid = 0; - goto datecrit_valid_0; /* ABORT */ - } - - $result = field->factory->newRaw(field->factory); - $result->append8($result, "f."); - $result->appendS($result, field); - $result->append8($result, op); - $result->append8($result, buf); - - datecrit_valid_0: - ; - } - ; - -dateop returns [ pANTLR3_COMMON_TOKEN op ] -@init { $op = NULL; } - : n = BEFORE - { $op = $n->getToken($n); } - | n = AFTER - { $op = $n->getToken($n); } - ; - -datespec returns [ time_t date, int valid ] -@init { $date = 0; $valid = 1; } - : r = dateref - { - if (!$r.valid) - $valid = 0; - else - $date = $r.date; - } - | ^(o = dateop r = dateref m = INT i = dateintval) - { - int32_t val; - int ret; - - if (!$r.valid || !$i.valid) - { - $valid = 0; - goto datespec_valid_0; /* ABORT */ - } - - ret = safe_atoi32((char *)$m->getText($m)->chars, &val); - if (ret < 0) - { - DPRINTF(E_LOG, L_RSP, "Could not convert '\%s' to integer\n", (char *)$m->getText($m)); - $valid = 0; - goto datespec_valid_0; /* ABORT */ - } - - switch ($o.op->getType($o.op)) - { - case BEFORE: - $date = $r.date - (val * $i.period); - break; - - case AFTER: - $date = $r.date + (val * $i.period); - break; - } - - datespec_valid_0: - ; - } - ; - -dateref returns [ time_t date, int valid ] -@init { $date = 0; $valid = 1; } - : n = DATE - { - struct tm tm; - char *ret; - - ret = strptime((char *)$n->getText($n), "\%Y-\%m-\%d", &tm); - if (!ret) - { - DPRINTF(E_LOG, L_RSP, "Date '\%s' could not be interpreted\n", (char *)$n->getText($n)); - $valid = 0; - goto dateref_valid_0; /* ABORT */ - } - else - { - if (*ret != '\0') - DPRINTF(E_LOG, L_RSP, "Garbage at end of date '\%s' ?!\n", (char *)$n->getText($n)); - - $date = mktime(&tm); - if ($date == (time_t) -1) - { - DPRINTF(E_LOG, L_RSP, "Date '\%s' could not be converted to an epoch\n", (char *)$n->getText($n)); - $valid = 0; - goto dateref_valid_0; /* ABORT */ - } - } - - dateref_valid_0: - ; - } - | TODAY - { $date = time(NULL); } - ; - -dateintval returns [ time_t period, int valid ] -@init { $period = 0; $valid = 1; } - : DAY - { $period = 24 * 60 * 60; } - | WEEK - { $period = 7 * 24 * 60 * 60; } - | MONTH - { $period = 30 * 24 * 60 * 60; } - | YEAR - { $period = 365 * 24 * 60 * 60; } - ; diff --git a/src/SMARTPL.g b/src/SMARTPL.g deleted file mode 100644 index 9275145c..00000000 --- a/src/SMARTPL.g +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Copyright (C) 2015 Christian Meffert - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -grammar SMARTPL; - -options { - output = AST; - ASTLabelType = pANTLR3_BASE_TREE; - language = C; -} - -playlist : STR '{' expression '}' EOF - ; - -expression : aexpr (OR^ aexpr)* (HAVING^ aexpr)? (ORDERBY^ orderexpr)? (LIMIT^ limitexpr)? - ; - -orderexpr : ordertag SORTDIR - ; - -ordertag : STRTAG - | INTTAG - | DATETAG - | ENUMTAG - | RANDOMTAG - | (XXX)? NeverUsedRule - ; - -NeverUsedRule: /* antlr3 seems to have a problem with ordertag, introducing the NeverUsedRule fixes it. See: https://stackoverflow.com/questions/20057063/follow-set-in-is-undefined-in-generated-parser */ - ; - -XXX : 'XXX' /**/ - ; - -limitexpr : INT - ; - -aexpr : nexpr (AND^ nexpr)* - ; - -nexpr : NOT^ crit - | crit - ; - -crit : LPAR expression RPAR -> expression - | STRTAG (INCLUDES|IS|STARTSWITH) STR - | INTTAG INTBOOL INT - | DATETAG (AFTER|BEFORE) dateval - | ENUMTAG IS ENUMVAL - | GROUPTAG INTBOOL INT - ; - -dateval : DATE - | interval BEFORE DATE - | interval AFTER DATE - | interval AGO - ; - -interval : INT DATINTERVAL - ; - -STRTAG : 'artist' - | 'album_artist' - | 'album' - | 'title' - | 'genre' - | 'composer' - | 'path' - | 'type' - | 'grouping' - | 'artist_id' - | 'album_id' - | 'songartistid' - | 'songalbumid' - | 'codectype' - | 'comment' - ; - -INTTAG : 'play_count' - | 'skip_count' - | 'rating' - | 'year' - | 'compilation' - | 'track' - | 'disc' - | 'bitrate' - | 'bits_per_sample' - | 'samplerate' - | 'song_length' - | 'usermark' - ; - -DATETAG : 'time_added' - | 'time_modified' - | 'time_played' - | 'time_skipped' - | 'date_released' - ; - -ENUMTAG : 'data_kind' - | 'media_kind' - | 'scan_kind' - ; - -GROUPTAG : 'track_count' - | 'album_count' - ; - -RANDOMTAG : 'random' - ; - -INCLUDES : 'includes' - ; - -IS : 'is' - ; - -STARTSWITH : 'starts with' - ; - -INTBOOL : (GREATER|GREATEREQUAL|LESS|LESSEQUAL|EQUAL) - ; - -fragment -GREATER : '>' - ; - -fragment -GREATEREQUAL: '>=' - ; - -fragment -LESS : '<' - ; - -fragment -LESSEQUAL : '<=' - ; - -fragment -EQUAL : '=' - ; - -AFTER : 'after' - ; - -BEFORE : 'before' - ; - -AGO : 'ago' - ; - -AND : 'AND' - | 'and' - ; - -OR : 'OR' - | 'or' - ; - -NOT : 'NOT' - | 'not' - ; - -LPAR : '(' - ; - -RPAR : ')' - ; - -DATE : ('0'..'9')('0'..'9')('0'..'9')('0'..'9')'-'('0'..'1')('0'..'9')'-'('0'..'3')('0'..'9') - | 'today' - | 'yesterday' - | 'last week' - | 'last month' - | 'last year' - ; - -DATINTERVAL : 'days' - | 'weeks' - | 'months' - | 'years' - ; - -ENUMVAL : 'music' - | 'movie' - | 'podcast' - | 'audiobook' - | 'tvshow' - | 'file' - | 'url' - | 'spotify' - | 'pipe' - | 'files' - | 'rss' - ; - -ORDERBY : 'order by' - | 'ORDER BY' - ; - -SORTDIR : 'asc' - | 'ASC' - | 'desc' - | 'DESC' - ; - -LIMIT : 'limit' - | 'LIMIT' - ; - -HAVING : 'having' - | 'HAVING' - ; - -STR : '"' ~('"')+ '"' - ; - -INT : ('0'..'9')+ - ; - -WHITESPACE : ('\t'|' '|'\r'|'\n'|'\u000C') { $channel = HIDDEN; } - ; - - diff --git a/src/SMARTPL2SQL.g b/src/SMARTPL2SQL.g deleted file mode 100644 index 1a763364..00000000 --- a/src/SMARTPL2SQL.g +++ /dev/null @@ -1,427 +0,0 @@ -/* - * Copyright (C) 2015 Christian Meffert - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -tree grammar SMARTPL2SQL; - -options { - tokenVocab = SMARTPL; - ASTLabelType = pANTLR3_BASE_TREE; - language = C; -} - -@header { - #include - #include - #include - #include - #include - #include - #include - - #include "logger.h" - #include "db.h" -} - -@members { - static void append_date(pANTLR3_STRING result, const char *datval, char beforeorafter, const char *interval) - { - if (strcmp((char *)datval, "today") == 0) - { - result->append8(result, "strftime('\%s', datetime('now', 'start of day'"); - } - else if (strcmp((char *)datval, "yesterday") == 0) - { - result->append8(result, "strftime('\%s', datetime('now', 'start of day', '-1 day'"); - } - else if (strcmp((char *)datval, "last week") == 0) - { - result->append8(result, "strftime('\%s', datetime('now', 'start of day', 'weekday 0', '-13 days'"); - } - else if (strcmp((char *)datval, "last month") == 0) - { - result->append8(result, "strftime('\%s', datetime('now', 'start of month', '-1 month'"); - } - else if (strcmp((char *)datval, "last year") == 0) - { - result->append8(result, "strftime('\%s', datetime('now', 'start of year', '-1 year'"); - } - else - { - result->append8(result, "strftime('\%s', datetime(\'"); - result->append8(result, datval); - result->append8(result, "\'"); - } - - if (beforeorafter) - { - result->append8(result, ", '"); - result->addc(result, beforeorafter); - result->append8(result, interval); - result->addc(result, '\''); - } - result->append8(result, ", 'utc'))"); - } -} - -playlist returns [ pANTLR3_STRING title, pANTLR3_STRING query, pANTLR3_STRING orderby, pANTLR3_STRING having, int limit ] -@init { $title = NULL; $query = NULL; $orderby = NULL; $having = NULL; $limit = -1; } - : STR '{' e = expression '}' - { - pANTLR3_UINT8 val; - val = $STR.text->toUTF8($STR.text)->chars; - val++; - val[strlen((const char *)val) - 1] = '\0'; - - $title = $STR.text->factory->newRaw($STR.text->factory); - $title->append8($title, (const char *)val); - - $query = $e.result->factory->newRaw($e.result->factory); - $query->append8($query, "("); - $query->appendS($query, $e.result); - $query->append8($query, ")"); - - $limit = $e.limit; - - $orderby = $e.result->factory->newRaw($e.result->factory); - $orderby->appendS($orderby, $e.orderby); - - $having = $e.result->factory->newRaw($e.result->factory); - $having->appendS($having, $e.having); - } - ; - -expression returns [ pANTLR3_STRING result, pANTLR3_STRING orderby, pANTLR3_STRING having, int limit ] -@init { $result = NULL; $orderby = NULL; $having = NULL; $limit = -1; } - : ^(LIMIT a = expression INT) - { - $result = $a.result->factory->newRaw($a.result->factory); - $result->appendS($result, $a.result); - - $having = $a.result->factory->newRaw($a.result->factory); - $having->appendS($having, $a.having); - - $orderby = $a.result->factory->newRaw($a.result->factory); - $orderby->appendS($orderby, $a.orderby); - - $limit = atoi((const char *)$INT.text->chars); - } - | ^(ORDERBY a = expression o = ordertag SORTDIR) - { - $result = $a.result->factory->newRaw($a.result->factory); - $result->appendS($result, $a.result); - - $having = $a.result->factory->newRaw($a.result->factory); - $having->appendS($having, $a.having); - - $orderby = $o.result->factory->newRaw($o.result->factory); - $orderby->appendS($orderby, $o.result); - $orderby->append8($orderby, " "); - $orderby->appendS($orderby, $SORTDIR.text->toUTF8($SORTDIR.text)); - } - | ^(HAVING a = expression b = expression) - { - $result = $a.result->factory->newRaw($a.result->factory); - $result->appendS($result, $a.result); - - $having = $b.result->factory->newRaw($b.result->factory); - $having->appendS($having, $b.result); - } - | ^(NOT a = expression) - { - $result = $a.result->factory->newRaw($a.result->factory); - $result->append8($result, "NOT("); - $result->appendS($result, $a.result); - $result->append8($result, ")"); - } - | ^(AND a = expression b = expression) - { - $result = $a.result->factory->newRaw($a.result->factory); - $result->append8($result, "("); - $result->appendS($result, $a.result); - $result->append8($result, " AND "); - $result->appendS($result, $b.result); - $result->append8($result, ")"); - } - | ^(OR a = expression b = expression) - { - $result = $a.result->factory->newRaw($a.result->factory); - $result->append8($result, "("); - $result->appendS($result, $a.result); - $result->append8($result, " OR "); - $result->appendS($result, $b.result); - $result->append8($result, ")"); - } - | STRTAG INCLUDES STR - { - pANTLR3_UINT8 val; - char *tmp; - - val = $STR.text->toUTF8($STR.text)->chars; - val++; - val[strlen((const char *)val) - 1] = '\0'; - - tmp = sqlite3_mprintf("\%q", (const char *)val); - - $result = $STR.text->factory->newRaw($STR.text->factory); - $result->append8($result, "f."); - $result->appendS($result, $STRTAG.text->toUTF8($STRTAG.text)); - $result->append8($result, " LIKE '\%"); - $result->append8($result, tmp); - $result->append8($result, "\%'"); - - sqlite3_free(tmp); - } - | STRTAG IS STR - { - pANTLR3_UINT8 val; - char *tmp; - - val = $STR.text->toUTF8($STR.text)->chars; - val++; - val[strlen((const char *)val) - 1] = '\0'; - - tmp = sqlite3_mprintf("\%q", (const char *)val); - - $result = $STR.text->factory->newRaw($STR.text->factory); - $result->append8($result, "f."); - $result->appendS($result, $STRTAG.text->toUTF8($STRTAG.text)); - $result->append8($result, " LIKE '"); - $result->append8($result, tmp); - $result->append8($result, "'"); - - sqlite3_free(tmp); - } - | STRTAG STARTSWITH STR - { - pANTLR3_UINT8 val; - char *tmp; - - val = $STR.text->toUTF8($STR.text)->chars; - val++; - val[strlen((const char *)val) - 1] = '\0'; - - tmp = sqlite3_mprintf("\%q", (const char *)val); - - $result = $STR.text->factory->newRaw($STR.text->factory); - $result->append8($result, "f."); - $result->appendS($result, $STRTAG.text->toUTF8($STRTAG.text)); - $result->append8($result, " LIKE '"); - $result->append8($result, tmp); - $result->append8($result, "\%'"); - - sqlite3_free(tmp); - } - | INTTAG INTBOOL INT - { - $result = $INTTAG.text->factory->newRaw($INTTAG.text->factory); - $result->append8($result, "f."); - $result->appendS($result, $INTTAG.text->toUTF8($INTTAG.text)); - $result->append8($result, " "); - $result->appendS($result, $INTBOOL.text->toUTF8($INTBOOL.text)); - $result->append8($result, " "); - $result->appendS($result, $INT.text->toUTF8($INT.text)); - } - | DATETAG AFTER dateval - { - $result = $DATETAG.text->factory->newRaw($DATETAG.text->factory); - $result->append8($result, "f."); - $result->appendS($result, $DATETAG.text->toUTF8($DATETAG.text)); - $result->append8($result, " > "); - $result->append8($result, (const char*)$dateval.result->chars); - } - | DATETAG BEFORE dateval - { - $result = $DATETAG.text->factory->newRaw($DATETAG.text->factory); - $result->append8($result, "f."); - $result->appendS($result, $DATETAG.text->toUTF8($DATETAG.text)); - $result->append8($result, " < "); - $result->append8($result, (const char*)$dateval.result->chars); - } - | ENUMTAG IS ENUMVAL - { - pANTLR3_UINT8 tag; - pANTLR3_UINT8 val; - char str[20]; - - sprintf(str, "1=1"); - - tag = $ENUMTAG.text->chars; - val = $ENUMVAL.text->chars; - if (strcmp((char *)tag, "media_kind") == 0) - { - if (strcmp((char *)val, "music") == 0) - { - sprintf(str, "f.media_kind = \%d", MEDIA_KIND_MUSIC); - } - else if (strcmp((char *)val, "movie") == 0) - { - sprintf(str, "f.media_kind = \%d", MEDIA_KIND_MOVIE); - } - else if (strcmp((char *)val, "podcast") == 0) - { - sprintf(str, "f.media_kind = \%d", MEDIA_KIND_PODCAST); - } - else if (strcmp((char *)val, "audiobook") == 0) - { - sprintf(str, "f.media_kind = \%d", MEDIA_KIND_AUDIOBOOK); - } - else if (strcmp((char *)val, "tvshow") == 0) - { - sprintf(str, "f.media_kind = \%d", MEDIA_KIND_TVSHOW); - } - } - else if (strcmp((char *)tag, "data_kind") == 0) - { - if (strcmp((char *)val, "file") == 0) - { - sprintf(str, "f.data_kind = \%d", DATA_KIND_FILE); - } - else if (strcmp((char *)val, "url") == 0) - { - sprintf(str, "f.data_kind = \%d", DATA_KIND_HTTP); - } - else if (strcmp((char *)val, "spotify") == 0) - { - sprintf(str, "f.data_kind = \%d", DATA_KIND_SPOTIFY); - } - else if (strcmp((char *)val, "pipe") == 0) - { - sprintf(str, "f.data_kind = \%d", DATA_KIND_PIPE); - } - } - else if (strcmp((char *)tag, "scan_kind") == 0) - { - if (strcmp((char *)val, "files") == 0) - { - sprintf(str, "f.scan_kind = \%d", SCAN_KIND_FILES); - } - else if (strcmp((char *)val, "spotify") == 0) - { - sprintf(str, "f.scan_kind = \%d", SCAN_KIND_SPOTIFY); - } - else if (strcmp((char *)val, "rss") == 0) - { - sprintf(str, "f.scan_kind = \%d", SCAN_KIND_RSS); - } - } - - $result = $ENUMTAG.text->factory->newRaw($ENUMTAG.text->factory); - $result->append8($result, str); - } - | GROUPTAG INTBOOL INT - { - $result = $GROUPTAG.text->factory->newRaw($GROUPTAG.text->factory); - $result->appendS($result, $GROUPTAG.text->toUTF8($GROUPTAG.text)); - $result->append8($result, " "); - $result->appendS($result, $INTBOOL.text->toUTF8($INTBOOL.text)); - $result->append8($result, " "); - $result->appendS($result, $INT.text->toUTF8($INT.text)); - } - ; - -ordertag returns [ pANTLR3_STRING result ] -@init { $result = NULL; } - : STRTAG - { - $result = $STRTAG.text->factory->newRaw($STRTAG.text->factory); - $result->append8($result, "f."); - $result->appendS($result, $STRTAG.text->toUTF8($STRTAG.text)); - } - | INTTAG - { - $result = $INTTAG.text->factory->newRaw($INTTAG.text->factory); - $result->append8($result, "f."); - $result->appendS($result, $INTTAG.text->toUTF8($INTTAG.text)); - } - | DATETAG - { - $result = $DATETAG.text->factory->newRaw($DATETAG.text->factory); - $result->append8($result, "f."); - $result->appendS($result, $DATETAG.text->toUTF8($DATETAG.text)); - } - | ENUMTAG - { - $result = $ENUMTAG.text->factory->newRaw($ENUMTAG.text->factory); - $result->append8($result, "f."); - $result->appendS($result, $ENUMTAG.text->toUTF8($ENUMTAG.text)); - } - | RANDOMTAG - { - $result = $RANDOMTAG.text->factory->newRaw($RANDOMTAG.text->factory); - $result->append8($result, "random()"); - } - ; - -dateval returns [ pANTLR3_STRING result ] -@init { $result = NULL; } - : DATE - { - pANTLR3_UINT8 datval; - - datval = $DATE.text->chars; - $result = $DATE.text->factory->newRaw($DATE.text->factory); - - append_date($result, (const char *)datval, 0, NULL); - } - | interval BEFORE DATE - { - $result = $DATE.text->factory->newRaw($DATE.text->factory); - append_date($result, (const char *)$DATE.text->chars, '-', (const char *)$interval.result->chars); - } - | interval AFTER DATE - { - $result = $DATE.text->factory->newRaw($DATE.text->factory); - append_date($result, (const char *)$DATE.text->chars, '+', (const char *)$interval.result->chars); - } - | interval AGO - { - $result = $AGO.text->factory->newRaw($AGO.text->factory); - append_date($result, "today", '-', (const char *)$interval.result->chars); - } - ; - -interval returns [ pANTLR3_STRING result ] -@init { $result = NULL; } - : INT DATINTERVAL - { - pANTLR3_UINT8 interval; - int intval; - char buf[25]; - - $result = $DATINTERVAL.text->factory->newRaw($DATINTERVAL.text->factory); - - // SQL doesnt have a modifer for 'week' but for day/hr/min/sec/month/yr - interval = $DATINTERVAL.text->chars; - if (strcmp((char *)interval, "weeks") == 0) - { - intval = atoi((const char *)$INT.text->chars) * 7; - snprintf(buf, sizeof(buf), "\%d days", intval); - - $result->append8($result, buf); - } - else - { - $result->append8($result, (const char *)$INT.text->chars); - $result->append8($result, " "); - $result->append8($result, (const char *)$DATINTERVAL.text->chars); - } - return $result; - } - ; - - diff --git a/src/daap_query.c b/src/daap_query.c index 42881fe9..79711363 100644 --- a/src/daap_query.c +++ b/src/daap_query.c @@ -29,129 +29,9 @@ #include "misc.h" #include "daap_query.h" -#include "DAAPLexer.h" -#include "DAAPParser.h" -#include "DAAP2SQL.h" - char * daap_query_parse_sql(const char *daap_query) { - /* Input DAAP query, fed to the lexer */ - pANTLR3_INPUT_STREAM query; - - /* Lexer and the resulting token stream, fed to the parser */ - pDAAPLexer lxr; - pANTLR3_COMMON_TOKEN_STREAM tkstream; - - /* Parser and the resulting AST, fed to the tree parser */ - pDAAPParser psr; - DAAPParser_query_return qtree; - pANTLR3_COMMON_TREE_NODE_STREAM nodes; - - /* Tree parser and the resulting SQL query string */ - pDAAP2SQL sqlconv; - pANTLR3_STRING sql; - - char *ret = NULL; - - if (!daap_query) - { - DPRINTF(E_LOG, L_DAAP, "DAAP query is null\n"); - return NULL; - } - - DPRINTF(E_DBG, L_DAAP, "Trying DAAP query -%s-\n", daap_query); - -#if ANTLR3C_NEW_INPUT - query = antlr3StringStreamNew ((pANTLR3_UINT8)daap_query, ANTLR3_ENC_8BIT, (ANTLR3_UINT64)strlen(daap_query), (pANTLR3_UINT8)"DAAP query"); -#else - query = antlr3NewAsciiStringInPlaceStream ((pANTLR3_UINT8)daap_query, (ANTLR3_UINT64)strlen(daap_query), (pANTLR3_UINT8)"DAAP query"); -#endif - if (!query) - { - DPRINTF(E_DBG, L_DAAP, "Could not create input stream\n"); - return NULL; - } - - lxr = DAAPLexerNew(query); - if (!lxr) - { - DPRINTF(E_DBG, L_DAAP, "Could not create DAAP lexer\n"); - goto lxr_fail; - } - - tkstream = antlr3CommonTokenStreamSourceNew(ANTLR3_SIZE_HINT, TOKENSOURCE(lxr)); - if (!tkstream) - { - DPRINTF(E_DBG, L_DAAP, "Could not create DAAP token stream\n"); - goto tkstream_fail; - } - - psr = DAAPParserNew(tkstream); - if (!psr) - { - DPRINTF(E_DBG, L_DAAP, "Could not create DAAP parser\n"); - goto psr_fail; - } - - qtree = psr->query(psr); - - /* Check for parser errors */ - if (psr->pParser->rec->state->errorCount > 0) - { - DPRINTF(E_LOG, L_DAAP, "DAAP query parser terminated with %d errors\n", psr->pParser->rec->state->errorCount); - goto psr_error; - } - - DPRINTF(E_SPAM, L_DAAP, "DAAP query AST:\n\t%s\n", qtree.tree->toStringTree(qtree.tree)->chars); - - nodes = antlr3CommonTreeNodeStreamNewTree(qtree.tree, ANTLR3_SIZE_HINT); - if (!nodes) - { - DPRINTF(E_DBG, L_DAAP, "Could not create node stream\n"); - goto psr_error; - } - - sqlconv = DAAP2SQLNew(nodes); - if (!sqlconv) - { - DPRINTF(E_DBG, L_DAAP, "Could not create SQL converter\n"); - goto sql_fail; - } - - sql = sqlconv->query(sqlconv); - - /* Check for tree parser errors */ - if (sqlconv->pTreeParser->rec->state->errorCount > 0) - { - DPRINTF(E_LOG, L_DAAP, "DAAP query tree parser terminated with %d errors\n", sqlconv->pTreeParser->rec->state->errorCount); - goto sql_error; - } - - if (sql) - { - DPRINTF(E_DBG, L_DAAP, "DAAP SQL query: -%s-\n", sql->chars); - ret = strdup((char *)sql->chars); - } - else - { - DPRINTF(E_LOG, L_DAAP, "Invalid DAAP query -%s-\n", daap_query); - ret = NULL; - } - - sql_error: - sqlconv->free(sqlconv); - sql_fail: - nodes->free(nodes); - psr_error: - psr->free(psr); - psr_fail: - tkstream->free(tkstream); - tkstream_fail: - lxr->free(lxr); - lxr_fail: - query->close(query); - - return ret; + return NULL; } diff --git a/src/rsp_query.c b/src/rsp_query.c index 82db6910..16ac76f8 100644 --- a/src/rsp_query.c +++ b/src/rsp_query.c @@ -29,122 +29,8 @@ #include "misc.h" #include "rsp_query.h" -#include "RSPLexer.h" -#include "RSPParser.h" -#include "RSP2SQL.h" - - char * rsp_query_parse_sql(const char *rsp_query) { - /* Input RSP query, fed to the lexer */ - pANTLR3_INPUT_STREAM query; - - /* Lexer and the resulting token stream, fed to the parser */ - pRSPLexer lxr; - pANTLR3_COMMON_TOKEN_STREAM tkstream; - - /* Parser and the resulting AST, fed to the tree parser */ - pRSPParser psr; - RSPParser_query_return qtree; - pANTLR3_COMMON_TREE_NODE_STREAM nodes; - - /* Tree parser and the resulting SQL query string */ - pRSP2SQL sqlconv; - pANTLR3_STRING sql; - - char *ret = NULL; - - DPRINTF(E_DBG, L_RSP, "Trying RSP query -%s-\n", rsp_query); - -#if ANTLR3C_NEW_INPUT - query = antlr3StringStreamNew ((pANTLR3_UINT8)rsp_query, ANTLR3_ENC_8BIT, (ANTLR3_UINT64)strlen(rsp_query), (pANTLR3_UINT8)"RSP query"); -#else - query = antlr3NewAsciiStringInPlaceStream ((pANTLR3_UINT8)rsp_query, (ANTLR3_UINT64)strlen(rsp_query), (pANTLR3_UINT8)"RSP query"); -#endif - if (!query) - { - DPRINTF(E_DBG, L_RSP, "Could not create input stream\n"); - return NULL; - } - - lxr = RSPLexerNew(query); - if (!lxr) - { - DPRINTF(E_DBG, L_RSP, "Could not create RSP lexer\n"); - goto lxr_fail; - } - - tkstream = antlr3CommonTokenStreamSourceNew(ANTLR3_SIZE_HINT, TOKENSOURCE(lxr)); - if (!tkstream) - { - DPRINTF(E_DBG, L_RSP, "Could not create RSP token stream\n"); - goto tkstream_fail; - } - - psr = RSPParserNew(tkstream); - if (!psr) - { - DPRINTF(E_DBG, L_RSP, "Could not create RSP parser\n"); - goto psr_fail; - } - - qtree = psr->query(psr); - - /* Check for parser errors */ - if (psr->pParser->rec->state->errorCount > 0) - { - DPRINTF(E_LOG, L_RSP, "RSP query parser terminated with %d errors\n", psr->pParser->rec->state->errorCount); - goto psr_error; - } - - DPRINTF(E_SPAM, L_RSP, "RSP query AST:\n\t%s\n", qtree.tree->toStringTree(qtree.tree)->chars); - - nodes = antlr3CommonTreeNodeStreamNewTree(qtree.tree, ANTLR3_SIZE_HINT); - if (!nodes) - { - DPRINTF(E_DBG, L_RSP, "Could not create node stream\n"); - goto psr_error; - } - - sqlconv = RSP2SQLNew(nodes); - if (!sqlconv) - { - DPRINTF(E_DBG, L_RSP, "Could not create SQL converter\n"); - goto sql_fail; - } - - sql = sqlconv->query(sqlconv); - - /* Check for tree parser errors */ - if (sqlconv->pTreeParser->rec->state->errorCount > 0) - { - DPRINTF(E_LOG, L_RSP, "RSP query tree parser terminated with %d errors\n", sqlconv->pTreeParser->rec->state->errorCount); - goto sql_error; - } - - if (sql) - { - ret = strdup((char *)sql->chars); - } - else - { - DPRINTF(E_LOG, L_RSP, "Invalid RSP query\n"); - ret = NULL; - } - - sql_error: - sqlconv->free(sqlconv); - sql_fail: - nodes->free(nodes); - psr_error: - psr->free(psr); - psr_fail: - tkstream->free(tkstream); - tkstream_fail: - lxr->free(lxr); - lxr_fail: - query->close(query); - - return ret; + return NULL; } diff --git a/src/smartpl_query.c b/src/smartpl_query.c index b990fa97..d563c1a7 100644 --- a/src/smartpl_query.c +++ b/src/smartpl_query.c @@ -35,183 +35,17 @@ #include "logger.h" #include "misc.h" -#include "SMARTPLLexer.h" -#include "SMARTPLParser.h" -#include "SMARTPL2SQL.h" - - -static int -parse_input(struct smartpl *smartpl, pANTLR3_INPUT_STREAM input) -{ - pSMARTPLLexer lxr; - pANTLR3_COMMON_TOKEN_STREAM tstream; - pSMARTPLParser psr; - SMARTPLParser_playlist_return qtree; - pANTLR3_COMMON_TREE_NODE_STREAM nodes; - pSMARTPL2SQL sqlconv; - SMARTPL2SQL_playlist_return plreturn; - int ret; - - lxr = SMARTPLLexerNew(input); - - // Need to check for errors - if (lxr == NULL) - { - DPRINTF(E_LOG, L_SCAN, "Could not create SMARTPL lexer\n"); - ret = -1; - goto lxr_fail; - } - - tstream = antlr3CommonTokenStreamSourceNew(ANTLR3_SIZE_HINT, TOKENSOURCE(lxr)); - - if (tstream == NULL) - { - DPRINTF(E_LOG, L_SCAN, "Could not create SMARTPL token stream\n"); - ret = -1; - goto tkstream_fail; - } - - // Finally, now that we have our lexer constructed, we can create the parser - psr = SMARTPLParserNew(tstream); // CParserNew is generated by ANTLR3 - - if (psr == NULL) - { - DPRINTF(E_LOG, L_SCAN, "Could not create SMARTPL parser\n"); - ret = -1; - goto psr_fail; - } - - qtree = psr->playlist(psr); - - /* Check for parser errors */ - if (psr->pParser->rec->state->errorCount > 0) - { - DPRINTF(E_LOG, L_SCAN, "SMARTPL query parser terminated with %d errors\n", psr->pParser->rec->state->errorCount); - ret = -1; - goto psr_error; - } - - DPRINTF(E_DBG, L_SCAN, "SMARTPL query AST:\n\t%s\n", qtree.tree->toStringTree(qtree.tree)->chars); - - nodes = antlr3CommonTreeNodeStreamNewTree(qtree.tree, ANTLR3_SIZE_HINT); - if (!nodes) - { - DPRINTF(E_LOG, L_SCAN, "Could not create node stream\n"); - ret = -1; - goto psr_error; - } - - sqlconv = SMARTPL2SQLNew(nodes); - if (!sqlconv) - { - DPRINTF(E_LOG, L_SCAN, "Could not create SQL converter\n"); - ret = -1; - goto sql_fail; - } - - plreturn = sqlconv->playlist(sqlconv); - - /* Check for tree parser errors */ - if (sqlconv->pTreeParser->rec->state->errorCount > 0) - { - DPRINTF(E_LOG, L_SCAN, "SMARTPL query tree parser terminated with %d errors\n", sqlconv->pTreeParser->rec->state->errorCount); - ret = -1; - goto sql_error; - } - - if (plreturn.title && plreturn.query) - { - DPRINTF(E_DBG, L_SCAN, "SMARTPL SQL title '%s', query: '%s', having: '%s', order by: '%s', limit: %d \n", plreturn.title->chars, plreturn.query->chars, plreturn.having->chars, plreturn.orderby->chars, plreturn.limit); - - if (smartpl->title) - free(smartpl->title); - smartpl->title = strdup((char *)plreturn.title->chars); - - if (smartpl->query_where) - free(smartpl->query_where); - smartpl->query_where = strdup((char *)plreturn.query->chars); - - if (smartpl->having) - free(smartpl->having); - smartpl->having = safe_strdup((char *)plreturn.having->chars); - - if (smartpl->order) - free(smartpl->order); - smartpl->order = safe_strdup((char *)plreturn.orderby->chars); - - smartpl->limit = plreturn.limit; - - ret = 0; - } - else - { - DPRINTF(E_LOG, L_SCAN, "Invalid SMARTPL query\n"); - ret = -1; - } - - sql_error: - sqlconv->free(sqlconv); - sql_fail: - nodes->free(nodes); - psr_error: - psr->free(psr); - psr_fail: - tstream->free(tstream); - tkstream_fail: - lxr->free(lxr); - lxr_fail: - - return ret; -} int smartpl_query_parse_file(struct smartpl *smartpl, const char *file) { - pANTLR3_INPUT_STREAM input; - int ret; - -#if ANTLR3C_NEW_INPUT - input = antlr3FileStreamNew((pANTLR3_UINT8) file, ANTLR3_ENC_8BIT); -#else - input = antlr3AsciiFileStreamNew((pANTLR3_UINT8) file); -#endif - - // The input will be created successfully, providing that there is enough memory and the file exists etc - if (input == NULL) - { - DPRINTF(E_LOG, L_SCAN, "Unable to open smart playlist file %s\n", file); - return -1; - } - - ret = parse_input(smartpl, input); - input->close(input); - - return ret; + return -1; } int smartpl_query_parse_string(struct smartpl *smartpl, const char *expression) { - pANTLR3_INPUT_STREAM input; - int ret; - -#if ANTLR3C_NEW_INPUT - input = antlr3StringStreamNew ((pANTLR3_UINT8)expression, ANTLR3_ENC_8BIT, (ANTLR3_UINT64)strlen(expression), (pANTLR3_UINT8)"SMARTPL expression"); -#else - input = antlr3NewAsciiStringInPlaceStream ((pANTLR3_UINT8)expression, (ANTLR3_UINT64)strlen(expression), (pANTLR3_UINT8)"SMARTPL expression"); -#endif - - // The input will be created successfully, providing that there is enough memory and the file exists etc - if (input == NULL) - { - DPRINTF(E_LOG, L_SCAN, "Unable to pars smart pl expression %s\n", expression); - return -1; - } - - ret = parse_input(smartpl, input); - input->close(input); - - return ret; + return -1; }