Add new ANTLR parser for DAAP queries

This commit is contained in:
Julien BLACHE 2009-06-02 15:51:38 +02:00
parent c4cf7c08a4
commit 68db2ae7c2
10 changed files with 741 additions and 3 deletions

View File

@ -11,6 +11,23 @@ AC_PROG_CC
AM_PROG_CC_C_O
LT_INIT([disable-static])
AC_CHECK_PROG(ANTLR, [antlr3], [antlr3])
if test "x$ANTLR" = x; then
AC_MSG_NOTICE([antlr3 wrapper not found, checking direct java invocation])
AC_CHECK_PROG(JAVA, [java], [java])
if test "x$JAVA" = x; then
AC_MSG_FAILURE([java not found; check your java installation])
else
if $JAVA org.antlr.Tool > /dev/null 2>&1; then
AC_MSG_NOTICE([Direct java invocation working, using java org.antlr.Tool to invoke antlr])
ANTLR="$JAVA org.antlr.Tool"
else
AC_MSG_FAILURE([antlr3 wrapper not found and direct java invocation failed; check your antlr3/java installation])
fi
fi
fi
AC_SUBST(ANTLR)
CFLAGS="$CFLAGS -Wall"
AC_CHECK_HEADERS([sys/wait.h])
@ -68,6 +85,10 @@ AC_CHECK_HEADER(avl.h, , AC_MSG_ERROR([avl.h not found]))
AC_CHECK_LIB([avl], [avl_alloc_tree], [LIBAVL_LIBS="-lavl"], AC_MSG_ERROR([libavl not found]))
AC_SUBST(LIBAVL_LIBS)
AC_CHECK_HEADER(antlr3.h, , AC_MSG_ERROR([antlr3.h not found]))
AC_CHECK_LIB([antlr3c], [antlr3BaseRecognizerNew], [ANTLR3C_LIBS="-lantlr3c"], AC_MSG_ERROR([ANTLR3 C runtime (libantlr3c) not found]))
AC_SUBST(ANTLR3C_LIBS)
if test x$use_flac = xtrue; then
PKG_CHECK_MODULES(FLAC, [ flac ])
fi

6
src/.gitignore vendored
View File

@ -1,2 +1,8 @@
mt-daapd
*.tokens
*Lexer.[ch]
*Parser.[ch]
DAAP2SQL.[ch]
*.u

63
src/DAAP.g Normal file
View File

@ -0,0 +1,63 @@
/*
* Copyright (C) 2009 Julien BLACHE <jb@jblache.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 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)
<http://stackoverflow.com/questions/504402/how-to-handle-escape-sequences-in-string-literals-in-antlr-3>
*/
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)"\'")); }
)
;

294
src/DAAP2SQL.g Normal file
View File

@ -0,0 +1,294 @@
/*
* Copyright (C) 2009 Julien BLACHE <jb@jblache.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
tree grammar DAAP2SQL;
options {
tokenVocab = DAAP;
ASTLabelType = pANTLR3_BASE_TREE;
language = C;
}
@header {
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
#include "logger.h"
#include "ff-dbstruct.h"
#include "db-generic.h"
#include "db-sql.h"
#include "daap_query.h"
}
query returns [ pANTLR3_STRING result ]
: 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)
{
$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, ")");
}
}
| ^(OPOR 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, ")");
}
}
| STR
{
pANTLR3_STRING str;
pANTLR3_UINT8 field;
pANTLR3_UINT8 val;
pANTLR3_UINT8 escaped;
ANTLR3_UINT8 op;
int neg_op;
struct dmap_query_field_map *dqfm;
char *end;
long longval;
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
*/
field = str->chars;
val = field;
while ((*val != '\0') && ((*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 (*val == '\0')
{
if (op == '!')
DPRINTF(E_LOG, L_DAAP, "Negation found but operator missing in clause '\%s\%c'\n", field, op);
else
DPRINTF(E_LOG, L_DAAP, "No value given in clause '\%s\%c'\n", field, op);
$valid = 0;
goto STR_result_valid_0; /* ABORT */
}
if (op == '!')
{
neg_op = 1;
op = *val;
val++;
if (*val == '\0')
{
DPRINTF(E_LOG, L_DAAP, "No value given in clause '\%s!\%c'\n", field, op);
$valid = 0;
goto STR_result_valid_0; /* ABORT */
}
}
else
neg_op = 0;
/* Lookup DMAP field in the query field map */
dqfm = daap_query_field_lookup((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 */
}
$result->append8($result, dqfm->db_col);
/* Int field: check integer conversion */
if (dqfm->as_int)
{
errno = 0;
longval = strtol((const char *)val, &end, 10);
if (((errno == ERANGE) && ((longval == LONG_MAX) || (longval == LONG_MIN)))
|| ((errno != 0) && (longval == 0)))
{
DPRINTF(E_LOG, L_DAAP, "Value '\%s' in clause '\%s\%c\%s' does not convert to an integer type\n",
val, field, op, val);
$valid = 0;
goto STR_result_valid_0; /* ABORT */
}
if (end == (char *)val)
{
DPRINTF(E_LOG, L_DAAP, "Value '\%s' in clause '\%s\%c\%s' does not represent an integer value\n",
val, field, op, val);
$valid = 0;
goto STR_result_valid_0; /* ABORT */
}
*end = '\0'; /* Cut out potential garbage - we're being kind */
}
/* String field: escape string, check for '*' */
else
{
if (neg_op)
{
DPRINTF(E_LOG, L_DAAP, "Negation not valid for string operations\n");
$valid = 0;
goto STR_result_valid_0; /* ABORT */
}
if (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_sql_escape_dup("\%q", 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[strlen((char *)val) - 1] == '*')
{
op = '\%';
val[strlen((char *)val) - 1] = '\%';
}
}
switch(op)
{
case ':':
if (neg_op) /* Not valid for strings, checked above */
$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, "'");
STR_result_valid_0: /* bail out label */
;
if (escaped)
free(escaped);
}
;

View File

@ -9,8 +9,17 @@ if COND_MUSEPACK
MUSEPACKSRC=scan-mpc.c
endif
ANTLR_GRAMMARS = \
DAAP.g DAAP2SQL.g
ANTLR_SOURCES = \
DAAPLexer.c DAAPLexer.h DAAPParser.c DAAPParser.h \
DAAP2SQL.c DAAP2SQL.h
ANTLR_PRODUCTS =
mt_daapd_CPPFLAGS = -D_GNU_SOURCE @AVAHI_CFLAGS@ @SQLITE3_CFLAGS@ @FFMPEG_CFLAGS@ @CONFUSE_CFLAGS@ @TAGLIB_CFLAGS@ @MINIXML_CFLAGS@
mt_daapd_LDADD = @AVAHI_LIBS@ @SQLITE3_LIBS@ @FFMPEG_LIBS@ @CONFUSE_LIBS@ @FLAC_LIBS@ @TAGLIB_LIBS@ @LIBEVENT_LIBS@ @LIBAVL_LIBS@ @MINIXML_LIBS@
mt_daapd_LDADD = @AVAHI_LIBS@ @SQLITE3_LIBS@ @FFMPEG_LIBS@ @CONFUSE_LIBS@ @FLAC_LIBS@ @TAGLIB_LIBS@ @LIBEVENT_LIBS@ @LIBAVL_LIBS@ @MINIXML_LIBS@ @ANTLR3C_LIBS@
mt_daapd_SOURCES = main.c \
logger.c logger.h \
conffile.c conffile.h \
@ -24,6 +33,7 @@ mt_daapd_SOURCES = main.c \
httpd_daap.c httpd_daap.h \
transcode.c transcode.h \
misc.c misc.h \
daap_query.c daap_query.h \
db-generic.c db-generic.h \
scan-wma.c \
smart-parser.c smart-parser.h \
@ -31,6 +41,34 @@ mt_daapd_SOURCES = main.c \
db-sql.c db-sql.h db-sql-sqlite3.c db-sql-sqlite3.h\
$(FLACSRC) $(MUSEPACKSRC)
EXTRA_DIST = scan-mpc.c \
nodist_mt_daapd_SOURCES = \
$(ANTLR_SOURCES)
EXTRA_DIST = \
$(ANTLR_GRAMMARS) \
scan-mpc.c \
scan-flac.c \
ff-dbstruct.h
# Let's help the dependencies a little.
daap_query.c: DAAPLexer.h DAAPParser.h DAAP2SQL.h
SUFFIXES = .g .u
%.tokens %.c %Lexer.c %Parser.c %Lexer.h %Parser.h %.h: %.g
@ANTLR@ $(ANTLR_OPTIONS) $<
%.u: %.g
@ANTLR@ -depend $< > $@
@echo -n "ANTLR_PRODUCTS += " > $@.tmp
@grep : $@ | cut -d : -f 1 | tr -d ' ' | { while read f; do test "$$f" != "$<" && echo -n "$$f "; done } >> $@.tmp
@cat $@.tmp >> $@
rm $@.tmp
clean-local:
-rm $(ANTLR_PRODUCTS)
rm $(ANTLR_GRAMMARS:.g=.u)
-include $(ANTLR_GRAMMARS:.g=.u)

262
src/daap_query.c Normal file
View File

@ -0,0 +1,262 @@
/*
* Copyright (C) 2009 Julien BLACHE <jb@jblache.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <avl.h>
#include "logger.h"
#include "misc.h"
#include "daap_query.h"
#include "DAAPLexer.h"
#include "DAAPParser.h"
#include "DAAP2SQL.h"
static struct dmap_query_field_map dmap_query_fields[] =
{
{ 0, 0, "dmap.itemname", "title" },
{ 0, 1, "dmap.itemid", "id" },
{ 0, 0, "daap.songalbum", "album" },
{ 0, 0, "daap.songartist", "artist" },
{ 0, 1, "daap.songbitrate", "bitrate" },
{ 0, 0, "daap.songcomment", "comment" },
{ 0, 1, "daap.songcompilation", "compilation" },
{ 0, 0, "daap.songcomposer", "composer" },
{ 0, 1, "daap.songdatakind", "data_kind" },
{ 0, 0, "daap.songdataurl", "url" },
{ 0, 1, "daap.songdateadded", "time_added" },
{ 0, 1, "daap.songdatemodified", "time_modified" },
{ 0, 0, "daap.songdescription", "description" },
{ 0, 1, "daap.songdisccount", "total_discs" },
{ 0, 1, "daap.songdiscnumber", "disc" },
{ 0, 0, "daap.songformat", "type" },
{ 0, 0, "daap.songgenre", "genre" },
{ 0, 1, "daap.songsamplerate", "samplerate" },
{ 0, 1, "daap.songsize", "file_size" },
{ 0, 1, "daap.songstoptime", "song_length" },
{ 0, 1, "daap.songtime", "song_length" },
{ 0, 1, "daap.songtrackcount", "total_tracks" },
{ 0, 1, "daap.songtracknumber", "track" },
{ 0, 1, "daap.songyear", "year" },
{ -1, -1, NULL, NULL }
};
static avl_tree_t *dmap_query_fields_hash;
static int
dmap_query_field_map_compare(const void *aa, const void *bb)
{
struct dmap_query_field_map *a = (struct dmap_query_field_map *)aa;
struct dmap_query_field_map *b = (struct dmap_query_field_map *)bb;
if (a->hash < b->hash)
return -1;
if (a->hash > b->hash)
return 1;
return 0;
}
struct dmap_query_field_map *
daap_query_field_lookup(char *field)
{
struct dmap_query_field_map dqfm;
avl_node_t *node;
dqfm.hash = djb_hash(field, strlen(field));
node = avl_search(dmap_query_fields_hash, &dqfm);
if (!node)
return NULL;
return (struct dmap_query_field_map *)node->item;
}
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;
DPRINTF(E_DBG, L_DAAP, "Trying DAAP query -%s-\n", daap_query);
query = antlr3NewAsciiStringInPlaceStream ((pANTLR3_UINT8)daap_query, (ANTLR3_UINT64)strlen(daap_query), (pANTLR3_UINT8)"DAAP query");
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\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;
}
int
daap_query_init(void)
{
avl_node_t *node;
struct dmap_query_field_map *dqfm;
int i;
dmap_query_fields_hash = avl_alloc_tree(dmap_query_field_map_compare, NULL);
if (!dmap_query_fields_hash)
{
DPRINTF(E_FATAL, L_DAAP, "DAAP query init could not allocate AVL tree\n");
return -1;
}
for (i = 0; dmap_query_fields[i].hash == 0; i++)
{
dmap_query_fields[i].hash = djb_hash(dmap_query_fields[i].dmap_field, strlen(dmap_query_fields[i].dmap_field));
node = avl_insert(dmap_query_fields_hash, &dmap_query_fields[i]);
if (!node)
{
if (errno != EEXIST)
DPRINTF(E_FATAL, L_DAAP, "DAAP query init failed; AVL insert error: %s\n", strerror(errno));
else
{
node = avl_search(dmap_query_fields_hash, &dmap_query_fields[i]);
dqfm = node->item;
DPRINTF(E_FATAL, L_DAAP, "DAAP query init failed; WARNING: duplicate hash key\n");
DPRINTF(E_FATAL, L_DAAP, "Hash %x, string %s\n", dmap_query_fields[i].hash, dmap_query_fields[i].dmap_field);
DPRINTF(E_FATAL, L_DAAP, "Hash %x, string %s\n", dqfm->hash, dqfm->dmap_field);
}
goto avl_insert_fail;
}
}
return 0;
avl_insert_fail:
avl_free_tree(dmap_query_fields_hash);
return -1;
}
void
daap_query_deinit(void)
{
avl_free_tree(dmap_query_fields_hash);
}

27
src/daap_query.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef __DAAP_QUERY_H__
#define __DAAP_QUERY_H__
#include <stdint.h>
struct dmap_query_field_map {
uint32_t hash;
int as_int;
char *dmap_field;
char *db_col;
};
struct dmap_query_field_map *
daap_query_field_lookup(char *field);
char *
daap_query_parse_sql(const char *daap_query);
int
daap_query_init(void);
void
daap_query_deinit(void);
#endif /* !__DAAP_QUERY_H__ */

View File

@ -150,6 +150,23 @@ int db_sql_escape(char *buffer, int *size, char *fmt, ...) {
return DB_E_SUCCESS;
}
char *
db_sql_escape_dup(char *fmt, ...)
{
va_list ap;
char *escaped;
char *ret;
va_start(ap, fmt);
escaped = db_sql_vmquery_fn(fmt, ap);
va_end(ap);
ret = strdup(escaped);
db_sql_vmfree_fn(escaped);
return ret;
}
/**
* fetch a single row, using the underlying database enum

View File

@ -31,6 +31,7 @@ extern int db_sql_open(char **pe, char *parameters);
extern int db_sql_init(int reload);
extern int db_sql_deinit(void);
extern int db_sql_escape(char *buffer, int *size, char *fmt, ...);
extern char * db_sql_escape_dup(char *fmt, ...);
extern int db_sql_add(char **pe, MP3FILE *pmp3, int *id);
extern int db_sql_enum_start(char **pe, DBQUERYINFO *pinfo);

View File

@ -46,6 +46,7 @@
#include "httpd.h"
#include "transcode.h"
#include "httpd_daap.h"
#include "daap_query.h"
struct uri_map {
@ -1949,6 +1950,10 @@ daap_init(void)
session_id = 100; /* gotta start somewhere, right? */
ret = daap_query_init();
if (ret < 0)
return ret;
for (i = 0; daap_handlers[i].handler; i++)
{
ret = regcomp(&daap_handlers[i].preg, daap_handlers[i].regexp, REG_EXTENDED | REG_NOSUB);
@ -1957,7 +1962,7 @@ daap_init(void)
regerror(ret, &daap_handlers[i].preg, buf, sizeof(buf));
DPRINTF(E_FATAL, L_DAAP, "DAAP init failed; regexp error: %s\n", buf);
return -1;
goto regexp_fail;
}
}
@ -2000,6 +2005,8 @@ daap_init(void)
avl_alloc_fail:
for (i = 0; daap_handlers[i].handler; i++)
regfree(&daap_handlers[i].preg);
regexp_fail:
daap_query_deinit();
return -1;
}
@ -2009,6 +2016,8 @@ daap_deinit(void)
{
int i;
daap_query_deinit();
for (i = 0; daap_handlers[i].handler; i++)
regfree(&daap_handlers[i].preg);