/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
/*
* oafd: OAF CORBA dameon.
*
* Copyright (C) 1999, 2000 Red Hat, Inc.
* Copyright (C) 1999, 2000 Eazel, Inc.
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Authors: Elliot Lee <sopwith@redhat.com>,
*
*/
/*
Likely bugs: Forgetting to initialize QueryExprConst.needs_free = crash
*/
#include <stdio.h>
#include <string.h>
#include "activation-context-query.h"
#include "activation-context-query-parser.h"
static QueryExpr *
qexp_new (void)
{
QueryExpr *retval = g_new (QueryExpr, 1);
retval->have_cached_value = FALSE;
retval->has_fields = TRUE;
return retval;
}
void
qexp_free (QueryExpr * qexp)
{
if (!qexp)
return;
switch (qexp->type) {
case EXPR_FUNCTION:
g_free (qexp->u.function_value.func_name);
g_slist_foreach (qexp->u.function_value.arguments,
(GFunc) qexp_free, NULL);
g_slist_free (qexp->u.function_value.arguments);
break;
case EXPR_VARIABLE:
g_free (qexp->u.var_value);
break;
case EXPR_ID:
g_free (qexp->u.id_value);
break;
case EXPR_BINOP:
qexp_free (qexp->u.binop_value.op1);
qexp_free (qexp->u.binop_value.op2);
break;
case EXPR_UNOP:
qexp_free (qexp->u.unop_value.op);
break;
case EXPR_CONSTANT:
if (qexp->u.constant_value.value_known) {
switch (qexp->u.constant_value.type) {
case CONST_STRING:
g_free (qexp->u.constant_value.u.v_string);
break;
case CONST_STRINGV:
g_strfreev (qexp->u.constant_value.
u.v_stringv);
break;
default:
break;
}
}
break;
}
g_free (qexp);
}
QueryExpr *
qexp_binop_new (QueryExpr * op1, int operand, QueryExpr * op2)
{
QueryExpr *retval = qexp_new ();
int optype;
switch (operand) {
case P_MULTIPLY:
optype = OP_MULTIPLY;
break;
case P_DIVIDE:
optype = OP_DIVIDE;
break;
case P_SUBTRACT:
optype = OP_SUBTRACT;
break;
case P_ADD:
optype = OP_ADD;
break;
case P_EQ:
optype = OP_EQ;
break;
case P_NEQ:
optype = OP_NEQ;
break;
case P_LEQ:
optype = OP_LEQ;
break;
case P_GEQ:
optype = OP_GEQ;
break;
case P_GT:
optype = OP_GT;
break;
case P_LT:
optype = OP_LT;
break;
case P_OR:
optype = OP_OR;
break;
case P_AND:
optype = OP_AND;
break;
case P_XOR:
optype = OP_XOR;
break;
default:
g_assert_not_reached ();
optype = 0;
break;
}
retval->u.binop_value.type = optype;
retval->u.binop_value.op1 = op1;
retval->u.binop_value.op2 = op2;
retval->type = EXPR_BINOP;
retval->has_fields = op1->has_fields || op2->has_fields;
return retval;
}
QueryExpr *
qexp_unop_new (int operand, QueryExpr * op)
{
QueryExpr *retval = qexp_new ();
int optype;
switch (operand) {
case P_SUBTRACT:
optype = OP_SUBTRACT;
break;
case P_NOT:
optype = OP_NOT;
break;
default:
g_assert_not_reached ();
optype = 0;
break;
}
retval->type = EXPR_UNOP;
retval->u.unop_value.type = optype;
retval->u.unop_value.op = op;
retval->has_fields = op->has_fields;
return retval;
}
QueryExpr *
qexp_function_new (char *name, GSList * exprlist)
{
GSList *cur;
QueryExpr *retval = qexp_new ();
retval->type = EXPR_FUNCTION;
retval->u.function_value.func_name = name;
retval->u.function_value.arguments = exprlist;
cur = exprlist;
while (cur != NULL && !((QueryExpr *) cur->data)->has_fields) {
cur = cur->next;
}
retval->has_fields = cur ? TRUE : FALSE;
return retval;
}
QueryExpr *
qexp_variable_new (char *name)
{
QueryExpr *retval = qexp_new ();
retval->type = EXPR_VARIABLE;
retval->u.var_value = name;
retval->has_fields = FALSE;
return retval;
}
QueryExpr *
qexp_id_new (char *name)
{
QueryExpr *retval = qexp_new ();
retval->type = EXPR_ID;
retval->u.var_value = name;
retval->has_fields = TRUE;
return retval;
}
QueryExpr *
qexp_constant_new (QueryExprConst setme)
{
QueryExpr *retval = qexp_new ();
retval->type = EXPR_CONSTANT;
retval->u.constant_value = setme;
retval->u.constant_value.value_known = TRUE;
retval->has_fields = FALSE;
return retval;
}
void
qexp_dump (QueryExpr * exp)
{
switch (exp->type) {
case EXPR_FUNCTION:
{
GSList *cur;
g_print ("%s(", exp->u.function_value.func_name);
for (cur = exp->u.function_value.arguments; cur;
cur = cur->next) {
qexp_dump (cur->data);
if (cur->next)
g_print (", ");
}
g_print (")");
}
break;
case EXPR_VARIABLE:
g_print ("$%s", exp->u.var_value);
break;
case EXPR_ID:
g_print ("%s", exp->u.id_value);
break;
case EXPR_BINOP:
{
char *opc;
g_print ("(");
qexp_dump (exp->u.binop_value.op1);
g_print (")");
switch (exp->u.binop_value.type) {
case OP_EQ:
opc = "=";
break;
case OP_NEQ:
opc = "!=";
break;
case OP_LEQ:
opc = "<=";
break;
case OP_GEQ:
opc = ">=";
break;
case OP_LT:
opc = "<";
break;
case OP_GT:
opc = ">";
break;
case OP_OR:
opc = "||";
break;
case OP_AND:
opc = "&&";
break;
case OP_MULTIPLY:
opc = "*";
break;
case OP_DIVIDE:
opc = "/";
break;
case OP_ADD:
opc = "+";
break;
case OP_SUBTRACT:
opc = "-";
break;
case OP_XOR:
opc = "^";
break;
default:
opc = NULL;
break;
}
g_print (" %s (", opc);
qexp_dump (exp->u.binop_value.op2);
g_print (")");
}
break;
case EXPR_UNOP:
switch (exp->u.unop_value.type) {
case OP_NOT:
g_print ("~(");
break;
case OP_NEGATE:
g_print ("-(");
break;
}
qexp_dump (exp->u.unop_value.op);
g_print (")");
break;
case EXPR_CONSTANT:
qexp_constant_dump (&exp->u.constant_value);
break;
default:
g_error ("Unknown exp type %d", exp->type);
break;
}
}
void
qexp_constant_dump (QueryExprConst * c)
{
if (c->value_known) {
switch (c->type) {
case CONST_STRINGV:
{
int i;
g_print ("[");
for (i = 0; c->u.v_stringv[i]; i++) {
g_print ("'%s'", c->u.v_stringv[i]);
if (c->u.v_stringv[i + 1])
g_print (", ");
}
g_print ("]");
}
break;
case CONST_STRING:
g_print ("'%s'", c->u.v_string);
break;
case CONST_NUMBER:
g_print ("%f", c->u.v_number);
break;
case CONST_BOOLEAN:
g_print ("%s", c->u.v_boolean ? "TRUE" : "FALSE");
break;
}
} else
g_print ("??");
}
/* Returns a value suitable for use in boolean expressions */
static gboolean
qexp_constant_bool (const QueryExprConst * c)
{
if (c->value_known)
switch (c->type) {
case CONST_BOOLEAN:
return c->u.v_boolean;
case CONST_NUMBER:
return (c->u.v_number != 0.0);
case CONST_STRING:
return (c->u.v_string != NULL);
case CONST_STRINGV:
return (c->u.v_stringv != NULL);
}
return FALSE;
}
gint
qexp_constant_compare (const QueryExprConst * c1, const QueryExprConst * c2)
{
if (c1->value_known && c2->value_known) {
g_return_val_if_fail (c1->type == c2->type, 0);
switch (c1->type) {
case CONST_STRING:
return strcmp (c1->u.v_string, c2->u.v_string);
break;
case CONST_BOOLEAN:
if (c1->u.v_boolean && !c2->u.v_boolean)
return -1;
else if (c2->u.v_boolean && !c1->u.v_boolean)
return 1;
else
return 0;
break;
case CONST_NUMBER:
{
if (c2->u.v_number > c1->u.v_number)
return 1;
else if (c2->u.v_number < c1->u.v_number)
return -1;
else
return 0;
}
break;
default:
g_assert_not_reached ();
break;
}
} else if (c1->value_known)
return -1;
else if (c2->value_known)
return 1;
return 0;
}
#define qexp_constant_unuse(c) if ((c).needs_free && (c).value_known) qexp_constant_free(&(c))
static void
qexp_constant_free (const QueryExprConst * c)
{
switch (c->type) {
case CONST_STRING:
g_free (c->u.v_string);
break;
case CONST_STRINGV:
g_strfreev (c->u.v_stringv);
break;
default:
break;
}
}
/********************************************* Now the fun stuff *****************************************/
/******* handling functions *********/
typedef QueryExprConst (*QueryExprEvalFunc) (Bonobo_ServerInfo * si,
QueryExpr * e,
QueryContext * qctx);
/* A table of the functions that can be used in queries */
typedef struct
{
const char *name;
QueryExprEvalFunc efunc;
int min_args, max_args;
}
QueryExprFuncInfo;
static QueryExprConst qexp_func_has_one (Bonobo_ServerInfo * si, QueryExpr * e,
QueryContext * qctx);
static QueryExprConst qexp_func_has_all (Bonobo_ServerInfo * si, QueryExpr * e,
QueryContext * qctx);
static QueryExprConst qexp_func_has (Bonobo_ServerInfo * si, QueryExpr * e,
QueryContext * qctx);
static QueryExprConst qexp_func_prefer_by_list_order (Bonobo_ServerInfo * si, QueryExpr * e,
QueryContext * qctx);
static QueryExprConst qexp_func_defined (Bonobo_ServerInfo * si, QueryExpr * e,
QueryContext * qctx);
static QueryExprConst qexp_func_max (Bonobo_ServerInfo * si, QueryExpr * e,
QueryContext * qctx);
static QueryExprConst qexp_func_min (Bonobo_ServerInfo * si, QueryExpr * e,
QueryContext * qctx);
static const QueryExprFuncInfo qexp_func_impls[] = {
{"has_one", qexp_func_has_one, 2},
{"has_all", qexp_func_has_all, 2},
{"has", qexp_func_has, 2},
{"prefer_by_list_order", qexp_func_prefer_by_list_order, 2},
{"defined", qexp_func_defined, 1},
{"max", qexp_func_max, 1},
{"min", qexp_func_min, 1},
{NULL}
};
static QueryExprConst
qexp_evaluate_function (Bonobo_ServerInfo * si, QueryExpr * e,
QueryContext * qctx)
{
QueryExprConst retval;
const char *func_name;
int i, n, max;
const QueryExprFuncInfo *fi;
func_name = e->u.function_value.func_name;
for (i = 0; qexp_func_impls[i].name; i++) {
if (!g_ascii_strcasecmp (func_name, qexp_func_impls[i].name))
break;
}
fi = &qexp_func_impls[i];
if (!fi->name) {
retval.value_known = FALSE;
retval.needs_free = FALSE;
g_warning ("Invalid function name '%s'", func_name);
return retval;
}
n = g_slist_length (e->u.function_value.arguments);
max = fi->max_args;
if (max < fi->min_args)
max = fi->min_args;
if ((n < fi->min_args) || (n > max)) {
g_warning
("Incorrect argument count to function '%s': got %d, need between %d and %d",
func_name, n, fi->min_args, max);
}
return fi->efunc (si, e, qctx);
}
static QueryExprConst
qexp_func_has_one (Bonobo_ServerInfo * si, QueryExpr * e, QueryContext * qctx)
{
QueryExprConst retval = { 0, }, v1, v2;
int i, j;
gboolean found;
char **check_one, **check_two;
v1 = qexp_evaluate (si, e->u.function_value.arguments->data, qctx);
v2 =
qexp_evaluate (si, e->u.function_value.arguments->next->data,
qctx);
retval.value_known = TRUE;
if (!v1.value_known || !v2.value_known) {
retval.type = CONST_BOOLEAN;
retval.u.v_boolean = FALSE;
} else if (v1.type != CONST_STRINGV || v2.type != CONST_STRINGV) {
retval.value_known = FALSE;
} else {
found = FALSE;
check_one = v1.u.v_stringv;
check_two = v2.u.v_stringv;
for (i = j = 0; check_one[i]; i++) {
for (j = 0; check_two[j]; j++) {
if (!strcmp (check_one[i], check_two[j])) {
found = TRUE;
break;
}
}
}
retval.type = CONST_BOOLEAN;
retval.u.v_boolean = found;
}
retval.needs_free = FALSE;
qexp_constant_unuse (v1);
qexp_constant_unuse (v2);
return retval;
}
static QueryExprConst
qexp_func_has_all (Bonobo_ServerInfo * si, QueryExpr * e, QueryContext * qctx)
{
QueryExprConst retval = { 0, }, v1, v2;
int i, j;
char **check_one, **check_two;
v1 = qexp_evaluate (si, e->u.function_value.arguments->data, qctx);
v2 =
qexp_evaluate (si, e->u.function_value.arguments->next->data,
qctx);
retval.value_known = TRUE;
if (!v1.value_known || !v2.value_known) {
retval.type = CONST_BOOLEAN;
retval.u.v_boolean = FALSE;
} else if (v1.type != CONST_STRINGV || v2.type != CONST_STRINGV) {
retval.value_known = FALSE;
} else {
check_one = v1.u.v_stringv;
check_two = v2.u.v_stringv;
for (i = j = 0; check_two[j] && check_one[i]; j++) {
for (i = 0; check_one[i]; i++) {
if (!strcmp (check_two[j], check_one[i]))
break;
}
}
retval.type = CONST_BOOLEAN;
retval.u.v_boolean = check_one[i] ? TRUE : FALSE;
}
retval.needs_free = FALSE;
qexp_constant_unuse (v1);
qexp_constant_unuse (v2);
return retval;
}
static QueryExprConst
qexp_func_has (Bonobo_ServerInfo * si, QueryExpr * e, QueryContext * qctx)
{
QueryExprConst retval = { 0, }, v1, v2;
char **check_one, *check_two;
int i;
v1 = qexp_evaluate (si, e->u.function_value.arguments->data, qctx);
v2 =
qexp_evaluate (si, e->u.function_value.arguments->next->data,
qctx);
retval.value_known = TRUE;
if (!v1.value_known || !v2.value_known) {
retval.type = CONST_BOOLEAN;
retval.u.v_boolean = FALSE;
} else if (v1.type != CONST_STRINGV || v2.type != CONST_STRING) {
retval.value_known = FALSE;
} else {
check_one = v1.u.v_stringv;
check_two = v2.u.v_string;
for (i = 0; check_one[i]; i++) {
if (!strcmp (check_one[i], check_two))
break;
}
retval.type = CONST_BOOLEAN;
retval.u.v_boolean = check_one[i] ? TRUE : FALSE;
}
retval.needs_free = FALSE;
qexp_constant_unuse (v1);
qexp_constant_unuse (v2);
return retval;
}
static QueryExprConst
qexp_func_prefer_by_list_order (Bonobo_ServerInfo *si,
QueryExpr *e,
QueryContext *qctx)
{
QueryExprConst retval = { 0, }, item, list;
char **check_one, *check_two;
int i;
int position;
item = qexp_evaluate (si, e->u.function_value.arguments->data, qctx);
list = qexp_evaluate (si, e->u.function_value.arguments->next->data, qctx);
retval.value_known = TRUE;
if (!item.value_known || !list.value_known) {
retval.type = CONST_BOOLEAN;
retval.u.v_boolean = FALSE;
} else if (item.type != CONST_STRING || list.type != CONST_STRINGV) {
retval.value_known = FALSE;
} else {
position = -1;
check_one = list.u.v_stringv;
check_two = item.u.v_string;
for (i = 0; check_one[i] != NULL; i++) {
if (position == -1 &&
strcmp (check_one[i], check_two) == 0) {
position = i;
}
}
if (position != -1) {
position = i - position;
}
retval.type = CONST_NUMBER;
retval.u.v_number = position;
}
retval.needs_free = FALSE;
qexp_constant_unuse (item);
qexp_constant_unuse (list);
return retval;
}
static QueryExprConst
qexp_func_defined (Bonobo_ServerInfo * si, QueryExpr * e, QueryContext * qctx)
{
QueryExprConst retval, v1;
v1 = qexp_evaluate (si, e->u.function_value.arguments->data, qctx);
retval.value_known = TRUE;
retval.type = CONST_BOOLEAN;
retval.u.v_boolean = v1.value_known ? TRUE : FALSE;
retval.needs_free = FALSE;
qexp_constant_unuse (v1);
return retval;
}
static QueryExprConst
qexp_func_max (Bonobo_ServerInfo * si, QueryExpr * e, QueryContext * qctx)
{
int i;
QueryExprConst max_val_so_far;
max_val_so_far.value_known = FALSE;
for (i = 0; i < qctx->nservers; i++) {
QueryExprConst new_val;
new_val =
qexp_evaluate (qctx->sil[i],
e->u.function_value.arguments->data,
qctx);
if (qexp_constant_compare (&max_val_so_far, &new_val) > 0)
max_val_so_far = new_val;
}
/* The value of this function never changes on a per-record basis, so we never have to revaluate it */
e->has_fields = FALSE;
return max_val_so_far;
}
static QueryExprConst
qexp_func_min (Bonobo_ServerInfo * si, QueryExpr * e, QueryContext * qctx)
{
int i;
QueryExprConst min_val_so_far;
min_val_so_far.value_known = FALSE;
for (i = 0; i < qctx->nservers; i++) {
QueryExprConst new_val;
new_val =
qexp_evaluate (qctx->sil[i],
e->u.function_value.arguments->data,
qctx);
if (qexp_constant_compare (&min_val_so_far, &new_val) > 0)
min_val_so_far = new_val;
}
/* see comment in qexp_func_max */
e->has_fields = FALSE;
return min_val_so_far;
}
/********** Variables *******/
static QueryExprConst
qexp_evaluate_variable (Bonobo_ServerInfo * si, QueryExpr * e,
QueryContext * qctx)
{
QueryExprConst retval = { 0, };
retval.value_known = FALSE;
if (qctx->cctx) {
CORBA_Environment ev;
CORBA_NVList nvout;
CORBA_exception_init (&ev);
CORBA_Context_get_values (qctx->cctx, NULL, 0, e->u.var_value,
&nvout, &ev);
/* FIXME bugzilla.eazel.com 2732: non-standard - I
* screwed up the NVList implementation in ORBit */
if (ev._major == CORBA_NO_EXCEPTION) {
if (nvout->list->len > 0) {
CORBA_NamedValue *nv;
retval.value_known = TRUE;
retval.type = CONST_STRING;
nv =
&g_array_index (nvout->list,
CORBA_NamedValue, 0);
retval.u.v_string =
g_strdup (*(char **) nv->
argument._value);
retval.needs_free = TRUE;
} else
g_warning ("Unknown variable %s",
e->u.var_value);
CORBA_NVList_free (nvout, &ev);
} else
g_warning ("Unknown variable %s", e->u.var_value);
CORBA_exception_free (&ev);
} else
g_warning ("Unknown variable %s", e->u.var_value);
return retval;
}
/********* fields ***********/
static QueryExprConst
qexp_evaluate_id (Bonobo_ServerInfo * si, QueryExpr * e, QueryContext * qctx)
{
QueryExprConst retval = { 0, };
retval.value_known = retval.needs_free = FALSE;
if (si) {
retval.value_known = TRUE;
retval.type = CONST_STRING;
if (!g_ascii_strcasecmp (e->u.id_value, "location_info"))
retval.u.v_string = si->location_info;
else if (!g_ascii_strcasecmp (e->u.id_value, "server_type"))
retval.u.v_string = si->server_type;
else if (!g_ascii_strcasecmp (e->u.id_value, "iid"))
retval.u.v_string = si->iid;
else if (!g_ascii_strcasecmp (e->u.id_value, "username"))
retval.u.v_string = si->username;
else if (!g_ascii_strcasecmp (e->u.id_value, "hostname"))
retval.u.v_string = si->hostname;
else {
int i;
for (i = 0; i < si->props._length; i++) {
if (!strcmp
(e->u.id_value,
si->props._buffer[i].name)) break;
}
retval.value_known = FALSE;
if (i < si->props._length) {
Bonobo_ActivationPropertyValue *av;
av = &si->props._buffer[i].v;
switch (av->_d) {
case Bonobo_ACTIVATION_P_STRING:
retval.type = CONST_STRING;
retval.u.v_string =
av->_u.value_string;
break;
case Bonobo_ACTIVATION_P_NUMBER:
retval.type = CONST_NUMBER;
retval.u.v_number =
av->_u.value_number;
break;
case Bonobo_ACTIVATION_P_BOOLEAN:
retval.type = CONST_BOOLEAN;
retval.u.v_boolean =
av->_u.value_boolean;
break;
case Bonobo_ACTIVATION_P_STRINGV:
{
/* FIXME bugzilla.eazel.com 2729: it would be nice to replace the
* NULL-terminated string arrays with
* CORBA_sequence_string all over
*/
int i;
retval.type = CONST_STRINGV;
retval.needs_free = TRUE;
retval.u.v_stringv =
g_malloc (sizeof
(char *) *
(av->
_u.value_stringv._length
+ 1));
for (i = 0;
i <
av->_u.
value_stringv._length;
i++) retval.
u.v_stringv[i]
=
g_strdup
(av->_u.value_stringv._buffer
[i]);
retval.u.v_stringv[i] = NULL;
}
break;
}
retval.value_known = TRUE;
} else if (qctx->id_evaluator)
retval =
qctx->id_evaluator (si, e->u.id_value,
qctx);
}
}
return retval;
}
/********* binary operators *********/
static QueryExprConst
qexp_evaluate_binop (Bonobo_ServerInfo * si, QueryExpr * e, QueryContext * qctx)
{
QueryExprConst retval = { 0, }, v1, v2;
gboolean negate_result = FALSE;
v2.value_known = FALSE; /* To make sure that qexp_constant_unuse works properly if we short-circuit */
retval.value_known = TRUE;
retval.needs_free = FALSE;
v1 = qexp_evaluate (si, e->u.binop_value.op1, qctx);
/* Perform short-circuiting */
switch (e->u.binop_value.type) {
case OP_OR:
if (v1.value_known && qexp_constant_bool (&v1)) {
retval.type = CONST_BOOLEAN;
retval.u.v_boolean = TRUE;
qexp_constant_unuse (v1);
return retval;
}
break;
case OP_AND:
if (v1.value_known && !qexp_constant_bool (&v1)) {
retval.type = CONST_BOOLEAN;
retval.u.v_boolean = FALSE;
qexp_constant_unuse (v1);
return retval;
}
break;
default:
break;
}
v2 = qexp_evaluate (si, e->u.binop_value.op2, qctx);
retval.value_known = TRUE;
switch (e->u.binop_value.type) {
case OP_NEQ:
negate_result = TRUE;
case OP_EQ:
retval.type = CONST_BOOLEAN;
retval.u.v_boolean = qexp_constant_compare (&v1, &v2) == 0;
break;
case OP_GEQ:
negate_result = TRUE;
case OP_LT:
retval.type = CONST_BOOLEAN;
retval.u.v_boolean = qexp_constant_compare (&v1, &v2) < 0;
break;
case OP_LEQ:
negate_result = TRUE;
case OP_GT:
retval.type = CONST_BOOLEAN;
retval.u.v_boolean = qexp_constant_compare (&v1, &v2) > 0;
break;
case OP_OR:
retval.type = CONST_BOOLEAN;
retval.u.v_boolean = qexp_constant_bool (&v1)
|| qexp_constant_bool (&v2);
break;
case OP_AND:
retval.type = CONST_BOOLEAN;
retval.u.v_boolean = qexp_constant_bool (&v1)
&& qexp_constant_bool (&v2);
break;
case OP_MULTIPLY:
if (v1.type != CONST_NUMBER || v2.type != CONST_NUMBER)
retval.value_known = FALSE;
else {
retval.type = CONST_NUMBER;
retval.u.v_number = v1.u.v_number * v2.u.v_number;
}
break;
case OP_DIVIDE:
if (v1.type != CONST_NUMBER || v2.type != CONST_NUMBER)
retval.value_known = FALSE;
else {
retval.type = CONST_NUMBER;
if (v2.u.v_number == 0.0)
retval.value_known = FALSE;
else
retval.u.v_number =
v1.u.v_number / v2.u.v_number;
}
break;
case OP_ADD:
if (v1.type != CONST_NUMBER || v2.type != CONST_NUMBER)
retval.value_known = FALSE;
else {
retval.type = CONST_NUMBER;
retval.u.v_number = v1.u.v_number + v2.u.v_number;
}
break;
case OP_SUBTRACT:
if (v1.type != CONST_NUMBER || v2.type != CONST_NUMBER)
retval.value_known = FALSE;
else {
retval.type = CONST_NUMBER;
retval.u.v_number = v1.u.v_number - v2.u.v_number;
}
break;
case OP_XOR:
retval.type = CONST_BOOLEAN;
retval.u.v_boolean =
qexp_constant_bool (&v1) ^ qexp_constant_bool (&v2);
break;
}
if (negate_result)
retval.u.v_boolean = !retval.u.v_boolean;
qexp_constant_unuse (v1);
qexp_constant_unuse (v2);
return retval;
}
/********** unary operators **********/
static QueryExprConst
qexp_evaluate_unop (Bonobo_ServerInfo * si, QueryExpr * e, QueryContext * qctx)
{
QueryExprConst retval = { 0, }, v1;
retval.value_known = TRUE;
v1 = qexp_evaluate (si, e->u.unop_value.op, qctx);
switch (e->u.unop_value.type) {
case OP_NOT:
retval.type = CONST_BOOLEAN;
retval.u.v_boolean = !qexp_constant_bool (&v1);
break;
case OP_NEGATE:
if (v1.type != CONST_NUMBER)
retval.value_known = FALSE;
else {
retval.type = CONST_NUMBER;
retval.u.v_number = -(v1.u.v_number);
}
break;
}
qexp_constant_unuse (v1);
return retval;
}
/********** constants ************/
static QueryExprConst
qexp_evaluate_constant (Bonobo_ServerInfo * si, QueryExpr * e,
QueryContext * qctx)
{
return e->u.constant_value;
}
/***** The grand poobah of functions *****/
QueryExprConst
qexp_evaluate (Bonobo_ServerInfo * si, QueryExpr * e, QueryContext * qctx)
{
QueryExprConst retval;
if (e->have_cached_value) {
retval = e->cached_value;
} else {
switch (e->type) {
case EXPR_FUNCTION:
retval = qexp_evaluate_function (si, e, qctx);
break;
case EXPR_VARIABLE:
retval = qexp_evaluate_variable (si, e, qctx);
break;
case EXPR_ID:
retval = qexp_evaluate_id (si, e, qctx);
break;
case EXPR_BINOP:
retval = qexp_evaluate_binop (si, e, qctx);
break;
case EXPR_UNOP:
retval = qexp_evaluate_unop (si, e, qctx);
break;
case EXPR_CONSTANT:
retval = qexp_evaluate_constant (si, e, qctx);
break;
}
}
if (!e->has_fields) {
e->cached_value = retval;
e->have_cached_value = TRUE;
retval.needs_free = FALSE; /* We don't want people freeing our cached value... */
}
return retval;
}
gboolean
qexp_matches (Bonobo_ServerInfo * si, QueryExpr * e, QueryContext * qctx)
{
QueryExprConst res;
gboolean retval;
res = qexp_evaluate (si, e, qctx);
retval = qexp_constant_bool (&res);
qexp_constant_unuse (res);
return retval;
}
typedef struct
{
QueryExpr **sexps;
int nexps;
QueryContext *qctx;
}
QexpSortData;
static int
qexp_sort_compare (gconstpointer a, gconstpointer b, gpointer user_data)
{
Bonobo_ServerInfo *x = * (Bonobo_ServerInfo **) a;
Bonobo_ServerInfo *y = * (Bonobo_ServerInfo **) b;
QexpSortData * sort_data = user_data;
int i;
if (x == NULL) {
return 1;
}
if (y == NULL) {
return -1;
}
for (i = 0; i < sort_data->nexps; i++) {
QueryExprConst cx, cy;
int res;
cx = qexp_evaluate (x, sort_data->sexps[i], sort_data->qctx);
cy = qexp_evaluate (y, sort_data->sexps[i], sort_data->qctx);
res = qexp_constant_compare (&cx, &cy);
qexp_constant_unuse (cx);
qexp_constant_unuse (cy);
if (res > 0)
return 1;
else if (res < 0)
return -1;
}
return 0;
}
void
qexp_sort (Bonobo_ServerInfo ** servers, int nservers, QueryExpr ** sexps,
int nexps, QueryContext * qctx)
{
QexpSortData sort_data;
sort_data.sexps = sexps;
sort_data.nexps = nexps;
sort_data.qctx = qctx;
g_qsort_with_data (servers, nservers, sizeof (Bonobo_ServerInfo *),
qexp_sort_compare, &sort_data);
}
syntax highlighted by Code2HTML, v. 0.9.1