Sat, 02 May 2015 18:52:04 +0200
fixed control flow in int_cmd + committed testcase (enable with #define DO_THE_TEST)
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2015 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include "davql.h" #include "methods.h" #include "utils.h" DavQuery dav_ql_parse(char *query, va_list ap) { DavQuery davquery; davquery.command = DAV_QUERY_ERROR; davquery.command_data = NULL; sstr_t q = sstr(query); q = sstrtrim(q); // get query command sstr_t cmd; cmd.ptr = NULL; int i; for(i=0;i<q.length;i++) { if(q.ptr[i] == ' ') { cmd = sstrsubsl(q, 0, i); break; } } if(!cmd.ptr) { fprintf(stderr, "DQL syntax error\n"); return davquery; } cmd = sstrtrim(cmd); q = sstrtrim(sstrsubs(q, i)); if(!sstrcmp(cmd, S("get"))) { davquery.command = DAV_QUERY_GET; davquery.command_data = dav_ql_parse_get(q, ap); } return davquery; } DavGetQuery* dav_ql_parse_get(sstr_t q, va_list ap) { sstr_t property_query = q; q = util_getsubstr_until_token(q, S("from"), &property_query); sstr_t from_query = q; sstr_t cond = util_getsubstr_until_token(q, S("where"), &from_query); sstr_t with = util_getsubstr_until_token(cond, S("with"), &cond); int depth = 1; // insert variable values UcxBuffer *fbuf = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND); int var = 0; for(int i=0;i<from_query.length;i++) { char c = from_query.ptr[i]; if(c == '%') { if(var) { ucx_buffer_putc(fbuf, '%'); // previous '%' } else { var = 1; } } else if(var) { switch(c) { case 's': { char *arg = va_arg(ap, char*); ucx_buffer_puts(fbuf, arg); break; } default: { ucx_buffer_putc(fbuf, '%'); ucx_buffer_putc(fbuf, c); } } var = 0; } else { ucx_buffer_putc(fbuf, c); } } // condition DavQOp *condition = NULL; size_t oplen = 0; if(cond.ptr) { //printf("cond: {%.*s}\n", cond.length, cond.ptr); UcxList *ops = NULL; if(dav_parse_condition(&ops, cond, ap)) { // TODO: error printf("parse error\n"); return NULL; } oplen = ucx_list_size(ops); condition = calloc(sizeof(DavQOp), oplen); int l = 0; UCX_FOREACH(elm, ops) { condition[l] = *(DavQOp*)elm->data; free(elm->data); l++; } ucx_list_free(ops); } // with if(with.ptr) { if(dav_parse_with(with, &depth, ap)) { // TODO: error printf("parse error\n"); return NULL; } } DavGetQuery *getquery = malloc(sizeof(DavGetQuery)); getquery->properties = sstrdup(property_query); getquery->from = sstrdup(sstrn(fbuf->space, fbuf->pos)); getquery->depth = depth; if(condition) { getquery->condition = condition; getquery->condlen = oplen; } else { getquery->condition = NULL; getquery->condlen = 0; } ucx_buffer_free(fbuf); return getquery; } void free_get_query(DavGetQuery *q) { free(q->from.ptr); free(q->properties.ptr); if(q->condition) { free(q->condition); } free(q); } int parse_path_query(sstr_t query, char **path, int *depth) { if(query.length == 1) { if(query.ptr[0] == '/') { *path = sstrdup(query).ptr; *depth = 1; return 0; } else { *path = NULL; return 1; } } if(query.ptr[query.length-1] == '*') { *depth = -1; *path = sstrdup(sstrsubsl(query, 0, query.length-1)).ptr; } else { *path = sstrdup(query).ptr; *depth = 1; } return 0; } static int dav_str2depth(sstr_t str, int *depth) { if(!sstrcmp(str, S("infinity"))) { *depth = -1; } else { sstr_t cp = sstrdup(str); // terminate *depth = atoi(cp.ptr); free(cp.ptr); } return 0; } int dav_parse_with(sstr_t with, int *depth, va_list ap) { int i; for(i=0;i<with.length;i++) { if(with.ptr[i] == ' ') { break; } } sstr_t name = sstrsubsl(with, 0, i); sstr_t value = sstrtrim(sstrsubs(with, i)); //printf("with {%.*s} {%.*s}\n", name.length, name.ptr, value.length, value.ptr); if(!sstrcmp(name, S("depth"))) { if(value.length == 0) { return 1; } else if(value.ptr[0] == '%') { switch(value.ptr[1]) { default: return 1; case 's': { sstr_t v = sstr(va_arg(ap, char*)); if(dav_str2depth(value, depth)) { return 1; } break; } case 'd': { *depth = va_arg(ap, int); break; } } } else { if(dav_str2depth(value, depth)) { return 1; } } } return 0; } int dav_parse_condition(UcxList **ops, sstr_t cond, va_list ap) { sstr_t token; DavQOp *op1 = NULL; // level 1 operator DavQOp *op2 = NULL; // level 2 operator DavQOp *op3 = NULL; // level 3 operator while((token = condition_parser_next_token(&cond)).length > 0) { //printf("token: {%.*s}[%d]\n", token.length, token.ptr, token.length); int64_t type = 0; int tkop = condition_operator_type(token, &type); DavQOp *operation = malloc(sizeof(DavQOp)); if(tkop > 0) { // operator token operation->type = DAVQOP_OPERATOR; operation->val = NULL; operation->intval = type; switch(tkop) { case 1: { // operators: + - / * not // add operation after next non operator token op1 = operation; break; } case 2: { // operators: < > == != <= >= if(op2) { *ops = ucx_list_append(*ops, op2); } op2 = operation; break; } case 3: { // operators: and or xor if(op2) { *ops = ucx_list_append(*ops, op2); op2 = NULL; } if(op3) { *ops = ucx_list_append(*ops, op3); } op3 = operation; break; } } } else { if(token.ptr[0] == '"' || token.ptr[0] == '\'') { operation->type = DAVQOP_STRING; operation->val = token.ptr+1; operation->intval = token.length-2; } else if(!sstrcmp(token, S("true")) || !sstrcmp(token, S("false"))) { operation->type = DAVQOP_INTEGER; operation->val = NULL; operation->intval = util_getboolean(token.ptr); } else if(token.length == 2 && token.ptr[0] == '%') { switch(token.ptr[1]) { case 's': { char *arg = va_arg(ap, char*); operation->type = DAVQOP_STRING; operation->val = arg; operation->intval = strlen(arg); break; } case 'd': { operation->type = DAVQOP_INTEGER; operation->val = NULL; operation->intval = va_arg(ap, int); break; } case 't': { operation->type = DAVQOP_INTEGER; operation->val = NULL; operation->intval = va_arg(ap, time_t); break; } default: { operation->type = DAVQOP_STRING; operation->val = token.ptr; operation->intval = token.length; } } } else { sstr_t d = sstrdup(token); int64_t val = 0; int intval = util_strtoint(d.ptr, &val); free(d.ptr); if(intval) { operation->type = DAVQOP_INTEGER; operation->val = NULL; operation->intval = val; } else { if(!sstrcmp(token, S("contentlength"))) { operation->type = DAVQOP_RESPROP; } else if(!sstrcmp(token, S("lastmodified"))) { operation->type = DAVQOP_RESPROP; } else if(!sstrcmp(token, S("creationdate"))) { operation->type = DAVQOP_RESPROP; } else if(!sstrcmp(token, S("name"))) { operation->type = DAVQOP_RESPROP; } else if(!sstrcmp(token, S("path"))) { operation->type = DAVQOP_RESPROP; } else if(!sstrcmp(token, S("iscollection"))) { operation->type = DAVQOP_RESPROP; } else { operation->type = DAVQOP_PROPERTY; } operation->val = token.ptr; operation->intval = token.length; } } // add operation *ops = ucx_list_append(*ops, operation); if(op1) { // add level 1 operator *ops = ucx_list_append(*ops, op1); op1 = NULL; } } } if(op1) { *ops = ucx_list_append(*ops, op1); } if(op2) { *ops = ucx_list_append(*ops, op2); } if(op3) { *ops = ucx_list_append(*ops, op3); } return 0; } sstr_t condition_parser_next_token(sstr_t *str) { sstr_t s = *str; sstr_t t; t.ptr = NULL; t.length = 0; // remove leading space int i; for(i=0;i<s.length;i++) { if(s.ptr[i] > 32) { break; } } s.length -= i; s.ptr += i; if(s.length == 0) { *str = s; return t; } // check for single char operators switch(s.ptr[0]) { case '<': case '>': case '+': case '-': case '*': case '/': { t.ptr = s.ptr; t.length = 1; str->ptr = s.ptr + 1; str->length = s.length - 1; return t; } } if(s.length > 1) { // check for double char operators int16_t op = *(int16_t*)s.ptr; if(op == '==' || op == '!=' || op == '>=' || op == '=<') { t.ptr = s.ptr; t.length = 2; str->ptr = s.ptr + 2; str->length = s.length - 2; return t; } } else { t.ptr = s.ptr; t.length = 1; str->ptr = s.ptr + 1; str->length = s.length - 1; return t; } // TODO: brackets // check for string literal if(s.ptr[0] == '\'' || s.ptr[0] == '"') { for(i=1;i<s.length;i++) { if(s.ptr[0] == s.ptr[i]) { i++; break; } } t.ptr = s.ptr; t.length = i; str->ptr = s.ptr + i; str->length = s.length - i; return t; } for(i=0;i<s.length;i++) { char c = s.ptr[i]; if((c < 33) || (c > 41 && c < 48) || (c > 59 && c < 63)) { break; } } t.ptr = s.ptr; t.length = i; str->ptr = s.ptr + i; str->length = s.length - i; return t; } int condition_operator_type(sstr_t token, int64_t *type) { // returns the operator level and sets the type if(token.ptr[0] == '"' || token.ptr[0] == '\'' || token.ptr[0] == '(') { return 0; } if(token.length == 1) { switch(token.ptr[0]) { case '+': *type = 1; return 1; case '-': *type = 2; return 1; case '*': *type = 3; return 1; case '/': *type = 4; return 1; case '<': *type = 5; return 2; case '>': *type = 6; return 2; } } if(!sstrcmp(token, S("not"))) { *type = 0; return 1; } if(!sstrcmp(token, S("=="))) { *type = 7; return 2; } if(!sstrcmp(token, S("!="))) { *type = 8; return 2; } if(!sstrcmp(token, S("<="))) { *type = 9; return 2; } if(!sstrcmp(token, S(">="))) { *type = 10; return 2; } if(!sstrcmp(token, S("and"))) { *type = 11; return 3; } if(!sstrcmp(token, S("or"))) { *type = 12; return 3; } if(!sstrcmp(token, S("xor"))) { *type = 13; return 3; } return 0; } int condition_eval(DavResource *res, DavQOp *cond, size_t len) { DavQOp stack[128]; int stackpos = 0; for(int i=0;i<len;i++) { DavQOp op = cond[i]; switch(op.type) { case DAVQOP_OPERATOR: { if(op.intval == 0) { // not operator if(stackpos < 1) { // error printf("no data on stack\n"); return 0; } int pos = stackpos-1; if(stack[pos].type == DAVQOP_INTEGER) { //printf("not %" PRId64 "\n", stack[pos].intval); stack[pos].intval = !stack[pos].intval; } else { // error printf("wrong value for 'not' operator\n"); return 0; } } else { DavQOp val1 = stack[stackpos-2]; DavQOp val2 = stack[stackpos-1]; DavQOp result; if(val1.type == DAVQOP_INTEGER) { if(val2.type == DAVQOP_INTEGER) { result = compare_intint( op.intval, val1.intval, val2.intval); } else { result = compare_intstr(op.intval, val1, val2); } } else { if(val2.type == DAVQOP_INTEGER) { result = compare_strint(op.intval, val1, val2); } else { result = compare_strstr(op.intval, val1, val2); } } stack[stackpos-2] = result; stackpos--; } break; } case DAVQOP_STRING: case DAVQOP_INTEGER: case DAVQOP_TIME: { if(op.type == DAVQOP_STRING) { //printf("put on stack: '%s'\n", op.val); } else { //printf("put on stack[%d]: %" PRId64 "\n", stackpos, op.intval); } stack[stackpos++] = op; break; } case DAVQOP_PROPERTY: { sstr_t pname = sstrn(op.val, op.intval); pname = sstrdup(pname); char *property_value = dav_get_property(res, pname.ptr); free(pname.ptr); DavQOp value; value.type = DAVQOP_STRING; if(property_value) { //printf("put on stack: \"%s\"\n", property_value); value.val = property_value; value.intval = strlen(property_value); } else { //printf("put on stack: null string\n"); value.val = NULL; value.intval = 0; } stack[stackpos++] = value; break; } case DAVQOP_RESPROP: { sstr_t name = sstrn(op.val, op.intval); DavQOp value; value.type = DAVQOP_INTEGER; value.val = NULL; if(!sstrcmp(name, S("contentlength"))) { //printf("put contentlength\n"); value.intval = res->contentlength; } else if(!sstrcmp(name, S("lastmodified"))) { //printf("put getlastmodified\n"); value.intval = res->lastmodified; } else if(!sstrcmp(name, S("creationdate"))) { value.intval = res->creationdate; } else if(!sstrcmp(name, S("name"))) { value.type = DAVQOP_STRING; value.val = res->name; value.intval = strlen(res->name); } else if(!sstrcmp(name, S("path"))) { value.type = DAVQOP_STRING; value.val = res->path; value.intval = strlen(res->path); } else if(!sstrcmp(name, S("iscollection"))) { value.type = DAVQOP_INTEGER; value.val = NULL; value.intval = res->iscollection; } stack[stackpos++] = value; break; } } } if(stackpos != 1) { return 0; } DavQOp result = stack[0]; //printf("result: %" PRId64 "\n", result.intval); return (int)result.intval; } DavQOp compare_intint(int op, int64_t v1, int64_t v2) { DavQOp res; res.type = DAVQOP_INTEGER; res.val = NULL; res.intval = 0; switch(op) { case 5: { // < //printf("compare: %" PRId64 " < %" PRId64 "\n", v1, v2); res.intval = v1 < v2; break; } case 6: { // > //printf("compare: %" PRId64 " > %" PRId64 "\n", v1, v2); res.intval = v1 > v2; break; } case 7: { // == //printf("compare: %" PRId64 " == %" PRId64 "\n", v1, v2); res.intval = v1 == v2; break; } case 8: { // != //printf("compare: %" PRId64 " != %" PRId64 "\n", v1, v2); res.intval = v1 != v2; break; } case 9: { // <= //printf("compare: %" PRId64 " <= %" PRId64 "\n", v1, v2); res.intval = v1 <= v2; break; } case 10: { // >= //printf("compare: %" PRId64 " >= %" PRId64 "\n", v1, v2); res.intval = v1 >= v2; break; } case 11: { // and //printf("compare: %" PRId64 " and %" PRId64 "\n", v1, v2); res.intval = v1 && v2; break; } case 12: { // or //printf("compare: %" PRId64 " or %" PRId64 "\n", v1, v2); res.intval = v1 || v2; break; } case 13: { // xor //printf("compare: %" PRId64 " xor %" PRId64 "\n", v1, v2); res.intval = v1 ^ v2; break; } } return res; } DavQOp compare_strint(int op, DavQOp v1, DavQOp v2) { int64_t v1int; sstr_t s1 = sstrn(v1.val, v1.intval); s1 = sstrdup(s1); if(util_strtoint(s1.ptr, &v1int)) { free(s1.ptr); return compare_intint(op, v1int, v2.intval); } else { free(s1.ptr); // TODO } } DavQOp compare_intstr(int op, DavQOp v1, DavQOp v2) { // TODO } DavQOp compare_strstr(int op, DavQOp v1, DavQOp v2) { DavQOp res; res.type = DAVQOP_INTEGER; res.val = NULL; res.intval = 0; sstr_t s1 = sstrn(v1.val, v1.intval); sstr_t s2 = sstrn(v2.val, v2.intval); switch(op) { case 5: { // < //printf("str compare: %.*s < %.*s\n", s1.length, s1.ptr, s2.length, s2.ptr); res.intval = s1.length < s2.length; break; } case 6: { // > //printf("str compare: %.*s > %.*s\n", s1.length, s1.ptr, s2.length, s2.ptr); res.intval = s1.length > s2.length; break; } case 7: { // == //printf("str compare: %.*s == %.*s\n", s1.length, s1.ptr, s2.length, s2.ptr); res.intval = sstrcmp(s1, s2) == 0; break; } case 8: { // != //printf("str compare: %.*s != %.*s\n", s1.length, s1.ptr, s2.length, s2.ptr); res.intval = sstrcmp(s1, s2) != 0; break; } case 9: { // <= //printf("str compare: %.*s <= %.*s\n", s1.length, s1.ptr, s2.length, s2.ptr); res.intval = s1.length <= s2.length; break; } case 10: { // >= //printf("str compare: %.*s >= %.*s\n", s1.length, s1.ptr, s2.length, s2.ptr); res.intval = s1.length >= s2.length; break; } case 11: { // and //printf("str compare: %.*s and %.*s\n", s1.length, s1.ptr, s2.length, s2.ptr); res.intval = s1.ptr && s2.ptr; break; } case 12: { // or //printf("str compare: %.*s or %.*s\n", s1.length, s1.ptr, s2.length, s2.ptr); res.intval = s1.ptr || s2.ptr; break; } case 13: { // xor //printf("str compare: %.*s xor %.*s\n", s1.length, s1.ptr, s2.length, s2.ptr); res.intval = (intptr_t)s1.ptr ^ (intptr_t)s2.ptr; break; } } return res; }