Logo Search packages:      
Sourcecode: audit version File versions  Download package

expression.c
/*
* expression.c - Expression parsing and handling
* Copyright (C) 2008 Red Hat Inc., Durham, North Carolina.
* All Rights Reserved.
*
* This software may be freely redistributed and/or modified under the
* terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2, or (at your option) any
* later version.
*
* This program 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 program; see the file COPYING. If not, write to the
* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Authors:
*   Miloslav Trma─Ź <mitr@redhat.com>
*/

#include <assert.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>

#include "expression.h"

 /* Utilities */

/* Free EXPR and all its subexpressions. */
void
expr_free(struct expr *expr)
{
      switch (expr->op) {
      case EO_NOT:
            expr_free(expr->v.sub[0]);
            break;

      case EO_AND: case EO_OR:
            expr_free(expr->v.sub[0]);
            expr_free(expr->v.sub[1]);
            break;

      case EO_RAW_EQ: case EO_RAW_NE: case EO_INTERPRETED_EQ:
      case EO_INTERPRETED_NE: case EO_VALUE_EQ: case EO_VALUE_NE:
      case EO_VALUE_LT: case EO_VALUE_LE: case EO_VALUE_GT: case EO_VALUE_GE:
            if (expr->virtual_field == 0)
                  free(expr->v.p.field.name);
            if (expr->precomputed_value == 0)
                  free(expr->v.p.value.string);
            break;

      case EO_FIELD_EXISTS:
            assert(expr->virtual_field == 0);
            free(expr->v.p.field.name);
            break;

      default:
            abort();
      }
      free(expr);
}

 /* Expression parsing. */

/* The formal grammar:

   start: or-expression

   or-expression: and-expression
   or-expression: or-expression || and-expression

   and-expression: primary-expression
   and-expression: and-expression && primary-expression

   primary-expression: ! primary-expression
   primary-expression: ( or-expression )
   primary-expression: comparison-expression

   comparison-expression: field op value
   field: string
   field: field-escape string
   value: string */

/* Token types */
enum token_type {
      /* EO_* */
      T_LEFT_PAREN = NUM_EO_VALUES, T_RIGHT_PAREN, T_STRING, T_FIELD_ESCAPE,
      T_UNKNOWN, T_EOF
};

/* Expression parsing status */
00096 struct parsing {
      char **error;           /* Error message destination. */
      enum token_type token;
      const char *token_start; /* Original "src" value */
      int token_len;          /* int because it must be usable in %.*s */
      char *token_value;      /* Non-NULL only for T_STRING, until used */
      const char *src;  /* Expression source, after the current token */
};

static struct expr *parse_or(struct parsing *p);

/* Allocate SIZE bytes.
   On error, return NULL and try to set *P->ERROR. */
static void *
parser_malloc(struct parsing *p, size_t size)
{
      void *res;

      res = malloc(size);
      if (res != NULL || size == 0)
            return res;
      *p->error = strdup("Out of memory");
      return NULL;
}

/* Reallocate PTR to SIZE bytes.
   On error, free(PTR), return NULL and try to set *P->ERROR.
   NOTE: realloc() does not free(PTR), this function does. */
static void *
parser_realloc(struct parsing *p, void *ptr, size_t size)
{
      void *res;

      res = realloc(ptr, size);
      if (res != NULL || size == 0)
            return res;
      free(ptr);
      *p->error = strdup("Out of memory");
      return NULL;
}

/* Discard P->token_value, if any, and parse the next token in P->src.
   On success, return 0.
   On error, set *P->ERROR to an error string (for free()) or NULL, and return
   -1. */
static int
lex(struct parsing *p)
{
      free(p->token_value);
      p->token_value = NULL;
      while (*p->src == ' ' || *p->src == '\t' || *p->src == '\n')
            p->src++;
      p->token_start = p->src;
      switch (*p->src) {
      case '\0':
            p->token = T_EOF;
            break;

      case '!':
            p->src++;
            if (*p->src == '=' && p->src[1] == '=') {
                  p->src += 2;
                  p->token = EO_VALUE_NE;
                  break;
            }
            p->token = EO_NOT;
            break;

      case '"': {
            char *buf;
            size_t dest, buf_size;

            buf_size = 8;
            buf = parser_malloc(p, buf_size);
            if (buf == NULL)
                  return -1;
            p->src++;
            dest = 0;
            while (*p->src != '"') {
                  if (*p->src == '\0') {
                        *p->error = strdup("Terminating quote missing");
                        free(buf);
                        return -1;
                  }
                  if (*p->src == '\\') {
                        p->src++;
                        if (*p->src != '\\' && *p->src != '"') {
                              *p->error = NULL;
                              asprintf(p->error, "Unknown escape "
                                     "sequence ``\\%c''", *p->src);
                              free(buf);
                              return -1;
                        }
                  }
                  /* +1: make sure there is space for the terminating
                     NUL. */
                  if (dest + 1 >= buf_size) {
                        if (buf_size > SIZE_MAX / 2) {
                              *p->error = strdup("Quoted string too "
                                             "long");
                              free(buf);
                              return -1;
                        }
                        buf_size *= 2;
                        buf = parser_realloc(p, buf, buf_size);
                        if (buf == NULL) {
                              *p->error = strdup("Out of memory");
                              return -1;
                        }
                  }
                  buf[dest] = *p->src;
                  dest++;
                  p->src++;
            }
            p->src++;
            buf[dest] = '\0';
            p->token_value = parser_realloc(p, buf, dest + 1);
            if (p->token_value == NULL)
                  return -1;
            p->token = T_STRING;
            break;
      }

      case '&':
            p->src++;
            if (*p->src == '&') {
                  p->src++;
                  p->token = EO_AND;
                  break;
            }
            p->token = T_UNKNOWN;
            break;

      case '(':
            p->src++;
            p->token = T_LEFT_PAREN;
            break;

      case ')':
            p->src++;
            p->token = T_RIGHT_PAREN;
            break;

      case '<':
            p->src++;
            if (*p->src == '=') {
                  p->src++;
                  p->token = EO_VALUE_LE;
                  break;
            }
            p->token = EO_VALUE_LT;
            break;

      case '=':
            p->src++;
            if (*p->src == '=') {
                  p->src++;
                  p->token = EO_VALUE_EQ;
                  break;
            }
            p->token = T_UNKNOWN;
            break;

      case '>':
            p->src++;
            if (*p->src == '=') {
                  p->src++;
                  p->token = EO_VALUE_GE;
                  break;
            }
            p->token = EO_VALUE_GT;
            break;

      case '\\':
            p->src++;
            p->token = T_FIELD_ESCAPE;
            break;

      case '|':
            p->src++;
            if (*p->src == '|') {
                  p->src++;
                  p->token = EO_OR;
                  break;
            }
            p->token = T_UNKNOWN;
            break;

      case 'i':
            if (p->src[1] == '=') {
                  p->src += 2;
                  p->token = EO_INTERPRETED_EQ;
                  break;
            } else if (p->src[1] == '!' && p->src[2] == '=') {
                  p->src += 3;
                  p->token = EO_INTERPRETED_NE;
                  break;
            }
            goto unquoted_string;

      case 'r':
            if (p->src[1] == '=') {
                  p->src += 2;
                  p->token = EO_RAW_EQ;
                  break;
            } else if (p->src[1] == '!' && p->src[2] == '=') {
                  p->src += 3;
                  p->token = EO_RAW_NE;
                  break;
            }
            goto unquoted_string;

      default:
            /* This assumes ASCII */
            assert ('Z' == 'A' + 25 && 'z' == 'a' + 25);
#define IS_UNQUOTED_STRING_CHAR(C)              \
                  (((C) >= 'a' && (C) <= 'z')   \
                   || ((C) >= 'A' && (C) <= 'Z')      \
                   || ((C) >= '0' && (C) <= '9')      \
                   || (C) == '_')
            if (IS_UNQUOTED_STRING_CHAR(*p->src)) {
                  size_t len;

            unquoted_string:
                  do
                        p->src++;
                  while (IS_UNQUOTED_STRING_CHAR(*p->src));
                  len = p->src - p->token_start;
                  p->token_value = parser_malloc(p, len + 1);
                  if (p->token_value == NULL)
                        return -1;
                  memcpy(p->token_value, p->token_start, len);
                  p->token_value[len] = '\0';
                  p->token = T_STRING;
                  break;
            }
            p->src++;
            p->token = T_UNKNOWN;
            break;
      }
      if (p->src - p->token_start > INT_MAX) {
            *p->error = strdup("Token too long");
            return -1;
      }
      p->token_len = p->src - p->token_start;
      return 0;
}

/* Parse an escaped field NAME to DEST.
   Return 0 on success, -1 if NAME is unknown. */
static int
parse_escaped_field_name(enum field_id *dest, const char *name)
{
      if (strcmp(name, "timestamp") == 0)
            *dest = EF_TIMESTAMP;
      else if (strcmp(name, "record_type") == 0)
            *dest = EF_RECORD_TYPE;
      else
            return -1;
      return 0;
}

/* Parse a \timestamp field value in P->token_value to DEST.
   On success, return 0.
   On error, set *P->ERROR to an error string (for free()) or NULL, and return
   -1. */
static int
parse_timestamp_value(struct expr *dest, struct parsing *p)
{
      intmax_t sec;

      assert(p->token == T_STRING);
      /* FIXME: other formats? */
      /* FIXME: parse it as a float instead of hard-coding milliseconds? */
      if (sscanf(p->token_value, "ts:%jd.%u", &sec,
               &dest->v.p.value.timestamp.milli)
          != 2) {
            *p->error = NULL;
            asprintf(p->error, "Invalid timestamp value `%.*s'",
                   p->token_len, p->token_start);
            return -1;
      }
      /* FIXME: validate milli */
      dest->v.p.value.timestamp.sec = sec;
      if (dest->v.p.value.timestamp.sec != sec) {
            *p->error = NULL;
            asprintf(p->error, "Timestamp overflow in `%.*s'", p->token_len,
                   p->token_start);
            return -1;
      }
      dest->precomputed_value = 1;
      return 0;
}

/* Parse a \record_type field value in P->token_value to DEST.
   On success, return 0.
   On error, set *P->ERROR to an error string (for free()) or NULL, and return
   -1. */
static int
parse_record_type_value(struct expr *dest, struct parsing *p)
{
      int type;

      assert(p->token == T_STRING);
      type = audit_name_to_msg_type(p->token_value);
      if (type < 0) {
            *p->error = NULL;
            asprintf(p->error, "Invalid record type `%.*s'", p->token_len,
                   p->token_start);
            return -1;
      }
      dest->v.p.value.int_value = type;
      dest->precomputed_value = 1;
      return 0;
}

/* Parse a virtual field value in P->token_value to DEST.
   On success, return 0.
   On error, set *P->ERROR to an error string (for free()) or NULL, and return
   NULL. */
static int
parse_virtual_field_value(struct expr *dest, struct parsing *p)
{
      switch (dest->v.p.field.id) {
      case EF_TIMESTAMP:
            return parse_timestamp_value(dest, p);

      case EF_RECORD_TYPE:
            return parse_record_type_value(dest, p);

      default:
            abort();
      }
}

/* Parse a comparison-expression string in *P.
   On success, return the parsed comparison-expression.
   On error, set *P->ERROR to an error string (for free()) or NULL, and return
   NULL. */
static struct expr *
parse_comparison(struct parsing *p)
{
      struct expr *res;

      res = parser_malloc(p, sizeof(*res));
      if (res == NULL)
            return NULL;
      if (p->token == T_FIELD_ESCAPE) {
            if (lex(p) != 0)
                  goto err_res;
            if (p->token != T_STRING) {
                  *p->error = strdup("Field name expected after field "
                                 "escape");
                  goto err_res;
            }
            res->virtual_field = 1;
            if (parse_escaped_field_name(&res->v.p.field.id, p->token_value)
                != 0) {
                  *p->error = NULL;
                  asprintf(p->error, "Unknown escaped field name `%.*s'",
                         p->token_len, p->token_start);
                  goto err_res;
            }
      } else {
            assert(p->token == T_STRING);
            res->virtual_field = 0;
            res->v.p.field.name = p->token_value;
            p->token_value = NULL;
      }
      if (lex(p) != 0)
            goto err_field;
      switch (p->token) {
      case EO_RAW_EQ: case EO_RAW_NE: case EO_INTERPRETED_EQ:
      case EO_INTERPRETED_NE:
            res->op = p->token;
            if (lex(p) != 0)
                  goto err_field;
            if (p->token != T_STRING) {
                  *p->error = NULL;
                  asprintf(p->error, "Value expected, got `%.*s'",
                         p->token_len, p->token_start);
                  goto err_field;
            }
            res->precomputed_value = 0;
            res->v.p.value.string = p->token_value;
            p->token_value = NULL;
            if (lex(p) != 0) {
                  expr_free(res);
                  return NULL;
            }
            break;

      case EO_VALUE_EQ: case EO_VALUE_NE: case EO_VALUE_LT: case EO_VALUE_LE:
      case EO_VALUE_GT: case EO_VALUE_GE:
            res->op = p->token;
            if (lex(p) != 0)
                  goto err_field;
            if (p->token != T_STRING) {
                  *p->error = NULL;
                  asprintf(p->error, "Value expected, got `%.*s'",
                         p->token_len, p->token_start);
                  goto err_field;
            }
            if (res->virtual_field == 0) {
                  *p->error = NULL;
                  asprintf (p->error, "Field `%s' does not support "
                          "value comparison",
                          res->v.p.field.name);
                  goto err_field;
            } else {
                  if (parse_virtual_field_value(res, p) != 0)
                        goto err_field;
            }
            if (lex(p) != 0) {
                  expr_free(res);
                  return NULL;
            }
            break;

      default:
            *p->error = NULL;
            asprintf(p->error, "Operator expected, got `%.*s'",
                   p->token_len, p->token_start);
            goto err_field;
      }
      return res;

err_field:
      if (res->virtual_field == 0)
            free(res->v.p.field.name);
err_res:
      free(res);
      return NULL;
}

/* Parse a primary-expression string in *P.
   On success, return the parsed primary-expression.
   On error, set *P->ERROR to an error string (for free()) or NULL, and return
   NULL. */
static struct expr *
parse_primary(struct parsing *p)
{
      struct expr *e;

      switch (p->token) {
      case EO_NOT: {
            struct expr *res;

            if (lex(p) != 0)
                  return NULL;
            e = parse_primary(p);
            if (e == NULL)
                  return NULL;
            res = parser_malloc(p, sizeof(*res));
            if (res == NULL)
                  goto err_e;
            res->op = EO_NOT;
            res->v.sub[0] = e;
            return res;
      }

      case T_LEFT_PAREN: {
            if (lex(p) != 0)
                  return NULL;
            e = parse_or(p);
            if (e == NULL)
                  return NULL;
            if (p->token != T_RIGHT_PAREN) {
                  *p->error = NULL;
                  asprintf(p->error, "Right paren expected, got `%.*s'",
                         p->token_len, p->token_start);
                  goto err_e;
            }
            if (lex(p) != 0)
                  goto err_e;
            return e;
      }

      case T_FIELD_ESCAPE: case T_STRING:
            return parse_comparison(p);

      default:
            *p->error = NULL;
            asprintf(p->error, "Unexpected token `%.*s'", p->token_len,
                   p->token_start);
            return NULL;
      }
      abort();          /* Should never get here */

err_e:
      expr_free(e);
      return NULL;
}

/* Parse an and-expression string in *P.
   On success, return the parsed and-expression.
   On error, set *P->ERROR to an error string (for free()) or NULL, and return
   NULL. */
static struct expr *
parse_and(struct parsing *p)
{
      struct expr *res;

      res = parse_primary(p);
      if (res == NULL)
            return NULL;
      while (p->token == EO_AND) {
            struct expr *e2, *e;

            if (lex(p) != 0)
                  goto err_res;
            e2 = parse_primary(p);
            if (e2 == NULL)
                  goto err_res;
            e = parser_malloc(p, sizeof(*e));
            if (e == NULL) {
                  expr_free(e2);
                  goto err_res;
            }
            e->op = EO_AND;
            e->v.sub[0] = res;
            e->v.sub[1] = e2;
            res = e;
      }
      return res;

err_res:
      expr_free(res);
      return NULL;
}

/* Parse an or-expression string in *P.
   On success, return the parsed or-expression.
   On error, set *P->ERROR to an error string (for free()) or NULL, and return
   NULL. */
static struct expr *
parse_or(struct parsing *p)
{
      struct expr *res;

      res = parse_and(p);
      if (res == NULL)
            return NULL;
      while (p->token == EO_OR) {
            struct expr *e2, *e;

            if (lex(p) != 0)
                  goto err_res;
            e2 = parse_and(p);
            if (e2 == NULL)
                  goto err_res;
            e = parser_malloc(p, sizeof(*e));
            if (e == NULL) {
                  expr_free(e2);
                  goto err_res;
            }
            e->op = EO_OR;
            e->v.sub[0] = res;
            e->v.sub[1] = e2;
            res = e;
      }
      return res;

err_res:
      expr_free(res);
      return NULL;
}

/* Parse STRING.
   On success, return the parsed expression tree.
   On error, set *ERROR to an error string (for free()) or NULL, and return
   NULL.  (*ERROR == NULL is allowed to handle out-of-memory errors) */
struct expr *
expr_parse(const char *string, char **error)
{
      struct parsing p;
      struct expr *res;

      p.error = error;
      p.token_value = NULL;
      p.src = string;
      if (lex(&p) != 0)
            goto err;
      if (p.token == T_EOF) {
            *error = strdup("Empty expression");
            goto err;
      }
      res = parse_or(&p);
      if (res != NULL && p.token != T_EOF) {
            expr_free(res);
            *error = NULL;
            asprintf(error, "Unexpected trailing token `%.*s'",
                   p.token_len, p.token_start);
            goto err;
      }
      free(p.token_value);
      return res;

err:
      free(p.token_value);
      return NULL;
}

 /* Manual expression creation */

/* Create a comparison-expression for FIELD, OP and VALUE.
   On success, return the created expression.
   On error, set errno and return NULL. */
struct expr *
expr_create_comparison(const char *field, unsigned op, const char *value)
{
      struct expr *res;

      res = malloc(sizeof(*res));
      if (res == NULL)
            goto err;
      assert(op == EO_RAW_EQ || op == EO_RAW_NE || op == EO_INTERPRETED_EQ
             || op == EO_INTERPRETED_NE);
      res->op = op;
      res->virtual_field = 0;
      res->precomputed_value = 0;
      res->v.p.field.name = strdup(field);
      if (res->v.p.field.name == NULL)
            goto err_res;
      res->v.p.value.string = strdup(value);
      if (res->v.p.value.string == NULL)
            goto err_field;
      return res;

err_field:
      free(res->v.p.field.name);
err_res:
      free(res);
err:
      return NULL;
}

/* Create a \timestamp comparison-expression for with OP, SEC, MILLI.
   On success, return the created expression.
   On error, set errno and return NULL. */
struct expr *
expr_create_timestamp_comparison(unsigned op, time_t sec, unsigned milli)
{
      struct expr *res;

      res = malloc(sizeof(*res));
      if (res == NULL)
            return NULL;
      assert(op == EO_VALUE_EQ || op == EO_VALUE_NE || op == EO_VALUE_LT
             || op == EO_VALUE_LE || op == EO_VALUE_GT || op == EO_VALUE_GE);
      res->op = op;
      res->virtual_field = 1;
      res->v.p.field.id = EF_TIMESTAMP;
      res->precomputed_value = 1;
      res->v.p.value.timestamp.sec = sec;
      assert(milli < 1000);
      res->v.p.value.timestamp.milli = milli;
      return res;
}

/* Create an EO_FIELD_EXISTS-expression for FIELD.
   On success, return the created expression.
   On error, set errno and return NULL. */
struct expr *
expr_create_field_exists(const char *field)
{
      struct expr *res;

      res = malloc(sizeof(*res));
      if (res == NULL)
            goto err;
      res->op = EO_FIELD_EXISTS;
      res->virtual_field = 0;
      res->v.p.field.name = strdup(field);
      if (res->v.p.field.name == NULL)
            goto err_res;
      return res;

err_res:
      free(res);
err:
      return NULL;
}

/* Create a binary expresion for OP and subexpressions E1 and E2.
   On success, return the created expresion.
   On error, set errno and return NULL. */
struct expr *
expr_create_binary(unsigned op, struct expr *e1, struct expr *e2)
{
      struct expr *res;

      res = malloc(sizeof(*res));
      if (res == NULL)
            return NULL;
      assert(op == EO_AND || op ==EO_OR);
      res->op = op;
      res->v.sub[0] = e1;
      res->v.sub[1] = e2;
      return res;
}

 /* Expression evaluation */

/* Return the "raw" value of the field in EXPR for RECORD in AU->le.  Set
   *FREE_IT to 1 if the return value should free()'d.
   Return NULL on error.  */
static char *
eval_raw_value(auparse_state_t *au, rnode *record, const struct expr *expr,
             int *free_it)
{
      if (expr->virtual_field == 0) {
            nvlist_first(&record->nv);
            if (nvlist_find_name(&record->nv, expr->v.p.field.name) == 0)
                  return NULL;
            *free_it = 0;
            return (char *)nvlist_get_cur_val(&record->nv);
      }
      switch (expr->v.p.field.id) {
      case EF_TIMESTAMP: case EF_RECORD_TYPE:
            return NULL;

      default:
            abort();
      }
}

/* Return the "interpreted" value of the field in EXPR for RECORD in AU->le.
   Set *FREE_IT to 1 if the return value should free()'d.
   Return NULL on *error.  */
static char *
eval_interpreted_value(auparse_state_t *au, rnode *record,
                   const struct expr *expr, int *free_it)
{
      if (expr->virtual_field == 0) {
            const char *res;

            nvlist_first(&record->nv);
            if (nvlist_find_name(&record->nv, expr->v.p.field.name) == 0)
                  return NULL;
            *free_it = 0;
            res = nvlist_interp_cur_val(record);
            if (res == NULL)
                  res = nvlist_get_cur_val(&record->nv);
            return (char *)res;
      }
      switch (expr->v.p.field.id) {
      case EF_TIMESTAMP: case EF_RECORD_TYPE:
            return NULL;

      default:
            abort();
      }
}

/* Return -1, 0, 1 depending on comparing the field in EXPR with RECORD in AU.
   Set *ERROR to 0 if OK, non-zero otherwise. */
static int
compare_values(auparse_state_t *au, rnode *record, const struct expr *expr,
             int *error)
{
      int res;
      if (expr->virtual_field == 0) {
            *error = 1;
            return 0;
      }
      switch (expr->v.p.field.id) {
      case EF_TIMESTAMP:
            if (au->le.e.sec < expr->v.p.value.timestamp.sec)
                  res = -1;
            else if (au->le.e.sec > expr->v.p.value.timestamp.sec)
                  res = 1;
            else if (au->le.e.milli < expr->v.p.value.timestamp.milli)
                  res = -1;
            else if (au->le.e.milli > expr->v.p.value.timestamp.milli)
                  res = 1;
            else
                  res = 0;
            break;

      case EF_RECORD_TYPE:
            if (record->type < expr->v.p.value.int_value)
                  res = -1;
            else if (record->type > expr->v.p.value.int_value)
                  res = 1;
            else
                  res = 0;
            break;

      default:
            abort();
      }
      *error = 0;
      return res;
}

/* Evaluate EXPR on RECORD in AU->le.
   Return 1 if EXPR is true, 0 if it false or if it fails.
   (No error reporting facility is provided; an invalid term is considered to
   be false; e.g. !invalid is true.) */
int
expr_eval(auparse_state_t *au, rnode *record, const struct expr *expr)
{
      switch (expr->op) {
      case EO_NOT:
            return !expr_eval(au, record, expr->v.sub[0]);

      case EO_AND:
            return (expr_eval(au, record, expr->v.sub[0])
                  && expr_eval(au, record, expr->v.sub[1]));

      case EO_OR:
            return (expr_eval(au, record, expr->v.sub[0])
                  || expr_eval(au, record, expr->v.sub[1]));

      case EO_RAW_EQ: case EO_RAW_NE: {
            int free_it, ne;
            char *value;

            value = eval_raw_value(au, record, expr, &free_it);
            if (value == NULL)
                  return 0;
            assert(expr->precomputed_value == 0);
            ne = strcmp(expr->v.p.value.string, value);
            if (free_it != 0)
                  free(value);
            return expr->op == EO_RAW_EQ ? ne == 0 : ne != 0;
      }

      case EO_INTERPRETED_EQ: case EO_INTERPRETED_NE: {
            int free_it, ne;
            char *value;

            value = eval_interpreted_value(au, record, expr, &free_it);
            if (value == NULL)
                  return 0;
            assert(expr->precomputed_value == 0);
            ne = strcmp(expr->v.p.value.string, value);
            if (free_it != 0)
                  free(value);
            return expr->op == EO_INTERPRETED_EQ ? ne == 0 : ne != 0;
      }

      case EO_VALUE_EQ: case EO_VALUE_NE: case EO_VALUE_LT: case EO_VALUE_LE:
      case EO_VALUE_GT: case EO_VALUE_GE: {
            int err, cmp;

            cmp = compare_values(au, record, expr, &err);
            if (err != 0)
                  return 0;
            switch (expr->op) {
            case EO_VALUE_EQ:
                  return cmp == 0;

            case EO_VALUE_NE:
                  return cmp != 0;

            case EO_VALUE_LT:
                  return cmp < 0;

            case EO_VALUE_LE:
                  return cmp <= 0;

            case EO_VALUE_GT:
                  return cmp > 0;

            case EO_VALUE_GE:
                  return cmp >= 0;

            default:
                  abort();
            }
      }

      case EO_FIELD_EXISTS:
            assert(expr->virtual_field == 0);
            nvlist_first(&record->nv);
            return nvlist_find_name(&record->nv, expr->v.p.field.name) != 0;

      default:
            abort();
      }
}

Generated by  Doxygen 1.6.0   Back to index