/* * Copyright (C) 2009 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 "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); } ;