Audacious $Id:Doxyfile42802007-03-2104:39:00Znenolod$
|
00001 /* 00002 * Audacious 00003 * Copyright (c) 2007 William Pitcock 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 #include <glib.h> 00022 #include <mowgli.h> 00023 00024 #include "config.h" 00025 #include "tuple.h" 00026 #include "tuple_formatter.h" 00027 #include "audstrings.h" 00028 00029 /* 00030 * TUPLE_USE_COMPILER: 00031 * Undefine this to disable usage of the Tuplez compiler implementation. 00032 * This may be useful for prototyping new features of the language. 00033 */ 00034 #define TUPLE_USE_COMPILER 00035 00036 /* 00037 * TUPLE_COMPILER_DEBUG: 00038 * Define this to debug the execution process of the Tuplez compiled 00039 * bytecode. This may be useful if bugs creep in. 00040 */ 00041 #undef TUPLE_COMPILER_DEBUG 00042 00043 #ifdef TUPLE_USE_COMPILER 00044 # include "tuple_compiler.h" 00045 static GStaticMutex tuplec_mutex = G_STATIC_MUTEX_INIT; 00046 #endif 00047 00048 #ifdef _DEBUG 00049 # define _TRACE(fmt, ...) g_print("[tuple-fmt] %s(%d) " fmt "\n", __FILE__, __LINE__, __VA_ARGS__); 00050 #else 00051 # define _TRACE(fmt, ...) 00052 #endif 00053 00054 /* 00055 * the tuple formatter: 00056 * 00057 * this is a data-driven meta-language which eventually hopes to be 00058 * turing complete. 00059 * 00060 * language constructs follow the following basic rules: 00061 * - begin with ${ 00062 * - end with } 00063 * 00064 * language constructs: 00065 * - ${field}: prints a field 00066 * - ${?field:expr}: evaluates expr if field exists 00067 * - ${=field,"value"}: defines field in the currently iterated 00068 * tuple as string value of "value" 00069 * - ${=field,value}: defines field in the currently iterated 00070 * tuple as integer value of "value" 00071 * - ${==field,field:expr}: evaluates expr if both fields are the same 00072 * - ${!=field,field:expr}: evaluates expr if both fields are not the same 00073 * - ${(empty)?field:expr}: evaluates expr if field is empty or does not exist 00074 * - %{function:args,arg2,...}: runs function and inserts the result. 00075 * 00076 * everything else is treated as raw text. 00077 * additionally, plugins can add additional instructions and functions! 00078 */ 00079 00080 typedef struct { 00081 Tuple *tuple; 00082 GString *str; 00083 } TupleFormatterContext; 00084 00085 /* processes a construct, e.g. "${?artist:artist is defined}" would 00086 return "artist is defined" if artist is defined. */ 00087 gchar * 00088 tuple_formatter_process_construct(Tuple *tuple, const gchar *string) 00089 { 00090 TupleFormatterContext *ctx; 00091 const gchar *iter; 00092 gchar *out; 00093 gint level = 0; 00094 00095 g_return_val_if_fail(tuple != NULL, NULL); 00096 g_return_val_if_fail(string != NULL, NULL); 00097 00098 ctx = g_new0(TupleFormatterContext, 1); 00099 ctx->str = g_string_new(""); 00100 00101 _TRACE("parsing <%s>", string); 00102 00103 /* parsers are ugly */ 00104 for (iter = string; *iter != '\0'; iter++) 00105 { 00106 /* if it's raw text, just copy the byte */ 00107 if (*iter != '$' && *iter != '%' && *iter != '}' ) 00108 { 00109 g_string_append_c(ctx->str, *iter); 00110 } 00111 else if (*iter == '}' && level > 0) 00112 { 00113 level--; 00114 } 00115 else if (g_str_has_prefix(iter, "${") == TRUE) 00116 { 00117 GString *expression = g_string_new(""); 00118 GString *argument = g_string_new(""); 00119 GString *sel = expression; 00120 gchar *result; 00121 level++; 00122 00123 for (iter += 2; *iter != '\0'; iter++) 00124 { 00125 if (*iter == ':') 00126 { 00127 if (sel != argument) 00128 { 00129 sel = argument; 00130 continue; 00131 } 00132 else 00133 g_string_append_c(sel, *iter); 00134 continue; 00135 } 00136 00137 if (g_str_has_prefix(iter, "${") == TRUE || g_str_has_prefix(iter, "%{") == TRUE) 00138 { 00139 if (sel == argument) 00140 { 00141 g_string_append_c(sel, *iter); 00142 level++; 00143 } 00144 } 00145 else if (*iter == '}') 00146 { 00147 level--; 00148 if (sel == argument) 00149 { 00150 if (level == 0) 00151 break; 00152 else 00153 g_string_append_c(sel, *iter); 00154 } 00155 else 00156 break; 00157 } 00158 else 00159 g_string_append_c(sel, *iter); 00160 } 00161 00162 if (expression->len == 0) 00163 { 00164 g_string_free(expression, TRUE); 00165 g_string_free(argument, TRUE); 00166 continue; 00167 } 00168 00169 result = tuple_formatter_process_expr(tuple, expression->str, argument->len ? argument->str : NULL); 00170 if (result != NULL) 00171 { 00172 g_string_append(ctx->str, result); 00173 g_free(result); 00174 } 00175 00176 g_string_free(expression, TRUE); 00177 g_string_free(argument, TRUE); 00178 00179 if (*iter == '\0') 00180 break; 00181 } 00182 else if (g_str_has_prefix(iter, "%{") == TRUE) 00183 { 00184 GString *expression = g_string_new(""); 00185 GString *argument = g_string_new(""); 00186 GString *sel = expression; 00187 gchar *result; 00188 level++; 00189 00190 for (iter += 2; *iter != '\0'; iter++) 00191 { 00192 if (*iter == ':') 00193 { 00194 if (sel != argument) 00195 { 00196 sel = argument; 00197 continue; 00198 } 00199 else 00200 g_string_append_c(sel, *iter); 00201 continue; 00202 } 00203 00204 if (g_str_has_prefix(iter, "${") == TRUE || g_str_has_prefix(iter, "%{") == TRUE) 00205 { 00206 if (sel == argument) 00207 { 00208 g_string_append_c(sel, *iter); 00209 level++; 00210 } 00211 } 00212 else if (*iter == '}') 00213 { 00214 level--; 00215 if (sel == argument) 00216 { 00217 if (level == 0) 00218 break; 00219 else 00220 g_string_append_c(sel, *iter); 00221 } 00222 else 00223 break; 00224 } 00225 else 00226 g_string_append_c(sel, *iter); 00227 } 00228 00229 if (expression->len == 0) 00230 { 00231 g_string_free(expression, TRUE); 00232 g_string_free(argument, TRUE); 00233 continue; 00234 } 00235 00236 result = tuple_formatter_process_function(tuple, expression->str, argument->len ? argument->str : NULL); 00237 if (result != NULL) 00238 { 00239 g_string_append(ctx->str, result); 00240 g_free(result); 00241 } 00242 00243 g_string_free(expression, TRUE); 00244 g_string_free(argument, TRUE); 00245 00246 if (*iter == '\0') 00247 break; 00248 } 00249 } 00250 00251 out = g_strdup(ctx->str->str); 00252 g_string_free(ctx->str, TRUE); 00253 g_free(ctx); 00254 00255 _TRACE("parsed <%s> as <%s>", string, out); 00256 00257 return out; 00258 } 00259 00260 static GList *tuple_formatter_expr_list = NULL; 00261 00262 typedef struct { 00263 const gchar *name; 00264 gboolean (*func)(Tuple *tuple, const gchar *expression); 00265 } TupleFormatterExpression; 00266 00267 /* processes an expression and optional argument pair. */ 00268 gchar * 00269 tuple_formatter_process_expr(Tuple *tuple, const gchar *expression, 00270 const gchar *argument) 00271 { 00272 TupleFormatterExpression *expr = NULL; 00273 GList *iter; 00274 00275 g_return_val_if_fail(tuple != NULL, NULL); 00276 g_return_val_if_fail(expression != NULL, NULL); 00277 00278 for (iter = tuple_formatter_expr_list; iter != NULL; iter = iter->next) 00279 { 00280 TupleFormatterExpression *tmp = (TupleFormatterExpression *) iter->data; 00281 00282 if (g_str_has_prefix(expression, tmp->name) == TRUE) 00283 { 00284 expr = tmp; 00285 expression += strlen(tmp->name); 00286 } 00287 } 00288 00289 /* ${artist} */ 00290 if (expr == NULL && argument == NULL) 00291 { 00292 TupleValueType type = tuple_get_value_type(tuple, -1, expression); 00293 00294 switch(type) 00295 { 00296 case TUPLE_STRING: 00297 return g_strdup(tuple_get_string(tuple, -1, expression)); 00298 break; 00299 case TUPLE_INT: 00300 return g_strdup_printf("%d", tuple_get_int(tuple, -1, expression)); 00301 break; 00302 case TUPLE_UNKNOWN: 00303 default: 00304 return NULL; 00305 } 00306 } 00307 else if (expr != NULL) 00308 { 00309 if (expr->func(tuple, expression) == TRUE && argument != NULL) 00310 return tuple_formatter_process_construct(tuple, argument); 00311 } 00312 00313 return NULL; 00314 } 00315 00316 static GList *tuple_formatter_func_list = NULL; 00317 00318 typedef struct { 00319 const gchar *name; 00320 gchar *(*func)(Tuple *tuple, gchar **args); 00321 } TupleFormatterFunction; 00322 00323 /* processes a function */ 00324 gchar * 00325 tuple_formatter_process_function(Tuple *tuple, const gchar *expression, 00326 const gchar *argument) 00327 { 00328 TupleFormatterFunction *expr = NULL; 00329 GList *iter; 00330 00331 g_return_val_if_fail(tuple != NULL, NULL); 00332 g_return_val_if_fail(expression != NULL, NULL); 00333 00334 for (iter = tuple_formatter_func_list; iter != NULL; iter = iter->next) 00335 { 00336 TupleFormatterFunction *tmp = (TupleFormatterFunction *) iter->data; 00337 00338 if (g_str_has_prefix(expression, tmp->name) == TRUE) 00339 { 00340 expr = tmp; 00341 expression += strlen(tmp->name); 00342 } 00343 } 00344 00345 if (expr != NULL) 00346 { 00347 gchar **args; 00348 gchar *ret; 00349 00350 if (argument) 00351 args = g_strsplit(argument, ",", 10); 00352 else 00353 args = NULL; 00354 00355 ret = expr->func(tuple, args); 00356 00357 if (args) 00358 g_strfreev(args); 00359 00360 return ret; 00361 } 00362 00363 return NULL; 00364 } 00365 00366 /* registers a formatter */ 00367 void 00368 tuple_formatter_register_expression(const gchar *keyword, 00369 gboolean (*func)(Tuple *tuple, const gchar *argument)) 00370 { 00371 TupleFormatterExpression *expr; 00372 00373 g_return_if_fail(keyword != NULL); 00374 g_return_if_fail(func != NULL); 00375 00376 expr = g_new0(TupleFormatterExpression, 1); 00377 expr->name = keyword; 00378 expr->func = func; 00379 00380 tuple_formatter_expr_list = g_list_append(tuple_formatter_expr_list, expr); 00381 } 00382 00383 /* registers a function */ 00384 void 00385 tuple_formatter_register_function(const gchar *keyword, 00386 gchar *(*func)(Tuple *tuple, gchar **argument)) 00387 { 00388 TupleFormatterFunction *expr; 00389 00390 g_return_if_fail(keyword != NULL); 00391 g_return_if_fail(func != NULL); 00392 00393 expr = g_new0(TupleFormatterFunction, 1); 00394 expr->name = keyword; 00395 expr->func = func; 00396 00397 tuple_formatter_func_list = g_list_append(tuple_formatter_func_list, expr); 00398 } 00399 00400 /* builtin-keyword: ${==arg1,arg2}, returns TRUE if <arg1> and <arg2> match. 00401 <arg1> and <arg2> can also be raw text, which should be enclosed in "double quotes". */ 00402 static gboolean 00403 tuple_formatter_expression_match(Tuple *tuple, const gchar *expression) 00404 { 00405 gchar **args = g_strsplit(expression, ",", 2); 00406 gchar *arg1 = NULL, *arg2 = NULL; 00407 gint ret; 00408 00409 if (args[0][0] == '\"') /* check if arg1 is "raw text" */ 00410 { 00411 if ( strlen(args[0]) > 1 ) 00412 { 00413 args[0][strlen(args[0]) - 1] = '\0'; 00414 arg1 = g_strdup(&(args[0][1])); 00415 args[0][strlen(args[0]) - 1] = '\"'; 00416 } 00417 else /* bad formatted arg */ 00418 return FALSE; 00419 } 00420 else if (tuple_get_value_type(tuple, -1, args[0]) == TUPLE_UNKNOWN) 00421 { 00422 g_strfreev(args); 00423 return FALSE; 00424 } 00425 00426 if (args[1][0] == '\"') /* check if arg2 is "raw text" */ 00427 { 00428 if ( strlen(args[1]) > 1 ) 00429 { 00430 args[1][strlen(args[1]) - 1] = '\0'; 00431 arg2 = g_strdup(&(args[1][1])); 00432 args[1][strlen(args[1]) - 1] = '\"'; 00433 } 00434 else /* bad formatted arg */ 00435 return FALSE; 00436 } 00437 else if (tuple_get_value_type(tuple, -1, args[1]) == TUPLE_UNKNOWN) 00438 { 00439 g_strfreev(args); 00440 return FALSE; 00441 } 00442 00443 if (!arg1) /* if arg1 is not "raw text", get the tuple value */ 00444 { 00445 if (tuple_get_value_type(tuple, -1, args[0]) == TUPLE_STRING) 00446 arg1 = g_strdup(tuple_get_string(tuple, -1, args[0])); 00447 else 00448 arg1 = g_strdup_printf("%d", tuple_get_int(tuple, -1, args[0])); 00449 } 00450 00451 if (!arg2) /* if arg2 is not "raw text", get the tuple value */ 00452 { 00453 if (tuple_get_value_type(tuple, -1, args[1]) == TUPLE_STRING) 00454 arg2 = g_strdup(tuple_get_string(tuple, -1, args[1])); 00455 else 00456 arg2 = g_strdup_printf("%d", tuple_get_int(tuple, -1, args[1])); 00457 } 00458 00459 ret = g_ascii_strcasecmp(arg1, arg2); 00460 g_free(arg1); 00461 g_free(arg2); 00462 g_strfreev(args); 00463 00464 return ret ? FALSE : TRUE; 00465 } 00466 00467 /* builtin-keyword: ${!=arg1,arg2}. returns TRUE if <arg1> and <arg2> don't match. 00468 <arg1> and <arg2> can also be raw text, which should be enclosed in "double quotes". */ 00469 static gboolean 00470 tuple_formatter_expression_nonmatch(Tuple *tuple, const gchar *expression) 00471 { 00472 return tuple_formatter_expression_match(tuple, expression) ^ 1; 00473 } 00474 00475 /* builtin-keyword: ${(empty)?}. returns TRUE if <arg> is empty. */ 00476 static gboolean 00477 tuple_formatter_expression_empty(Tuple *tuple, const gchar *expression) 00478 { 00479 gboolean ret = TRUE; 00480 const gchar *iter; 00481 TupleValueType type = tuple_get_value_type(tuple, -1, expression); 00482 00483 if (type == TUPLE_UNKNOWN) 00484 return TRUE; 00485 00486 if (type == TUPLE_INT) 00487 return (tuple_get_int(tuple, -1, expression) == 0); 00488 00489 iter = tuple_get_string(tuple, -1, expression); 00490 if (!iter) 00491 return TRUE; 00492 00493 while (ret && *iter != '\0') 00494 { 00495 if (*iter == ' ') 00496 iter++; 00497 else 00498 ret = FALSE; 00499 } 00500 00501 return ret; 00502 } 00503 00504 /* builtin-keyword: ${?arg}, returns TRUE if <arg> exists. */ 00505 static gboolean 00506 tuple_formatter_expression_exists(Tuple *tuple, const gchar *expression) 00507 { 00508 return !tuple_formatter_expression_empty(tuple, expression); 00509 } 00510 00511 /* builtin function: %{audacious-version} */ 00512 static gchar * 00513 tuple_formatter_function_version(Tuple *tuple, gchar **args) 00514 { 00515 return g_strdup(PACKAGE_NAME " " PACKAGE_VERSION); 00516 } 00517 00518 /* 00519 * Compile a tuplez string and cache the result. 00520 * This caches the result for the last string, so that 00521 * successive calls are sped up. 00522 * 00523 * TODO/1.5: Implement a more efficient use of the compiler. 00524 */ 00525 gchar * tuple_formatter_process_string (const Tuple * tuple, const gchar * string) 00526 { 00527 static gboolean initialized = FALSE; 00528 static gchar *last_string = NULL; 00529 #ifdef TUPLE_USE_COMPILER 00530 static TupleEvalContext *last_ctx = NULL; 00531 static TupleEvalNode *last_ev = NULL; 00532 gchar *result = NULL; 00533 #endif 00534 00535 if (initialized == FALSE) 00536 { 00537 tuple_formatter_register_expression("?", tuple_formatter_expression_exists); 00538 tuple_formatter_register_expression("==", tuple_formatter_expression_match); 00539 tuple_formatter_register_expression("!=", tuple_formatter_expression_nonmatch); 00540 tuple_formatter_register_expression("(empty)?", tuple_formatter_expression_empty); 00541 00542 tuple_formatter_register_function("audacious-version", tuple_formatter_function_version); 00543 initialized = TRUE; 00544 } 00545 00546 #ifdef TUPLE_USE_COMPILER 00547 g_static_mutex_lock (& tuplec_mutex); 00548 00549 if (last_string == NULL || 00550 (last_string != NULL && strcmp(last_string, string))) 00551 { 00552 g_free(last_string); 00553 00554 if (last_ctx != NULL) 00555 { 00556 tuple_evalctx_free(last_ctx); 00557 tuple_evalnode_free(last_ev); 00558 } 00559 00560 last_ctx = tuple_evalctx_new(); 00561 last_string = g_strdup(string); 00562 last_ev = tuple_formatter_compile(last_ctx, last_string); 00563 if (last_ctx->iserror) { 00564 g_warning("[TuplezCC]: %s", last_ctx->errmsg); 00565 } 00566 if (!last_ev) { 00567 g_warning("[TuplezCC]: Compilation failed!\n"); 00568 } 00569 } 00570 00571 #ifdef TUPLE_COMPILER_DEBUG 00572 { 00573 gint level = 0; 00574 00575 tuple_formatter_print(stderr, &level, last_ctx, last_ev); 00576 } 00577 #endif 00578 00579 tuple_evalctx_reset(last_ctx); 00580 00581 result = tuple_formatter_eval(last_ctx, last_ev, tuple); 00582 if (last_ctx->iserror) { 00583 g_warning("[TuplezEV]: %s", last_ctx->errmsg); 00584 } 00585 00586 g_static_mutex_unlock (& tuplec_mutex); 00587 00588 return result; 00589 #else 00590 return tuple_formatter_process_construct(tuple, string); 00591 #endif 00592 } 00593 00594 gchar * tuple_formatter_make_title_string (const Tuple * tuple, const gchar * 00595 string) 00596 { 00597 gchar *title = tuple_formatter_process_string(tuple, string); 00598 00599 if (title == NULL || !title[0]) 00600 { 00601 const char *filename = tuple_get_string(tuple, FIELD_FILE_NAME, NULL); 00602 00603 g_free(title); 00604 title = g_strdup((filename != NULL) ? filename : ""); 00605 string_cut_extension(title); 00606 } 00607 00608 return title; 00609 }