blob: bd64f02671a933704ba4e6dca595990d253f5970 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012, 2013 Wind River Systems, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
* You may elect to redistribute this code under either of these licenses.
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
#include <tcf/config.h>
#if SERVICE_ContextQuery
#include <tcf/framework/json.h>
#include <tcf/framework/myalloc.h>
#include <tcf/framework/exceptions.h>
#include <tcf/services/contextquery.h>
typedef struct Comparator {
char * attr_name;
ContextQueryComparator * callback;
struct Comparator * next;
} Comparator;
/* TODO: need hash table for faster search of query comparators */
static Comparator * comparators = NULL;
void add_context_query_comparator(const char * attr_name, ContextQueryComparator * callback) {
Comparator * c = (Comparator *)loc_alloc_zero(sizeof(Comparator));
c->attr_name = loc_strdup(attr_name);
c->callback = callback;
c->next = comparators;
comparators = c;
}
static const char * CONTEXT_QUERY = "ContextQuery";
typedef struct Attribute {
struct Attribute * next;
struct Attribute * parent;
char * name;
char * value;
} Attribute;
static Attribute * attrs = NULL;
static char * str_buf = NULL;
static size_t str_pos = 0;
static size_t str_max = 0;
static int abs_path = 0;
static const char * query_attr_name = NULL;
static void add_char(char ch) {
if (str_pos >= str_max) {
str_max *= 2;
str_buf = (char *)tmp_realloc(str_buf, str_max);
}
str_buf[str_pos++] = ch;
}
static int parse_wildcard(const char ** q) {
const char * c = *q;
str_pos = 0;
add_char(*c++);
if (*c == '*') add_char(*c++);
if (*c != '\0' && *c != '/') {
set_errno(ERR_OTHER, "Invalid context query syntax: * and ** are"
" the only valid wildcards");
return -1;
}
*q = c;
add_char('\0');
return 0;
}
static int parse_number(const char ** q) {
const char * c = *q;
str_pos = 0;
while (*c >= '0' && *c <= '9') add_char(*c++);
if (*c != ',' && *c != '/' && *c != '\0' ) {
set_errno(ERR_OTHER, "Invalid context query syntax: expecting [0-9] "
"or ',' or '/' after number");
return -1;
}
*q = c;
add_char('\0');
return 0;
}
static int parse_symbol(const char **q) {
const char * c = *q;
str_pos = 0;
while (*c) {
if ((*c != '_') &&
(((*c < '0') || (*c > '9')) &&
((*c < 'a') || (*c > 'z')) &&
((*c < 'A') || (*c > 'Z')))) {
break;
}
add_char(*c++);
}
if (*c != '/' && *c != '=' && *c != ',' && *c != '\0') {
set_errno(ERR_OTHER, "Invalid context query syntax:"
" unquoted strings must only contain"
" alphanumerical characters or '_'");
return -1;
}
*q = c;
add_char('\0');
return 0;
}
static int parse_quoted_string(const char **q) {
const char * c = *q;
str_pos = 0;
c++;
while (*c != '"') {
if (*c == '\\') {
c++;
if (*c != '\\' && *c != '"') {
set_errno(ERR_OTHER, "Invalid context query syntax: \" and \\"
" are the only characters that can be escaped");
return -1;
}
}
else if (*c == '\0') {
set_errno(ERR_OTHER, "Invalid context query syntax: missing closing"
" quote character");
return -1;
}
add_char(*c++);
}
c++;
*q = c;
add_char('\0');
return 0;
}
static int parse_string(const char **q) {
const char * c = *q;
if (*c == '"') {
if (parse_quoted_string(&c) < 0) return -1;
}
else {
if (parse_symbol(&c) < 0) return -1;
}
*q = c;
return 0;
}
static int parse_value(const char **q) {
const char * c = *q;
if ((*c >= '0') && (*c <= '9')) {
if (parse_number(&c) < 0) return -1;
}
else if (*c == '"') {
if (parse_quoted_string(&c) < 0) return -1;
}
else {
if (parse_symbol(&c) < 0) return -1;
}
*q = c;
return 0;
}
static Attribute * parse_property(const char **q) {
const char * c = *q;
Attribute * attr = (Attribute *)tmp_alloc_zero(sizeof(Attribute));
if (parse_string(&c) < 0) return NULL;
attr->name = tmp_strdup(str_buf);
if (*c == '=') {
c++;
if (*c == '/' || *c == '=' || *c == ',' || *c == '\0') {
set_errno(ERR_OTHER, "Invalid context query syntax: missing value");
return NULL;
}
if (parse_value(&c) < 0) return NULL;
attr->value = tmp_strdup(str_buf);
if (*c == '=') {
set_errno(ERR_OTHER, "Invalid context query syntax: can't assign "
"several values to a property at the same "
"time");
return NULL;
}
}
if (attr->value == NULL) {
attr->value = attr->name;
attr->name = NULL;
}
*q = c;
return attr;
}
int parse_context_query(const char * q) {
Attribute * attr = NULL;
str_pos = 0;
str_buf = NULL;
attrs = NULL;
abs_path = 0;
if (q == NULL || *q == '\0') return 0;
str_max = 64;
str_buf = (char *)tmp_alloc(str_max);
if ((abs_path = *q == '/') != 0) q++;
if (*q == '/' || *q == '=' || *q == ',' || *q == '\0') {
set_errno(ERR_OTHER, "Invalid context query syntax: missing context "
"name, property or wildcard");
return -1;
}
while (*q) {
Attribute * a;
str_pos = 0;
if (*q == '*') {
if (parse_wildcard(&q) < 0) return -1;
a = (Attribute *) tmp_alloc_zero(sizeof(Attribute));
a->value = tmp_strdup(str_buf);
}
else {
a = parse_property(&q);
if (a == NULL) return -1;
}
a->next = attr;
attr = a;
if (*q == '/') { /* start parsing a new part */
attr->parent = attrs;
attrs = attr;
attr = NULL;
q++;
if (*q == '/' || *q == '=' || *q == ',' || *q == '\0') {
set_errno(ERR_OTHER, "Invalid context query syntax: missing "
"context name, property or wildcard");
return -1;
}
}
else if (*q == ',') { /* start parsing a new property */
q++;
if (*q == '/' || *q == '=' || *q == ',' || *q == '\0') {
set_errno(ERR_OTHER, "Invalid context query syntax: "
"missing property");
return -1;
}
}
}
attr->parent = attrs;
attrs = attr;
return 0;
}
static int match_attribute(Context * ctx, const char * key, const char * val) {
int res = 0;
Comparator * c = comparators;
query_attr_name = key;
while (c != NULL) {
if (strcasecmp(c->attr_name, key) == 0) {
res = c->callback(ctx, val);
break;
}
c = c->next;
}
if (c == NULL) {
/* Comparator not found, check default comparators */
c = comparators;
while (c != NULL) {
if (strcmp(c->attr_name, DEFAULT_CONTEXT_QUERY_COMPARATOR) == 0) {
res = c->callback(ctx, val);
if (res) break;
}
c = c->next;
}
}
query_attr_name = NULL;
return res;
}
static int match(Context * ctx, Attribute * attr, GetContextParent * get_parent) {
Context * parent = get_parent(ctx);
if (attr->name == NULL && strcmp(attr->value, "**") == 0) {
if (attr->parent == NULL) return 1;
if (match(ctx, attr->parent, get_parent)) return 1;
while (parent != NULL) {
ctx = parent;
parent = get_parent(ctx);
if (match(ctx, attr->parent, get_parent)) return 1;
}
return 0;
}
if (attr->parent != NULL && (parent == NULL || !match(parent, attr->parent, get_parent))) return 0;
if (attr->parent == NULL && abs_path && parent != NULL) return 0;
while (attr != NULL) {
if (attr->name != NULL) {
if (!match_attribute(ctx, attr->name, attr->value)) return 0;
}
else if (strcmp(attr->value, "*") != 0) {
if (!match_attribute(ctx, "Name", attr->value)) return 0;
}
attr = attr->next;
}
return 1;
}
static Context * get_context_parent(Context * ctx) {
return ctx->parent;
}
int run_context_query(Context * ctx) {
return run_context_query_ext(ctx, get_context_parent);
}
int run_context_query_ext(Context * ctx, GetContextParent * get_parent) {
if (attrs == NULL) return !abs_path;
return match(ctx, attrs, get_parent);
}
const char * get_context_query_attr_name(void) {
return query_attr_name;
}
int context_query(Context * ctx, const char * query) {
parse_context_query(query);
return run_context_query(ctx);
}
static void command_query(char * token, Channel * c) {
int err = 0;
char * query = json_read_alloc_string(&c->inp);
json_test_char(&c->inp, MARKER_EOA);
json_test_char(&c->inp, MARKER_EOM);
if (parse_context_query(query) < 0) err = errno;
write_stringz(&c->out, "R");
write_stringz(&c->out, token);
write_errno(&c->out, err);
write_stream(&c->out, '[');
if (!err) {
LINK * l;
unsigned cnt = 0;
for (l = context_root.next; l != &context_root; l = l->next) {
Context * ctx = ctxl2ctxp(l);
if (ctx->exited) continue;
if (run_context_query(ctx)) {
if (cnt > 0) write_stream(&c->out, ',');
json_write_string(&c->out, ctx->id);
cnt++;
}
}
}
write_stream(&c->out, ']');
write_stream(&c->out, 0);
write_stream(&c->out, MARKER_EOM);
loc_free(query);
}
static void command_get_attr_names(char * token, Channel * c) {
unsigned cnt = 0;
Comparator * l;
json_test_char(&c->inp, MARKER_EOM);
write_stringz(&c->out, "R");
write_stringz(&c->out, token);
write_errno(&c->out, 0);
write_stream(&c->out, '[');
l = comparators;
while (l != NULL) {
if (cnt > 0) write_stream(&c->out, ',');
json_write_string(&c->out, l->attr_name);
l = l->next;
cnt++;
}
write_stream(&c->out, ']');
write_stream(&c->out, 0);
write_stream(&c->out, MARKER_EOM);
}
static int cmp_id(Context * ctx, const char * v) {
return strcmp(ctx->id, v) == 0;
}
static int cmp_name(Context * ctx, const char * v) {
if (ctx->name != NULL) return strcmp(ctx->name, v) == 0;
return strcmp(ctx->id, v) == 0;
}
void ini_context_query_service(Protocol * proto) {
add_context_query_comparator("ID", cmp_id);
add_context_query_comparator("Name", cmp_name);
add_command_handler(proto, CONTEXT_QUERY, "query", command_query);
add_command_handler(proto, CONTEXT_QUERY, "getAttrNames", command_get_attr_names);
}
#else
#include <tcf/services/contextquery.h>
void add_context_query_comparator(const char * attr_name, ContextQueryComparator * callback) {}
int parse_context_query(const char * query) { return 0; }
int run_context_query(Context * ctx) { return 0; }
int run_context_query_ext(Context * ctx, GetContextParent * get_parent) { return 0; }
int context_query(Context * ctx, const char * query) { return query == NULL || *query == 0; }
#endif