Audacious
$Id:Doxyfile42802007-03-2104:39:00Znenolod$
|
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_patricia_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 = NULL; 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 }