Audacious  $Id:Doxyfile42802007-03-2104:39:00Znenolod$
tuple_compiler.c
Go to the documentation of this file.
1 /*
2  * tuple_compiler.c
3  * Copyright (c) 2007 Matti 'ccr' Hämäläinen
4  * Copyright (c) 2011 John Lindgren
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright notice,
10  * this list of conditions, and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright notice,
13  * this list of conditions, and the following disclaimer in the documentation
14  * provided with the distribution.
15  *
16  * This software is provided "as is" and without any warranty, express or
17  * implied. In no event shall the authors be liable for any damages arising from
18  * the use of this software.
19  */
20 
21 /*
22  * TODO:
23  * - Unicode/UTF-8 support in format strings. using any non-ASCII
24  * characters in Tuplez format strings WILL cause things go boom
25  * at the moment!
26  *
27  * - implement definitions (${=foo,"baz"} ${=foo,1234})
28  * - implement functions
29  * - implement handling of external expressions
30  * - evaluation context: how local variables should REALLY work?
31  * currently there is just a single context, is a "global" context needed?
32  */
33 
34 #include <ctype.h>
35 #include <stdarg.h>
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <string.h>
39 
40 #include <glib.h>
41 
42 #include "tuple_compiler.h"
43 
44 #define MAX_STR (256)
45 #define MIN_ALLOC_NODES (8)
46 #define MIN_ALLOC_BUF (64)
47 #define TUPLEZ_MAX_VARS (4)
48 
49 #define tuple_error(ctx, ...) fprintf (stderr, "Tuple compiler: " __VA_ARGS__)
50 
51 enum {
52  OP_RAW = 0, /* plain text */
53  OP_FIELD, /* a field/variable */
62 };
63 
64 enum {
67 };
68 
70  int opcode; /* operator, see OP_ enums */
71  int var[TUPLEZ_MAX_VARS]; /* tuple variable references */
72  char *text; /* raw text, if any (OP_RAW) */
73  struct _TupleEvalNode *children, *next, *prev; /* children of this struct, and pointer to next node. */
74 };
75 
76 typedef struct {
77  char *name;
78  int type; /* Type of variable, see VAR_* */
79  int defvali;
80  TupleValueType ctype; /* Type of constant/def value */
81 
82  int fieldidx; /* if >= 0: Index # of "pre-defined" Tuple fields */
83  bool_t fieldread, fieldvalid;
84  char * fieldstr;
85 } TupleEvalVar;
86 
90 };
91 
92 
94 {
95  g_free(var->name);
96  str_unref (var->fieldstr);
97  g_free(var);
98 }
99 
100 
101 /* Initialize an evaluation context
102  */
103 TupleEvalContext * tuple_evalctx_new(void)
104 {
105  return g_new0(TupleEvalContext, 1);
106 }
107 
108 
109 /* "Reset" the evaluation context
110  */
111 void tuple_evalctx_reset(TupleEvalContext *ctx)
112 {
113  int i;
114 
115  for (i = 0; i < ctx->nvariables; i++)
116  if (ctx->variables[i]) {
117  ctx->variables[i]->fieldread = FALSE;
118  ctx->variables[i]->fieldvalid = FALSE;
119  str_unref (ctx->variables[i]->fieldstr);
120  ctx->variables[i]->fieldstr = NULL;
121  }
122 }
123 
124 
125 /* Free an evaluation context and associated data
126  */
127 void tuple_evalctx_free(TupleEvalContext *ctx)
128 {
129  int i;
130 
131  if (!ctx) return;
132 
133  /* Deallocate variables */
134  for (i = 0; i < ctx->nvariables; i++)
135  if (ctx->variables[i])
136  tuple_evalctx_free_var(ctx->variables[i]);
137 
138  g_free(ctx->variables);
139  g_free(ctx);
140 }
141 
142 
143 static int tuple_evalctx_add_var (TupleEvalContext * ctx, const char * name,
144  const int type, const TupleValueType ctype)
145 {
146  int i;
147  TupleEvalVar *tmp = g_new0(TupleEvalVar, 1);
148 
149  tmp->name = g_strdup(name);
150  tmp->type = type;
151  tmp->fieldidx = -1;
152  tmp->ctype = ctype;
153 
154  /* Find fieldidx, if any */
155  switch (type) {
156  case TUPLE_VAR_FIELD:
157  tmp->fieldidx = tuple_field_by_name (name);
158  tmp->ctype = tuple_field_get_type (tmp->fieldidx);
159  break;
160 
161  case TUPLE_VAR_CONST:
162  if (ctype == TUPLE_INT)
163  tmp->defvali = atoi(name);
164  break;
165  }
166 
167  /* Find a free slot */
168  for (i = 0; i < ctx->nvariables; i++)
169  if (!ctx->variables[i]) {
170  ctx->variables[i] = tmp;
171  return i;
172  }
173 
174  i = ctx->nvariables;
175  ctx->variables = g_renew(TupleEvalVar *, ctx->variables, ctx->nvariables + MIN_ALLOC_NODES);
176  memset(&(ctx->variables[ctx->nvariables]), 0, MIN_ALLOC_NODES * sizeof(TupleEvalVar *));
177  ctx->nvariables += MIN_ALLOC_NODES;
178  ctx->variables[i] = tmp;
179 
180  return i;
181 }
182 
183 
184 static void tuple_evalnode_insert(TupleEvalNode **nodes, TupleEvalNode *node)
185 {
186  if (*nodes) {
187  node->prev = (*nodes)->prev;
188  (*nodes)->prev->next = node;
189  (*nodes)->prev = node;
190  node->next = NULL;
191  } else {
192  *nodes = node;
193  node->prev = node;
194  node->next = NULL;
195  }
196 }
197 
198 
199 static TupleEvalNode *tuple_evalnode_new(void)
200 {
201  return g_new0(TupleEvalNode, 1);
202 }
203 
204 
205 void tuple_evalnode_free(TupleEvalNode *expr)
206 {
207  TupleEvalNode *curr = expr, *next;
208 
209  while (curr) {
210  next = curr->next;
211 
212  g_free(curr->text);
213 
214  if (curr->children)
215  tuple_evalnode_free(curr->children);
216 
217  g_free(curr);
218 
219  curr = next;
220  }
221 }
222 
223 
224 static TupleEvalNode *tuple_compiler_pass1(int *level, TupleEvalContext *ctx, const char **expression);
225 
226 
227 static bool_t tc_get_item(TupleEvalContext *ctx,
228  const char **str, char *buf, gssize max,
229  char endch, bool_t *literal, char *errstr, const char *item)
230 {
231  gssize i = 0;
232  const char *s = *str;
233  char tmpendch;
234 
235  if (*s == '"') {
236  if (*literal == FALSE) {
237  tuple_error(ctx, "Literal string value not allowed in '%s'.\n", item);
238  return FALSE;
239  }
240  s++;
241  *literal = TRUE;
242  tmpendch = '"';
243  } else {
244  *literal = FALSE;
245  tmpendch = endch;
246  }
247 
248  if (*literal == FALSE) {
249  while (*s != '\0' && *s != tmpendch && (isalnum(*s) || *s == '-') && i < (max - 1)) {
250  buf[i++] = *(s++);
251  }
252 
253  if (*s != tmpendch && *s != '}' && !isalnum(*s) && *s != '-') {
254  tuple_error(ctx, "Invalid field '%s' in '%s'.\n", *str, item);
255  return FALSE;
256  } else if (*s != tmpendch) {
257  tuple_error(ctx, "Expected '%c' in '%s'.\n", tmpendch, item);
258  return FALSE;
259  }
260  } else {
261  while (*s != '\0' && *s != tmpendch && i < (max - 1)) {
262  if (*s == '\\') s++;
263  buf[i++] = *(s++);
264  }
265  }
266  buf[i] = '\0';
267 
268  if (*literal) {
269  if (*s == tmpendch)
270  s++;
271  else {
272  tuple_error(ctx, "Expected literal string end ('%c') in '%s'.\n", tmpendch, item);
273  return FALSE;
274  }
275  }
276 
277  if (*s != endch) {
278  tuple_error(ctx, "Expected '%c' after %s in '%s'\n", endch, errstr, item);
279  return FALSE;
280  } else {
281  *str = s;
282  return TRUE;
283  }
284 }
285 
286 
287 static int tc_get_variable(TupleEvalContext *ctx, char *name, int type)
288 {
289  int i;
291 
292  if (name == '\0') return -1;
293 
294  if (isdigit(name[0])) {
295  ctype = TUPLE_INT;
296  type = TUPLE_VAR_CONST;
297  } else
298  ctype = TUPLE_STRING;
299 
300  if (type != TUPLE_VAR_CONST) {
301  for (i = 0; i < ctx->nvariables; i++)
302  if (ctx->variables[i] && !strcmp(ctx->variables[i]->name, name))
303  return i;
304  }
305 
306  return tuple_evalctx_add_var(ctx, name, type, ctype);
307 }
308 
309 
310 static bool_t tc_parse_construct(TupleEvalContext *ctx, TupleEvalNode **res,
311  const char *item, const char **c, int *level, int opcode)
312 {
313  char tmps1[MAX_STR], tmps2[MAX_STR];
314  bool_t literal1 = TRUE, literal2 = TRUE;
315 
316  (*c)++;
317  if (tc_get_item(ctx, c, tmps1, MAX_STR, ',', &literal1, "tag1", item)) {
318  (*c)++;
319  if (tc_get_item(ctx, c, tmps2, MAX_STR, ':', &literal2, "tag2", item)) {
320  TupleEvalNode *tmp = tuple_evalnode_new();
321  (*c)++;
322 
323  tmp->opcode = opcode;
324  if ((tmp->var[0] = tc_get_variable(ctx, tmps1, literal1 ? TUPLE_VAR_CONST : TUPLE_VAR_FIELD)) < 0) {
325  tuple_evalnode_free(tmp);
326  tuple_error(ctx, "Invalid variable '%s' in '%s'.\n", tmps1, item);
327  return FALSE;
328  }
329  if ((tmp->var[1] = tc_get_variable(ctx, tmps2, literal2 ? TUPLE_VAR_CONST : TUPLE_VAR_FIELD)) < 0) {
330  tuple_evalnode_free(tmp);
331  tuple_error(ctx, "Invalid variable '%s' in '%s'.\n", tmps2, item);
332  return FALSE;
333  }
334  tmp->children = tuple_compiler_pass1(level, ctx, c);
335  tuple_evalnode_insert(res, tmp);
336  } else
337  return FALSE;
338  } else
339  return FALSE;
340 
341  return TRUE;
342 }
343 
344 
345 /* Compile format expression into TupleEvalNode tree.
346  * A "simple" straight compilation is sufficient in first pass, later
347  * passes can perform subexpression removal and other optimizations.
348  */
349 static TupleEvalNode *tuple_compiler_pass1(int *level, TupleEvalContext *ctx, const char **expression)
350 {
351  TupleEvalNode *res = NULL, *tmp = NULL;
352  const char *c = *expression, *item;
353  char tmps1[MAX_STR];
354  bool_t literal, end = FALSE;
355 
356  (*level)++;
357 
358  while (*c != '\0' && !end) {
359  tmp = NULL;
360  if (*c == '}') {
361  c++;
362  (*level)--;
363  end = TRUE;
364  } else if (*c == '$') {
365  /* Expression? */
366  item = c++;
367  if (*c == '{') {
368  int opcode;
369  const char *expr = ++c;
370 
371  switch (*c) {
372  case '?': c++;
373  /* Exists? */
374  literal = FALSE;
375  if (tc_get_item(ctx, &c, tmps1, MAX_STR, ':', &literal, "tag", item)) {
376  c++;
377  tmp = tuple_evalnode_new();
378  tmp->opcode = OP_EXISTS;
379  if ((tmp->var[0] = tc_get_variable(ctx, tmps1, TUPLE_VAR_FIELD)) < 0) {
380  tuple_error(ctx, "Invalid variable '%s' in '%s'.\n", tmps1, expr);
381  goto ret_error;
382  }
383  tmp->children = tuple_compiler_pass1(level, ctx, &c);
384  tuple_evalnode_insert(&res, tmp);
385  } else
386  goto ret_error;
387  break;
388 
389  case '=': c++;
390  if (*c != '=') {
391  /* Definition */
392  literal = FALSE;
393  if (tc_get_item(ctx, &c, tmps1, MAX_STR, ',', &literal, "variable", item)) {
394  c++;
395  if (*c == '"') {
396  /* String */
397  c++;
398  } else if (isdigit(*c)) {
399  /* Integer */
400  }
401 
402  tuple_error(ctx, "Definitions are not yet supported!\n");
403  goto ret_error;
404  } else
405  goto ret_error;
406  } else {
407  /* Equals? */
408  if (!tc_parse_construct(ctx, &res, item, &c, level, OP_EQUALS))
409  goto ret_error;
410  }
411  break;
412 
413  case '!': c++;
414  if (*c != '=') goto ext_expression;
415  if (!tc_parse_construct(ctx, &res, item, &c, level, OP_NOT_EQUALS))
416  goto ret_error;
417  break;
418 
419  case '<': c++;
420  if (*c == '=') {
421  opcode = OP_LTEQ;
422  c++;
423  } else
424  opcode = OP_LT;
425 
426  if (!tc_parse_construct(ctx, &res, item, &c, level, opcode))
427  goto ret_error;
428  break;
429 
430  case '>': c++;
431  if (*c == '=') {
432  opcode = OP_GTEQ;
433  c++;
434  } else
435  opcode = OP_GT;
436 
437  if (!tc_parse_construct(ctx, &res, item, &c, level, opcode))
438  goto ret_error;
439  break;
440 
441  case '(': c++;
442  if (!strncmp(c, "empty)?", 7)) {
443  c += 7;
444  literal = FALSE;
445  if (tc_get_item(ctx, &c, tmps1, MAX_STR, ':', &literal, "tag", item)) {
446  c++;
447  tmp = tuple_evalnode_new();
448  tmp->opcode = OP_IS_EMPTY;
449  if ((tmp->var[0] = tc_get_variable(ctx, tmps1, TUPLE_VAR_FIELD)) < 0) {
450  tuple_error(ctx, "Invalid variable '%s' in '%s'.\n", tmps1, expr);
451  goto ret_error;
452  }
453  tmp->children = tuple_compiler_pass1(level, ctx, &c);
454  tuple_evalnode_insert(&res, tmp);
455  } else
456  goto ret_error;
457  } else
458  goto ext_expression;
459  break;
460 
461  default:
462  ext_expression:
463  /* Get expression content */
464  c = expr;
465  literal = FALSE;
466  if (tc_get_item(ctx, &c, tmps1, MAX_STR, '}', &literal, "field", item)) {
467  /* FIXME!! FIX ME! Check for external expressions */
468 
469  /* I HAS A FIELD - A field. You has it. */
470  tmp = tuple_evalnode_new();
471  tmp->opcode = OP_FIELD;
472  if ((tmp->var[0] = tc_get_variable(ctx, tmps1, TUPLE_VAR_FIELD)) < 0) {
473  tuple_error(ctx, "Invalid variable '%s' in '%s'.\n", tmps1, expr);
474  goto ret_error;
475  }
476  tuple_evalnode_insert(&res, tmp);
477  c++;
478 
479  } else
480  goto ret_error;
481  }
482  } else {
483  tuple_error(ctx, "Expected '{', got '%c' in '%s'.\n", *c, c);
484  goto ret_error;
485  }
486 
487  } else if (*c == '%') {
488  /* Function? */
489  item = c++;
490  if (*c == '{') {
491  gssize i = 0;
492  c++;
493 
494  while (*c != '\0' && (isalnum(*c) || *c == '-') && *c != '}' && *c != ':' && i < (MAX_STR - 1))
495  tmps1[i++] = *(c++);
496  tmps1[i] = '\0';
497 
498  if (*c == ':') {
499  c++;
500  } else if (*c == '}') {
501  c++;
502  } else if (*c == '\0') {
503  tuple_error(ctx, "Expected '}' or function arguments in '%s'\n", item);
504  goto ret_error;
505  }
506  } else {
507  tuple_error(ctx, "Expected '{', got '%c' in '%s'.\n", *c, c);
508  goto ret_error;
509  }
510  } else {
511  /* Parse raw/literal text */
512  gssize i = 0;
513  while (*c != '\0' && *c != '$' && *c != '%' && *c != '}' && i < (MAX_STR - 1)) {
514  if (*c == '\\') c++;
515  tmps1[i++] = *(c++);
516  }
517  tmps1[i] = '\0';
518 
519  tmp = tuple_evalnode_new();
520  tmp->opcode = OP_RAW;
521  tmp->text = g_strdup(tmps1);
522  tuple_evalnode_insert(&res, tmp);
523  }
524  }
525 
526  if (*level <= 0) {
527  tuple_error(ctx, "Syntax error! Uneven/unmatched nesting of elements in '%s'!\n", c);
528  goto ret_error;
529  }
530 
531  *expression = c;
532  return res;
533 
534 ret_error:
535  tuple_evalnode_free(tmp);
536  tuple_evalnode_free(res);
537  return NULL;
538 }
539 
540 
541 TupleEvalNode *tuple_formatter_compile(TupleEvalContext *ctx, const char *expr)
542 {
543  int level = 0;
544  const char *tmpexpr = expr;
545  TupleEvalNode *res1;
546 
547  res1 = tuple_compiler_pass1(&level, ctx, &tmpexpr);
548 
549  if (level != 1) {
550  tuple_error(ctx, "Syntax error! Uneven/unmatched nesting of elements! (%d)\n", level);
551  tuple_evalnode_free(res1);
552  return NULL;
553  }
554 
555  return res1;
556 }
557 
558 
559 /* Fetch a tuple field value. Return TRUE if found. */
560 static bool_t tf_get_fieldval (TupleEvalVar * var, const Tuple * tuple)
561 {
562  if (var->type != TUPLE_VAR_FIELD || var->fieldidx < 0)
563  return FALSE;
564 
565  if (var->fieldread)
566  return var->fieldvalid;
567 
568  if (tuple_get_value_type (tuple, var->fieldidx, NULL) != var->ctype) {
569  var->fieldread = TRUE;
570  var->fieldvalid = FALSE;
571  return FALSE;
572  }
573 
574  if (var->ctype == TUPLE_INT)
575  var->defvali = tuple_get_int (tuple, var->fieldidx, NULL);
576  else if (var->ctype == TUPLE_STRING)
577  var->fieldstr = tuple_get_str (tuple, var->fieldidx, NULL);
578 
579  var->fieldread = TRUE;
580  var->fieldvalid = TRUE;
581  return TRUE;
582 }
583 
584 
585 /* Fetch string or int value of given variable, whatever type it might be.
586  * Return VAR_* type for the variable.
587  */
588 static TupleValueType tf_get_var (char * * tmps, int * tmpi, TupleEvalVar *
589  var, const Tuple * tuple)
590 {
592  *tmps = NULL;
593  *tmpi = 0;
594 
595  switch (var->type) {
596  case TUPLE_VAR_CONST:
597  switch (var->ctype) {
598  case TUPLE_STRING: *tmps = var->name; break;
599  case TUPLE_INT: *tmpi = var->defvali; break;
600  default: /* Cannot happen */ break;
601  }
602  type = var->ctype;
603  break;
604 
605  case TUPLE_VAR_FIELD:
606  if (tf_get_fieldval (var, tuple)) {
607  type = var->ctype;
608  if (type == TUPLE_INT)
609  * tmpi = var->defvali;
610  else if (type == TUPLE_STRING)
611  * tmps = var->fieldstr;
612  }
613  break;
614  }
615 
616  return type;
617 }
618 
619 
620 /* Evaluate tuple in given TupleEval expression in given
621  * context and return resulting string.
622  */
623 static bool_t tuple_formatter_eval_do (TupleEvalContext * ctx, TupleEvalNode *
624  expr, const Tuple * tuple, GString * out)
625 {
626  TupleEvalNode *curr = expr;
627  TupleEvalVar *var0, *var1;
628  TupleValueType type0, type1;
629  int tmpi0, tmpi1;
630  char tmps[MAX_STR], *tmps0, *tmps1, *tmps2;
631  bool_t result;
632  int resulti;
633 
634  if (!expr) return FALSE;
635 
636  while (curr) {
637  const char *str = NULL;
638 
639  switch (curr->opcode) {
640  case OP_RAW:
641  str = curr->text;
642  break;
643 
644  case OP_FIELD:
645  var0 = ctx->variables[curr->var[0]];
646 
647  switch (var0->type) {
648  case TUPLE_VAR_FIELD:
649  if (tf_get_fieldval (var0, tuple)) {
650  switch (var0->ctype) {
651  case TUPLE_STRING:
652  str = var0->fieldstr;
653  break;
654 
655  case TUPLE_INT:
656  g_snprintf (tmps, sizeof (tmps), "%d", var0->defvali);
657  str = tmps;
658  break;
659 
660  default:
661  str = NULL;
662  }
663  }
664  break;
665  }
666  break;
667 
668  case OP_EQUALS:
669  case OP_NOT_EQUALS:
670  case OP_LT: case OP_LTEQ:
671  case OP_GT: case OP_GTEQ:
672  var0 = ctx->variables[curr->var[0]];
673  var1 = ctx->variables[curr->var[1]];
674 
675  type0 = tf_get_var(&tmps0, &tmpi0, var0, tuple);
676  type1 = tf_get_var(&tmps1, &tmpi1, var1, tuple);
677  result = FALSE;
678 
679  if (type0 != TUPLE_UNKNOWN && type1 != TUPLE_UNKNOWN) {
680  if (type0 == type1) {
681  if (type0 == TUPLE_STRING)
682  resulti = strcmp(tmps0, tmps1);
683  else
684  resulti = tmpi0 - tmpi1;
685  } else {
686  if (type0 == TUPLE_INT)
687  resulti = tmpi0 - atoi(tmps1);
688  else
689  resulti = atoi(tmps0) - tmpi1;
690  }
691 
692  switch (curr->opcode) {
693  case OP_EQUALS: result = (resulti == 0); break;
694  case OP_NOT_EQUALS: result = (resulti != 0); break;
695  case OP_LT: result = (resulti < 0); break;
696  case OP_LTEQ: result = (resulti <= 0); break;
697  case OP_GT: result = (resulti > 0); break;
698  case OP_GTEQ: result = (resulti >= 0); break;
699  default: result = FALSE;
700  }
701  }
702 
703  if (result && ! tuple_formatter_eval_do (ctx, curr->children, tuple, out))
704  return FALSE;
705  break;
706 
707  case OP_EXISTS:
708  if (tf_get_fieldval (ctx->variables[curr->var[0]], tuple)) {
709  if (! tuple_formatter_eval_do (ctx, curr->children, tuple, out))
710  return FALSE;
711  }
712  break;
713 
714  case OP_IS_EMPTY:
715  var0 = ctx->variables[curr->var[0]];
716 
717  if (tf_get_fieldval (var0, tuple)) {
718  switch (var0->ctype) {
719  case TUPLE_INT:
720  result = (var0->defvali == 0);
721  break;
722 
723  case TUPLE_STRING:
724  result = TRUE;
725  tmps2 = var0->fieldstr;
726 
727  while (result && tmps2 && *tmps2 != '\0') {
728  gunichar uc = g_utf8_get_char(tmps2);
729  if (g_unichar_isspace(uc))
730  tmps2 = g_utf8_next_char(tmps2);
731  else
732  result = FALSE;
733  }
734  break;
735 
736  default:
737  result = TRUE;
738  }
739  } else
740  result = TRUE;
741 
742  if (result && ! tuple_formatter_eval_do (ctx, curr->children, tuple, out))
743  return FALSE;
744  break;
745 
746  default:
747  tuple_error(ctx, "Unimplemented opcode %d!\n", curr->opcode);
748  return FALSE;
749  break;
750  }
751 
752  if (str)
753  g_string_append (out, str);
754 
755  curr = curr->next;
756  }
757 
758  return TRUE;
759 }
760 
761 void tuple_formatter_eval (TupleEvalContext * ctx, TupleEvalNode * expr,
762  const Tuple * tuple, GString * out)
763 {
764  g_string_truncate (out, 0);
765  tuple_formatter_eval_do (ctx, expr, tuple, out);
766 }
struct _TupleEvalNode * prev
struct _TupleEvalNode * next
struct _TupleEvalNode * children
static TupleValueType tf_get_var(char **tmps, int *tmpi, TupleEvalVar *var, const Tuple *tuple)
#define MAX_STR
static TupleEvalNode * tuple_compiler_pass1(int *level, TupleEvalContext *ctx, const char **expression)
static int tc_get_variable(TupleEvalContext *ctx, char *name, int type)
static void tuple_evalnode_insert(TupleEvalNode **nodes, TupleEvalNode *node)
static bool_t tf_get_fieldval(TupleEvalVar *var, const Tuple *tuple)
type
Definition: plugins-api.h:41
bool_t fieldread
#define FALSE
Definition: core.h:35
EXPORT int tuple_field_by_name(const char *name)
Definition: tuple.c:165
Index Index bool_t
Definition: playlist-api.h:122
static bool_t tc_get_item(TupleEvalContext *ctx, const char **str, char *buf, gssize max, char endch, bool_t *literal, char *errstr, const char *item)
TupleValueType
Definition: tuple.h:82
const char * name
Definition: plugin-init.c:38
#define NULL
Definition: core.h:27
TupleEvalNode * tuple_formatter_compile(TupleEvalContext *ctx, const char *expr)
void tuple_evalctx_reset(TupleEvalContext *ctx)
EXPORT TupleValueType tuple_field_get_type(int field)
Definition: tuple.c:186
#define TRUE
Definition: core.h:37
void tuple_evalnode_free(TupleEvalNode *expr)
void str_unref(char *str)
Definition: strpool.c:89
static TupleEvalNode * tuple_evalnode_new(void)
TupleEvalContext * tuple_evalctx_new(void)
#define tuple_error(ctx,...)
void tuple_formatter_eval(TupleEvalContext *ctx, TupleEvalNode *expr, const Tuple *tuple, GString *out)
static bool_t tc_parse_construct(TupleEvalContext *ctx, TupleEvalNode **res, const char *item, const char **c, int *level, int opcode)
EXPORT char * tuple_get_str(const Tuple *tuple, int nfield, const char *field)
Definition: tuple.c:478
static int tuple_evalctx_add_var(TupleEvalContext *ctx, const char *name, const int type, const TupleValueType ctype)
#define TUPLEZ_MAX_VARS
EXPORT TupleValueType tuple_get_value_type(const Tuple *tuple, int nfield, const char *field)
Returns TupleValueType of given #Tuple field.
Definition: tuple.c:459
static bool_t tuple_formatter_eval_do(TupleEvalContext *ctx, TupleEvalNode *expr, const Tuple *tuple, GString *out)
TupleValueType ctype
TupleEvalVar ** variables
bool_t fieldvalid
void tuple_evalctx_free(TupleEvalContext *ctx)
static void tuple_evalctx_free_var(TupleEvalVar *var)
struct @17::@18::@20 s
#define MIN_ALLOC_NODES
int var[TUPLEZ_MAX_VARS]
EXPORT int tuple_get_int(const Tuple *tuple, int nfield, const char *field)
Returns integer associated to #Tuple field.
Definition: tuple.c:509