#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <cx/utils.h>
#include <cx/map.h>
#include <cx/hash_map.h>
#include <cx/printf.h>
#include <cx/mempool.h>
#include "davqlexec.h"
#include "utils.h"
#include "methods.h"
#include "session.h"
#include "resource.h"
DavQLArgList* dav_ql_get_args(DavQLStatement *st, va_list ap) {
DavQLArgList *args = malloc(
sizeof(DavQLArgList));
if(!args) {
return NULL;
}
args->first =
NULL;
if(!st->args) {
args->first =
NULL;
args->current =
NULL;
return args;
}
DavQLArg *cur =
NULL;
CxIterator i = cxListIterator(st->args);
cx_foreach(
void*, data, i) {
intptr_t type = (
intptr_t)data;
DavQLArg *arg = calloc(
1,
sizeof(DavQLArg));
if(!arg) {
dav_ql_free_arglist(args);
return NULL;
}
arg->type = type;
switch(type) {
case 'd': {
arg->value.d = va_arg(ap,
int);
break;
}
case 'u': {
arg->value.u = va_arg(ap,
unsigned int);
break;
}
case 's': {
arg->value.s = va_arg(ap,
char*);
break;
}
case 't': {
arg->value.t = va_arg(ap,
time_t);
break;
}
default: {
free(arg);
dav_ql_free_arglist(args);
return NULL;
}
}
if(cur) {
cur->next = arg;
}
else {
args->first = arg;
}
cur = arg;
}
args->current = args->first;
return args;
}
void dav_ql_free_arglist(DavQLArgList *args) {
DavQLArg *arg = args->first;
while(arg) {
DavQLArg *next = arg->next;
free(arg);
arg = next;
}
free(args);
}
static DavQLArg* arglist_get(DavQLArgList *args) {
DavQLArg *a = args->current;
if(a) {
args->current = a->next;
}
return a;
}
int dav_ql_getarg_int(DavQLArgList *args) {
DavQLArg *a = arglist_get(args);
if(a && a->type ==
'd') {
return a->value.d;
}
return 0;
}
unsigned int dav_ql_getarg_uint(DavQLArgList *args) {
DavQLArg *a = arglist_get(args);
if(a && a->type ==
'u') {
return a->value.u;
}
return 0;
}
char* dav_ql_getarg_str(DavQLArgList *args) {
DavQLArg *a = arglist_get(args);
if(a && a->type ==
's') {
return a->value.s;
}
return "";
}
time_t dav_ql_getarg_time(DavQLArgList *args) {
DavQLArg *a = arglist_get(args);
if(a && a->type ==
't') {
return a->value.t;
}
return 0;
}
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;
if(st->type ==
DAVQL_ERROR) {
return result;
}
if(st->type ==
DAVQL_SELECT) {
return dav_exec_select(sn, st, ap);
}
else {
}
return result;
}
cxmutstr dav_format_string(
const CxAllocator *a, cxstring fstr, DavQLArgList *ap,
davqlerror_t *error) {
CxBuffer buf;
cxBufferInit(&buf,
NULL,
128, a,
CX_BUFFER_AUTO_EXTEND);
int placeholder =
0;
for(
int i=
0;i<fstr.length;i++) {
char c = fstr.ptr[i];
if(placeholder) {
if(c ==
'%') {
cxBufferPut(&buf, c);
}
else {
int err =
0;
switch(c) {
case 's': {
char *arg = dav_ql_getarg_str(ap);
cxBufferPutString(&buf, arg);
break;
}
case 'd': {
int arg = dav_ql_getarg_int(ap);
cx_bprintf(&buf,
"%d", arg);
break;
}
case 'u': {
unsigned int arg = dav_ql_getarg_uint(ap);
cx_bprintf(&buf,
"%u", arg);
break;
}
case 't': {
err =
1;
break;
}
default: {
*error =
DAVQL_UNKNOWN_FORMATCHAR;
err =
1;
}
}
if(err) {
cxBufferDestroy(&buf);
return (cxmutstr){
NULL,
0};
}
}
placeholder =
0;
}
else {
if(c ==
'%') {
placeholder =
1;
}
else {
cxBufferPut(&buf, c);
}
}
}
if(cxBufferPut(&buf,
'\0')) {
*error =
DAVQL_OOM;
cxBufferDestroy(&buf);
return (cxmutstr){
NULL,
0};
}
*error =
DAVQL_OK;
return cx_mutstrn(buf.space, buf.size-
1);
}
static int fl_add_properties(DavSession *sn,
const CxAllocator *a, CxMap *map, DavQLExpression *expression) {
if(!expression) {
return 0;
}
if(expression->type ==
DAVQL_IDENTIFIER) {
DavProperty *property = cxMalloc(a,
sizeof(DavProperty));
char *name;
DavNamespace *ns = dav_get_property_namespace(
sn->context,
cx_strdup_a(a, expression->srctext).ptr,
&name);
if(!ns) {
return -
1;
}
property->ns = ns;
property->name = name;
property->value =
NULL;
cxMapPut(map, cx_hash_key(expression->srctext.ptr, expression->srctext.length), property);
}
if(expression->left) {
if(fl_add_properties(sn, a, map, expression->left)) {
return -
1;
}
}
if(expression->right) {
if(fl_add_properties(sn, a, map, expression->right)) {
return -
1;
}
}
return 0;
}
static CxBuffer* fieldlist2propfindrequest(DavSession *sn,
const CxAllocator *a, CxList *fields,
int *isallprop) {
CxMap *properties = cxHashMapCreate(cxDefaultAllocator,
CX_STORE_POINTERS,
32);
*isallprop =
0;
CxIterator i = cxListIterator(fields);
cx_foreach(DavQLField*, field, i) {
if(!cx_strcmp(field->name,
CX_STR(
"*"))) {
cxMapDestroy(properties);
*isallprop =
1;
return create_allprop_propfind_request();
}
else if(!cx_strcmp(field->name,
CX_STR(
"-"))) {
cxMapDestroy(properties);
return create_propfind_request(sn,
NULL,
"propfind",
0);
}
else {
if(fl_add_properties(sn, a, properties, field->expr)) {
cxMapDestroy(properties);
return NULL;
}
}
}
i = cxMapIteratorValues(properties);
CxList *list = cxLinkedListCreateSimple(
CX_STORE_POINTERS);
cx_foreach(DavProperty*, value, i) {
cxListAdd(list, value);
}
CxBuffer *reqbuf = create_propfind_request(sn, list,
"propfind",
0);
cxListDestroy(list);
cxMapDestroy(properties);
return reqbuf;
}
static int reset_properties(DavSession *sn, DavResult *result, DavResource *res, CxList *fields) {
CxMap *new_properties = cxHashMapCreate(sn->mp->allocator,
CX_STORE_POINTERS,
32);
DavResourceData *data = (DavResourceData*)res->data;
void *value;
cxmutstr cl_keystr = dav_property_key(
"DAV:",
"getcontentlength");
CxHashKey cl_key = cx_hash_key(cl_keystr.ptr, cl_keystr.length);
value = cxMapGet(data->properties, cl_key);
if(value) {
cxMapPut(new_properties, cl_key, value);
}
cxmutstr cd_keystr = dav_property_key(
"DAV:",
"creationdate");
CxHashKey cd_key = cx_hash_key(cd_keystr.ptr, cd_keystr.length);
value = cxMapGet(data->properties, cd_key);
if(value) {
cxMapPut(new_properties, cd_key, value);
}
cxmutstr lm_keystr = dav_property_key(
"DAV:",
"getlastmodified");
CxHashKey lm_key = cx_hash_key(lm_keystr.ptr, lm_keystr.length);
value = cxMapGet(data->properties, lm_key);
if(value) {
cxMapPut(new_properties, lm_key, value);
}
cxmutstr ct_keystr = dav_property_key(
"DAV:",
"getcontenttype");
CxHashKey ct_key = cx_hash_key(ct_keystr.ptr, ct_keystr.length);
value = cxMapGet(data->properties, ct_key);
if(value) {
cxMapPut(new_properties, ct_key, value);
}
cxmutstr rt_keystr = dav_property_key(
"DAV:",
"resourcetype");
CxHashKey rt_key = cx_hash_key(rt_keystr.ptr, rt_keystr.length);
value = cxMapGet(data->properties, rt_key);
if(value) {
cxMapPut(new_properties, rt_key, value);
}
cxmutstr cn_keystr = dav_property_key(
DAV_NS,
"crypto-name");
CxHashKey cn_key = cx_hash_key(cn_keystr.ptr, cn_keystr.length);
value = cxMapGet(data->properties, cn_key);
if(value) {
cxMapPut(new_properties, cn_key, value);
}
cxmutstr ck_keystr = dav_property_key(
DAV_NS,
"crypto-key");
CxHashKey ck_key = cx_hash_key(ck_keystr.ptr, ck_keystr.length);
value = cxMapGet(data->properties, ck_key);
if(value) {
cxMapPut(new_properties, ck_key, value);
}
cxmutstr ch_keystr = dav_property_key(
DAV_NS,
"crypto-hash");
CxHashKey ch_key = cx_hash_key(ch_keystr.ptr, ch_keystr.length);
value = cxMapGet(data->properties, ch_key);
if(value) {
cxMapPut(new_properties, ch_key, value);
}
if(fields) {
CxIterator i = cxListIterator(fields);
cx_foreach(DavCompiledField*, field, i) {
DavQLStackObj field_result;
if(!dav_exec_expr(field->code, res, &field_result)) {
cxmutstr str;
str.ptr =
NULL;
str.length =
0;
DavXmlNode *node =
NULL;
if(field_result.type ==
0) {
str = cx_asprintf_a(
sn->mp->allocator,
"%" PRId64,
field_result.data.integer);
}
else if(field_result.type ==
1) {
if(field_result.data.string) {
str = cx_strdup_a(sn->mp->allocator, cx_strn(
field_result.data.string,
field_result.length));
}
}
else if(field_result.type ==
2) {
node = dav_copy_node(field_result.data.node);
}
else {
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) {
cxmutstr key = dav_property_key(field->ns, field->name);
DavNamespace *namespace = dav_session_malloc(sn,
sizeof(DavNamespace));
namespace->prefix =
NULL;
namespace->name = dav_session_strdup(sn, field->ns);
DavProperty *prop = dav_session_malloc(sn,
sizeof(DavProperty));
prop->name = dav_session_strdup(sn, field->name);
prop->ns = namespace;
prop->value = node;
cxMapPut(new_properties, cx_hash_key(key.ptr, key.length), prop);
free(key.ptr);
}
}
else {
resource_free_properties(sn, new_properties);
return -
1;
}
}
}
cxMapRemove(data->properties, cl_key);
cxMapRemove(data->properties, cd_key);
cxMapRemove(data->properties, lm_key);
cxMapRemove(data->properties, ct_key);
cxMapRemove(data->properties, rt_key);
cxMapRemove(data->properties, cn_key);
cxMapRemove(data->properties, ck_key);
cxMapRemove(data->properties, ch_key);
resource_free_properties(sn, data->properties);
data->properties = new_properties;
free(cl_keystr.ptr);
free(cd_keystr.ptr);
free(lm_keystr.ptr);
free(ct_keystr.ptr);
free(rt_keystr.ptr);
free(cn_keystr.ptr);
free(ck_keystr.ptr);
free(ch_keystr.ptr);
return 0;
}
DavResult dav_exec_select(DavSession *sn, DavQLStatement *st, va_list ap) {
CxMempool *mp = cxBasicMempoolCreate(
128);
DavResult result;
result.result =
NULL;
result.status =
1;
DavQLArgList *args = dav_ql_get_args(st, ap);
if(!args) {
return result;
}
cxMempoolRegister(mp, args, (cx_destructor_func)dav_ql_free_arglist);
int isallprop;
CxBuffer *rqbuf = fieldlist2propfindrequest(sn, mp->allocator, st->fields, &isallprop);
if(!rqbuf) {
cxMempoolDestroy(mp);
return result;
}
cxMempoolRegister(mp, rqbuf, (cx_destructor_func)cxBufferFree);
CxList *cfieldlist = cxLinkedListCreate(mp->allocator,
NULL,
CX_STORE_POINTERS);
if(st->fields) {
CxIterator i = cxListIterator(st->fields);
cx_foreach(DavQLField*, field, i) {
if(cx_strcmp(field->name,
CX_STR(
"*")) && cx_strcmp(field->name,
CX_STR(
"-"))) {
CxBuffer *code = dav_compile_expr(
sn->context,
mp->allocator,
field->expr,
args);
if(!code) {
return result;
}
DavCompiledField *cfield = cxMalloc(
mp->allocator,
sizeof(DavCompiledField));
char *ns;
char *name;
dav_get_property_namespace_str(
sn->context,
cx_strdup_a(mp->allocator, field->name).ptr,
&ns,
&name);
if(!ns || !name) {
return result;
}
cfield->ns = ns;
cfield->name = name;
cfield->code = code;
cxListAdd(cfieldlist, cfield);
}
}
}
davqlerror_t error;
cxmutstr path = dav_format_string(mp->allocator, st->path, args, &error);
if(error) {
cxMempoolDestroy(mp);
return result;
}
int depth = st->depth ==
DAV_DEPTH_PLACEHOLDER ?
dav_ql_getarg_int(args) : st->depth;
CxBuffer *where = dav_compile_expr(sn->context, mp->allocator, st->where, args);
if(st->where && !where) {
cxMempoolDestroy(mp);
return result;
}
CxList *ordercr =
NULL;
if(st->orderby) {
ordercr = cxLinkedListCreate(mp->allocator,
NULL,
sizeof(DavOrderCriterion));
CxIterator i = cxListIterator(st->orderby);
cx_foreach(DavQLOrderCriterion*, oc, i) {
DavQLExpression *column = oc->column;
if(column->type ==
DAVQL_IDENTIFIER) {
davqlresprop_t resprop;
cxstring propertyname = cx_strchr(column->srctext,
':');
if(propertyname.length >
0) {
char *ns;
char *name;
dav_get_property_namespace_str(
sn->context,
cx_strdup_a(mp->allocator, column->srctext).ptr,
&ns,
&name);
if(ns && name) {
DavOrderCriterion cr;
cr.type =
1;
cxmutstr keystr = dav_property_key_a(mp->allocator, ns, name);
cr.column.property = cx_hash_key(keystr.ptr, keystr.length);
cr.descending = oc->descending;
cxListAdd(ordercr, &cr);
}
else {
cxMempoolDestroy(mp);
return result;
}
}
else if(dav_identifier2resprop(column->srctext, &resprop)) {
DavOrderCriterion cr;
cr.type =
0;
cr.column.resprop = resprop;
cr.descending = oc->descending;
cxListAdd(ordercr, &cr);
}
else {
cxMempoolDestroy(mp);
return result;
}
}
else if(column->type ==
DAVQL_NUMBER) {
fprintf(stderr,
"order by number not supported\n");
return result;
}
else {
cxMempoolDestroy(mp);
return result;
}
}
}
DavResource *selroot = dav_resource_new(sn, path.ptr);
CxList *stack = cxLinkedListCreateSimple(
sizeof(DavQLRes));
DavQLRes res;
res.resource = selroot;
res.depth =
0;
cxListInsert(stack,
0, &res);
CxBuffer *rpbuf = cxBufferCreate(
NULL,
4096, mp->allocator,
CX_BUFFER_FREE_CONTENTS|
CX_BUFFER_AUTO_EXTEND);
if(!rpbuf) {
cxMempoolDestroy(mp);
return result;
}
result.result = selroot;
result.status =
0;
while(cxListSize(stack) >
0) {
DavQLRes *sr_ptr = cxListAt(stack,
0);
DavResource *root = sr_ptr->resource;
int res_depth = sr_ptr->depth;
cxListRemove(stack,
0);
util_set_url(sn, dav_resource_get_href(root));
CURLcode ret = do_propfind_request(sn, rqbuf, rpbuf);
long http_status =
0;
curl_easy_getinfo(sn->handle,
CURLINFO_RESPONSE_CODE, &http_status);
if(ret ==
CURLE_OK && http_status ==
207) {
dav_set_effective_href(sn, root);
char *url =
"http://url/";
PropfindParser *parser = create_propfind_parser(rpbuf, url);
if(!parser) {
result.status = -
1;
break;
}
ResponseTag response;
int r;
while((r = get_propfind_response(parser, &response)) !=
0) {
if(r == -
1) {
result.status = -
1;
cleanup_response(&response);
break;
}
if(hrefeq(sn, root->href, response.href)) {
add_properties(root, &response);
cleanup_response(&response);
if(root == selroot) {
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);
cxListDestroy(stack);
break;
}
}
else {
DavResource *child = response2resource(
sn,
&response,
root->path);
cleanup_response(&response);
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_ordered_child(root, child, ordercr);
if(child->iscollection &&
(depth <
0 || depth > res_depth+
1))
{
DavQLRes rs;
rs.resource = child;
rs.depth = res_depth +
1;
cxListInsert(stack,
0, &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;
}
cxBufferSeek(rpbuf,
SEEK_SET,
0);
}
cxMempoolDestroy(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(cxstring src,
davqlresprop_t *prop) {
if(!cx_strcmp(src,
CX_STR(
"name"))) {
*prop =
DAVQL_RES_NAME;
}
else if(!cx_strcmp(src,
CX_STR(
"path"))) {
*prop =
DAVQL_RES_PATH;
}
else if(!cx_strcmp(src,
CX_STR(
"href"))) {
*prop =
DAVQL_RES_HREF;
}
else if(!cx_strcmp(src,
CX_STR(
"contentlength"))) {
*prop =
DAVQL_RES_CONTENTLENGTH;
}
else if(!cx_strcmp(src,
CX_STR(
"contenttype"))) {
*prop =
DAVQL_RES_CONTENTTYPE;
}
else if(!cx_strcmp(src,
CX_STR(
"creationdate"))) {
*prop =
DAVQL_RES_CREATIONDATE;
}
else if(!cx_strcmp(src,
CX_STR(
"lastmodified"))) {
*prop =
DAVQL_RES_LASTMODIFIED;
}
else if(!cx_strcmp(src,
CX_STR(
"iscollection"))) {
*prop =
DAVQL_RES_ISCOLLECTION;
}
else {
return 0;
}
return 1;
}
static int add_cmd(DavContext *ctx,
const CxAllocator *a, CxBuffer *bcode, DavQLExpression *expr, DavQLArgList *ap) {
if(!expr) {
return 0;
}
int numcmd =
1;
DavQLCmd cmd;
memset(&cmd,
0,
sizeof(DavQLCmd));
davqlerror_t error;
cxstring src = expr->srctext;
switch(expr->type) {
default:
break;
case DAVQL_NUMBER: {
cmd.type =
DAVQL_CMD_INT;
if(src.ptr[
0] ==
'%') {
cmd.data.integer = dav_ql_getarg_int(ap);
}
else if(util_strtoint(src.ptr, &cmd.data.integer)) {
cxBufferWrite(&cmd,
sizeof(cmd),
1, bcode);
}
else {
return -
1;
}
break;
}
case DAVQL_STRING: {
cmd.type =
DAVQL_CMD_STRING;
cmd.data.string = dav_format_string(a, src, ap, &error);
cxBufferWrite(&cmd,
sizeof(cmd),
1, bcode);
break;
}
case DAVQL_TIMESTAMP: {
if(src.ptr[
0] ==
'%') {
cmd.type =
DAVQL_CMD_TIMESTAMP;
cmd.data.timestamp = dav_ql_getarg_time(ap);
cxBufferWrite(&cmd,
sizeof(cmd),
1, bcode);
}
else {
return -
1;
}
break;
}
case DAVQL_IDENTIFIER: {
cxstring propertyname = cx_strchr(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,
cx_strdup_a(a, src).ptr,
&ns,
&name);
if(ns && name) {
cmd.data.property.ns = ns;
cmd.data.property.name = name;
}
else {
return -
1;
}
}
else if(!dav_identifier2resprop(src, &cmd.data.resprop)) {
if(!cx_strcmp(src,
CX_STR(
"true"))) {
cmd.type =
DAVQL_CMD_INT;
cmd.data.integer =
1;
}
else if(!cx_strcmp(src,
CX_STR(
"false"))) {
cmd.type =
DAVQL_CMD_INT;
cmd.data.integer =
0;
}
else {
return -
1;
}
}
cxBufferWrite(&cmd,
sizeof(cmd),
1, bcode);
break;
}
case DAVQL_UNARY: {
numcmd += add_cmd(ctx, a, bcode, expr->left, ap);
switch(expr->op) {
case DAVQL_ADD: {
numcmd =
0;
break;
}
case DAVQL_SUB: {
cmd.type =
DAVQL_CMD_OP_UNARY_SUB;
cxBufferWrite(&cmd,
sizeof(cmd),
1, bcode);
break;
}
case DAVQL_NEG: {
cmd.type =
DAVQL_CMD_OP_UNARY_NEG;
cxBufferWrite(&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;
}
cxBufferWrite(&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;
cxBufferWrite(&cmd,
sizeof(cmd),
1, bcode);
break;
}
case DAVQL_LAND: {
cmd.type =
DAVQL_CMD_OP_LOGICAL_AND;
cxBufferWrite(&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);
cxBufferWrite(&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;
cxBufferWrite(&cmd,
sizeof(cmd),
1, bcode);
numcmd += nleft + nright;
break;
}
case DAVQL_LXOR: {
cmd.type =
DAVQL_CMD_OP_LOGICAL_XOR;
cxBufferWrite(&cmd,
sizeof(cmd),
1, bcode);
break;
}
case DAVQL_EQ: {
cmd.type =
DAVQL_CMD_OP_EQ;
cxBufferWrite(&cmd,
sizeof(cmd),
1, bcode);
break;
}
case DAVQL_NEQ: {
cmd.type =
DAVQL_CMD_OP_NEQ;
cxBufferWrite(&cmd,
sizeof(cmd),
1, bcode);
break;
}
case DAVQL_LT: {
cmd.type =
DAVQL_CMD_OP_LT;
cxBufferWrite(&cmd,
sizeof(cmd),
1, bcode);
break;
}
case DAVQL_GT: {
cmd.type =
DAVQL_CMD_OP_GT;
cxBufferWrite(&cmd,
sizeof(cmd),
1, bcode);
break;
}
case DAVQL_LE: {
cmd.type =
DAVQL_CMD_OP_LE;
cxBufferWrite(&cmd,
sizeof(cmd),
1, bcode);
break;
}
case DAVQL_GE: {
cmd.type =
DAVQL_CMD_OP_GE;
cxBufferWrite(&cmd,
sizeof(cmd),
1, bcode);
break;
}
case DAVQL_LIKE: {
cmd.type =
DAVQL_CMD_OP_LIKE;
cxBufferWrite(&cmd,
sizeof(cmd),
1, bcode);
break;
}
case DAVQL_UNLIKE: {
cmd.type =
DAVQL_CMD_OP_UNLIKE;
cxBufferWrite(&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);
DavQLExpression *funcid = expr->left;
if(!funcid && funcid->type !=
DAVQL_IDENTIFIER) {
return -
1;
}
cmd.type =
DAVQL_CMD_INT;
cmd.data.integer = count_func_args(expr);
cxBufferWrite(&cmd,
sizeof(cmd),
1, bcode);
cmd.type =
DAVQL_CMD_CALL;
cmd.data.func =
NULL;
cxBufferWrite(&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;
}
CxBuffer* dav_compile_expr(DavContext *ctx,
const CxAllocator *a, DavQLExpression *lexpr, DavQLArgList *ap) {
CxBuffer *bcode = cxBufferCreate(
NULL,
512, a,
CX_BUFFER_FREE_CONTENTS|
CX_BUFFER_AUTO_EXTEND);
if(!bcode) {
return NULL;
}
if(add_cmd(ctx, a, bcode, lexpr, ap) <=
0) {
cxBufferFree(bcode);
return NULL;
}
return bcode;
}
static int cmd_str_cmp(DavQLStackObj obj1, DavQLStackObj obj2,
davqlcmdtype_t cmd) {
cxmutstr s1m = obj1.type ==
1 ?
cx_mutstrn(obj1.data.string, obj1.length) :
cx_asprintf(
"%" PRId64, obj1.data.integer);
cxmutstr s2m = obj1.type ==
1 ?
cx_mutstrn(obj2.data.string, obj2.length) :
cx_asprintf(
"%" PRId64, obj2.data.integer);
cxstring s1 = cx_strcast(s1m);
cxstring s2 = cx_strcast(s2m);
int res =
0;
switch(cmd) {
case DAVQL_CMD_OP_EQ: {
res = cx_strcmp(s1, s2) ==
0;
break;
}
case DAVQL_CMD_OP_NEQ: {
res = cx_strcmp(s1, s2) !=
0;
break;
}
case DAVQL_CMD_OP_LT: {
res = cx_strcmp(s1, s2) <
0;
break;
}
case DAVQL_CMD_OP_GT: {
res = cx_strcmp(s1, s2) >
0;
break;
}
case DAVQL_CMD_OP_LE: {
res = cx_strcmp(s1, s2) <=
0;
break;
}
case DAVQL_CMD_OP_GE: {
res = cx_strcmp(s1, s2) >=
0;
break;
}
default:
break;
}
if(obj1.type ==
0) {
free(s1m.ptr);
}
if(obj2.type ==
0) {
free(s2m.ptr);
}
return res;
}
int dav_exec_expr(CxBuffer *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;
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: {
obj.type =
0;
obj.length =
0;
obj.data.integer = cmd.data.integer;
DAVQL_PUSH(obj);
break;
}
case DAVQL_CMD_STRING: {
obj.type =
1;
obj.length = cmd.data.string.length;
obj.data.string = cmd.data.string.ptr;
DAVQL_PUSH(obj);
break;
}
case DAVQL_CMD_TIMESTAMP: {
obj.type =
0;
obj.length =
0;
obj.data.integer = (
int64_t)cmd.data.timestamp;
DAVQL_PUSH(obj);
break;
}
case DAVQL_CMD_RES_IDENTIFIER: {
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: {
DavXmlNode *value = dav_get_property_ns(res, cmd.data.property.ns, cmd.data.property.name);
if(dav_xml_isstring(value)) {
obj.type =
1;
obj.length = (
uint32_t)value->contentlength;
obj.data.string = value->content;
}
else {
obj.type =
2;
obj.length =
0;
obj.data.node = value;
}
DAVQL_PUSH(obj);
break;
}
case DAVQL_CMD_OP_UNARY_SUB: {
obj =
DAVQL_POP();
if(obj.type ==
0) {
obj.data.integer = -obj.data.integer;
DAVQL_PUSH(obj);
}
else {
ret = -
1;
i = count;
}
break;
}
case DAVQL_CMD_OP_UNARY_NEG: {
obj =
DAVQL_POP();
if(obj.type ==
0) {
obj.data.integer = obj.data.integer ==
0 ?
1 :
0;
DAVQL_PUSH(obj);
}
else {
ret = -
1;
i = count;
}
break;
}
case DAVQL_CMD_OP_BINARY_ADD: {
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 {
}
break;
}
case DAVQL_CMD_OP_BINARY_SUB: {
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 {
ret = -
1;
i = count;
}
break;
}
case DAVQL_CMD_OP_BINARY_MUL: {
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 {
ret = -
1;
i = count;
}
break;
}
case DAVQL_CMD_OP_BINARY_DIV: {
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 {
ret = -
1;
i = count;
}
break;
}
case DAVQL_CMD_OP_BINARY_AND: {
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 {
ret = -
1;
i = count;
}
break;
}
case DAVQL_CMD_OP_BINARY_OR: {
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 {
ret = -
1;
i = count;
}
break;
}
case DAVQL_CMD_OP_BINARY_XOR: {
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 {
ret = -
1;
i = count;
}
break;
}
case DAVQL_CMD_OP_LOGICAL_NOT: {
break;
}
case DAVQL_CMD_OP_LOGICAL_AND: {
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: {
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;
}
break;
}
case DAVQL_CMD_OP_LOGICAL_OR: {
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: {
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: {
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: {
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: {
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: {
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: {
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: {
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: {
break;
}
case DAVQL_CMD_OP_UNLIKE: {
break;
}
case DAVQL_CMD_CALL: {
break;
}
}
}
if(stpos ==
1) {
*result = stack[
0];
}
else {
ret = -
1;
}
free(stack);
return ret;
}