Audacious  $Id:Doxyfile42802007-03-2104:39:00Znenolod$
tuple_compiler.c
Go to the documentation of this file.
00001 /*
00002  * Audacious - Tuplez compiler
00003  * Copyright (c) 2007 Matti 'ccr' Hämäläinen
00004  *
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation; under version 3 of the License.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program.  If not, see <http://www.gnu.org/licenses>.
00016  *
00017  * The Audacious team does not consider modular code linking to
00018  * Audacious or using our public API to be a derived work.
00019  */
00020 
00021 /*
00022  * TODO:
00023  * - Unicode/UTF-8 support in format strings. using any non-ASCII
00024  *   characters in Tuplez format strings WILL cause things go boom
00025  *   at the moment!
00026  *
00027  * - implement definitions (${=foo,"baz"} ${=foo,1234})
00028  * - implement functions
00029  * - implement handling of external expressions
00030  * - evaluation context: how local variables should REALLY work?
00031  *   currently there is just a single context, is a "global" context needed?
00032  */
00033 
00034 #include "config.h"
00035 #include <assert.h>
00036 #include <stdarg.h>
00037 #include "tuple_compiler.h"
00038 
00039 #define MAX_STR         (256)
00040 #define MIN_ALLOC_NODES (8)
00041 #define MIN_ALLOC_BUF   (64)
00042 
00043 
00044 void tuple_error(TupleEvalContext *ctx, const gchar *fmt, ...)
00045 {
00046   va_list ap;
00047   g_free(ctx->errmsg);
00048   va_start(ap, fmt);
00049   ctx->errmsg = g_strdup_vprintf(fmt, ap);
00050   va_end(ap);
00051   ctx->iserror = TRUE;
00052 }
00053 
00054 
00055 static void tuple_evalctx_free_var(TupleEvalVar *var)
00056 {
00057   assert(var != NULL);
00058   var->fieldidx = -1;
00059   g_free(var->defvals);
00060   g_free(var->name);
00061   g_free(var);
00062 }
00063 
00064 
00065 static void tuple_evalctx_free_function(TupleEvalFunc *func)
00066 {
00067   assert(func != NULL);
00068 
00069   g_free(func->name);
00070   g_free(func);
00071 }
00072 
00073 
00074 /* Initialize an evaluation context
00075  */
00076 TupleEvalContext * tuple_evalctx_new(void)
00077 {
00078   return g_new0(TupleEvalContext, 1);
00079 }
00080 
00081 
00082 /* "Reset" the evaluation context, clean up temporary variables.
00083  */
00084 void tuple_evalctx_reset(TupleEvalContext *ctx)
00085 {
00086   gint i;
00087 
00088   for (i = 0; i < ctx->nvariables; i++)
00089     if (ctx->variables[i]) {
00090       ctx->variables[i]->fieldref = NULL;
00091 
00092       if (ctx->variables[i]->istemp)
00093         tuple_evalctx_free_var(ctx->variables[i]);
00094     }
00095 
00096   ctx->iserror = FALSE;
00097 }
00098 
00099 
00100 /* Free an evaluation context and associated data
00101  */
00102 void tuple_evalctx_free(TupleEvalContext *ctx)
00103 {
00104   gint i;
00105 
00106   if (!ctx) return;
00107 
00108   /* Deallocate variables */
00109   for (i = 0; i < ctx->nvariables; i++)
00110     if (ctx->variables[i])
00111       tuple_evalctx_free_var(ctx->variables[i]);
00112 
00113   g_free(ctx->variables);
00114 
00115   /* Deallocate functions */
00116   for (i = 0; i < ctx->nfunctions; i++)
00117     if (ctx->functions[i])
00118       tuple_evalctx_free_function(ctx->functions[i]);
00119 
00120   g_free(ctx->functions);
00121   g_free(ctx);
00122 }
00123 
00124 
00125 gint tuple_evalctx_add_var(TupleEvalContext *ctx, const gchar *name, const gboolean istemp, const gint type, const TupleValueType ctype)
00126 {
00127   gint i, ref = -1;
00128   TupleEvalVar *tmp = g_new0(TupleEvalVar, 1);
00129   assert(tmp != NULL);
00130 
00131   tmp->name = g_strdup(name);
00132   tmp->istemp = istemp;
00133   tmp->type = type;
00134   tmp->fieldidx = ref;
00135   tmp->ctype = ctype;
00136 
00137   /* Find fieldidx, if any */
00138   switch (type) {
00139     case TUPLE_VAR_FIELD:
00140       for (i = 0; i < FIELD_LAST && ref < 0; i++)
00141         if (strcmp(tuple_fields[i].name, name) == 0) ref = i;
00142 
00143         tmp->fieldidx = ref;
00144       break;
00145 
00146     case TUPLE_VAR_CONST:
00147       if (ctype == TUPLE_INT)
00148         tmp->defvali = atoi(name);
00149       break;
00150   }
00151 
00152   /* Find a free slot */
00153   for (i = 0; i < ctx->nvariables; i++)
00154   if (!ctx->variables[i]) {
00155     ctx->variables[i] = tmp;
00156     return i;
00157   }
00158 
00159   i = ctx->nvariables;
00160   ctx->variables = g_renew(TupleEvalVar *, ctx->variables, ctx->nvariables + MIN_ALLOC_NODES);
00161   memset(&(ctx->variables[ctx->nvariables]), 0, MIN_ALLOC_NODES * sizeof(TupleEvalVar *));
00162   ctx->nvariables += MIN_ALLOC_NODES;
00163   ctx->variables[i] = tmp;
00164 
00165   return i;
00166 }
00167 
00168 
00169 gint tuple_evalctx_add_function(TupleEvalContext *ctx, gchar *name)
00170 {
00171   assert(ctx != NULL);
00172   assert(name != NULL);
00173 
00174   return -1;
00175 }
00176 
00177 
00178 static void tuple_evalnode_insert(TupleEvalNode **nodes, TupleEvalNode *node)
00179 {
00180   assert(nodes != NULL);
00181   assert(node != NULL);
00182 
00183   if (*nodes) {
00184     node->prev = (*nodes)->prev;
00185     (*nodes)->prev->next = node;
00186     (*nodes)->prev = node;
00187     node->next = NULL;
00188   } else {
00189     *nodes = node;
00190     node->prev = node;
00191     node->next = NULL;
00192   }
00193 }
00194 
00195 
00196 static TupleEvalNode *tuple_evalnode_new(void)
00197 {
00198   return g_new0(TupleEvalNode, 1);
00199 }
00200 
00201 
00202 void tuple_evalnode_free(TupleEvalNode *expr)
00203 {
00204   TupleEvalNode *curr = expr, *next;
00205 
00206   while (curr) {
00207     next = curr->next;
00208 
00209     g_free(curr->text);
00210 
00211     if (curr->children)
00212       tuple_evalnode_free(curr->children);
00213 
00214     g_free(curr);
00215 
00216     curr = next;
00217   }
00218 }
00219 
00220 
00221 static TupleEvalNode *tuple_compiler_pass1(gint *level, TupleEvalContext *ctx, gchar **expression);
00222 
00223 
00224 static gboolean tc_get_item(TupleEvalContext *ctx,
00225     gchar **str, gchar *buf, gssize max,
00226     gchar endch, gboolean *literal, gchar *errstr, gchar *item)
00227 {
00228   gssize i = 0;
00229   gchar *s = *str, tmpendch;
00230   assert(str != NULL);
00231   assert(buf != NULL);
00232 
00233   if (*s == '"') {
00234     if (*literal == FALSE) {
00235       tuple_error(ctx, "Literal string value not allowed in '%s'.\n", item);
00236       return FALSE;
00237     }
00238     s++;
00239     *literal = TRUE;
00240     tmpendch = '"';
00241   } else {
00242     *literal = FALSE;
00243     tmpendch = endch;
00244   }
00245 
00246   if (*literal == FALSE) {
00247     while (*s != '\0' && *s != tmpendch && (isalnum(*s) || *s == '-') && i < (max - 1)) {
00248       buf[i++] = *(s++);
00249     }
00250 
00251     if (*s != tmpendch && *s != '}' && !isalnum(*s) && *s != '-') {
00252       tuple_error(ctx, "Invalid field '%s' in '%s'.\n", *str, item);
00253       return FALSE;
00254     } else if (*s != tmpendch) {
00255       tuple_error(ctx, "Expected '%c' in '%s'.\n", tmpendch, item);
00256       return FALSE;
00257     }
00258   } else {
00259     while (*s != '\0' && *s != tmpendch && i < (max - 1)) {
00260       if (*s == '\\') s++;
00261       buf[i++] = *(s++);
00262     }
00263   }
00264   buf[i] = '\0';
00265 
00266   if (*literal) {
00267     if (*s == tmpendch)
00268       s++;
00269     else {
00270       tuple_error(ctx, "Expected literal string end ('%c') in '%s'.\n", tmpendch, item);
00271       return FALSE;
00272     }
00273   }
00274 
00275   if (*s != endch) {
00276     tuple_error(ctx, "Expected '%c' after %s in '%s'\n", endch, errstr, item);
00277     return FALSE;
00278   } else {
00279     *str = s;
00280     return TRUE;
00281   }
00282 }
00283 
00284 
00285 static gint tc_get_variable(TupleEvalContext *ctx, gchar *name, gint type)
00286 {
00287   gint i;
00288   TupleValueType ctype = TUPLE_UNKNOWN;
00289 
00290   if (name == '\0') return -1;
00291 
00292   if (isdigit(name[0])) {
00293     ctype = TUPLE_INT;
00294     type = TUPLE_VAR_CONST;
00295   } else
00296     ctype = TUPLE_STRING;
00297 
00298   if (type != TUPLE_VAR_CONST) {
00299     for (i = 0; i < ctx->nvariables; i++)
00300       if (ctx->variables[i] && !strcmp(ctx->variables[i]->name, name))
00301         return i;
00302   }
00303 
00304   return tuple_evalctx_add_var(ctx, name, FALSE, type, ctype);
00305 }
00306 
00307 
00308 static gboolean tc_parse_construct(TupleEvalContext *ctx, TupleEvalNode **res, gchar *item, gchar **c, gint *level, gint opcode)
00309 {
00310   gchar tmps1[MAX_STR], tmps2[MAX_STR];
00311   gboolean literal1 = TRUE, literal2 = TRUE;
00312 
00313   (*c)++;
00314   if (tc_get_item(ctx, c, tmps1, MAX_STR, ',', &literal1, "tag1", item)) {
00315     (*c)++;
00316     if (tc_get_item(ctx, c, tmps2, MAX_STR, ':', &literal2, "tag2", item)) {
00317       TupleEvalNode *tmp = tuple_evalnode_new();
00318       (*c)++;
00319 
00320       tmp->opcode = opcode;
00321       if ((tmp->var[0] = tc_get_variable(ctx, tmps1, literal1 ? TUPLE_VAR_CONST : TUPLE_VAR_FIELD)) < 0) {
00322         tuple_evalnode_free(tmp);
00323         tuple_error(ctx, "Invalid variable '%s' in '%s'.\n", tmps1, item);
00324         return FALSE;
00325       }
00326       if ((tmp->var[1] = tc_get_variable(ctx, tmps2, literal2 ? TUPLE_VAR_CONST : TUPLE_VAR_FIELD)) < 0) {
00327         tuple_evalnode_free(tmp);
00328         tuple_error(ctx, "Invalid variable '%s' in '%s'.\n", tmps2, item);
00329         return FALSE;
00330       }
00331       tmp->children = tuple_compiler_pass1(level, ctx, c);
00332       tuple_evalnode_insert(res, tmp);
00333     } else
00334       return FALSE;
00335   } else
00336     return FALSE;
00337 
00338   return TRUE;
00339 }
00340 
00341 
00342 /* Compile format expression into TupleEvalNode tree.
00343  * A "simple" straight compilation is sufficient in first pass, later
00344  * passes can perform subexpression removal and other optimizations.
00345  */
00346 static TupleEvalNode *tuple_compiler_pass1(gint *level, TupleEvalContext *ctx, gchar **expression)
00347 {
00348   TupleEvalNode *res = NULL, *tmp = NULL;
00349   gchar *c = *expression, *item, tmps1[MAX_STR];
00350   gboolean literal, end = FALSE;
00351   assert(ctx != NULL);
00352   assert(expression != NULL);
00353 
00354   (*level)++;
00355 
00356   while (*c != '\0' && !end) {
00357     tmp = NULL;
00358     if (*c == '}') {
00359       c++;
00360       (*level)--;
00361       end = TRUE;
00362     } else if (*c == '$') {
00363       /* Expression? */
00364       item = c++;
00365       if (*c == '{') {
00366         gint opcode;
00367         gchar *expr = ++c;
00368 
00369         switch (*c) {
00370           case '?': c++;
00371             /* Exists? */
00372             literal = FALSE;
00373             if (tc_get_item(ctx, &c, tmps1, MAX_STR, ':', &literal, "tag", item)) {
00374               c++;
00375               tmp = tuple_evalnode_new();
00376               tmp->opcode = OP_EXISTS;
00377               if ((tmp->var[0] = tc_get_variable(ctx, tmps1, TUPLE_VAR_FIELD)) < 0) {
00378                 tuple_error(ctx, "Invalid variable '%s' in '%s'.\n", tmps1, expr);
00379                 goto ret_error;
00380               }
00381               tmp->children = tuple_compiler_pass1(level, ctx, &c);
00382               tuple_evalnode_insert(&res, tmp);
00383             } else
00384               goto ret_error;
00385             break;
00386 
00387           case '=': c++;
00388             if (*c != '=') {
00389               /* Definition */
00390               literal = FALSE;
00391               if (tc_get_item(ctx, &c, tmps1, MAX_STR, ',', &literal, "variable", item)) {
00392                 c++;
00393                 if (*c == '"') {
00394                   /* String */
00395                   c++;
00396                 } else if (isdigit(*c)) {
00397                   /* Integer */
00398                 }
00399 
00400                 tuple_error(ctx, "Definitions are not yet supported!\n");
00401                 goto ret_error;
00402               } else
00403                 goto ret_error;
00404             } else {
00405               /* Equals? */
00406               if (!tc_parse_construct(ctx, &res, item, &c, level, OP_EQUALS))
00407                 goto ret_error;
00408             }
00409             break;
00410 
00411           case '!': c++;
00412             if (*c != '=') goto ext_expression;
00413             if (!tc_parse_construct(ctx, &res, item, &c, level, OP_NOT_EQUALS))
00414               goto ret_error;
00415             break;
00416 
00417           case '<': c++;
00418             if (*c == '=') {
00419               opcode = OP_LTEQ;
00420               c++;
00421             } else
00422               opcode = OP_LT;
00423 
00424             if (!tc_parse_construct(ctx, &res, item, &c, level, opcode))
00425               goto ret_error;
00426             break;
00427 
00428           case '>': c++;
00429             if (*c == '=') {
00430               opcode = OP_GTEQ;
00431               c++;
00432             } else
00433               opcode = OP_GT;
00434 
00435             if (!tc_parse_construct(ctx, &res, item, &c, level, opcode))
00436               goto ret_error;
00437             break;
00438 
00439           case '(': c++;
00440             if (!strncmp(c, "empty)?", 7)) {
00441               c += 7;
00442               literal = FALSE;
00443               if (tc_get_item(ctx, &c, tmps1, MAX_STR, ':', &literal, "tag", item)) {
00444                 c++;
00445                 tmp = tuple_evalnode_new();
00446                 tmp->opcode = OP_IS_EMPTY;
00447                 if ((tmp->var[0] = tc_get_variable(ctx, tmps1, TUPLE_VAR_FIELD)) < 0) {
00448                   tuple_error(ctx, "Invalid variable '%s' in '%s'.\n", tmps1, expr);
00449                   goto ret_error;
00450                 }
00451                 tmp->children = tuple_compiler_pass1(level, ctx, &c);
00452                 tuple_evalnode_insert(&res, tmp);
00453               } else
00454                 goto ret_error;
00455             } else
00456               goto ext_expression;
00457             break;
00458 
00459           default:
00460           ext_expression:
00461             /* Get expression content */
00462             c = expr;
00463             literal = FALSE;
00464             if (tc_get_item(ctx, &c, tmps1, MAX_STR, '}', &literal, "field", item)) {
00465               /* FIXME!! FIX ME! Check for external expressions */
00466 
00467               /* I HAS A FIELD - A field. You has it. */
00468               tmp = tuple_evalnode_new();
00469               tmp->opcode = OP_FIELD;
00470               if ((tmp->var[0] = tc_get_variable(ctx, tmps1, TUPLE_VAR_FIELD)) < 0) {
00471                 tuple_error(ctx, "Invalid variable '%s' in '%s'.\n", tmps1, expr);
00472                 goto ret_error;
00473               }
00474               tuple_evalnode_insert(&res, tmp);
00475               c++;
00476 
00477             } else
00478               goto ret_error;
00479         }
00480       } else {
00481         tuple_error(ctx, "Expected '{', got '%c' in '%s'.\n", *c, c);
00482         goto ret_error;
00483       }
00484 
00485     } else if (*c == '%') {
00486       /* Function? */
00487       item = c++;
00488       if (*c == '{') {
00489         gssize i = 0;
00490         c++;
00491 
00492         while (*c != '\0' && (isalnum(*c) || *c == '-') && *c != '}' && *c != ':' && i < (MAX_STR - 1))
00493           tmps1[i++] = *(c++);
00494         tmps1[i] = '\0';
00495 
00496         if (*c == ':') {
00497           c++;
00498         } else if (*c == '}') {
00499           c++;
00500         } else if (*c == '\0') {
00501           tuple_error(ctx, "Expected '}' or function arguments in '%s'\n", item);
00502           goto ret_error;
00503         }
00504       } else {
00505         tuple_error(ctx, "Expected '{', got '%c' in '%s'.\n", *c, c);
00506         goto ret_error;
00507       }
00508     } else {
00509       /* Parse raw/literal text */
00510       gssize i = 0;
00511       while (*c != '\0' && *c != '$' && *c != '%' && *c != '}' && i < (MAX_STR - 1)) {
00512         if (*c == '\\') c++;
00513         tmps1[i++] = *(c++);
00514       }
00515       tmps1[i] = '\0';
00516 
00517       tmp = tuple_evalnode_new();
00518       tmp->opcode = OP_RAW;
00519       tmp->text = g_strdup(tmps1);
00520       tuple_evalnode_insert(&res, tmp);
00521     }
00522   }
00523 
00524   if (*level <= 0) {
00525     tuple_error(ctx, "Syntax error! Uneven/unmatched nesting of elements in '%s'!\n", c);
00526     goto ret_error;
00527   }
00528 
00529   *expression = c;
00530   return res;
00531 
00532 ret_error:
00533   tuple_evalnode_free(tmp);
00534   tuple_evalnode_free(res);
00535   return NULL;
00536 }
00537 
00538 
00539 static TupleEvalNode *tuple_compiler_pass2(gboolean *changed, TupleEvalContext *ctx, TupleEvalNode *expr)
00540 {
00541   /* TupleEvalNode *curr = expr; */
00542   TupleEvalNode *res = NULL;
00543   assert(ctx != NULL);
00544 
00545   return res;
00546 }
00547 
00548 
00549 TupleEvalNode *tuple_formatter_compile(TupleEvalContext *ctx, gchar *expr)
00550 {
00551   gint level = 0;
00552   gboolean changed = FALSE;
00553   gchar *tmpexpr = expr;
00554   TupleEvalNode *res1, *res2;
00555 
00556   res1 = tuple_compiler_pass1(&level, ctx, &tmpexpr);
00557 
00558   if (level != 1) {
00559     tuple_error(ctx, "Syntax error! Uneven/unmatched nesting of elements! (%d)\n", level);
00560     tuple_evalnode_free(res1);
00561     return NULL;
00562   }
00563 
00564   res2 = tuple_compiler_pass2(&changed, ctx, res1);
00565 
00566   if (changed) {
00567     tuple_evalnode_free(res1);
00568     return res2;
00569   } else {
00570     tuple_evalnode_free(res2);
00571     return res1;
00572   }
00573 }
00574 
00575 
00576 /* Fetch a reference to a tuple field for given variable by fieldidx or dict.
00577  * Return pointer to field, NULL if not available.
00578  */
00579 static TupleValue * tf_get_fieldref (TupleEvalVar * var, const Tuple * tuple)
00580 {
00581   if (var->type == TUPLE_VAR_FIELD && var->fieldref == NULL) {
00582     if (var->fieldidx < 0)
00583       var->fieldref = mowgli_dictionary_retrieve(tuple->dict, var->name);
00584     else
00585       var->fieldref = tuple->values[var->fieldidx];
00586   }
00587 
00588   return var->fieldref;
00589 }
00590 
00591 
00592 /* Fetch string or int value of given variable, whatever type it might be.
00593  * Return VAR_* type for the variable.
00594  */
00595 static TupleValueType tf_get_var (gchar * * tmps, gint * tmpi, TupleEvalVar *
00596  var, const Tuple * tuple)
00597 {
00598   TupleValueType type = TUPLE_UNKNOWN;
00599   *tmps = NULL;
00600   *tmpi = 0;
00601 
00602   switch (var->type) {
00603     case TUPLE_VAR_DEF:
00604       switch (var->ctype) {
00605         case TUPLE_STRING: *tmps = var->defvals; break;
00606         case TUPLE_INT: *tmpi = var->defvali; break;
00607         default: /* Possible, but should be prevented elsewhere */ break;
00608       }
00609       type = var->ctype;
00610       break;
00611 
00612     case TUPLE_VAR_CONST:
00613       switch (var->ctype) {
00614         case TUPLE_STRING: *tmps = var->name; break;
00615         case TUPLE_INT: *tmpi = var->defvali; break;
00616         default: /* Cannot happen */ break;
00617       }
00618       type = var->ctype;
00619       break;
00620 
00621     case TUPLE_VAR_FIELD:
00622       if (tf_get_fieldref(var, tuple)) {
00623         if (var->fieldref->type == TUPLE_STRING)
00624           *tmps = var->fieldref->value.string;
00625         else
00626           *tmpi = var->fieldref->value.integer;
00627         type = var->fieldref->type;
00628       }
00629       break;
00630   }
00631 
00632   return type;
00633 }
00634 
00635 
00636 /* Evaluate tuple in given TupleEval expression in given
00637  * context and return resulting string.
00638  */
00639 static gboolean tuple_formatter_eval_do (TupleEvalContext * ctx, TupleEvalNode *
00640  expr, const Tuple * tuple, gchar * * res, gssize * resmax, gssize * reslen)
00641 {
00642   TupleEvalNode *curr = expr;
00643   TupleEvalVar *var0, *var1;
00644   TupleValueType type0, type1;
00645   gint tmpi0, tmpi1;
00646   gchar tmps[MAX_STR], *tmps0, *tmps1, *tmps2;
00647   gboolean result;
00648   gint resulti;
00649 
00650   if (!expr) return FALSE;
00651 
00652   while (curr) {
00653     const gchar *str = NULL;
00654 
00655     switch (curr->opcode) {
00656       case OP_RAW:
00657         str = curr->text;
00658         break;
00659 
00660       case OP_FIELD:
00661         var0 = ctx->variables[curr->var[0]];
00662 
00663         switch (var0->type) {
00664           case TUPLE_VAR_DEF:
00665             switch (var0->ctype) {
00666               case TUPLE_STRING:
00667                 str = var0->defvals;
00668                 break;
00669 
00670               case TUPLE_INT:
00671                 g_snprintf(tmps, sizeof(tmps), "%d", var0->defvali);
00672                 str = tmps;
00673                 break;
00674 
00675               default:
00676                 break;
00677             }
00678             break;
00679 
00680           case TUPLE_VAR_FIELD:
00681             if (tf_get_fieldref(var0, tuple)) {
00682               switch (var0->fieldref->type) {
00683                 case TUPLE_STRING:
00684                   str = var0->fieldref->value.string;
00685                   break;
00686 
00687                 case TUPLE_INT:
00688                   g_snprintf(tmps, sizeof(tmps), "%d", var0->fieldref->value.integer);
00689                   str = tmps;
00690                   break;
00691 
00692                 default:
00693                   str = NULL;
00694               }
00695             }
00696             break;
00697         }
00698         break;
00699 
00700       case OP_EQUALS:
00701       case OP_NOT_EQUALS:
00702       case OP_LT: case OP_LTEQ:
00703       case OP_GT: case OP_GTEQ:
00704         var0 = ctx->variables[curr->var[0]];
00705         var1 = ctx->variables[curr->var[1]];
00706 
00707         type0 = tf_get_var(&tmps0, &tmpi0, var0, tuple);
00708         type1 = tf_get_var(&tmps1, &tmpi1, var1, tuple);
00709         result = FALSE;
00710 
00711         if (type0 != TUPLE_UNKNOWN && type1 != TUPLE_UNKNOWN) {
00712           if (type0 == type1) {
00713             if (type0 == TUPLE_STRING)
00714               resulti = strcmp(tmps0, tmps1);
00715             else
00716               resulti = tmpi0 - tmpi1;
00717           } else {
00718             if (type0 == TUPLE_INT)
00719               resulti = tmpi0 - atoi(tmps1);
00720             else
00721               resulti = atoi(tmps0) - tmpi1;
00722           }
00723 
00724           switch (curr->opcode) {
00725             case OP_EQUALS:     result = (resulti == 0); break;
00726             case OP_NOT_EQUALS: result = (resulti != 0); break;
00727             case OP_LT:         result = (resulti <  0); break;
00728             case OP_LTEQ:       result = (resulti <= 0); break;
00729             case OP_GT:         result = (resulti >  0); break;
00730             case OP_GTEQ:       result = (resulti >= 0); break;
00731           default:            result = FALSE;
00732           }
00733         }
00734 
00735         if (result && !tuple_formatter_eval_do(ctx, curr->children, tuple, res, resmax, reslen))
00736           return FALSE;
00737         break;
00738 
00739       case OP_EXISTS:
00740 #ifdef NO_EXISTS_HACK
00741         if (tf_get_fieldref(ctx->variables[curr->var[0]], tuple)) {
00742           if (!tuple_formatter_eval_do(ctx, curr->children, tuple, res, resmax, reslen))
00743             return FALSE;
00744         }
00745         break;
00746 #endif
00747 
00748       case OP_IS_EMPTY:
00749         var0 = ctx->variables[curr->var[0]];
00750 
00751         if (tf_get_fieldref(var0, tuple)) {
00752 
00753           switch (var0->fieldref->type) {
00754           case TUPLE_INT:
00755             result = (var0->fieldref->value.integer == 0);
00756             break;
00757 
00758           case TUPLE_STRING:
00759             result = TRUE;
00760             tmps2 = var0->fieldref->value.string;
00761 
00762             while (result && tmps2 && *tmps2 != '\0') {
00763               gunichar uc = g_utf8_get_char(tmps2);
00764               if (g_unichar_isspace(uc))
00765                 tmps2 = g_utf8_next_char(tmps2);
00766               else
00767                 result = FALSE;
00768             }
00769             break;
00770 
00771           default:
00772             result = TRUE;
00773           }
00774         } else
00775           result = TRUE;
00776 
00777 #ifdef NO_EXISTS_HACK
00778         if (result && !tuple_formatter_eval_do(ctx, curr->children, tuple, res, resmax, reslen))
00779           return FALSE;
00780 #else
00781         if ((curr->opcode == OP_EXISTS && !result) || (curr->opcode == OP_IS_EMPTY && result)) {
00782           if (!tuple_formatter_eval_do(ctx, curr->children, tuple, res, resmax, reslen))
00783             return FALSE;
00784         }
00785 #endif
00786         break;
00787 
00788       default:
00789         tuple_error(ctx, "Unimplemented opcode %d!\n", curr->opcode);
00790         return FALSE;
00791         break;
00792     }
00793 
00794     if (str) {
00795       /* (Re)allocate res for more space, if needed. */
00796       *reslen += strlen(str);
00797       if (*res) {
00798         if (*reslen >= *resmax) {
00799           *resmax += *reslen + MIN_ALLOC_BUF;
00800           *res = g_realloc(*res, *resmax);
00801         }
00802 
00803         strcat(*res, str);
00804       } else {
00805         *resmax = *reslen + MIN_ALLOC_BUF;
00806         *res = g_malloc(*resmax);
00807 
00808         g_strlcpy(*res, str, *resmax);
00809       }
00810     }
00811 
00812     curr = curr->next;
00813   }
00814 
00815   return TRUE;
00816 }
00817 
00818 
00819 gchar * tuple_formatter_eval (TupleEvalContext * ctx, TupleEvalNode * expr,
00820  const Tuple * tuple)
00821 {
00822   gchar *res = g_strdup("");
00823   gssize resmax = 0, reslen = 0;
00824   assert(ctx != NULL);
00825   assert(tuple != NULL);
00826 
00827   if (!expr) return res;
00828 
00829   tuple_formatter_eval_do(ctx, expr, tuple, &res, &resmax, &reslen);
00830 
00831   return res;
00832 }
00833 
00834 
00835 static void print_vars(FILE *f, TupleEvalContext *ctx, TupleEvalNode *node, gint start, gint end)
00836 {
00837   gint i;
00838 
00839   for (i = start; i <= end; i++) {
00840     TupleEvalVar *v = NULL;
00841     gchar *s = NULL;
00842     gint n = node->var[i];
00843 
00844     if (n >= 0) {
00845       v = ctx->variables[n];
00846       if (v) {
00847         s = v->name;
00848 
00849         if (v->type == TUPLE_VAR_CONST)
00850           fprintf(f, "(const)");
00851         else if (v->type == TUPLE_VAR_DEF)
00852           fprintf(f, "(def)");
00853       }
00854     }
00855 
00856     fprintf(f, "var[%d]=(%d),\"%s\" ", i, n, s);
00857   }
00858 }
00859 
00860 
00861 gint tuple_formatter_print(FILE *f, gint *level, TupleEvalContext *ctx, TupleEvalNode *expr)
00862 {
00863   TupleEvalNode *curr = expr;
00864 
00865   if (!expr) return -1;
00866 
00867   (*level)++;
00868 
00869   while (curr) {
00870     gint i;
00871     for (i = 0; i < *level; i++)
00872       fprintf(f, "  ");
00873 
00874     switch (curr->opcode) {
00875       case OP_RAW:
00876         fprintf(f, "OP_RAW text=\"%s\"\n", curr->text);
00877         break;
00878 
00879       case OP_FIELD:
00880         fprintf(f, "OP_FIELD ");
00881         print_vars(f, ctx, curr, 0, 0);
00882         fprintf(f, "\n");
00883         break;
00884 
00885       case OP_EXISTS:
00886         fprintf(f, "OP_EXISTS ");
00887         print_vars(f, ctx, curr, 0, 0);
00888         fprintf(f, "\n");
00889         tuple_formatter_print(f, level, ctx, curr->children);
00890         break;
00891 
00892       case OP_DEF_STRING:
00893         fprintf(f, "OP_DEF_STRING ");
00894         fprintf(f, "\n");
00895         break;
00896 
00897       case OP_DEF_INT:
00898         fprintf(f, "OP_DEF_INT ");
00899         fprintf(f, "\n");
00900         break;
00901 
00902       case OP_EQUALS:
00903         fprintf(f, "OP_EQUALS ");
00904         print_vars(f, ctx, curr, 0, 1);
00905         fprintf(f, "\n");
00906         tuple_formatter_print(f, level, ctx, curr->children);
00907         break;
00908 
00909       case OP_NOT_EQUALS:
00910         fprintf(f, "OP_NOT_EQUALS ");
00911         print_vars(f, ctx, curr, 0, 1);
00912         fprintf(f, "\n");
00913         tuple_formatter_print(f, level, ctx, curr->children);
00914         break;
00915 
00916       case OP_IS_EMPTY:
00917         fprintf(f, "OP_IS_EMPTY ");
00918         print_vars(f, ctx, curr, 0, 0);
00919         fprintf(f, "\n");
00920         tuple_formatter_print(f, level, ctx, curr->children);
00921         break;
00922 
00923       default:
00924         fprintf(f, "Unimplemented opcode %d!\n", curr->opcode);
00925         break;
00926     }
00927 
00928     curr = curr->next;
00929   }
00930 
00931   (*level)--;
00932 
00933   return 0;
00934 }