Audacious $Id:Doxyfile42802007-03-2104:39:00Znenolod$
|
00001 /* 00002 * Audacious - Tuplez compiler 00003 * Copyright (c) 2007 Matti 'ccr' Hämäläinen 00004 * Copyright (c) 2011 John Lindgren 00005 * 00006 * This program is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU General Public License as published by 00008 * the Free Software Foundation; under version 3 of the License. 00009 * 00010 * This program is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 * GNU General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU General Public License 00016 * along with this program. If not, see <http://www.gnu.org/licenses>. 00017 * 00018 * The Audacious team does not consider modular code linking to 00019 * Audacious or using our public API to be a derived work. 00020 */ 00021 00022 /* 00023 * TODO: 00024 * - Unicode/UTF-8 support in format strings. using any non-ASCII 00025 * characters in Tuplez format strings WILL cause things go boom 00026 * at the moment! 00027 * 00028 * - implement definitions (${=foo,"baz"} ${=foo,1234}) 00029 * - implement functions 00030 * - implement handling of external expressions 00031 * - evaluation context: how local variables should REALLY work? 00032 * currently there is just a single context, is a "global" context needed? 00033 */ 00034 00035 #include <ctype.h> 00036 #include <stdarg.h> 00037 #include <stdlib.h> 00038 #include <stdio.h> 00039 #include <string.h> 00040 00041 #include <glib.h> 00042 00043 #include "tuple_compiler.h" 00044 00045 #define MAX_STR (256) 00046 #define MIN_ALLOC_NODES (8) 00047 #define MIN_ALLOC_BUF (64) 00048 #define TUPLEZ_MAX_VARS (4) 00049 00050 #define tuple_error(ctx, ...) fprintf (stderr, "Tuple compiler: " __VA_ARGS__) 00051 00052 enum { 00053 OP_RAW = 0, /* plain text */ 00054 OP_FIELD, /* a field/variable */ 00055 OP_EXISTS, 00056 OP_EQUALS, 00057 OP_NOT_EQUALS, 00058 OP_GT, 00059 OP_GTEQ, 00060 OP_LT, 00061 OP_LTEQ, 00062 OP_IS_EMPTY 00063 }; 00064 00065 enum { 00066 TUPLE_VAR_FIELD = 0, 00067 TUPLE_VAR_CONST 00068 }; 00069 00070 struct _TupleEvalNode { 00071 int opcode; /* operator, see OP_ enums */ 00072 int var[TUPLEZ_MAX_VARS]; /* tuple variable references */ 00073 char *text; /* raw text, if any (OP_RAW) */ 00074 struct _TupleEvalNode *children, *next, *prev; /* children of this struct, and pointer to next node. */ 00075 }; 00076 00077 typedef struct { 00078 char *name; 00079 int type; /* Type of variable, see VAR_* */ 00080 int defvali; 00081 TupleValueType ctype; /* Type of constant/def value */ 00082 00083 int fieldidx; /* if >= 0: Index # of "pre-defined" Tuple fields */ 00084 bool_t fieldread, fieldvalid; 00085 char * fieldstr; 00086 } TupleEvalVar; 00087 00088 struct _TupleEvalContext { 00089 int nvariables; 00090 TupleEvalVar **variables; 00091 }; 00092 00093 00094 static void tuple_evalctx_free_var(TupleEvalVar *var) 00095 { 00096 g_free(var->name); 00097 str_unref (var->fieldstr); 00098 g_free(var); 00099 } 00100 00101 00102 /* Initialize an evaluation context 00103 */ 00104 TupleEvalContext * tuple_evalctx_new(void) 00105 { 00106 return g_new0(TupleEvalContext, 1); 00107 } 00108 00109 00110 /* "Reset" the evaluation context 00111 */ 00112 void tuple_evalctx_reset(TupleEvalContext *ctx) 00113 { 00114 int i; 00115 00116 for (i = 0; i < ctx->nvariables; i++) 00117 if (ctx->variables[i]) { 00118 ctx->variables[i]->fieldread = FALSE; 00119 ctx->variables[i]->fieldvalid = FALSE; 00120 str_unref (ctx->variables[i]->fieldstr); 00121 ctx->variables[i]->fieldstr = NULL; 00122 } 00123 } 00124 00125 00126 /* Free an evaluation context and associated data 00127 */ 00128 void tuple_evalctx_free(TupleEvalContext *ctx) 00129 { 00130 int i; 00131 00132 if (!ctx) return; 00133 00134 /* Deallocate variables */ 00135 for (i = 0; i < ctx->nvariables; i++) 00136 if (ctx->variables[i]) 00137 tuple_evalctx_free_var(ctx->variables[i]); 00138 00139 g_free(ctx->variables); 00140 g_free(ctx); 00141 } 00142 00143 00144 static int tuple_evalctx_add_var (TupleEvalContext * ctx, const char * name, 00145 const int type, const TupleValueType ctype) 00146 { 00147 int i; 00148 TupleEvalVar *tmp = g_new0(TupleEvalVar, 1); 00149 00150 tmp->name = g_strdup(name); 00151 tmp->type = type; 00152 tmp->fieldidx = -1; 00153 tmp->ctype = ctype; 00154 00155 /* Find fieldidx, if any */ 00156 switch (type) { 00157 case TUPLE_VAR_FIELD: 00158 tmp->fieldidx = tuple_field_by_name (name); 00159 tmp->ctype = tuple_field_get_type (tmp->fieldidx); 00160 break; 00161 00162 case TUPLE_VAR_CONST: 00163 if (ctype == TUPLE_INT) 00164 tmp->defvali = atoi(name); 00165 break; 00166 } 00167 00168 /* Find a free slot */ 00169 for (i = 0; i < ctx->nvariables; i++) 00170 if (!ctx->variables[i]) { 00171 ctx->variables[i] = tmp; 00172 return i; 00173 } 00174 00175 i = ctx->nvariables; 00176 ctx->variables = g_renew(TupleEvalVar *, ctx->variables, ctx->nvariables + MIN_ALLOC_NODES); 00177 memset(&(ctx->variables[ctx->nvariables]), 0, MIN_ALLOC_NODES * sizeof(TupleEvalVar *)); 00178 ctx->nvariables += MIN_ALLOC_NODES; 00179 ctx->variables[i] = tmp; 00180 00181 return i; 00182 } 00183 00184 00185 static void tuple_evalnode_insert(TupleEvalNode **nodes, TupleEvalNode *node) 00186 { 00187 if (*nodes) { 00188 node->prev = (*nodes)->prev; 00189 (*nodes)->prev->next = node; 00190 (*nodes)->prev = node; 00191 node->next = NULL; 00192 } else { 00193 *nodes = node; 00194 node->prev = node; 00195 node->next = NULL; 00196 } 00197 } 00198 00199 00200 static TupleEvalNode *tuple_evalnode_new(void) 00201 { 00202 return g_new0(TupleEvalNode, 1); 00203 } 00204 00205 00206 void tuple_evalnode_free(TupleEvalNode *expr) 00207 { 00208 TupleEvalNode *curr = expr, *next; 00209 00210 while (curr) { 00211 next = curr->next; 00212 00213 g_free(curr->text); 00214 00215 if (curr->children) 00216 tuple_evalnode_free(curr->children); 00217 00218 g_free(curr); 00219 00220 curr = next; 00221 } 00222 } 00223 00224 00225 static TupleEvalNode *tuple_compiler_pass1(int *level, TupleEvalContext *ctx, char **expression); 00226 00227 00228 static bool_t tc_get_item(TupleEvalContext *ctx, 00229 char **str, char *buf, gssize max, 00230 char endch, bool_t *literal, char *errstr, char *item) 00231 { 00232 gssize i = 0; 00233 char *s = *str, tmpendch; 00234 00235 if (*s == '"') { 00236 if (*literal == FALSE) { 00237 tuple_error(ctx, "Literal string value not allowed in '%s'.\n", item); 00238 return FALSE; 00239 } 00240 s++; 00241 *literal = TRUE; 00242 tmpendch = '"'; 00243 } else { 00244 *literal = FALSE; 00245 tmpendch = endch; 00246 } 00247 00248 if (*literal == FALSE) { 00249 while (*s != '\0' && *s != tmpendch && (isalnum(*s) || *s == '-') && i < (max - 1)) { 00250 buf[i++] = *(s++); 00251 } 00252 00253 if (*s != tmpendch && *s != '}' && !isalnum(*s) && *s != '-') { 00254 tuple_error(ctx, "Invalid field '%s' in '%s'.\n", *str, item); 00255 return FALSE; 00256 } else if (*s != tmpendch) { 00257 tuple_error(ctx, "Expected '%c' in '%s'.\n", tmpendch, item); 00258 return FALSE; 00259 } 00260 } else { 00261 while (*s != '\0' && *s != tmpendch && i < (max - 1)) { 00262 if (*s == '\\') s++; 00263 buf[i++] = *(s++); 00264 } 00265 } 00266 buf[i] = '\0'; 00267 00268 if (*literal) { 00269 if (*s == tmpendch) 00270 s++; 00271 else { 00272 tuple_error(ctx, "Expected literal string end ('%c') in '%s'.\n", tmpendch, item); 00273 return FALSE; 00274 } 00275 } 00276 00277 if (*s != endch) { 00278 tuple_error(ctx, "Expected '%c' after %s in '%s'\n", endch, errstr, item); 00279 return FALSE; 00280 } else { 00281 *str = s; 00282 return TRUE; 00283 } 00284 } 00285 00286 00287 static int tc_get_variable(TupleEvalContext *ctx, char *name, int type) 00288 { 00289 int i; 00290 TupleValueType ctype = TUPLE_UNKNOWN; 00291 00292 if (name == '\0') return -1; 00293 00294 if (isdigit(name[0])) { 00295 ctype = TUPLE_INT; 00296 type = TUPLE_VAR_CONST; 00297 } else 00298 ctype = TUPLE_STRING; 00299 00300 if (type != TUPLE_VAR_CONST) { 00301 for (i = 0; i < ctx->nvariables; i++) 00302 if (ctx->variables[i] && !strcmp(ctx->variables[i]->name, name)) 00303 return i; 00304 } 00305 00306 return tuple_evalctx_add_var(ctx, name, type, ctype); 00307 } 00308 00309 00310 static bool_t tc_parse_construct(TupleEvalContext *ctx, TupleEvalNode **res, char *item, char **c, int *level, int opcode) 00311 { 00312 char tmps1[MAX_STR], tmps2[MAX_STR]; 00313 bool_t literal1 = TRUE, literal2 = TRUE; 00314 00315 (*c)++; 00316 if (tc_get_item(ctx, c, tmps1, MAX_STR, ',', &literal1, "tag1", item)) { 00317 (*c)++; 00318 if (tc_get_item(ctx, c, tmps2, MAX_STR, ':', &literal2, "tag2", item)) { 00319 TupleEvalNode *tmp = tuple_evalnode_new(); 00320 (*c)++; 00321 00322 tmp->opcode = opcode; 00323 if ((tmp->var[0] = tc_get_variable(ctx, tmps1, literal1 ? TUPLE_VAR_CONST : TUPLE_VAR_FIELD)) < 0) { 00324 tuple_evalnode_free(tmp); 00325 tuple_error(ctx, "Invalid variable '%s' in '%s'.\n", tmps1, item); 00326 return FALSE; 00327 } 00328 if ((tmp->var[1] = tc_get_variable(ctx, tmps2, literal2 ? TUPLE_VAR_CONST : TUPLE_VAR_FIELD)) < 0) { 00329 tuple_evalnode_free(tmp); 00330 tuple_error(ctx, "Invalid variable '%s' in '%s'.\n", tmps2, item); 00331 return FALSE; 00332 } 00333 tmp->children = tuple_compiler_pass1(level, ctx, c); 00334 tuple_evalnode_insert(res, tmp); 00335 } else 00336 return FALSE; 00337 } else 00338 return FALSE; 00339 00340 return TRUE; 00341 } 00342 00343 00344 /* Compile format expression into TupleEvalNode tree. 00345 * A "simple" straight compilation is sufficient in first pass, later 00346 * passes can perform subexpression removal and other optimizations. 00347 */ 00348 static TupleEvalNode *tuple_compiler_pass1(int *level, TupleEvalContext *ctx, char **expression) 00349 { 00350 TupleEvalNode *res = NULL, *tmp = NULL; 00351 char *c = *expression, *item, tmps1[MAX_STR]; 00352 bool_t literal, end = FALSE; 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 int opcode; 00367 char *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 TupleEvalNode *tuple_formatter_compile(TupleEvalContext *ctx, char *expr) 00540 { 00541 int level = 0; 00542 char *tmpexpr = expr; 00543 TupleEvalNode *res1; 00544 00545 res1 = tuple_compiler_pass1(&level, ctx, &tmpexpr); 00546 00547 if (level != 1) { 00548 tuple_error(ctx, "Syntax error! Uneven/unmatched nesting of elements! (%d)\n", level); 00549 tuple_evalnode_free(res1); 00550 return NULL; 00551 } 00552 00553 return res1; 00554 } 00555 00556 00557 /* Fetch a tuple field value. Return TRUE if found. */ 00558 static bool_t tf_get_fieldval (TupleEvalVar * var, const Tuple * tuple) 00559 { 00560 if (var->type != TUPLE_VAR_FIELD || var->fieldidx < 0) 00561 return FALSE; 00562 00563 if (var->fieldread) 00564 return var->fieldvalid; 00565 00566 if (tuple_get_value_type (tuple, var->fieldidx, NULL) != var->ctype) { 00567 var->fieldread = TRUE; 00568 var->fieldvalid = FALSE; 00569 return FALSE; 00570 } 00571 00572 if (var->ctype == TUPLE_INT) 00573 var->defvali = tuple_get_int (tuple, var->fieldidx, NULL); 00574 else if (var->ctype == TUPLE_STRING) 00575 var->fieldstr = tuple_get_str (tuple, var->fieldidx, NULL); 00576 00577 var->fieldread = TRUE; 00578 var->fieldvalid = TRUE; 00579 return TRUE; 00580 } 00581 00582 00583 /* Fetch string or int value of given variable, whatever type it might be. 00584 * Return VAR_* type for the variable. 00585 */ 00586 static TupleValueType tf_get_var (char * * tmps, int * tmpi, TupleEvalVar * 00587 var, const Tuple * tuple) 00588 { 00589 TupleValueType type = TUPLE_UNKNOWN; 00590 *tmps = NULL; 00591 *tmpi = 0; 00592 00593 switch (var->type) { 00594 case TUPLE_VAR_CONST: 00595 switch (var->ctype) { 00596 case TUPLE_STRING: *tmps = var->name; break; 00597 case TUPLE_INT: *tmpi = var->defvali; break; 00598 default: /* Cannot happen */ break; 00599 } 00600 type = var->ctype; 00601 break; 00602 00603 case TUPLE_VAR_FIELD: 00604 if (tf_get_fieldval (var, tuple)) { 00605 type = var->ctype; 00606 if (type == TUPLE_INT) 00607 * tmpi = var->defvali; 00608 else if (type == TUPLE_STRING) 00609 * tmps = var->fieldstr; 00610 } 00611 break; 00612 } 00613 00614 return type; 00615 } 00616 00617 00618 /* Evaluate tuple in given TupleEval expression in given 00619 * context and return resulting string. 00620 */ 00621 static bool_t tuple_formatter_eval_do (TupleEvalContext * ctx, TupleEvalNode * 00622 expr, const Tuple * tuple, GString * out) 00623 { 00624 TupleEvalNode *curr = expr; 00625 TupleEvalVar *var0, *var1; 00626 TupleValueType type0, type1; 00627 int tmpi0, tmpi1; 00628 char tmps[MAX_STR], *tmps0, *tmps1, *tmps2; 00629 bool_t result; 00630 int resulti; 00631 00632 if (!expr) return FALSE; 00633 00634 while (curr) { 00635 const char *str = NULL; 00636 00637 switch (curr->opcode) { 00638 case OP_RAW: 00639 str = curr->text; 00640 break; 00641 00642 case OP_FIELD: 00643 var0 = ctx->variables[curr->var[0]]; 00644 00645 switch (var0->type) { 00646 case TUPLE_VAR_FIELD: 00647 if (tf_get_fieldval (var0, tuple)) { 00648 switch (var0->ctype) { 00649 case TUPLE_STRING: 00650 str = var0->fieldstr; 00651 break; 00652 00653 case TUPLE_INT: 00654 g_snprintf (tmps, sizeof (tmps), "%d", var0->defvali); 00655 str = tmps; 00656 break; 00657 00658 default: 00659 str = NULL; 00660 } 00661 } 00662 break; 00663 } 00664 break; 00665 00666 case OP_EQUALS: 00667 case OP_NOT_EQUALS: 00668 case OP_LT: case OP_LTEQ: 00669 case OP_GT: case OP_GTEQ: 00670 var0 = ctx->variables[curr->var[0]]; 00671 var1 = ctx->variables[curr->var[1]]; 00672 00673 type0 = tf_get_var(&tmps0, &tmpi0, var0, tuple); 00674 type1 = tf_get_var(&tmps1, &tmpi1, var1, tuple); 00675 result = FALSE; 00676 00677 if (type0 != TUPLE_UNKNOWN && type1 != TUPLE_UNKNOWN) { 00678 if (type0 == type1) { 00679 if (type0 == TUPLE_STRING) 00680 resulti = strcmp(tmps0, tmps1); 00681 else 00682 resulti = tmpi0 - tmpi1; 00683 } else { 00684 if (type0 == TUPLE_INT) 00685 resulti = tmpi0 - atoi(tmps1); 00686 else 00687 resulti = atoi(tmps0) - tmpi1; 00688 } 00689 00690 switch (curr->opcode) { 00691 case OP_EQUALS: result = (resulti == 0); break; 00692 case OP_NOT_EQUALS: result = (resulti != 0); break; 00693 case OP_LT: result = (resulti < 0); break; 00694 case OP_LTEQ: result = (resulti <= 0); break; 00695 case OP_GT: result = (resulti > 0); break; 00696 case OP_GTEQ: result = (resulti >= 0); break; 00697 default: result = FALSE; 00698 } 00699 } 00700 00701 if (result && ! tuple_formatter_eval_do (ctx, curr->children, tuple, out)) 00702 return FALSE; 00703 break; 00704 00705 case OP_EXISTS: 00706 if (tf_get_fieldval (ctx->variables[curr->var[0]], tuple)) { 00707 if (! tuple_formatter_eval_do (ctx, curr->children, tuple, out)) 00708 return FALSE; 00709 } 00710 break; 00711 00712 case OP_IS_EMPTY: 00713 var0 = ctx->variables[curr->var[0]]; 00714 00715 if (tf_get_fieldval (var0, tuple)) { 00716 switch (var0->ctype) { 00717 case TUPLE_INT: 00718 result = (var0->defvali == 0); 00719 break; 00720 00721 case TUPLE_STRING: 00722 result = TRUE; 00723 tmps2 = var0->fieldstr; 00724 00725 while (result && tmps2 && *tmps2 != '\0') { 00726 gunichar uc = g_utf8_get_char(tmps2); 00727 if (g_unichar_isspace(uc)) 00728 tmps2 = g_utf8_next_char(tmps2); 00729 else 00730 result = FALSE; 00731 } 00732 break; 00733 00734 default: 00735 result = TRUE; 00736 } 00737 } else 00738 result = TRUE; 00739 00740 if (result && ! tuple_formatter_eval_do (ctx, curr->children, tuple, out)) 00741 return FALSE; 00742 break; 00743 00744 default: 00745 tuple_error(ctx, "Unimplemented opcode %d!\n", curr->opcode); 00746 return FALSE; 00747 break; 00748 } 00749 00750 if (str) 00751 g_string_append (out, str); 00752 00753 curr = curr->next; 00754 } 00755 00756 return TRUE; 00757 } 00758 00759 void tuple_formatter_eval (TupleEvalContext * ctx, TupleEvalNode * expr, 00760 const Tuple * tuple, GString * out) 00761 { 00762 g_string_truncate (out, 0); 00763 tuple_formatter_eval_do (ctx, expr, tuple, out); 00764 }