mirror of
https://github.com/owntone/owntone-server.git
synced 2025-01-14 08:15:02 -05:00
[rsp] Add basic bison/flex RSP parser
This commit is contained in:
parent
9c1f7dd380
commit
71b4444118
@ -56,14 +56,13 @@ endif
|
||||
|
||||
GPERF_FILES = \
|
||||
daap_query.gperf \
|
||||
rsp_query.gperf \
|
||||
dacp_prop.gperf \
|
||||
dmap_fields.gperf
|
||||
|
||||
GPERF_SRC = $(GPERF_FILES:.gperf=_hash.h)
|
||||
|
||||
LEXER_SRC = parsers/daap_lexer.l parsers/smartpl_lexer.l
|
||||
PARSER_SRC = parsers/daap_parser.y parsers/smartpl_parser.y
|
||||
LEXER_SRC = parsers/daap_lexer.l parsers/smartpl_lexer.l parsers/rsp_lexer.l
|
||||
PARSER_SRC = parsers/daap_parser.y parsers/smartpl_parser.y parsers/rsp_parser.y
|
||||
|
||||
# This flag is given to Bison and tells it to produce headers. Note that
|
||||
# automake recognizes this flag too, and has special logic around it, so don't
|
||||
@ -116,7 +115,6 @@ owntone_SOURCES = main.c \
|
||||
misc.c misc.h \
|
||||
misc_json.c misc_json.h \
|
||||
rng.c rng.h \
|
||||
rsp_query.c rsp_query.h \
|
||||
daap_query.c daap_query.h \
|
||||
smartpl_query.c smartpl_query.h \
|
||||
player.c player.h \
|
||||
|
@ -39,7 +39,7 @@
|
||||
#include "misc.h"
|
||||
#include "httpd.h"
|
||||
#include "transcode.h"
|
||||
#include "rsp_query.h"
|
||||
#include "parsers/rsp_parser.h"
|
||||
|
||||
#define RSP_VERSION "1.0"
|
||||
#define RSP_XML_ROOT "?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?"
|
||||
@ -209,9 +209,9 @@ rsp_send_error(struct evhttp_request *req, char *errmsg)
|
||||
static int
|
||||
query_params_set(struct query_params *qp, struct httpd_request *hreq)
|
||||
{
|
||||
struct rsp_result parse_result;
|
||||
const char *param;
|
||||
char query[1024];
|
||||
char *filter;
|
||||
int ret;
|
||||
|
||||
qp->offset = 0;
|
||||
@ -243,6 +243,7 @@ query_params_set(struct query_params *qp, struct httpd_request *hreq)
|
||||
else
|
||||
qp->idx_type = I_NONE;
|
||||
|
||||
qp->filter = NULL;
|
||||
param = evhttp_find_header(hreq->query, "query");
|
||||
if (param)
|
||||
{
|
||||
@ -263,19 +264,15 @@ query_params_set(struct query_params *qp, struct httpd_request *hreq)
|
||||
return -1;
|
||||
}
|
||||
|
||||
qp->filter = rsp_query_parse_sql(query);
|
||||
if (!qp->filter)
|
||||
DPRINTF(E_LOG, L_RSP, "Ignoring improper RSP query\n");
|
||||
if (rsp_lex_parse(&parse_result, query) != 0)
|
||||
DPRINTF(E_LOG, L_RSP, "Ignoring improper RSP query: %s\n", query);
|
||||
else
|
||||
qp->filter = safe_asprintf("(%s) AND %s", parse_result.str, rsp_filter_files);
|
||||
}
|
||||
|
||||
// Always filter to include only files (not streams and Spotify)
|
||||
if (qp->filter)
|
||||
filter = safe_asprintf("%s AND %s", qp->filter, rsp_filter_files);
|
||||
else
|
||||
filter = strdup(rsp_filter_files);
|
||||
|
||||
free(qp->filter);
|
||||
qp->filter = filter;
|
||||
if (!qp->filter)
|
||||
qp->filter = strdup(rsp_filter_files);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
82
src/parsers/rsp_lexer.l
Normal file
82
src/parsers/rsp_lexer.l
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2021-2022 Espen Jürgensen <espenjurgensen@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/* =========================== BOILERPLATE SECTION ===========================*/
|
||||
|
||||
/* This is to avoid compiler warnings about unused functions. More options are
|
||||
noyyalloc noyyrealloc noyyfree. */
|
||||
%option noyywrap nounput noinput
|
||||
|
||||
/* Thread safe scanner */
|
||||
%option reentrant
|
||||
|
||||
/* To avoid symbol name conflicts with multiple lexers */
|
||||
%option prefix="rsp_"
|
||||
|
||||
/* Automake's ylwrap expexts the output to have this name */
|
||||
%option outfile="lex.yy.c"
|
||||
|
||||
/* Makes a Bison-compatible yylex */
|
||||
%option bison-bridge
|
||||
|
||||
%{
|
||||
#include <stdio.h>
|
||||
#include "rsp_parser.h"
|
||||
|
||||
/* Unknown why this is required despite using prefix */
|
||||
#define YYSTYPE RSP_STYPE
|
||||
|
||||
%}
|
||||
|
||||
/* ========================= NON-BOILERPLATE SECTION =========================*/
|
||||
|
||||
/* quoted \"(\\.|[^\\"])*\" */
|
||||
quoted \"(\\.|[^"])+\"
|
||||
yyyymmdd [0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]
|
||||
|
||||
%%
|
||||
|
||||
[\n\t ]+ /* Ignore whitespace */
|
||||
|
||||
artist { yylval->str = strdup(yytext); return RSP_T_STRTAG; }
|
||||
album_artist { yylval->str = strdup(yytext); return RSP_T_STRTAG; }
|
||||
album { yylval->str = strdup(yytext); return RSP_T_STRTAG; }
|
||||
title { yylval->str = strdup(yytext); return RSP_T_STRTAG; }
|
||||
genre { yylval->str = strdup(yytext); return RSP_T_STRTAG; }
|
||||
composer { yylval->str = strdup(yytext); return RSP_T_STRTAG; }
|
||||
|
||||
id { yylval->str = strdup(yytext); return RSP_T_INTTAG; }
|
||||
|
||||
includes { return RSP_T_INCLUDES; }
|
||||
= { return RSP_T_EQUAL; }
|
||||
|
||||
or { return RSP_T_OR; }
|
||||
and { return RSP_T_AND; }
|
||||
not { return RSP_T_NOT; }
|
||||
|
||||
{quoted} { yylval->str=strdup(yytext+1);
|
||||
if(yylval->str[strlen(yylval->str)-1] == '"')
|
||||
yylval->str[strlen(yylval->str)-1] = '\0';
|
||||
return RSP_T_STRING; }
|
||||
|
||||
[0-9]+ { yylval->ival=atoi(yytext); return RSP_T_NUM; }
|
||||
|
||||
. { return yytext[0]; }
|
||||
|
||||
%%
|
||||
|
416
src/parsers/rsp_parser.y
Normal file
416
src/parsers/rsp_parser.y
Normal file
@ -0,0 +1,416 @@
|
||||
/*
|
||||
* Copyright (C) 2021-2022 Espen Jürgensen <espenjurgensen@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/* =========================== BOILERPLATE SECTION ===========================*/
|
||||
|
||||
/* No global variables and yylex has scanner as argument */
|
||||
%define api.pure true
|
||||
|
||||
/* Change prefix of symbols from yy to avoid clashes with any other parsers we
|
||||
may want to link */
|
||||
%define api.prefix {rsp_}
|
||||
|
||||
/* Gives better errors than "syntax error" */
|
||||
%define parse.error verbose
|
||||
|
||||
/* Enables debug mode */
|
||||
%define parse.trace
|
||||
|
||||
/* Adds output parameter to the parser */
|
||||
%parse-param {struct rsp_result *result}
|
||||
|
||||
/* Adds "scanner" as argument to the parses calls to yylex, which is required
|
||||
when the lexer is in reentrant mode. The type is void because caller caller
|
||||
shouldn't need to know about yyscan_t */
|
||||
%param {void *scanner}
|
||||
|
||||
%code provides {
|
||||
/* Convenience functions for caller to use instead of interfacing with lexer and
|
||||
parser directly */
|
||||
int rsp_lex_cb(char *input, void (*cb)(int, const char *));
|
||||
int rsp_lex_parse(struct rsp_result *result, const char *input);
|
||||
}
|
||||
|
||||
/* Implementation of the convenience function and the parsing error function
|
||||
required by Bison */
|
||||
%code {
|
||||
#include "rsp_lexer.h"
|
||||
|
||||
int rsp_lex_cb(char *input, void (*cb)(int, const char *))
|
||||
{
|
||||
int ret;
|
||||
yyscan_t scanner;
|
||||
YY_BUFFER_STATE buf;
|
||||
YYSTYPE val;
|
||||
|
||||
if ((ret = rsp_lex_init(&scanner)) != 0)
|
||||
return ret;
|
||||
|
||||
buf = rsp__scan_string(input, scanner);
|
||||
|
||||
while ((ret = rsp_lex(&val, scanner)) > 0)
|
||||
cb(ret, rsp_get_text(scanner));
|
||||
|
||||
rsp__delete_buffer(buf, scanner);
|
||||
rsp_lex_destroy(scanner);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rsp_lex_parse(struct rsp_result *result, const char *input)
|
||||
{
|
||||
YY_BUFFER_STATE buffer;
|
||||
yyscan_t scanner;
|
||||
int retval = -1;
|
||||
int ret;
|
||||
|
||||
result->errmsg[0] = '\0'; // For safety
|
||||
|
||||
ret = rsp_lex_init(&scanner);
|
||||
if (ret != 0)
|
||||
goto error_init;
|
||||
|
||||
buffer = rsp__scan_string(input, scanner);
|
||||
if (!buffer)
|
||||
goto error_buffer;
|
||||
|
||||
ret = rsp_parse(result, scanner);
|
||||
if (ret != 0)
|
||||
goto error_parse;
|
||||
|
||||
retval = 0;
|
||||
|
||||
error_parse:
|
||||
rsp__delete_buffer(buffer, scanner);
|
||||
error_buffer:
|
||||
rsp_lex_destroy(scanner);
|
||||
error_init:
|
||||
return retval;
|
||||
}
|
||||
|
||||
void rsp_error(struct rsp_result *result, yyscan_t scanner, const char *msg)
|
||||
{
|
||||
snprintf(result->errmsg, sizeof(result->errmsg), "%s", msg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============ ABSTRACT SYNTAX TREE (AST) BOILERPLATE SECTION ===============*/
|
||||
|
||||
%code {
|
||||
struct ast
|
||||
{
|
||||
int type;
|
||||
struct ast *l;
|
||||
struct ast *r;
|
||||
void *data;
|
||||
int ival;
|
||||
};
|
||||
|
||||
__attribute__((unused)) static struct ast * ast_new(int type, struct ast *l, struct ast *r)
|
||||
{
|
||||
struct ast *a = calloc(1, sizeof(struct ast));
|
||||
|
||||
a->type = type;
|
||||
a->l = l;
|
||||
a->r = r;
|
||||
return a;
|
||||
}
|
||||
|
||||
/* Note *data is expected to be freeable with regular free() */
|
||||
__attribute__((unused)) static struct ast * ast_data(int type, void *data)
|
||||
{
|
||||
struct ast *a = calloc(1, sizeof(struct ast));
|
||||
|
||||
a->type = type;
|
||||
a->data = data;
|
||||
return a;
|
||||
}
|
||||
|
||||
__attribute__((unused)) static struct ast * ast_int(int type, int ival)
|
||||
{
|
||||
struct ast *a = calloc(1, sizeof(struct ast));
|
||||
|
||||
a->type = type;
|
||||
a->ival = ival;
|
||||
return a;
|
||||
}
|
||||
|
||||
__attribute__((unused)) static void ast_free(struct ast *a)
|
||||
{
|
||||
if (!a)
|
||||
return;
|
||||
|
||||
ast_free(a->l);
|
||||
ast_free(a->r);
|
||||
free(a->data);
|
||||
free(a);
|
||||
}
|
||||
}
|
||||
|
||||
%destructor { free($$); } <str>
|
||||
%destructor { ast_free($$); } <ast>
|
||||
|
||||
|
||||
/* ========================= NON-BOILERPLATE SECTION =========================*/
|
||||
|
||||
/* Includes required by the parser rules */
|
||||
%code top {
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h> // For vsnprintf
|
||||
#include <assert.h>
|
||||
|
||||
#define INVERT_MASK 0x80000000
|
||||
}
|
||||
|
||||
/* Dependencies, mocked or real */
|
||||
%code top {
|
||||
#ifndef DEBUG_PARSER_MOCK
|
||||
#include "db.h"
|
||||
#include "misc.h"
|
||||
#else
|
||||
#include "owntonefunctions.h"
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Definition of struct that will hold the parsing result */
|
||||
%code requires {
|
||||
struct rsp_result {
|
||||
char str[1024];
|
||||
int offset;
|
||||
int err;
|
||||
char errmsg[128];
|
||||
};
|
||||
}
|
||||
|
||||
%code {
|
||||
enum sql_append_type {
|
||||
SQL_APPEND_OPERATOR,
|
||||
SQL_APPEND_OPERATOR_STR,
|
||||
SQL_APPEND_OPERATOR_LIKE,
|
||||
SQL_APPEND_FIELD,
|
||||
SQL_APPEND_STR,
|
||||
SQL_APPEND_INT,
|
||||
SQL_APPEND_PARENS,
|
||||
};
|
||||
|
||||
static void sql_from_ast(struct rsp_result *, struct ast *);
|
||||
|
||||
// Escapes any '%' or '_' that might be in the string
|
||||
static void sql_like_escape(char **value, char *escape_char)
|
||||
{
|
||||
char *s = *value;
|
||||
size_t len = strlen(s);
|
||||
char *new;
|
||||
|
||||
*escape_char = 0;
|
||||
|
||||
// Fast path, nothing to escape
|
||||
if (!strpbrk(s, "_%"))
|
||||
return;
|
||||
|
||||
len = 2 * len; // Enough for every char to be escaped
|
||||
new = realloc(s, len);
|
||||
safe_snreplace(new, len, "%", "\\%");
|
||||
safe_snreplace(new, len, "_", "\\_");
|
||||
*escape_char = '\\';
|
||||
*value = new;
|
||||
}
|
||||
|
||||
static void sql_str_escape(char **value)
|
||||
{
|
||||
char *s = *value;
|
||||
|
||||
if (strchr(s, '\"'))
|
||||
safe_snreplace(s, strlen(s) + 1, "\\\"", "\""); // See test case 3
|
||||
|
||||
*value = db_escape_string(s);
|
||||
free(s);
|
||||
}
|
||||
|
||||
static void sql_append(struct rsp_result *result, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int remaining = sizeof(result->str) - result->offset;
|
||||
int ret;
|
||||
|
||||
if (remaining <= 0)
|
||||
goto nospace;
|
||||
|
||||
va_start(ap, fmt);
|
||||
ret = vsnprintf(result->str + result->offset, remaining, fmt, ap);
|
||||
va_end(ap);
|
||||
if (ret < 0 || ret >= remaining)
|
||||
goto nospace;
|
||||
|
||||
result->offset += ret;
|
||||
return;
|
||||
|
||||
nospace:
|
||||
snprintf(result->errmsg, sizeof(result->errmsg), "Parser output buffer too small (%zu bytes)", sizeof(result->str));
|
||||
result->err = -2;
|
||||
}
|
||||
|
||||
static void sql_append_recursive(struct rsp_result *result, struct ast *a, const char *op, const char *op_not, bool is_not, enum sql_append_type append_type)
|
||||
{
|
||||
char escape_char;
|
||||
|
||||
switch (append_type)
|
||||
{
|
||||
case SQL_APPEND_OPERATOR:
|
||||
sql_from_ast(result, a->l);
|
||||
sql_append(result, " %s ", is_not ? op_not : op);
|
||||
sql_from_ast(result, a->r);
|
||||
break;
|
||||
case SQL_APPEND_OPERATOR_STR:
|
||||
sql_from_ast(result, a->l);
|
||||
sql_append(result, " %s '", is_not ? op_not : op);
|
||||
sql_from_ast(result, a->r);
|
||||
sql_append(result, "'");
|
||||
break;
|
||||
case SQL_APPEND_OPERATOR_LIKE:
|
||||
sql_from_ast(result, a->l);
|
||||
sql_append(result, " %s '%%", is_not ? op_not : op);
|
||||
sql_like_escape((char **)(&a->r->data), &escape_char);
|
||||
sql_from_ast(result, a->r);
|
||||
sql_append(result, "%%'");
|
||||
if (escape_char)
|
||||
sql_append(result, " ESCAPE '%c'", escape_char);
|
||||
break;
|
||||
case SQL_APPEND_FIELD:
|
||||
assert(a->l == NULL);
|
||||
assert(a->r == NULL);
|
||||
sql_append(result, "f.%s", (char *)a->data);
|
||||
break;
|
||||
case SQL_APPEND_STR:
|
||||
assert(a->l == NULL);
|
||||
assert(a->r == NULL);
|
||||
sql_str_escape((char **)&a->data);
|
||||
sql_append(result, "%s", (char *)a->data);
|
||||
break;
|
||||
case SQL_APPEND_INT:
|
||||
assert(a->l == NULL);
|
||||
assert(a->r == NULL);
|
||||
sql_append(result, "%d", a->ival);
|
||||
break;
|
||||
case SQL_APPEND_PARENS:
|
||||
assert(a->r == NULL);
|
||||
sql_append(result, "(");
|
||||
sql_from_ast(result, a->l);
|
||||
sql_append(result, ")");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void sql_from_ast(struct rsp_result *result, struct ast *a)
|
||||
{
|
||||
if (!a || result->err < 0)
|
||||
return;
|
||||
|
||||
// Not currently used since grammar below doesn't ever set with INVERT_MASK
|
||||
bool is_not = (a->type & INVERT_MASK);
|
||||
a->type &= ~INVERT_MASK;
|
||||
|
||||
switch (a->type)
|
||||
{
|
||||
case RSP_T_OR:
|
||||
sql_append_recursive(result, a, "OR", "OR NOT", is_not, SQL_APPEND_OPERATOR); break;
|
||||
case RSP_T_AND:
|
||||
sql_append_recursive(result, a, "AND", "AND NOT", is_not, SQL_APPEND_OPERATOR); break;
|
||||
case RSP_T_EQUAL:
|
||||
sql_append_recursive(result, a, "=", "!=", is_not, SQL_APPEND_OPERATOR); break;
|
||||
case RSP_T_IS:
|
||||
sql_append_recursive(result, a, "=", "!=", is_not, SQL_APPEND_OPERATOR_STR); break;
|
||||
case RSP_T_INCLUDES:
|
||||
sql_append_recursive(result, a, "LIKE", "NOT LIKE", is_not, SQL_APPEND_OPERATOR_LIKE); break;
|
||||
case RSP_T_STRTAG:
|
||||
case RSP_T_INTTAG:
|
||||
sql_append_recursive(result, a, NULL, NULL, 0, SQL_APPEND_FIELD); break;
|
||||
case RSP_T_NUM:
|
||||
sql_append_recursive(result, a, NULL, NULL, 0, SQL_APPEND_INT); break;
|
||||
case RSP_T_STRING:
|
||||
sql_append_recursive(result, a, NULL, NULL, 0, SQL_APPEND_STR); break;
|
||||
break;
|
||||
case RSP_T_PARENS:
|
||||
sql_append_recursive(result, a, NULL, NULL, 0, SQL_APPEND_PARENS); break;
|
||||
default:
|
||||
snprintf(result->errmsg, sizeof(result->errmsg), "Parser produced unrecognized AST type %d", a->type);
|
||||
result->err = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int result_set(struct rsp_result *result, struct ast *a)
|
||||
{
|
||||
memset(result, 0, sizeof(struct rsp_result));
|
||||
sql_from_ast(result, a);
|
||||
ast_free(a);
|
||||
return result->err;
|
||||
}
|
||||
}
|
||||
|
||||
%union {
|
||||
unsigned int ival;
|
||||
char *str;
|
||||
struct ast *ast;
|
||||
}
|
||||
|
||||
/* A string that was quoted. Quotes were stripped by lexer. */
|
||||
%token <str> RSP_T_STRING
|
||||
|
||||
/* A number (integer) */
|
||||
%token <ival> RSP_T_NUM
|
||||
|
||||
/* The semantic value holds the actual name of the field */
|
||||
%token <str> RSP_T_STRTAG
|
||||
%token <str> RSP_T_INTTAG
|
||||
|
||||
%token RSP_T_PARENS
|
||||
%token RSP_T_OR
|
||||
%token RSP_T_AND
|
||||
%token RSP_T_NOT
|
||||
|
||||
%token RSP_T_EQUAL
|
||||
%token RSP_T_IS
|
||||
%token RSP_T_INCLUDES
|
||||
|
||||
%left RSP_T_OR RSP_T_AND
|
||||
|
||||
%type <ast> criteria
|
||||
%type <ast> predicate
|
||||
|
||||
%%
|
||||
|
||||
query:
|
||||
criteria { return result_set(result, $1); }
|
||||
;
|
||||
|
||||
criteria: criteria RSP_T_AND criteria { $$ = ast_new(RSP_T_AND, $1, $3); }
|
||||
| criteria RSP_T_OR criteria { $$ = ast_new(RSP_T_OR, $1, $3); }
|
||||
| '(' criteria ')' { $$ = ast_new(RSP_T_PARENS, $2, NULL); }
|
||||
| predicate
|
||||
;
|
||||
|
||||
predicate: RSP_T_STRTAG RSP_T_EQUAL RSP_T_STRING { $$ = ast_new(RSP_T_IS, ast_data(RSP_T_STRTAG, $1), ast_data(RSP_T_STRING, $3)); }
|
||||
| RSP_T_STRTAG RSP_T_INCLUDES RSP_T_STRING { $$ = ast_new(RSP_T_INCLUDES, ast_data(RSP_T_STRTAG, $1), ast_data(RSP_T_STRING, $3)); }
|
||||
| RSP_T_INTTAG RSP_T_EQUAL RSP_T_NUM { $$ = ast_new(RSP_T_EQUAL, ast_data(RSP_T_INTTAG, $1), ast_int(RSP_T_NUM, $3)); }
|
||||
;
|
||||
|
||||
%%
|
||||
|
@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 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
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "logger.h"
|
||||
#include "misc.h"
|
||||
#include "rsp_query.h"
|
||||
|
||||
char *
|
||||
rsp_query_parse_sql(const char *rsp_query)
|
||||
{
|
||||
return NULL;
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
%language=ANSI-C
|
||||
%readonly-tables
|
||||
%enum
|
||||
enum rsp_field_types {
|
||||
RSP_TYPE_STRING,
|
||||
RSP_TYPE_INT,
|
||||
RSP_TYPE_DATE,
|
||||
};
|
||||
%switch=1
|
||||
%compare-lengths
|
||||
%define hash-function-name rsp_query_field_hash
|
||||
%define lookup-function-name rsp_query_field_lookup
|
||||
%define slot-name rsp_field
|
||||
%struct-type
|
||||
struct rsp_query_field_map {
|
||||
char *rsp_field;
|
||||
int field_type;
|
||||
};
|
||||
%%
|
||||
"id", RSP_TYPE_INT
|
||||
"path", RSP_TYPE_STRING
|
||||
"fname", RSP_TYPE_STRING
|
||||
"title", RSP_TYPE_STRING
|
||||
"artist", RSP_TYPE_STRING
|
||||
"album", RSP_TYPE_STRING
|
||||
"genre", RSP_TYPE_STRING
|
||||
"comment", RSP_TYPE_STRING
|
||||
"type", RSP_TYPE_STRING
|
||||
"composer", RSP_TYPE_STRING
|
||||
"orchestra", RSP_TYPE_STRING
|
||||
"grouping", RSP_TYPE_STRING
|
||||
"url", RSP_TYPE_STRING
|
||||
"bitrate", RSP_TYPE_INT
|
||||
"samplerate", RSP_TYPE_INT
|
||||
"song_length", RSP_TYPE_INT
|
||||
"file_size", RSP_TYPE_INT
|
||||
"year", RSP_TYPE_INT
|
||||
"track", RSP_TYPE_INT
|
||||
"total_tracks", RSP_TYPE_INT
|
||||
"disc", RSP_TYPE_INT
|
||||
"total_discs", RSP_TYPE_INT
|
||||
"bpm", RSP_TYPE_INT
|
||||
"compilation", RSP_TYPE_INT
|
||||
"rating", RSP_TYPE_INT
|
||||
"play_count", RSP_TYPE_INT
|
||||
"skip_count", RSP_TYPE_INT
|
||||
"data_kind", RSP_TYPE_INT
|
||||
"item_kind", RSP_TYPE_INT
|
||||
"description", RSP_TYPE_STRING
|
||||
"time_added", RSP_TYPE_DATE
|
||||
"time_modified", RSP_TYPE_DATE
|
||||
"time_played", RSP_TYPE_DATE
|
||||
"time_skipped", RSP_TYPE_DATE
|
||||
"db_timestamp", RSP_TYPE_DATE
|
||||
"sample_count", RSP_TYPE_INT
|
||||
"codectype", RSP_TYPE_STRING
|
||||
"idx", RSP_TYPE_INT
|
||||
"has_video", RSP_TYPE_INT
|
||||
"contentrating", RSP_TYPE_INT
|
||||
"bits_per_sample", RSP_TYPE_INT
|
||||
"album_artist", RSP_TYPE_STRING
|
@ -1,9 +0,0 @@
|
||||
|
||||
#ifndef __RSP_QUERY_H__
|
||||
#define __RSP_QUERY_H__
|
||||
|
||||
|
||||
char *
|
||||
rsp_query_parse_sql(const char *rsp_query);
|
||||
|
||||
#endif /* !__RSP_QUERY_H__ */
|
Loading…
Reference in New Issue
Block a user