Thu, 21 Dec 2017 19:48:27 +0100
davql: allow ANYWHERE keyword in SELECT statements
This may seem pointless, but users might want to be explicit about this and the grammar is more consistent.
This commit also adds some no-ops to the functions body of the SET parser, because some day the grammar might allow more clauses after the WHERE clause.
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2016 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 <inttypes.h> #include <ucx/utils.h> #include <ucx/map.h> #include "davqlexec.h" #include "utils.h" #include "methods.h" #include "session.h" #include "resource.h" DavResult dav_statement_exec(DavSession *sn, DavQLStatement *st, ...) { va_list ap; va_start(ap, st); DavResult result = dav_statement_execv(sn, st, ap); va_end(ap); return result; } DavResult dav_statement_execv(DavSession *sn, DavQLStatement *st, va_list ap) { DavResult result; result.result = NULL; result.status = 1; // make sure the statement was successfully parsed if(st->type == DAVQL_ERROR) { return result; } if(st->type == DAVQL_SELECT) { return dav_exec_select(sn, st, ap); } else { // TODO } return result; } sstr_t dav_format_string(UcxAllocator *a, sstr_t fstr, va_list ap, davqlerror_t *error) { UcxBuffer *buf = ucx_buffer_new(NULL, 128, UCX_BUFFER_AUTOEXTEND); int placeholder = 0; for(int i=0;i<fstr.length;i++) { char c = fstr.ptr[i]; if(placeholder) { if(c == '%') { // no placeholder, %% transposes to % ucx_buffer_putc(buf, c); } else { // detect placeholder type and insert arg int err = 0; switch(c) { case 's': { char *arg = va_arg(ap, char*); ucx_buffer_puts(buf, arg); break; } case 'd': { int arg = va_arg(ap, int); ucx_bprintf(buf, "%d", arg); break; } case 'u': { unsigned int arg = va_arg(ap, unsigned int); ucx_bprintf(buf, "%u", arg); break; } case 't': { // time arguments not supported for strings err = 1; break; } default: { *error = DAVQL_UNKNOWN_FORMATCHAR; err = 1; } } if(err) { ucx_buffer_free(buf); sstr_t n; n.ptr = NULL; n.length = 0; return n; } } placeholder = 0; } else { if(c == '%') { placeholder = 1; } else { ucx_buffer_putc(buf, c); } } } *error = DAVQL_OK; sstr_t ret = sstrdup_a(a, sstrn(buf->space, buf->size)); ucx_buffer_free(buf); return ret; } static int fl_add_properties(DavSession *sn, UcxMempool *mp, UcxMap *map, DavQLExpression *expression) { if(!expression) { return 0; } if(expression->type == DAVQL_IDENTIFIER) { DavProperty *property = ucx_mempool_malloc(mp, sizeof(DavProperty)); char *name; DavNamespace *ns = dav_get_property_namespace( sn->context, sstrdup_a(mp->allocator, expression->srctext).ptr, &name); if(!ns) { return -1; } property->ns = ns; property->name = name; property->value = NULL; ucx_map_sstr_put(map, expression->srctext, property); } if(expression->left) { if(fl_add_properties(sn, mp, map, expression->left)) { return -1; } } if(expression->right) { if(fl_add_properties(sn, mp, map, expression->right)) { return -1; } } return 0; } static UcxBuffer* fieldlist2propfindrequest(DavSession *sn, UcxMempool *mp, UcxList *fields, int *isallprop) { UcxMap *properties = ucx_map_new(32); *isallprop = 0; UCX_FOREACH(elm, fields) { DavQLField *field = elm->data; if(!sstrcmp(field->name, S("*"))) { ucx_map_free(properties); *isallprop = 1; return create_allprop_propfind_request(); } else if(!sstrcmp(field->name, S("-"))) { ucx_map_free(properties); return create_propfind_request(sn, NULL); } else { if(fl_add_properties(sn, mp, properties, field->expr)) { // TODO: set error ucx_map_free(properties); return NULL; } } } UcxMapIterator i = ucx_map_iterator(properties); UcxKey key; DavProperty *value; UcxList *list = NULL; UCX_MAP_FOREACH(key, value, i) { list = ucx_list_append(list, value); } UcxBuffer *reqbuf = create_propfind_request(sn, list); ucx_list_free(list); ucx_map_free(properties); return reqbuf; } static int reset_properties(DavSession *sn, DavResult *result, DavResource *res, UcxList *fields) { UcxMap *new_properties = ucx_map_new_a(sn->mp->allocator, 32); DavResourceData *data = (DavResourceData*)res->data; // add basic properties char *value; UcxKey cl_key = dav_property_key("DAV:", "getcontentlength"); value = ucx_map_get(data->properties, cl_key); if(value) { ucx_map_put(new_properties, cl_key, value); } UcxKey cd_key = dav_property_key("DAV:", "creationdate"); value = ucx_map_get(data->properties, cd_key); if(value) { ucx_map_put(new_properties, cd_key, value); } UcxKey lm_key = dav_property_key("DAV:", "getlastmodified"); value = ucx_map_get(data->properties, lm_key); if(value) { ucx_map_put(new_properties, lm_key, value); } UcxKey ct_key = dav_property_key("DAV:", "getcontenttype"); value = ucx_map_get(data->properties, ct_key); if(value) { ucx_map_put(new_properties, ct_key, value); } UcxKey rt_key = dav_property_key("DAV:", "resourcetype"); value = ucx_map_get(data->properties, rt_key); if(value) { ucx_map_put(new_properties, rt_key, value); } UcxKey cn_key = dav_property_key(DAV_NS, "crypto-name"); value = ucx_map_get(data->properties, cn_key); if(value) { ucx_map_put(new_properties, cn_key, value); } UcxKey ck_key = dav_property_key(DAV_NS, "crypto-key"); value = ucx_map_get(data->properties, ck_key); if(value) { ucx_map_put(new_properties, ck_key, value); } UcxKey ch_key = dav_property_key(DAV_NS, "crypto-hash"); value = ucx_map_get(data->properties, ch_key); if(value) { ucx_map_put(new_properties, ch_key, value); } // add properties from field list UCX_FOREACH(elm, fields) { DavCompiledField *field = elm->data; DavQLStackObj field_result; if(!dav_exec_expr(field->code, res, &field_result)) { sstr_t str; str.ptr = NULL; str.length = 0; DavXmlNode *node = NULL; if(field_result.type == 0) { str = ucx_asprintf( sn->mp->allocator, "%d", field_result.data.integer); } else if(field_result.type == 1) { if(field_result.data.string) { str = sstrdup_a(sn->mp->allocator, sstrn( field_result.data.string, field_result.length)); } } else if(field_result.type == 2) { // TODO: } else { // unknown type // TODO: error resource_free_properties(sn, new_properties); return -1; } if(str.ptr) { node = dav_session_malloc(sn, sizeof(DavXmlNode)); memset(node, 0, sizeof(DavXmlNode)); node->type = DAV_XML_TEXT; node->content = str.ptr; node->contentlength = str.length; } if(node) { UcxKey key = dav_property_key(field->ns, field->name); ucx_map_put(new_properties, key, node); free(key.data); } } else { // TODO: error resource_free_properties(sn, new_properties); return -1; } } ucx_map_remove(data->properties, cl_key); ucx_map_remove(data->properties, cd_key); ucx_map_remove(data->properties, lm_key); ucx_map_remove(data->properties, ct_key); ucx_map_remove(data->properties, rt_key); ucx_map_remove(data->properties, cn_key); ucx_map_remove(data->properties, ck_key); ucx_map_remove(data->properties, ch_key); resource_free_properties(sn, data->properties); data->properties = new_properties; free(cl_key.data); free(cd_key.data); free(lm_key.data); free(ct_key.data); free(rt_key.data); free(cn_key.data); free(ck_key.data); free(ch_key.data); return 0; } /* * execute a davql select statement */ DavResult dav_exec_select(DavSession *sn, DavQLStatement *st, va_list ap) { UcxMempool *mp = ucx_mempool_new(128); DavResult result; result.result = NULL; result.status = 1; int isallprop; UcxBuffer *rqbuf = fieldlist2propfindrequest(sn, mp, st->fields, &isallprop); if(!rqbuf) { ucx_mempool_destroy(mp); return result; } ucx_mempool_reg_destr(mp, rqbuf, (ucx_destructor)ucx_buffer_free); // compile field list UcxList *cfieldlist = NULL; UCX_FOREACH(elm, st->fields) { DavQLField *field = elm->data; if(sstrcmp(field->name, S("*")) && sstrcmp(field->name, S("-"))) { // compile field expression UcxBuffer *code = dav_compile_expr( sn->context, mp->allocator, field->expr, ap); if(!code) { // TODO: set error string return result; } ucx_mempool_reg_destr(mp, code, (ucx_destructor)ucx_buffer_free); DavCompiledField *cfield = ucx_mempool_malloc( mp, sizeof(DavCompiledField)); char *ns; char *name; dav_get_property_namespace_str( sn->context, sstrdup_a(mp->allocator, field->name).ptr, &ns, &name); if(!ns || !name) { // TODO: set error string return result; } cfield->ns = ns; cfield->name = name; cfield->code = code; cfieldlist = ucx_list_append_a(mp->allocator, cfieldlist, cfield); } } // get path string davqlerror_t error; sstr_t path = dav_format_string(mp->allocator, st->path, ap, &error); if(error) { // TODO: cleanup ucx_mempool_destroy(mp); return result; } int depth = st->depth == DAV_DEPTH_PLACEHOLDER ? va_arg(ap, int) : st->depth; UcxBuffer *where = dav_compile_expr(sn->context, mp->allocator, st->where, ap); if(st->where && !where) { // TODO: cleanup ucx_mempool_destroy(mp); return result; } if(where) { ucx_mempool_reg_destr(mp, where, (ucx_destructor)ucx_buffer_free); } // compile order criterion UcxList *ordercr = NULL; UCX_FOREACH(elm, st->orderby) { DavQLOrderCriterion *oc = elm->data; DavQLExpression *column = oc->column; //printf("%.*s %s\n", column->srctext.length, column->srctext.ptr, oc->descending ? "desc" : "asc"); if(column->type == DAVQL_IDENTIFIER) { // TODO: remove code duplication (add_cmd) davqlresprop_t resprop; sstr_t propertyname = sstrchr(column->srctext, ':'); if(propertyname.length > 0) { char *ns; char *name; dav_get_property_namespace_str( sn->context, sstrdup_a(mp->allocator, column->srctext).ptr, &ns, &name); if(ns && name) { DavOrderCriterion *cr = ucx_mempool_malloc(mp, sizeof(DavOrderCriterion)); cr->type = 1; cr->column.property = dav_property_key_a(mp->allocator, ns, name); cr->descending = oc->descending; ordercr = ucx_list_append_a(mp->allocator, ordercr, cr); } else { // error // TODO: cleanup ucx_mempool_destroy(mp); return result; } } else if(dav_identifier2resprop(column->srctext, &resprop)) { DavOrderCriterion *cr = ucx_mempool_malloc(mp, sizeof(DavOrderCriterion)); cr->type = 0; cr->column.resprop = resprop; cr->descending = oc->descending; ordercr = ucx_list_append_a(mp->allocator, ordercr, cr); } else { // error // TODO: cleanup ucx_mempool_destroy(mp); return result; } } else if(column->type == DAVQL_NUMBER) { // TODO: implement fprintf(stderr, "order by number not supported\n"); return result; } else { // something is broken // TODO: cleanup ucx_mempool_destroy(mp); return result; } } DavResource *selroot = dav_resource_new(sn, path.ptr); UcxList *stack = NULL; // stack with DavResource* elements // initialize the stack with the requested resource DavQLRes *res = ucx_mempool_malloc(mp, sizeof(DavQLRes)); res->resource = selroot; res->depth = 0; stack = ucx_list_prepend(stack, res); // reuseable response buffer UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND); if(!rpbuf) { // TODO: cleanup ucx_mempool_destroy(mp); return result; } ucx_mempool_reg_destr(mp, rpbuf, (ucx_destructor)ucx_buffer_free); result.result = selroot; result.status = 0; // do a propfind request for each resource on the stack while(stack) { DavQLRes *sr = stack->data; // get first element from the stack stack = ucx_list_remove(stack, stack); // remove first element DavResource *root = sr->resource; util_set_url(sn, dav_resource_get_href(sr->resource)); CURLcode ret = do_propfind_request(sn, rqbuf, rpbuf); long http_status = 0; curl_easy_getinfo(sn->handle, CURLINFO_RESPONSE_CODE, &http_status); //printf("rpbuf: %s %s\n%.*s\n\n", sr->resource->path, sr->resource->href, rpbuf->pos, rpbuf->space); if(ret == CURLE_OK && http_status == 207) { // propfind request successful, now parse the response char *url = "http://url/"; PropfindParser *parser = create_propfind_parser(rpbuf, url); // TODO: test if parser is null ResponseTag response; int r; while((r = get_propfind_response(parser, &response)) != 0) { if(r == -1) { // error result.status = -1; // TODO: free resources cleanup_response(&response); break; } // the propfind multistatus response contains responses // for the requested resource and all childs // determine if the response is a child or not if(hrefeq(sn, root->href, response.href)) { // response is the currently requested resource // and not a child // add properties add_properties(root, &response); cleanup_response(&response); if(root == selroot) { // The current root is the root of the select query. // In this case we have to check the where clause. // If root is not selroot, the where clause was // already checked for the resource before it was // added to the stack. DavQLStackObj where_result; if(!dav_exec_expr(where, root, &where_result)) { if(where_result.data.integer != 0) { if(!reset_properties(sn, &result, root, cfieldlist)) { continue; } result.status = -1; } } result.result = NULL; result.status = -1; dav_resource_free_all(selroot); ucx_list_free(stack); break; } } else { DavResource *child = response2resource( sn, &response, root->path); cleanup_response(&response); // check where clause DavQLStackObj where_result; if(!dav_exec_expr(where, child, &where_result)) { if(where_result.data.integer != 0) { if(!reset_properties(sn, &result, child, cfieldlist)) { //resource_add_child(root, child); resource_add_ordered_child(root, child, ordercr); if(child->iscollection && (depth < 0 || depth > sr->depth+1)) { DavQLRes *rs = ucx_mempool_malloc( mp, sizeof(DavQLRes)); rs->resource = child; rs->depth = sr->depth + 1; stack = ucx_list_prepend(stack, rs); } } else { dav_resource_free(child); } } else { dav_resource_free(child); } } } } destroy_propfind_parser(parser); } else { dav_session_set_error(sn, ret, http_status); result.result = NULL; result.status = -1; dav_resource_free_all(selroot); break; } // reset response buffer ucx_buffer_seek(rpbuf, SEEK_SET, 0); } ucx_mempool_destroy(mp); return result; } static int count_func_args(DavQLExpression *expr) { int count = 0; DavQLExpression *arg = expr->right; while(arg) { count++; if(arg->op == DAVQL_ARGLIST) { arg = arg->right; } else { break; } } return count; } int dav_identifier2resprop(sstr_t src, davqlresprop_t *prop) { if(!sstrcmp(src, S("name"))) { *prop = DAVQL_RES_NAME; } else if(!sstrcmp(src, S("path"))) { *prop = DAVQL_RES_PATH; } else if(!sstrcmp(src, S("href"))) { *prop = DAVQL_RES_HREF; } else if(!sstrcmp(src, S("contentlength"))) { *prop = DAVQL_RES_CONTENTLENGTH; } else if(!sstrcmp(src, S("contenttype"))) { *prop = DAVQL_RES_CONTENTTYPE; } else if(!sstrcmp(src, S("creationdate"))) { *prop = DAVQL_RES_CREATIONDATE; } else if(!sstrcmp(src, S("lastmodified"))) { *prop = DAVQL_RES_LASTMODIFIED; } else if(!sstrcmp(src, S("iscollection"))) { *prop = DAVQL_RES_ISCOLLECTION; } else { return 0; } return 1; } static int add_cmd(DavContext *ctx, UcxAllocator *a, UcxBuffer *bcode, DavQLExpression *expr, va_list ap) { if(!expr) { return 0; } int numcmd = 1; DavQLCmd cmd; memset(&cmd, sizeof(DavQLCmd), 0); davqlerror_t error; sstr_t src = expr->srctext; switch(expr->type) { default: break; case DAVQL_NUMBER: { cmd.type = DAVQL_CMD_INT; if(src.ptr[0] == '%') { cmd.data.integer = va_arg(ap, int); } else if(util_strtoint(src.ptr, &cmd.data.integer)) { ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); } else { // error return -1; } break; } case DAVQL_STRING: { cmd.type = DAVQL_CMD_STRING; cmd.data.string = dav_format_string(a, src, ap, &error); ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); break; } case DAVQL_TIMESTAMP: { if(src.ptr[0] == '%') { cmd.type = DAVQL_CMD_TIMESTAMP; cmd.data.timestamp = va_arg(ap, time_t); ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); } else { // error return -1; } break; } case DAVQL_IDENTIFIER: { sstr_t propertyname = sstrchr(src, ':'); cmd.type = DAVQL_CMD_RES_IDENTIFIER; if(propertyname.length > 0) { cmd.type = DAVQL_CMD_PROP_IDENTIFIER; char *ns; char *name; dav_get_property_namespace_str( ctx, sstrdup_a(a, src).ptr, &ns, &name); if(ns && name) { cmd.data.property.ns = ns; cmd.data.property.name = name; } else { // error return -1; } } else if(!dav_identifier2resprop(src, &cmd.data.resprop)) { if(!sstrcmp(src, S("true"))) { cmd.type = DAVQL_CMD_INT; cmd.data.integer = 1; } else if(!sstrcmp(src, S("false"))) { cmd.type = DAVQL_CMD_INT; cmd.data.integer = 0; } else { // error, unknown identifier return -1; } } ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); break; } case DAVQL_UNARY: { numcmd += add_cmd(ctx, a, bcode, expr->left, ap); switch(expr->op) { case DAVQL_ADD: { // noop numcmd = 0; break; } case DAVQL_SUB: { cmd.type = DAVQL_CMD_OP_UNARY_SUB; ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); break; } case DAVQL_NEG: { cmd.type = DAVQL_CMD_OP_UNARY_NEG; ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); break; } default: break; } break; } case DAVQL_BINARY: { numcmd += add_cmd(ctx, a, bcode, expr->left, ap); numcmd += add_cmd(ctx, a, bcode, expr->right, ap); switch(expr->op) { case DAVQL_ADD: { cmd.type = DAVQL_CMD_OP_BINARY_ADD; break; } case DAVQL_SUB: { cmd.type = DAVQL_CMD_OP_BINARY_SUB; break; } case DAVQL_MUL: { cmd.type = DAVQL_CMD_OP_BINARY_MUL; break; } case DAVQL_DIV: { cmd.type = DAVQL_CMD_OP_BINARY_DIV; break; } case DAVQL_AND: { cmd.type = DAVQL_CMD_OP_BINARY_AND; break; } case DAVQL_OR: { cmd.type = DAVQL_CMD_OP_BINARY_OR; break; } case DAVQL_XOR: { cmd.type = DAVQL_CMD_OP_BINARY_XOR; break; } default: break; } ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); break; } case DAVQL_LOGICAL: { if(expr->left && expr->right && expr->op != DAVQL_LOR) { numcmd += add_cmd(ctx, a, bcode, expr->left, ap); numcmd += add_cmd(ctx, a, bcode, expr->right, ap); } switch(expr->op) { case DAVQL_NOT: { numcmd += add_cmd(ctx, a, bcode, expr->left, ap); cmd.type = DAVQL_CMD_OP_LOGICAL_NOT; ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); break; } case DAVQL_LAND: { cmd.type = DAVQL_CMD_OP_LOGICAL_AND; ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); break; } case DAVQL_LOR: { int nleft = add_cmd(ctx, a, bcode, expr->left, ap); cmd.type = DAVQL_CMD_OP_LOGICAL_OR_L; DavQLCmd *or_l = (DavQLCmd*)(bcode->space + bcode->pos); ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); int nright = add_cmd(ctx, a, bcode, expr->right, ap); or_l->data.integer = nright + 1; cmd.type = DAVQL_CMD_OP_LOGICAL_OR; cmd.data.integer = 0; ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); numcmd += nleft + nright; break; } case DAVQL_LXOR: { cmd.type = DAVQL_CMD_OP_LOGICAL_XOR; ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); break; } case DAVQL_EQ: { cmd.type = DAVQL_CMD_OP_EQ; ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); break; } case DAVQL_NEQ: { cmd.type = DAVQL_CMD_OP_NEQ; ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); break; } case DAVQL_LT: { cmd.type = DAVQL_CMD_OP_LT; ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); break; } case DAVQL_GT: { cmd.type = DAVQL_CMD_OP_GT; ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); break; } case DAVQL_LE: { cmd.type = DAVQL_CMD_OP_LE; ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); break; } case DAVQL_GE: { cmd.type = DAVQL_CMD_OP_GE; ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); break; } case DAVQL_LIKE: { cmd.type = DAVQL_CMD_OP_LIKE; ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); break; } case DAVQL_UNLIKE: { cmd.type = DAVQL_CMD_OP_UNLIKE; ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); break; } default: break; } break; } case DAVQL_FUNCCALL: { switch(expr->op) { case DAVQL_CALL: { int nright = add_cmd(ctx, a, bcode, expr->right, ap); // TODO: count args DavQLExpression *funcid = expr->left; if(!funcid && funcid->type != DAVQL_IDENTIFIER) { // fail return -1; } // numargs cmd.type = DAVQL_CMD_INT; cmd.data.integer = count_func_args(expr); ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); // TODO: resolve function name cmd.type = DAVQL_CMD_CALL; cmd.data.func = NULL; ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); numcmd = 2; numcmd += nright; break; } case DAVQL_ARGLIST: { numcmd = 0; numcmd += add_cmd(ctx, a, bcode, expr->left, ap); numcmd += add_cmd(ctx, a, bcode, expr->right, ap); break; } default: break; } break; } } return numcmd; } UcxBuffer* dav_compile_expr(DavContext *ctx, UcxAllocator *a, DavQLExpression *lexpr, va_list ap) { UcxBuffer *bcode = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOEXTEND); if(!bcode) { return NULL; } if(add_cmd(ctx, a, bcode, lexpr, ap) <= 0) { ucx_buffer_free(bcode); return NULL; } return bcode; } static int cmd_str_cmp(DavQLStackObj obj1, DavQLStackObj obj2, davqlcmdtype_t cmd) { sstr_t s1 = obj1.type == 1 ? sstrn(obj1.data.string, obj1.length) : ucx_sprintf("%" PRId64, obj1.data.integer); sstr_t s2 = obj1.type == 1 ? sstrn(obj2.data.string, obj2.length) : ucx_sprintf("%" PRId64, obj2.data.integer); int res = 0; switch(cmd) { case DAVQL_CMD_OP_EQ: { res = sstrcmp(s1, s2) == 0; break; } case DAVQL_CMD_OP_NEQ: { res = sstrcmp(s1, s2) != 0; break; } case DAVQL_CMD_OP_LT: { res = sstrcmp(s1, s2) < 0; break; } case DAVQL_CMD_OP_GT: { res = sstrcmp(s1, s2) > 0; break; } case DAVQL_CMD_OP_LE: { res = sstrcmp(s1, s2) <= 0; break; } case DAVQL_CMD_OP_GE: { res = sstrcmp(s1, s2) >= 0; break; } default: break; } if(obj1.type == 0) { free(s1.ptr); } if(obj2.type == 0) { free(s2.ptr); } return res; } int dav_exec_expr(UcxBuffer *bcode, DavResource *res, DavQLStackObj *result) { if(!bcode) { result->type = 0; result->length = 0; result->data.integer = 1; return 0; } size_t count = bcode->pos / sizeof(DavQLCmd); DavQLCmd *cmds = (DavQLCmd*)bcode->space; // create execution stack size_t stsize = 64; size_t stpos = 0; DavQLStackObj *stack = calloc(stsize, sizeof(DavQLStackObj)); #define DAVQL_PUSH(obj) \ if(stpos == stsize) { \ stsize += 64; \ DavQLStackObj *stack_newptr; \ stack_newptr = realloc(stack, stsize * sizeof(DavQLStackObj)); \ if(stack_newptr) { \ stack = stack_newptr; \ } else { \ free(stack); \ return -1; \ }\ } \ stack[stpos++] = obj; #define DAVQL_PUSH_INT(intval) \ { \ DavQLStackObj intobj; \ intobj.type = 0; \ intobj.length = 0; \ intobj.data.integer = intval; \ DAVQL_PUSH(intobj); \ } #define DAVQL_POP() stack[--stpos] DavQLStackObj obj; int ret = 0; for(size_t i=0;i<count;i++) { DavQLCmd cmd = cmds[i]; switch(cmd.type) { case DAVQL_CMD_INT: { //printf("int %lld\n", cmd.data.integer); obj.type = 0; obj.length = 0; obj.data.integer = cmd.data.integer; DAVQL_PUSH(obj); break; } case DAVQL_CMD_STRING: { //printf("string \"%.*s\"\n", cmd.data.string.length, cmd.data.string.ptr); obj.type = 1; obj.length = cmd.data.string.length; obj.data.string = cmd.data.string.ptr; DAVQL_PUSH(obj); break; } case DAVQL_CMD_TIMESTAMP: { //printf("timestamp %d\n", cmd.data.timestamp); obj.type = 0; obj.length = 0; obj.data.integer = (int64_t)cmd.data.timestamp; DAVQL_PUSH(obj); break; } case DAVQL_CMD_RES_IDENTIFIER: { //char *rid[8] = {"name", "path", "href", "contentlength", "contenttype", "creationdate", "lastmodified", "iscollection"}; //printf("resprop %s\n", rid[cmd.data.resprop]); switch(cmd.data.resprop) { case DAVQL_RES_NAME: { obj.type = 1; obj.length = strlen(res->name); obj.data.string = res->name; break; } case DAVQL_RES_PATH: { obj.type = 1; obj.length = strlen(res->path); obj.data.string = res->path; break; } case DAVQL_RES_HREF: { obj.type = 1; obj.length = strlen(res->href); obj.data.string = res->href; break; } case DAVQL_RES_CONTENTLENGTH: { obj.type = 0; obj.length = 0; obj.data.integer = res->contentlength; break; } case DAVQL_RES_CONTENTTYPE: { obj.type = 1; obj.length = strlen(res->contenttype); obj.data.string = res->contenttype; break; } case DAVQL_RES_CREATIONDATE: { obj.type = 0; obj.length = 0; obj.data.integer = res->creationdate; break; } case DAVQL_RES_LASTMODIFIED: { obj.type = 0; obj.length = 0; obj.data.integer = res->lastmodified; break; } case DAVQL_RES_ISCOLLECTION: { obj.type = 0; obj.length = 0; obj.data.integer = res->iscollection; break; } } DAVQL_PUSH(obj); break; } case DAVQL_CMD_PROP_IDENTIFIER: { //printf("property %s:%s\n", cmd.data.property.ns, cmd.data.property.name); char *value = dav_get_string_property_ns(res, cmd.data.property.ns, cmd.data.property.name); obj.type = 1; obj.length = value ? strlen(value) : 0; obj.data.string = value; DAVQL_PUSH(obj); break; } //case DAVQL_CMD_OP_UNARY_ADD: { // printf("uadd\n"); // break; //} case DAVQL_CMD_OP_UNARY_SUB: { //printf("usub\n"); obj = DAVQL_POP(); if(obj.type == 0) { obj.data.integer = -obj.data.integer; DAVQL_PUSH(obj); } else { ret = -1; i = count; // end loop } break; } case DAVQL_CMD_OP_UNARY_NEG: { //printf("uneg\n"); obj = DAVQL_POP(); if(obj.type == 0) { obj.data.integer = obj.data.integer == 0 ? 1 : 0; DAVQL_PUSH(obj); } else { ret = -1; i = count; // end loop } break; } case DAVQL_CMD_OP_BINARY_ADD: { //printf("add\n"); DavQLStackObj obj2 = DAVQL_POP(); DavQLStackObj obj1 = DAVQL_POP(); if(obj1.type == 0 && obj2.type == 0) { DAVQL_PUSH_INT(obj1.data.integer + obj2.data.integer); } else { // TODO: string concat } break; } case DAVQL_CMD_OP_BINARY_SUB: { //printf("sub\n"); DavQLStackObj obj2 = DAVQL_POP(); DavQLStackObj obj1 = DAVQL_POP(); if(obj1.type == 0 && obj2.type == 0) { DAVQL_PUSH_INT(obj1.data.integer - obj2.data.integer); } else { // error ret = -1; i = count; // end loop } break; } case DAVQL_CMD_OP_BINARY_MUL: { //printf("mul\n"); DavQLStackObj obj2 = DAVQL_POP(); DavQLStackObj obj1 = DAVQL_POP(); if(obj1.type == 0 && obj2.type == 0) { DAVQL_PUSH_INT(obj1.data.integer * obj2.data.integer); } else { // error ret = -1; i = count; // end loop } break; } case DAVQL_CMD_OP_BINARY_DIV: { //printf("div\n"); DavQLStackObj obj2 = DAVQL_POP(); DavQLStackObj obj1 = DAVQL_POP(); if(obj1.type == 0 && obj2.type == 0) { DAVQL_PUSH_INT(obj1.data.integer / obj2.data.integer); } else { // error ret = -1; i = count; // end loop } break; } case DAVQL_CMD_OP_BINARY_AND: { //printf("and\n"); DavQLStackObj obj2 = DAVQL_POP(); DavQLStackObj obj1 = DAVQL_POP(); if(obj1.type == 0 && obj2.type == 0) { DAVQL_PUSH_INT(obj1.data.integer & obj2.data.integer); } else { // error ret = -1; i = count; // end loop } break; } case DAVQL_CMD_OP_BINARY_OR: { //printf("or\n"); DavQLStackObj obj2 = DAVQL_POP(); DavQLStackObj obj1 = DAVQL_POP(); if(obj1.type == 0 && obj2.type == 0) { DAVQL_PUSH_INT(obj1.data.integer | obj2.data.integer); } else { // error ret = -1; i = count; // end loop } break; } case DAVQL_CMD_OP_BINARY_XOR: { //printf("xor\n"); DavQLStackObj obj2 = DAVQL_POP(); DavQLStackObj obj1 = DAVQL_POP(); if(obj1.type == 0 && obj2.type == 0) { DAVQL_PUSH_INT(obj1.data.integer ^ obj2.data.integer); } else { // error ret = -1; i = count; // end loop } break; } case DAVQL_CMD_OP_LOGICAL_NOT: { //printf("not\n"); break; } case DAVQL_CMD_OP_LOGICAL_AND: { //printf("land\n"); DavQLStackObj obj2 = DAVQL_POP(); DavQLStackObj obj1 = DAVQL_POP(); int v1 = obj1.type == 0 ? (int)obj1.data.integer : (obj1.data.string ? 1 : 0); int v2 = obj2.type == 0 ? (int)obj2.data.integer : (obj2.data.string ? 1 : 0); DAVQL_PUSH_INT(v1 && v2); break; } case DAVQL_CMD_OP_LOGICAL_OR_L: { //printf("or_l %d\n", cmd.data.integer); DavQLStackObj obj1 = stack[stpos]; if((obj1.type == 0 && obj1.data.integer) || (obj1.type == 1 && obj1.data.string)) { stpos--; DAVQL_PUSH_INT(1); i += cmd.data.integer; // jump, skip right subtree of 'or' } break; } case DAVQL_CMD_OP_LOGICAL_OR: { //printf("or\n"); DavQLStackObj obj2 = DAVQL_POP(); DavQLStackObj obj1 = DAVQL_POP(); int v1 = obj1.type == 0 ? (int)obj1.data.integer : (obj1.data.string ? 1 : 0); int v2 = obj2.type == 0 ? (int)obj2.data.integer : (obj2.data.string ? 1 : 0); DAVQL_PUSH_INT(v1 || v2); break; } case DAVQL_CMD_OP_LOGICAL_XOR: { //printf("lxor\n"); DavQLStackObj obj2 = DAVQL_POP(); DavQLStackObj obj1 = DAVQL_POP(); int v1 = obj1.type == 0 ? (int)obj1.data.integer : (obj1.data.string ? 1 : 0); int v2 = obj2.type == 0 ? (int)obj2.data.integer : (obj2.data.string ? 1 : 0); DAVQL_PUSH_INT(!v1 != !v2); break; } case DAVQL_CMD_OP_EQ: { //printf("eq\n"); DavQLStackObj obj2 = DAVQL_POP(); DavQLStackObj obj1 = DAVQL_POP(); if(obj1.type == 0 && obj2.type == 0) { DAVQL_PUSH_INT(obj1.data.integer == obj2.data.integer); } else { DAVQL_PUSH_INT(cmd_str_cmp(obj1, obj2, cmd.type)); } break; } case DAVQL_CMD_OP_NEQ: { //printf("neq\n"); DavQLStackObj obj2 = DAVQL_POP(); DavQLStackObj obj1 = DAVQL_POP(); if(obj1.type == 0 && obj2.type == 0) { DAVQL_PUSH_INT(obj1.data.integer != obj2.data.integer); } else { DAVQL_PUSH_INT(cmd_str_cmp(obj1, obj2, cmd.type)); } break; } case DAVQL_CMD_OP_LT: { //printf("lt\n"); DavQLStackObj obj2 = DAVQL_POP(); DavQLStackObj obj1 = DAVQL_POP(); if(obj1.type == 0 && obj2.type == 0) { DAVQL_PUSH_INT(obj1.data.integer < obj2.data.integer); } else { DAVQL_PUSH_INT(cmd_str_cmp(obj1, obj2, cmd.type)); } break; } case DAVQL_CMD_OP_GT: { //printf("gt\n"); DavQLStackObj obj2 = DAVQL_POP(); DavQLStackObj obj1 = DAVQL_POP(); if(obj1.type == 0 && obj2.type == 0) { DAVQL_PUSH_INT(obj1.data.integer > obj2.data.integer); } else { DAVQL_PUSH_INT(cmd_str_cmp(obj1, obj2, cmd.type)); } break; } case DAVQL_CMD_OP_LE: { //printf("le\n"); DavQLStackObj obj2 = DAVQL_POP(); DavQLStackObj obj1 = DAVQL_POP(); if(obj1.type == 0 && obj2.type == 0) { DAVQL_PUSH_INT(obj1.data.integer <= obj2.data.integer); } else { DAVQL_PUSH_INT(cmd_str_cmp(obj1, obj2, cmd.type)); } break; } case DAVQL_CMD_OP_GE: { //printf("ge\n"); DavQLStackObj obj2 = DAVQL_POP(); DavQLStackObj obj1 = DAVQL_POP(); if(obj1.type == 0 && obj2.type == 0) { DAVQL_PUSH_INT(obj1.data.integer >= obj2.data.integer); } else { DAVQL_PUSH_INT(cmd_str_cmp(obj1, obj2, cmd.type)); } break; } case DAVQL_CMD_OP_LIKE: { //printf("like\n"); break; } case DAVQL_CMD_OP_UNLIKE: { //printf("unlike\n"); break; } case DAVQL_CMD_CALL: { //printf("call %x\n", cmd.data.func); break; } } } if(stpos == 1) { *result = stack[0]; } else { ret = -1; } free(stack); return ret; }