1 /*
2 DeaDBeeF -- the music player
3 Copyright (C) 2009-2014 Alexey Yakovenko and other contributors
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17
18 2. Altered source versions must be plainly marked as such, and must not be
19 misrepresented as being the original software.
20
21 3. This notice may not be removed or altered from any source distribution.
22 */
23
24 // basic syntax:
25 // function call: $function([arg1[,arg2[,...]]])
26 // meta fields, with spaces allowed: %field name%
27 // if_defined block: [text$func()%field%more text]
28 // plain text: anywhere outside of the above
29 // escaping: $, %, [, ], \ must be escaped
30
31 // bytecode format
32 // 0: indicates start of special block
33 // 1: function call
34 // func_idx:byte, num_args:byte, arg1_len:byte[,arg2_len:byte[,...]]
35 // 2: meta field
36 // len:byte, data
37 // 3: if_defined block
38 // len:int32, data
39 // 4: pre-interpreted text
40 // len:int32, data
41 // !0: plain text
42
43 #ifdef HAVE_CONFIG_H
44 #include "config.h"
45 #endif
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <stdint.h>
50 #include <ctype.h>
51 #include <inttypes.h>
52 #include <math.h>
53 #include "streamer.h"
54 #include "utf8.h"
55 #include "playlist.h"
56 #include "playqueue.h"
57 #include "tf.h"
58 #include "gettext.h"
59 #include "plugins.h"
60 #include "junklib.h"
61
62 #define min(x,y) ((x)<(y)?(x):(y))
63
64 //#define trace(...) { fprintf(stderr, __VA_ARGS__); }
65 #define trace(fmt,...)
66
67 typedef struct {
68 const char *i;
69 char *o;
70 int eol;
71 } tf_compiler_t;
72
73 typedef int (*tf_func_ptr_t)(ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef);
74
75 #define TF_MAX_FUNCS 0xff
76
77 typedef struct {
78 const char *name;
79 tf_func_ptr_t func;
80 } tf_func_def;
81
82 static int
83 tf_eval_int (ddb_tf_context_t *ctx, const char *code, int size, char *out, int outlen, int *bool_out, int fail_on_undef);
84
85 #define TF_EVAL_CHECK(res, ctx, arg, arg_len, out, outlen, fail_on_undef)\
86 res = tf_eval_int (ctx, arg, arg_len, out, outlen, &bool_out, fail_on_undef);\
87 if (res < 0) { *out = 0; return -1; }
88
89 // empty track is used when ctx.it is null
90 static playItem_t empty_track;
91 // empty playlist is used when ctx.plt is null
92 static playlist_t empty_playlist;
93 // empty code is used when "code" argumen is null
94 static char empty_code[4] = {0};
95
96 int
tf_eval(ddb_tf_context_t * ctx,const char * code,char * out,int outlen)97 tf_eval (ddb_tf_context_t *ctx, const char *code, char *out, int outlen) {
98 if (!code) {
99 code = empty_code;
100 }
101
102 int null_it = 0;
103 if (!ctx->it) {
104 null_it = 1;
105 ctx->it = (ddb_playItem_t *)&empty_track;
106 }
107
108 int null_plt = 0;
109 if (!ctx->plt) {
110 null_plt = 1;
111 ctx->plt = (ddb_playlist_t *)&empty_playlist;
112 }
113
114 int32_t codelen = *((int32_t *)code);
115 code += 4;
116 memset (out, 0, outlen);
117 int l = 0;
118
119 int bool_out = 0;
120 int id = -1;
121 if (ctx->flags & DDB_TF_CONTEXT_HAS_ID) {
122 id = ctx->id;
123 }
124
125 switch (id) {
126 case DB_COLUMN_FILENUMBER:
127 if (ctx->flags & DDB_TF_CONTEXT_HAS_INDEX) {
128 l = snprintf (out, outlen, "%d", ctx->idx+1);
129 }
130 else if (ctx->plt) {
131 int idx = plt_get_item_idx ((playlist_t *)ctx->plt, (playItem_t *)ctx->it, PL_MAIN);
132 l = snprintf (out, outlen, "%d", idx+1);
133 }
134 break;
135 case DB_COLUMN_PLAYING:
136 l = pl_format_item_queue ((playItem_t *)ctx->it, out, outlen);
137 break;
138 default:
139 // tf_eval_int expects outlen to not include the terminating zero
140 l = tf_eval_int (ctx, code, codelen, out, outlen-1, &bool_out, 0);
141 break;
142 }
143
144 if (!(ctx->flags & DDB_TF_CONTEXT_MULTILINE)) {
145 for (; *out; out++) {
146 if (*out == '\n') {
147 *out = ';';
148 }
149 }
150 }
151
152 if (null_it) {
153 ctx->it = NULL;
154 }
155 if (null_plt) {
156 ctx->plt = NULL;
157 }
158 return l;
159 }
160
161 // $greater(a,b) returns true if a is greater than b, otherwise false
162 int
tf_func_greater(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)163 tf_func_greater (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
164 if (argc != 2) {
165 return -1;
166 }
167 const char *arg = args;
168
169 int bool_out = 0;
170
171 char a[10];
172 int len;
173 TF_EVAL_CHECK(len, ctx, arg, arglens[0], a, sizeof (a), fail_on_undef);
174
175 int aa = atoi (a);
176
177 arg += arglens[0];
178 char b[10];
179 TF_EVAL_CHECK(len, ctx, arg, arglens[1], b, sizeof (b), fail_on_undef);
180 int bb = atoi (b);
181
182 return aa > bb;
183 }
184
185 // $strcmp(s1,s2) compares s1 and s2, returns true if equal, otherwise false
186 int
tf_func_strcmp(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)187 tf_func_strcmp (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
188 if (argc != 2) {
189 return -1;
190 }
191 const char *arg = args;
192
193 int bool_out = 0;
194
195 char s1[1000];
196 int len;
197 TF_EVAL_CHECK(len, ctx, arg, arglens[0], s1, sizeof (s1), fail_on_undef);
198
199 arg += arglens[0];
200 char s2[1000];
201 TF_EVAL_CHECK(len, ctx, arg, arglens[1], s2, sizeof (s2), fail_on_undef);
202
203 int res = strcmp (s1, s2);
204 return !res;
205 }
206
207 // $num(n,len) Formats the integer number n in decimal notation with len characters. Pads with zeros
208 // from the left if necessary. len includes the dash when the number is negative. If n is not numeric, it is treated as zero.
209 int
tf_func_num(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)210 tf_func_num (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
211 const char *arg = args;
212 int bool_out = 0;
213 int len;
214
215 if (argc != 2) {
216 return -1;
217 }
218
219 TF_EVAL_CHECK(len, ctx, arg, arglens[0], out, outlen, fail_on_undef);
220 arg += arglens[0];
221 int n = atoi (out);
222
223 TF_EVAL_CHECK(len, ctx, arg, arglens[1], out, outlen, fail_on_undef);
224 int n_len = atoi (out);
225
226 if (outlen < 1 || outlen < n_len) {
227 *out = 0;
228 return -1;
229 }
230
231 if (n_len < 0) {
232 n_len = 0;
233 }
234
235 char *out_w = out;
236 int is_negative = n < 0;
237 int num_len = 0;
238
239 int cnt = n;
240 do {
241 num_len++;
242 cnt /= 10;
243 } while (cnt);
244
245 if (is_negative) {
246 *out_w++ = '-';
247 num_len++;
248 n *= -1;
249 }
250
251 int num_len_plus_padding = num_len;
252 while (num_len_plus_padding < n_len) {
253 *out_w++ = '0';
254 num_len_plus_padding++;
255 }
256
257 out_w += num_len - is_negative;
258 do {
259 *--out_w = (n % 10) + '0';
260 n /= 10;
261 } while (n);
262
263 return n_len > num_len ? n_len : num_len;
264 }
265
266 int
tf_func_abbr(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)267 tf_func_abbr (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
268 if (argc != 1 && argc != 2) {
269 return -1;
270 }
271
272 const char *arg = args;
273
274 int bool_out = 0;
275
276 int len;
277 TF_EVAL_CHECK(len, ctx, arg, arglens[0], out, outlen, fail_on_undef);
278
279 if (argc == 2) {
280 char num_chars_str[10];
281 arg += arglens[0];
282 int l;
283 TF_EVAL_CHECK(l, ctx, arg, arglens[1], num_chars_str, sizeof (num_chars_str), fail_on_undef);
284 int num_chars = atoi (num_chars_str);
285 if (len <= num_chars) {
286 return len;
287 }
288 }
289
290 char *p = out;
291 char *pout = out;
292 const char skipchars[] = "() ,/\\|";
293 while (*p) {
294 // skip whitespace/paren
295 while (*p && strchr (skipchars, *p)) {
296 p++;
297 }
298 if (!*p) {
299 break;
300 }
301
302 // take the first letter for abbrev
303 int is_bracket = *p == '[' || *p == ']';
304 int32_t size = 0;
305 u8_nextchar(p, &size);
306 memmove (pout, p, size);
307 pout += size;
308 p += size;
309
310 // skip to the end of word
311 while (*p && !strchr (skipchars, *p)) {
312 if (!is_bracket) {
313 p++;
314 }
315 else {
316 size = 0;
317 u8_nextchar(p, &size);
318 memmove (pout, p, size);
319 pout += size;
320 p += size;
321 }
322 }
323 }
324
325 *pout = 0;
326 return (int)(pout - out);
327 }
328
329 int
tf_func_ansi(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)330 tf_func_ansi (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
331 if (argc != 1) {
332 return -1;
333 }
334
335 int bool_out = 0;
336
337 int len;
338 TF_EVAL_CHECK(len, ctx, args, arglens[0], out, outlen, fail_on_undef);
339 return len;
340 }
341
342 int
tf_func_ascii(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)343 tf_func_ascii (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
344 if (argc != 1) {
345 return -1;
346 }
347
348 int bool_out = 0;
349
350 int len;
351 char temp_str[1000];
352 TF_EVAL_CHECK(len, ctx, args, arglens[0], temp_str, sizeof (temp_str), fail_on_undef);
353
354 len = junk_iconv (temp_str, len, out, outlen, "utf-8", "ascii");
355
356 return len;
357 }
358
359 int
tf_caps_impl(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef,int do_lowercasing)360 tf_caps_impl (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef, int do_lowercasing) {
361 if (argc != 1) {
362 return -1;
363 }
364
365 int bool_out = 0;
366
367 int len;
368 TF_EVAL_CHECK(len, ctx, args, arglens[0], out, outlen, fail_on_undef);
369
370 char *p = out;
371 char *end = p + len;
372 const char skipchars[] = "() ,/\\|";
373 while (*p) {
374 // skip whitespace/paren
375 while (*p && strchr (skipchars, *p)) {
376 p++;
377 }
378 if (!*p) {
379 break;
380 }
381
382 int is_bracket = *p == '[' || *p == ']';
383
384 char temp[5];
385
386 // uppercase the first letter
387 int32_t size = 0;
388 u8_nextchar (p, &size);
389 int32_t uppersize = u8_toupper ((const signed char *)p, size, temp);
390 if (uppersize != size) {
391 memmove (p+uppersize, p+size, end-(p+size));
392 end += uppersize - size;
393 *end = 0;
394 }
395 memcpy (p, temp, uppersize);
396
397 p += uppersize;
398
399 // lowercase to the end of word
400 while (*p && !strchr (skipchars, *p)) {
401 if (is_bracket) {
402 p++;
403 }
404 else {
405 size = 0;
406 u8_nextchar ((const char *)p, &size);
407 if (do_lowercasing) {
408 int32_t lowersize = u8_tolower ((const signed char *)p, size, temp);
409 if (lowersize != size) {
410 memmove (p+lowersize, p+size, end-(p+size));
411 end += lowersize - size;
412 *end = 0;
413 }
414 memcpy (p, temp, lowersize);
415 p += lowersize;
416 }
417 else {
418 p += size;
419 }
420 }
421 }
422 }
423
424 return (int)(end - out);
425 }
426
427 int
tf_func_caps(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)428 tf_func_caps (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
429 return tf_caps_impl (ctx, argc, arglens, args, out, outlen, fail_on_undef, 1);
430 }
431
432 int
tf_func_caps2(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)433 tf_func_caps2 (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
434 return tf_caps_impl (ctx, argc, arglens, args, out, outlen, fail_on_undef, 0);
435 }
436
437 int
tf_func_char(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)438 tf_func_char (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
439 if (argc != 1) {
440 return -1;
441 }
442
443 int bool_out = 0;
444
445 int len;
446 TF_EVAL_CHECK(len, ctx, args, arglens[0], out, outlen, fail_on_undef);
447
448 int n = atoi (out);
449 *out = 0;
450
451 if (outlen < 5) {
452 return -1;
453 }
454 len = u8_wc_toutf8 (out, n);
455 out[len] = 0;
456 return len;
457 }
458
459 int
tf_func_crc32(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)460 tf_func_crc32 (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
461 if (argc != 1) {
462 return -1;
463 }
464
465 static const uint32_t tab[256] = {
466 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
467 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
468 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
469 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
470 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
471 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
472 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
473 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
474 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
475 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
476 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
477 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
478 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
479 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
480 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
481 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
482 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
483 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
484 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
485 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
486 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
487 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
488 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
489 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
490 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
491 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
492 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
493 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
494 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
495 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
496 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
497 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
498 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
499 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
500 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
501 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
502 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
503 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
504 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
505 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
506 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
507 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
508 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
509 };
510
511 int bool_out = 0;
512
513 int len;
514 TF_EVAL_CHECK(len, ctx, args, arglens[0], out, outlen, fail_on_undef);
515
516 uint32_t crc = 0xffffffff;
517
518 for (int i = 0; i < len; i++) {
519 crc = (crc >> 8) ^ tab[(crc ^ (uint8_t)out[i]) & 0xff];
520 }
521
522 crc ^= 0xffffffff;
523
524 return snprintf (out, outlen, "%u", crc);
525 }
526
527 int
tf_func_crlf(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)528 tf_func_crlf (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
529 if (argc != 0 || outlen < 2) {
530 return -1;
531 }
532 out[0] = '\n';
533 out[1] = 0;
534 return 1;
535 }
536
537 // $left(text,n) returns the first n characters of text
538 int
tf_func_left(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)539 tf_func_left (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
540 if (argc != 2) {
541 return -1;
542 }
543 const char *arg = args;
544
545 int bool_out = 0;
546
547 // get number of characters
548 char num_chars_str[10];
549 arg += arglens[0];
550 int len;
551 TF_EVAL_CHECK(len, ctx, arg, arglens[1], num_chars_str, sizeof (num_chars_str), fail_on_undef);
552 int num_chars = atoi (num_chars_str);
553 if (num_chars <= 0 || num_chars > outlen) {
554 *out = 0;
555 return -1;
556 }
557
558 // get text
559 char text[1000];
560 arg = args;
561 TF_EVAL_CHECK(len, ctx, arg, arglens[0], text, sizeof (text), fail_on_undef);
562
563 int res = u8_strncpy (out, text, num_chars);
564 trace ("left: (%s,%d) -> (%s), res: %d\n", text, num_chars, out, res);
565 return res;
566 }
567
568 int
tf_func_directory(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)569 tf_func_directory (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
570 if (argc < 1 || argc > 2) {
571 return -1;
572 }
573
574 int bool_out = 0;
575
576 int len;
577 TF_EVAL_CHECK(len, ctx, args, arglens[0], out, outlen, fail_on_undef);
578
579 int path_len = len;
580
581 int levels = 1;
582 if (argc == 2) {
583 char temp[20];
584 args += arglens[0];
585 TF_EVAL_CHECK(len, ctx, args, arglens[1], temp, sizeof (temp), fail_on_undef);
586 levels = atoi (temp);
587 if (levels < 0) {
588 return -1;
589 }
590 }
591
592 char *end = out + path_len - 1;
593 char *start = end;
594
595 while (levels--) {
596 // get to the last delimiter
597 while (end >= out && *end != '/') {
598 end--;
599 }
600
601 if (end < out) {
602 *out = 0;
603 return -1;
604 }
605
606 // skip multiple delimiters
607 while (end >= out && *end == '/') {
608 end--;
609 }
610 end++;
611
612 if (end < out) {
613 *out = 0;
614 return -1;
615 }
616
617 // find another delimiter
618 start = end - 1;
619 while (start > out && *start != '/') {
620 start--;
621 }
622
623 if (*start == '/') {
624 start++;
625 }
626
627 if (levels) {
628 end = start;
629 while (end >= out && *end == '/') {
630 end--;
631 }
632 }
633 }
634
635 memmove (out, start, end-start);
636 out[end-start] = 0;
637 return (int)(end-start);
638 }
639
640 int
tf_func_directory_path(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)641 tf_func_directory_path (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
642 if (argc < 1 || argc > 2) {
643 return -1;
644 }
645
646 int bool_out = 0;
647
648 int len;
649 TF_EVAL_CHECK(len, ctx, args, arglens[0], out, outlen, fail_on_undef);
650
651 char *p = out + len - 1;
652
653 while (p >= out && *p != '/') {
654 p--;
655 }
656 while (p >= out && *p == '/') {
657 p--;
658 }
659 if (p < out) {
660 *out = 0;
661 return -1;
662 }
663
664 p++;
665 *p = 0;
666 return (int)(p-out);
667 }
668
669 int
tf_func_ext(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)670 tf_func_ext (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
671 if (argc < 1 || argc > 2) {
672 return -1;
673 }
674
675 int bool_out = 0;
676
677 int len;
678 TF_EVAL_CHECK(len, ctx, args, arglens[0], out, outlen, fail_on_undef);
679
680 char *e = out + len;
681 char *c = e - 1;
682 char *p = NULL;
683
684 while (c >= out && *c != '/') {
685 if (*c == '.') {
686 p = c+1;
687 break;
688 }
689 c--;
690 }
691
692 if (!p) {
693 *out = 0;
694 return 0;
695 }
696
697 memmove (out, p, e-p+1);
698 return (int)(e-p);
699 }
700
701 int
tf_func_filename(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)702 tf_func_filename (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
703 if (argc < 1 || argc > 2) {
704 return -1;
705 }
706
707 int bool_out = 0;
708
709 int len;
710 TF_EVAL_CHECK(len, ctx, args, arglens[0], out, outlen, fail_on_undef);
711
712 char *e = out + len;
713 char *p = e - 1;
714 while (p >= out && *p != '/') {
715 p--;
716 }
717
718 p++;
719
720 memmove (out, p, e-p+1);
721 return (int)(e-p);
722 }
723
724 int
tf_func_add(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)725 tf_func_add (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
726 int bool_out = 0;
727
728 int outval = 0;
729 const char *arg = args;
730 for (int i = 0; i < argc; i++) {
731 int len;
732 TF_EVAL_CHECK(len, ctx, arg, arglens[i], out, outlen, fail_on_undef);
733 outval += atoi (out);
734 arg += arglens[i];
735 }
736 return snprintf (out, outlen, "%d", outval);
737 }
738
739 int
tf_func_div(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)740 tf_func_div (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
741 int bool_out = 0;
742
743 if (argc < 2) {
744 return -1;
745 }
746
747 float outval = 0;
748 const char *arg = args;
749 for (int i = 0; i < argc; i++) {
750 int len;
751 TF_EVAL_CHECK(len, ctx, arg, arglens[i], out, outlen, fail_on_undef);
752 if (i == 0) {
753 outval = atoi (out);
754 }
755 else {
756 int divider = atoi (out);
757 if (divider == 0) {
758 out[0] = 0;
759 return -1;
760 }
761 outval /= divider;
762 }
763 arg += arglens[i];
764 }
765 int res = snprintf (out, outlen, "%d", (int)round (outval));
766 return res;
767 }
768
769 int
tf_func_max(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)770 tf_func_max (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
771 int bool_out = 0;
772
773 if (argc == 0) {
774 return -1;
775 }
776
777 int nmax = -1;
778 const char *arg = args;
779 for (int i = 0; i < argc; i++) {
780 int len;
781 TF_EVAL_CHECK(len, ctx, arg, arglens[i], out, outlen, fail_on_undef);
782 int n = atoi (out);
783 if (n > nmax) {
784 nmax = n;
785 }
786 arg += arglens[i];
787 }
788 int res = snprintf (out, outlen, "%d", nmax);
789 return res;
790 }
791
792 int
tf_func_min(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)793 tf_func_min (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
794 int bool_out = 0;
795
796 if (argc == 0) {
797 return -1;
798 }
799
800 int nmin = 0x7fffffff;
801 const char *arg = args;
802 for (int i = 0; i < argc; i++) {
803 int len;
804 TF_EVAL_CHECK(len, ctx, arg, arglens[i], out, outlen, fail_on_undef);
805 int n = atoi (out);
806 if (n < nmin) {
807 nmin = n;
808 }
809 arg += arglens[i];
810 }
811 int res = snprintf (out, outlen, "%d", nmin);
812 return res;
813 }
814
815 int
tf_func_mod(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)816 tf_func_mod (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
817 int bool_out = 0;
818
819 if (argc < 2) {
820 return -1;
821 }
822
823 int outval = 0;
824 const char *arg = args;
825 for (int i = 0; i < argc; i++) {
826 int len;
827 TF_EVAL_CHECK(len, ctx, arg, arglens[i], out, outlen, fail_on_undef);
828 if (i == 0) {
829 outval = atoi (out);
830 }
831 else {
832 int divider = atoi (out);
833 if (divider == 0) {
834 *out = 0;
835 return -1;
836 }
837 outval %= divider;
838 }
839 arg += arglens[i];
840 }
841 int res = snprintf (out, outlen, "%d", outval);
842 return res;
843 }
844
845 int
tf_func_mul(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)846 tf_func_mul (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
847 int bool_out = 0;
848
849 if (argc < 2) {
850 return -1;
851 }
852
853 int outval = 0;
854 const char *arg = args;
855 for (int i = 0; i < argc; i++) {
856 int len;
857 TF_EVAL_CHECK(len, ctx, arg, arglens[i], out, outlen, fail_on_undef);
858 if (i == 0) {
859 outval = atoi (out);
860 }
861 else {
862 outval *= atoi (out);
863 }
864 arg += arglens[i];
865 }
866 int res = snprintf (out, outlen, "%d", outval);
867 return res;
868 }
869
870 int
tf_func_muldiv(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)871 tf_func_muldiv (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
872 int bool_out = 0;
873
874 if (argc != 3) {
875 return -1;
876 }
877
878 int vals[3];
879 const char *arg = args;
880 for (int i = 0; i < argc; i++) {
881 int len;
882 TF_EVAL_CHECK(len, ctx, arg, arglens[i], out, outlen, fail_on_undef);
883 vals[i] = atoi (out);
884 arg += arglens[i];
885 }
886
887 if (vals[2] == 0) {
888 *out = 0;
889 return -1;
890 }
891
892 int outval = (int)round(vals[0] * vals[1] / (float)vals[2]);
893
894 int res = snprintf (out, outlen, "%d", outval);
895 return res;
896 }
897
898 int
tf_func_rand(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)899 tf_func_rand (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
900 if (argc != 0) {
901 return -1;
902 }
903
904 int outval = rand ();
905
906 int res = snprintf (out, outlen, "%d", outval);
907 return res;
908 }
909
910 int
tf_func_sub(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)911 tf_func_sub (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
912 int bool_out = 0;
913
914 if (argc < 2) {
915 return -1;
916 }
917
918 int outval = 0;
919 const char *arg = args;
920 for (int i = 0; i < argc; i++) {
921 int len;
922 TF_EVAL_CHECK(len, ctx, arg, arglens[i], out, outlen, fail_on_undef);
923 if (i == 0) {
924 outval = atoi (out);
925 }
926 else {
927 outval -= atoi (out);
928 }
929 arg += arglens[i];
930 }
931 int res = snprintf (out, outlen, "%d", outval);
932 return res;
933 }
934
935 int
tf_func_if(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)936 tf_func_if (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
937 if (argc < 2 || argc > 3) {
938 return -1;
939 }
940 int bool_out = 0;
941
942 const char *arg = args;
943 int res;
944 TF_EVAL_CHECK(res, ctx, arg, arglens[0], out, outlen, fail_on_undef);
945 arg += arglens[0];
946 if (bool_out) {
947 trace ("condition true, eval then block\n");
948 TF_EVAL_CHECK(res, ctx, arg, arglens[1], out, outlen, fail_on_undef);
949 }
950 else if (argc == 3) {
951 trace ("condition false, eval else block\n");
952 arg += arglens[1];
953 TF_EVAL_CHECK(res, ctx, arg, arglens[2], out, outlen, fail_on_undef);
954 }
955
956 return res;
957 }
958
959 int
tf_func_if2(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)960 tf_func_if2 (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
961 if (argc != 2) {
962 return -1;
963 }
964 int bool_out = 0;
965
966 const char *arg = args;
967 int res;
968 TF_EVAL_CHECK(res, ctx, arg, arglens[0], out, outlen, fail_on_undef);
969 arg += arglens[0];
970 if (bool_out) {
971 return res;
972 }
973 else {
974 TF_EVAL_CHECK(res, ctx, arg, arglens[1], out, outlen, fail_on_undef);
975 }
976
977 return res;
978 }
979
980 int
tf_func_if3(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)981 tf_func_if3 (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
982 if (argc < 2) {
983 return -1;
984 }
985 int bool_out = 0;
986
987 const char *arg = args;
988 for (int i = 0; i < argc; i++) {
989 int res;
990 TF_EVAL_CHECK(res, ctx, arg, arglens[i], out, outlen, fail_on_undef);
991 arg += arglens[i];
992 if (bool_out || i == argc-1) {
993 return res;
994 }
995 }
996 *out = 0;
997 return -1;
998 }
999
1000 int
tf_func_ifequal(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)1001 tf_func_ifequal (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
1002 if (argc != 4) {
1003 return -1;
1004 }
1005
1006 int bool_out = 0;
1007
1008 const char *arg = args;
1009 int len;
1010 TF_EVAL_CHECK(len, ctx, arg, arglens[0], out, outlen, fail_on_undef);
1011
1012 int arg1 = atoi (out);
1013
1014 arg += arglens[0];
1015 TF_EVAL_CHECK(len, ctx, arg, arglens[1], out, outlen, fail_on_undef);
1016
1017 int arg2 = atoi (out);
1018
1019 arg += arglens[1];
1020
1021 int idx = 2;
1022 if (arg1 != arg2) {
1023 arg += arglens[2];
1024 idx = 3;
1025 }
1026
1027 TF_EVAL_CHECK(len, ctx, arg, arglens[idx], out, outlen, fail_on_undef);
1028 return len;
1029 }
1030
1031 int
tf_func_ifgreater(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)1032 tf_func_ifgreater (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
1033 if (argc != 4) {
1034 return -1;
1035 }
1036
1037 int bool_out = 0;
1038
1039 const char *arg = args;
1040 int len;
1041 TF_EVAL_CHECK(len, ctx, arg, arglens[0], out, outlen, fail_on_undef);
1042
1043 int arg1 = atoi (out);
1044
1045 arg += arglens[0];
1046 TF_EVAL_CHECK(len, ctx, arg, arglens[1], out, outlen, fail_on_undef);
1047
1048 int arg2 = atoi (out);
1049
1050 arg += arglens[1];
1051
1052 int idx = 2;
1053 if (arg1 <= arg2) {
1054 arg += arglens[2];
1055 idx = 3;
1056 }
1057
1058 TF_EVAL_CHECK(len, ctx, arg, arglens[idx], out, outlen, fail_on_undef);
1059 return len;
1060 }
1061
1062 int
tf_func_iflonger(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)1063 tf_func_iflonger (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
1064 if (argc != 4) {
1065 return -1;
1066 }
1067
1068 int bool_out = 0;
1069
1070 const char *arg = args;
1071 int len;
1072 TF_EVAL_CHECK(len, ctx, arg, arglens[0], out, outlen, fail_on_undef);
1073 int l1 = (int)strlen (out);
1074
1075 arg += arglens[0];
1076 TF_EVAL_CHECK(len, ctx, arg, arglens[1], out, outlen, fail_on_undef);
1077 int l2 = (int)strlen (out);
1078
1079 arg += arglens[1];
1080 int idx = 2;
1081 if (l1 <= l2) {
1082 arg += arglens[2];
1083 idx = 3;
1084 }
1085
1086 TF_EVAL_CHECK(len, ctx, arg, arglens[idx], out, outlen, fail_on_undef);
1087 return len;
1088 }
1089
1090 int
tf_func_select(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)1091 tf_func_select (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
1092 if (argc < 3) {
1093 return -1;
1094 }
1095
1096 const char *arg = args;
1097
1098 int bool_out = 0;
1099
1100 int res;
1101 TF_EVAL_CHECK(res, ctx, arg, arglens[0], out, outlen, fail_on_undef);
1102
1103 int n = atoi (out);
1104 if (n < 1 || n >= argc) {
1105 return 0;
1106 }
1107
1108 arg += arglens[0];
1109
1110 for (int i = 1; i < n; i++) {
1111 arg += arglens[i];
1112 }
1113 TF_EVAL_CHECK(res, ctx, arg, arglens[n], out, outlen, fail_on_undef);
1114 return res;
1115 }
1116
1117 static void
tf_append_out(char ** out,int * out_len,const char * in,int in_len)1118 tf_append_out (char **out, int *out_len, const char *in, int in_len) {
1119 in_len = min (in_len, *out_len);
1120 in_len = u8_strnbcpy (*out, in, in_len);
1121 *out_len -= in_len;
1122 *out += in_len;
1123 }
1124
1125 int
tf_func_meta(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)1126 tf_func_meta (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
1127 if (argc != 1) {
1128 return -1;
1129 }
1130
1131 if (!ctx->it) {
1132 return 0;
1133 }
1134
1135 int bool_out = 0;
1136
1137 const char *arg = args;
1138 int len;
1139 TF_EVAL_CHECK(len, ctx, arg, arglens[0], out, outlen, fail_on_undef);
1140
1141 const char *meta = pl_find_meta_raw ((playItem_t *)ctx->it, out);
1142 if (!meta) {
1143 return 0;
1144 }
1145
1146 return u8_strnbcpy(out, meta, outlen);
1147 }
1148
1149 const char *
tf_get_channels_string_for_track(playItem_t * it)1150 tf_get_channels_string_for_track (playItem_t *it) {
1151 const char *val = pl_find_meta_raw (it, ":CHANNELS");
1152 if (val) {
1153 int ch = atoi (val);
1154 if (ch == 1) {
1155 val = _("mono");
1156 }
1157 else if (ch == 2) {
1158 val = _("stereo");
1159 }
1160 }
1161 else {
1162 val = _("stereo");
1163 }
1164 return val;
1165 }
1166
1167 int
tf_func_channels(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)1168 tf_func_channels (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
1169 if (argc != 0) {
1170 return -1;
1171 }
1172
1173 if (!ctx->it) {
1174 return 0;
1175 }
1176
1177 const char *val = tf_get_channels_string_for_track ((playItem_t *)ctx->it);
1178 return u8_strnbcpy(out, val, outlen);
1179 }
1180
1181 // Boolean
1182 int
tf_func_and(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)1183 tf_func_and (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
1184 int bool_out = 0;
1185
1186 const char *arg = args;
1187 for (int i = 0; i < argc; i++) {
1188 int len;
1189 TF_EVAL_CHECK(len, ctx, arg, arglens[i], out, outlen, fail_on_undef);
1190 if (!bool_out) {
1191 return 0;
1192 }
1193 arg += arglens[i];
1194 }
1195 *out = 0;
1196 return 1;
1197 }
1198
1199 int
tf_func_or(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)1200 tf_func_or (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
1201 int bool_out = 0;
1202
1203 const char *arg = args;
1204 for (int i = 0; i < argc; i++) {
1205 int len;
1206 TF_EVAL_CHECK(len, ctx, arg, arglens[i], out, outlen, fail_on_undef);
1207 if (bool_out) {
1208 return 1;
1209 }
1210 arg += arglens[i];
1211 }
1212 *out = 0;
1213 return 0;
1214 }
1215
1216 int
tf_func_not(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)1217 tf_func_not (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
1218 if (argc != 1) {
1219 return -1;
1220 }
1221 int bool_out = 0;
1222
1223 int len;
1224 TF_EVAL_CHECK(len, ctx, args, arglens[0], out, outlen, fail_on_undef);
1225 return !bool_out;
1226 }
1227
1228 int
tf_func_xor(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)1229 tf_func_xor (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
1230 int bool_out = 0;
1231 int result = 0;
1232
1233 const char *arg = args;
1234 for (int i = 0; i < argc; i++) {
1235 int len;
1236 TF_EVAL_CHECK(len, ctx, arg, arglens[i], out, outlen, fail_on_undef);
1237 if (i == 0) {
1238 result = bool_out;
1239 }
1240 else {
1241 result ^= bool_out;
1242 }
1243 arg += arglens[i];
1244 }
1245 *out = 0;
1246 return result;
1247 }
1248
1249 int
tf_func_fix_eol(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)1250 tf_func_fix_eol (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
1251 if (argc != 1 && argc != 2) {
1252 return -1;
1253 }
1254
1255 int bool_out = 0;
1256
1257 int len;
1258
1259 char *p = out;
1260 TF_EVAL_CHECK(len, ctx, args, arglens[0], out, outlen, fail_on_undef);
1261
1262 char ind[1000];
1263 int indlen = sizeof (ind);
1264 if (argc == 2) {
1265 TF_EVAL_CHECK(indlen, ctx, args + arglens[0], arglens[1], ind, indlen, fail_on_undef);
1266 }
1267 else {
1268 strcpy (ind, " (...)");
1269 indlen = (int)strlen (ind);
1270 }
1271
1272 for (int n = 0; n < len; n++, p++) {
1273 if (*p == '\n') {
1274 if (outlen-n < indlen) {
1275 *out = 0;
1276 return -1;
1277 }
1278 memcpy (p, ind, indlen);
1279 len = n + indlen;
1280 break;
1281 }
1282 }
1283
1284 return len;
1285 }
1286
1287 int
tf_func_hex(ddb_tf_context_t * ctx,int argc,const char * arglens,const char * args,char * out,int outlen,int fail_on_undef)1288 tf_func_hex (ddb_tf_context_t *ctx, int argc, const char *arglens, const char *args, char *out, int outlen, int fail_on_undef) {
1289 if (argc != 1 && argc != 2) {
1290 return -1;
1291 }
1292
1293 int bool_out = 0;
1294
1295 int len;
1296
1297 TF_EVAL_CHECK(len, ctx, args, arglens[0], out, outlen, fail_on_undef);
1298 int num = atoi (out);
1299 int pad = 0;
1300 *out = 0;
1301
1302 if (argc == 2) {
1303 TF_EVAL_CHECK(len, ctx, args + arglens[0], arglens[1], out, outlen, fail_on_undef);
1304 if (!isdigit (*out)) {
1305 *out = 0;
1306 return -1;
1307 }
1308 pad = atoi (out);
1309 *out = 0;
1310 }
1311
1312 int n = num;
1313 int cnt = 0;
1314 do {
1315 n >>= 4;
1316 cnt++;
1317 } while (n);
1318
1319 char *p = out;
1320
1321 if (pad > outlen || cnt > outlen) {
1322 return -1;
1323 }
1324
1325 if (pad > cnt) {
1326 for (n = 0; n < pad-cnt; n++, p++) {
1327 *p = '0';
1328 }
1329 }
1330 p += cnt;
1331 *p-- = 0;
1332
1333 n = num;
1334
1335 const char hex[] = "0123456789abcdef";
1336
1337 do {
1338 *p-- = hex[n & 0x0f];
1339 n >>= 4;
1340 } while (n);
1341
1342 return (int)strlen (out);
1343 }
1344
1345 tf_func_def tf_funcs[TF_MAX_FUNCS] = {
1346 // Control flow
1347 { "if", tf_func_if },
1348 { "if2", tf_func_if2 },
1349 { "if3", tf_func_if3 },
1350 { "ifequal", tf_func_ifequal },
1351 { "ifgreater", tf_func_ifgreater },
1352 { "iflonger", tf_func_iflonger },
1353 { "select", tf_func_select },
1354 // Arithmetic
1355 { "add", tf_func_add },
1356 { "div", tf_func_div },
1357 { "greater", tf_func_greater },
1358 { "max", tf_func_max },
1359 { "min", tf_func_min },
1360 { "mod", tf_func_mod },
1361 { "mul", tf_func_mul },
1362 { "muldiv", tf_func_muldiv },
1363 { "rand", tf_func_rand },
1364 { "sub", tf_func_sub },
1365 // Boolean
1366 { "and", tf_func_and },
1367 { "or", tf_func_or },
1368 { "not", tf_func_not },
1369 { "xor", tf_func_xor },
1370 // String
1371 { "abbr", tf_func_abbr },
1372 { "ansi", tf_func_ansi },
1373 { "ascii", tf_func_ascii },
1374 { "caps", tf_func_caps },
1375 { "caps2", tf_func_caps2 },
1376 { "char", tf_func_char },
1377 { "crc32", tf_func_crc32 },
1378 { "crlf", tf_func_crlf },
1379 { "cut", tf_func_left },
1380 { "left", tf_func_left }, // alias of 'cut'
1381 { "directory", tf_func_directory },
1382 { "directory_path", tf_func_directory_path },
1383 { "ext", tf_func_ext },
1384 { "filename", tf_func_filename },
1385 { "fix_eol", tf_func_fix_eol },
1386 { "hex", tf_func_hex },
1387 { "strcmp", tf_func_strcmp },
1388 { "num", tf_func_num },
1389 // Track info
1390 { "meta", tf_func_meta },
1391 { "channels", tf_func_channels },
1392 { NULL, NULL }
1393 };
1394
1395 static int
tf_eval_int(ddb_tf_context_t * ctx,const char * code,int size,char * out,int outlen,int * bool_out,int fail_on_undef)1396 tf_eval_int (ddb_tf_context_t *ctx, const char *code, int size, char *out, int outlen, int *bool_out, int fail_on_undef) {
1397 playItem_t *it = (playItem_t *)ctx->it;
1398 char *init_out = out;
1399 *bool_out = 0;
1400 while (size) {
1401 if (*code) {
1402 int len = u8_charcpy (out, code, outlen);
1403 if (len == 0) {
1404 break;
1405 }
1406 code += len;
1407 size -= len;
1408 out += len;
1409 outlen -= len;
1410 }
1411 else {
1412 code++;
1413 size--;
1414 if (*code == 1) {
1415 code++;
1416 size--;
1417 tf_func_ptr_t func = tf_funcs[*code].func;
1418 code++;
1419 size--;
1420 int res = func (ctx, code[0], code+1, code+1+code[0], out, outlen, fail_on_undef);
1421 if (res == -1) {
1422 return -1;
1423 }
1424 if (res > 0) {
1425 *bool_out = 1;
1426 // hack for returning true + empty string result (e.g. tf_func_and)
1427 if (*out == 0) {
1428 res = 0;
1429 }
1430 }
1431
1432 out += res;
1433 outlen -= res;
1434
1435 int blocksize = 1 + code[0];
1436 for (int i = 0; i < code[0]; i++) {
1437 blocksize += code[1+i];
1438 }
1439 code += blocksize;
1440 size -= blocksize;
1441 }
1442 else if (*code == 2) {
1443 code++;
1444 size--;
1445 uint8_t len = *code;
1446 code++;
1447 size--;
1448
1449 char name[len+1];
1450 memcpy (name, code, len);
1451 name[len] = 0;
1452
1453 // special cases
1454 // most if not all of this stuff is to make tf scripts
1455 // compatible with fb2k syntax
1456 pl_lock ();
1457 const char *val = NULL;
1458 const char *aa_fields[] = { "album artist", "albumartist", "band", "artist", "composer", "performer", NULL };
1459 const char *a_fields[] = { "artist", "album artist", "albumartist", "composer", "performer", NULL };
1460 const char *alb_fields[] = { "album", "venue", NULL };
1461
1462 // set to 1 if special case handler successfully wrote the output
1463 int skip_out = 0;
1464
1465 // temp vars used for strcmp optimizations
1466 int tmp_a = 0, tmp_b = 0, tmp_c = 0, tmp_d = 0;
1467
1468 if (!strcmp (name, aa_fields[0])) {
1469 for (int i = 0; !val && aa_fields[i]; i++) {
1470 val = pl_find_meta_raw (it, aa_fields[i]);
1471 }
1472 }
1473 else if (!strcmp (name, a_fields[0])) {
1474 for (int i = 0; !val && a_fields[i]; i++) {
1475 val = pl_find_meta_raw (it, a_fields[i]);
1476 }
1477 }
1478 else if (!strcmp (name, "album")) {
1479 for (int i = 0; !val && alb_fields[i]; i++) {
1480 val = pl_find_meta_raw (it, alb_fields[i]);
1481 }
1482 }
1483 else if (!strcmp (name, "track artist")) {
1484 const char *aa = NULL;
1485 for (int i = 0; !val && aa_fields[i]; i++) {
1486 val = pl_find_meta_raw (it, aa_fields[i]);
1487 }
1488 aa = val;
1489 val = NULL;
1490 for (int i = 0; !val && a_fields[i]; i++) {
1491 val = pl_find_meta_raw (it, a_fields[i]);
1492 }
1493 if (val && aa && !strcmp (val, aa)) {
1494 val = NULL;
1495 }
1496 }
1497 else if (!strcmp (name, "tracknumber")) {
1498 const char *v = pl_find_meta_raw (it, "track");
1499 if (v) {
1500 const char *p = v;
1501 while (*p) {
1502 if (!isdigit (*p)) {
1503 break;
1504 }
1505 p++;
1506 }
1507 if (p > v) {
1508 int len = snprintf (out, outlen, "%02d", atoi(v));
1509 out += len;
1510 outlen -= len;
1511 skip_out = 1;
1512 }
1513 }
1514 }
1515 else if (!strcmp (name, "title")) {
1516 val = pl_find_meta_raw (it, "title");
1517 if (!val) {
1518 const char *v = pl_find_meta_raw (it, ":URI");
1519 if (v) {
1520 const char *start = strrchr (v, '/');
1521 if (start) {
1522 start++;
1523 }
1524 else {
1525 start = v;
1526 }
1527 const char *end = strrchr (start, '.');
1528 if (end) {
1529 int n = (int)(end-start);
1530 n = min ((int)(end-start), outlen);
1531 n = u8_strnbcpy (out, start, n);
1532 outlen -= n;
1533 out += n;
1534 }
1535 }
1536 }
1537 }
1538 else if (!strcmp (name, "discnumber")) {
1539 val = pl_find_meta_raw (it, "disc");
1540 }
1541 else if (!strcmp (name, "totaldiscs")) {
1542 val = pl_find_meta_raw (it, "numdiscs");
1543 }
1544 else if (!strcmp (name, "track number")) {
1545 const char *v = pl_find_meta_raw (it, "track");
1546 if (v) {
1547 const char *p = v;
1548 while (*p) {
1549 if (!isdigit (*p)) {
1550 break;
1551 }
1552 p++;
1553 }
1554 if (p > v) {
1555 int len = snprintf (out, outlen, "%d", atoi(v));
1556 out += len;
1557 outlen -= len;
1558 skip_out = 1;
1559 }
1560 }
1561 }
1562 else if (!strcmp (name, "date")) {
1563 // NOTE: foobar2000 uses "date" instead of "year"
1564 // so for %date% we simply return the content of "year"
1565 val = pl_find_meta_raw (it, "year");
1566 }
1567 else if (!strcmp (name, "samplerate")) {
1568 val = pl_find_meta_raw (it, ":SAMPLERATE");
1569 }
1570 else if (!strcmp (name, "bitrate")) {
1571 val = pl_find_meta_raw (it, ":BITRATE");
1572 }
1573 else if (!strcmp (name, "filesize")) {
1574 val = pl_find_meta_raw (it, ":FILE_SIZE");
1575 }
1576 else if (!strcmp (name, "filesize_natural")) {
1577 const char *v = pl_find_meta_raw (it, ":FILE_SIZE");
1578 if (v) {
1579 int64_t bs = atoll (v);
1580 int len;
1581 if (bs >= 1024*1024*1024) {
1582 double gb = (double)bs / (double)(1024*1024*1024);
1583 len = snprintf (out, outlen, "%.3lf GB", gb);
1584 }
1585 else if (bs >= 1024*1024) {
1586 double mb = (double)bs / (double)(1024*1024);
1587 len = snprintf (out, outlen, "%.3lf MB", mb);
1588 }
1589 else if (bs >= 1024) {
1590 double kb = (double)bs / (double)(1024);
1591 len = snprintf (out, outlen, "%.3lf KB", kb);
1592 }
1593 else {
1594 len = snprintf (out, outlen, "%lld B", bs);
1595 }
1596 out += len;
1597 outlen -= len;
1598 skip_out = 1;
1599 }
1600 }
1601 else if (!strcmp (name, "channels")) {
1602 val = tf_get_channels_string_for_track (it);
1603 }
1604 else if (!strcmp (name, "codec")) {
1605 val = pl_find_meta_raw (it, ":FILETYPE");
1606 }
1607 else if (!strcmp (name, "replaygain_album_gain")) {
1608 val = pl_find_meta_raw (it, ":REPLAYGAIN_ALBUMGAIN");
1609 }
1610 else if (!strcmp (name, "replaygain_album_peak")) {
1611 val = pl_find_meta_raw (it, ":REPLAYGAIN_ALBUMPEAK");
1612 }
1613 else if (!strcmp (name, "replaygain_track_gain")) {
1614 val = pl_find_meta_raw (it, ":REPLAYGAIN_TRACKGAIN");
1615 }
1616 else if (!strcmp (name, "replaygain_track_peak")) {
1617 val = pl_find_meta_raw (it, ":REPLAYGAIN_TRACKPEAK");
1618 }
1619 else if ((tmp_a = !strcmp (name, "playback_time")) || (tmp_b = !strcmp (name, "playback_time_seconds")) || (tmp_c = !strcmp (name, "playback_time_remaining")) || (tmp_d = !strcmp (name, "playback_time_remaining_seconds"))) {
1620 playItem_t *playing = streamer_get_playing_track ();
1621 if (it && playing == it && !(ctx->flags & DDB_TF_CONTEXT_NO_DYNAMIC)) {
1622 float t = streamer_get_playpos ();
1623 if (tmp_c || tmp_d) {
1624 printf ("inverse time %d %d %d %d\n", tmp_a, tmp_b, tmp_c, tmp_d);
1625 float dur = pl_get_item_duration (it);
1626 t = dur - t;
1627 }
1628 if (t >= 0) {
1629 int len = 0;
1630 if (tmp_a || tmp_c) {
1631 int hr = t/3600;
1632 int mn = (t-hr*3600)/60;
1633 int sc = t-hr*3600-mn*60;
1634 if (hr) {
1635 len = snprintf (out, outlen, "%d:%02d:%02d", hr, mn, sc);
1636 }
1637 else {
1638 len = snprintf (out, outlen, "%d:%02d", mn, sc);
1639 }
1640 }
1641 else if (tmp_b || tmp_d) {
1642 len = snprintf (out, outlen, "%0.2f", t);
1643 }
1644 out += len;
1645 outlen -= len;
1646 skip_out = 1;
1647 // notify the caller about update interval
1648 if (!ctx->update || (ctx->update > 1000)) {
1649 ctx->update = 1000;
1650 }
1651 }
1652 }
1653 if (playing) {
1654 pl_item_unref (playing);
1655 }
1656 }
1657 else if ((tmp_a = !strcmp (name, "length")) || (tmp_b = !strcmp (name, "length_ex"))) {
1658 float t = pl_get_item_duration (it);
1659 if (tmp_a) {
1660 t = roundf (t);
1661 }
1662 else if (tmp_b) {
1663 t = roundf(t * 1000) / 1000.f;
1664 }
1665 if (t >= 0) {
1666 int hr = t/3600;
1667 int mn = (t-hr*3600)/60;
1668 int sc = tmp_a ? t-hr*3600-mn*60 : t-hr*3600-mn*60;
1669 int ms = tmp_b ? (t-hr*3600-mn*60-sc) * 1000.f : 0;
1670 int len = 0;
1671 if (tmp_a) {
1672 if (hr) {
1673 len = snprintf (out, outlen, "%d:%02d:%02d", hr, mn, sc);
1674 }
1675 else {
1676 len = snprintf (out, outlen, "%d:%02d", mn, sc);
1677 }
1678 }
1679 else if (tmp_b) {
1680 if (hr) {
1681 len = snprintf (out, outlen, "%d:%02d:%02d.%03d", hr, mn, sc, ms);
1682 }
1683 else {
1684 len = snprintf (out, outlen, "%d:%02d.%03d", mn, sc, ms);
1685 }
1686 }
1687 out += len;
1688 outlen -= len;
1689 skip_out = 1;
1690 }
1691 }
1692 else if ((tmp_a = !strcmp (name, "length_seconds") || (tmp_b = !strcmp (name, "length_seconds_fp")))) {
1693 float t = pl_get_item_duration (it);
1694 if (t >= 0) {
1695 int len;
1696 if (tmp_a) {
1697 len = snprintf (out, outlen, "%d", (int)roundf(t));
1698 }
1699 else {
1700 len = snprintf (out, outlen, "%0.3f", t);
1701 }
1702 out += len;
1703 outlen -= len;
1704 skip_out = 1;
1705 }
1706 }
1707 else if (!strcmp (name, "length_samples")) {
1708 int len = snprintf (out, outlen, "%d", ctx->it->endsample - ctx->it->startsample);
1709 out += len;
1710 outlen -= len;
1711 skip_out = 1;
1712 }
1713 else if ((tmp_a = !strcmp (name, "isplaying")) || (tmp_b = !strcmp (name, "ispaused"))) {
1714 playItem_t *playing = streamer_get_playing_track ();
1715
1716 if (playing &&
1717 (
1718 (tmp_a && plug_get_output ()->state () == OUTPUT_STATE_PLAYING)
1719 || (tmp_b && plug_get_output ()->state () == OUTPUT_STATE_PAUSED)
1720 )) {
1721 *out++ = '1';
1722 outlen--;
1723 skip_out = 1;
1724 }
1725 if (playing) {
1726 pl_item_unref (playing);
1727 }
1728 }
1729 else if (!strcmp (name, "filename")) {
1730 const char *v = pl_find_meta_raw (it, ":URI");
1731 if (v) {
1732 const char *start = strrchr (v, '/');
1733 if (start) {
1734 start++;
1735 }
1736 else {
1737 start = v;
1738 }
1739 const char *end = strrchr (start, '.');
1740 if (end) {
1741 tf_append_out(&out, &outlen, start, (int)(end-start));
1742 skip_out = 1;
1743 }
1744 }
1745 }
1746 else if (!strcmp (name, "filename_ext")) {
1747 const char *v = pl_find_meta_raw (it, ":URI");
1748 if (v) {
1749 const char *start = strrchr (v, '/');
1750 if (start) {
1751 tf_append_out (&out, &outlen, start+1, (int)strlen (start+1));
1752 skip_out = 1;
1753 }
1754 }
1755 }
1756 else if (!strcmp (name, "directoryname")) {
1757 const char *v = pl_find_meta_raw (it, ":URI");
1758 if (v) {
1759 const char *end = strrchr (v, '/');
1760 if (end) {
1761 const char *start = end - 1;
1762 while (start >= v && *start != '/') {
1763 start--;
1764 }
1765 if (start && start != end) {
1766 start++;
1767 tf_append_out(&out, &outlen, start, (int)(end-start));
1768 skip_out = 1;
1769 }
1770 }
1771 }
1772 }
1773 else if (!strcmp (name, "path")) {
1774 val = pl_find_meta_raw (it, ":URI");
1775 }
1776 // index of track in playlist (zero-padded)
1777 else if (!strcmp (name, "list_index")) {
1778 if (it) {
1779 int total_tracks = plt_get_item_count ((playlist_t *)ctx->plt, ctx->iter);
1780 int digits = 0;
1781 do {
1782 total_tracks /= 10;
1783 digits++;
1784 } while (total_tracks);
1785
1786 int idx = 0;
1787 if (ctx->flags & DDB_TF_CONTEXT_HAS_INDEX) {
1788 idx = ctx->idx + 1;
1789 }
1790 else {
1791 idx = pl_get_idx_of_iter (it, ctx->iter) + 1;
1792 }
1793 int len = snprintf (out, outlen, "%0*d", digits, idx);
1794 out += len;
1795 outlen -= len;
1796 skip_out = 1;
1797 }
1798 }
1799 // total number of tracks in playlist
1800 else if (!strcmp (name, "list_total")) {
1801 int total_tracks = -1;
1802 if (ctx->plt) {
1803 total_tracks = plt_get_item_count ((playlist_t *)ctx->plt, ctx->iter);
1804 }
1805 else {
1806 playlist_t *plt = plt_get_curr ();
1807 if (plt) {
1808 total_tracks = plt_get_item_count (plt, ctx->iter);
1809 plt_unref (plt);
1810 }
1811 }
1812 if (total_tracks >= 0) {
1813 int len = snprintf (out, outlen, "%d", total_tracks);
1814 out += len;
1815 outlen -= len;
1816 skip_out = 1;
1817 }
1818 }
1819 // index of track in queue
1820 else if (!strcmp (name, "queue_index")) {
1821 if (it) {
1822 int idx = playqueue_test (it) + 1;
1823 if (idx >= 1) {
1824 int len = snprintf (out, outlen, "%d", idx);
1825 out += len;
1826 outlen -= len;
1827 skip_out = 1;
1828 }
1829 }
1830 }
1831 // indexes of track in queue
1832 else if (!strcmp (name, "queue_indexes")) {
1833 if (it) {
1834 int idx = playqueue_test (it) + 1;
1835 if (idx >= 1) {
1836 int len = snprintf (out, outlen, "%d", idx);
1837 out += len;
1838 outlen -= len;
1839 int count = playqueue_getcount ();
1840 for (int i = idx; i < count; i++) {
1841 playItem_t *trk = playqueue_get_item (i);
1842 if (trk) {
1843 if (it == trk) {
1844 len = snprintf (out, outlen, ",%d", i + 1);
1845 out += len;
1846 outlen -= len;
1847 }
1848 pl_item_unref (trk);
1849 }
1850 }
1851 skip_out = 1;
1852 }
1853 }
1854 }
1855 // total amount of tracks in queue
1856 else if (!strcmp (name, "queue_total")) {
1857 int count = playqueue_getcount ();
1858 if (count >= 0) {
1859 int len = snprintf (out, outlen, "%d", count);
1860 out += len;
1861 outlen -= len;
1862 skip_out = 1;
1863 }
1864 }
1865 else if (!strcmp (name, "_deadbeef_version")) {
1866 val = VERSION;
1867 }
1868 else {
1869 val = pl_find_meta_raw (it, name);
1870 }
1871
1872 if (val || (!val && out > init_out)) {
1873 *bool_out = 1;
1874 }
1875
1876 // default case
1877 if (!skip_out && val) {
1878 int32_t l = u8_strnbcpy (out, val, outlen);
1879
1880 // replace any \n with ; for display
1881 char *p = out;
1882 while (p < out + l) {
1883 if (*p == '\n') {
1884 //*p = ';';
1885 }
1886 p++;
1887 }
1888
1889 out += l;
1890 outlen -= l;
1891 }
1892 pl_unlock ();
1893 if (!skip_out && !val && fail_on_undef) {
1894 return -1;
1895 }
1896
1897 code += len;
1898 size -= len;
1899 }
1900 else if (*code == 3) {
1901 code++;
1902 size--;
1903 int32_t len;
1904 memcpy (&len, code, 4);
1905 code += 4;
1906 size -= 4;
1907
1908 int bool_out = 0;
1909 int res = tf_eval_int (ctx, code, len, out, outlen, &bool_out, 1);
1910 if (res > 0) {
1911 out += res;
1912 outlen -= res;
1913 }
1914 else if (res < 0 && fail_on_undef) {
1915 return res;
1916 }
1917 code += len;
1918 size -= len;
1919 }
1920 else if (*code == 4) {
1921 code++;
1922 size--;
1923 int32_t len;
1924 memcpy (&len, code, 4);
1925 code += 4;
1926 size -= 4;
1927 int32_t l = u8_strnbcpy(out, code, len);
1928 out += l;
1929 outlen -= l;
1930 code += len;
1931 size -= len;
1932 }
1933 else {
1934 return -1;
1935 }
1936 }
1937 }
1938 *out = 0;
1939 return (int)(out-init_out);
1940 }
1941
1942 int
1943 tf_compile_plain (tf_compiler_t *c);
1944
1945 int
tf_compile_func(tf_compiler_t * c)1946 tf_compile_func (tf_compiler_t *c) {
1947 c->i++;
1948
1949 // function marker
1950 *(c->o++) = 0;
1951 *(c->o++) = 1;
1952
1953 const char *name_start = c->i;
1954
1955 // find opening (
1956 while (*(c->i) && *(c->i) != '(') {
1957 c->i++;
1958 }
1959
1960 if (!*(c->i)) {
1961 return -1;
1962 }
1963
1964 int i;
1965 for (i = 0; tf_funcs[i].name; i++) {
1966 int l = (int)strlen (tf_funcs[i].name);
1967 if (c->i - name_start == l && !memcmp (tf_funcs[i].name, name_start, l)) {
1968 *(c->o++) = i;
1969 break;
1970 }
1971 }
1972 if (!tf_funcs[i].name) {
1973 return -1;
1974 }
1975
1976 char func_name[c->i - name_start + 1];
1977 memcpy (func_name, name_start, c->i-name_start);
1978 func_name[c->i-name_start] = 0;
1979
1980 c->i++;
1981
1982 // remember ptr and start reading args
1983 char *start = c->o;
1984 *(c->o++) = 0; // num args
1985 char *argstart = c->o;
1986
1987 //parse comma separated args until )
1988 while (*(c->i)) {
1989 if (*(c->i) == ',' || *(c->i) == ')') {
1990 // next arg
1991 int len = (int)(c->o - argstart);
1992
1993 // special case for empty argument list
1994 if (len == 0 && *(c->i) == ')' && (*start) == 0) {
1995 break;
1996 }
1997
1998 // expand arg lengths buffer by 1
1999 memmove (start+(*start)+2, start+(*start)+1, c->o - start - (*start));
2000 c->o++;
2001 (*start)++; // num args++
2002 // store arg length
2003 start[*start] = len;
2004 argstart = c->o;
2005
2006 if (*(c->i) == ')') {
2007 break;
2008 }
2009 c->i++;
2010 }
2011 else if (tf_compile_plain (c)) {
2012 return -1;
2013 }
2014 }
2015 if (*(c->i) != ')') {
2016 return -1;
2017 }
2018 c->i++;
2019
2020 return 0;
2021 }
2022
2023 int
tf_compile_field(tf_compiler_t * c)2024 tf_compile_field (tf_compiler_t *c) {
2025 c->i++;
2026 *(c->o++) = 0;
2027 *(c->o++) = 2;
2028
2029 const char *fstart = c->i;
2030 char *plen = c->o;
2031 c->o += 1;
2032 while (*(c->i)) {
2033 if (*(c->i) == '%') {
2034 break;
2035 }
2036 else {
2037 *(c->o++) = *(c->i++);
2038 }
2039 }
2040 if (*(c->i) != '%') {
2041 return -1;
2042 }
2043 c->i++;
2044
2045 int32_t len = (int32_t)(c->o - plen - 1);
2046 if (len > 0xff) {
2047 return -1;
2048 }
2049 *plen = len;
2050
2051 char field[len+1];
2052 memcpy (field, fstart, len);
2053 field[len] = 0;
2054 return 0;
2055 }
2056
2057 int
tf_compile_ifdef(tf_compiler_t * c)2058 tf_compile_ifdef (tf_compiler_t *c) {
2059 c->i++;
2060 *(c->o++) = 0;
2061 *(c->o++) = 3;
2062
2063 char *plen = c->o;
2064 c->o += 4;
2065
2066 char *start = c->o;
2067
2068 while (*(c->i)) {
2069 if (*(c->i) == '\\') {
2070 c->i++;
2071 if (*(c->i) != 0) {
2072 *(c->o++) = *(c->i++);
2073 }
2074 }
2075 else if (*(c->i) == ']') {
2076 break;
2077 }
2078 else if (tf_compile_plain (c)) {
2079 return -1;
2080 }
2081 }
2082
2083 if (*(c->i) != ']') {
2084 return -1;
2085 }
2086 c->i++;
2087
2088 int32_t len = (int32_t)(c->o - plen - 4);
2089 memcpy (plen, &len, 4);
2090
2091 char value[len+1];
2092 memcpy (value, start, len);
2093 value[len] = 0;
2094 return 0;
2095 }
2096
2097 int
tf_compile_plain(tf_compiler_t * c)2098 tf_compile_plain (tf_compiler_t *c) {
2099 int eol = c->eol;
2100 c->eol = 0;
2101 char i = *(c->i);
2102 if (i == '$') {
2103 if (c->i[1] == '$') {
2104 c->i++;
2105 *(c->o++) = *(c->i++);
2106 }
2107 else if (tf_compile_func (c)) {
2108 return -1;
2109 }
2110 }
2111 else if (i == '[') {
2112 if (tf_compile_ifdef (c)) {
2113 return -1;
2114 }
2115 }
2116 else if (i == '%') {
2117 if (c->i[1] == '%') {
2118 c->i++;
2119 *(c->o++) = *(c->i++);
2120 return 0;
2121 }
2122 if (tf_compile_field (c)) {
2123 return -1;
2124 }
2125 }
2126 // FIXME this is not fb2k spec
2127 else if (*(c->i) == '\\') {
2128 c->i++;
2129 if (*(c->i) != 0) {
2130 *(c->o++) = *(c->i++);
2131 }
2132 }
2133 else if (eol && i == '/' && c->i[1] == '/') {
2134 // skip to end of line
2135 while (c->i[0] && c->i[0] != '\n') {
2136 c->i++;
2137 }
2138 c->eol = 1;
2139 }
2140 else if (i == '\'') {
2141 // copy as plain text to next single-quote
2142 c->i++;
2143
2144 if (c->i[0] == '\'') {
2145 *(c->o++) = *(c->i++);
2146 }
2147 else {
2148 while (c->i[0] && c->i[0] != '\'') {
2149 *(c->o++) = *(c->i++);
2150 }
2151 if (c->i[0] == '\'') {
2152 c->i++;
2153 }
2154 }
2155 }
2156 else if (i == '\n') {
2157 c->i++;
2158 c->eol = 1;
2159 }
2160 else {
2161 *(c->o++) = *(c->i++);
2162 }
2163 return 0;
2164 }
2165
2166 char *
tf_compile(const char * script)2167 tf_compile (const char *script) {
2168 tf_compiler_t c;
2169 memset (&c, 0, sizeof (c));
2170
2171 c.i = script;
2172
2173 char code[strlen(script) * 3];
2174 memset (code, 0, sizeof (code));
2175
2176 c.o = code;
2177
2178 c.eol = 1;
2179
2180 while (*(c.i)) {
2181 if (tf_compile_plain (&c)) {
2182 return NULL;
2183 }
2184 }
2185
2186 size_t size = c.o - code;
2187 char *out = malloc (size + 8);
2188 memcpy (out + 4, code, size);
2189 memset (out + 4 + size, 0, 4); // FIXME: this is the padding for possible buffer overflow bug fix
2190 *((int32_t *)out) = (int32_t)(size);
2191 return out;
2192 }
2193
2194 void
tf_free(char * code)2195 tf_free (char *code) {
2196 free (code);
2197 }
2198
2199 void
tf_import_legacy(const char * fmt,char * out,int outsize)2200 tf_import_legacy (const char *fmt, char *out, int outsize) {
2201 while (*fmt && outsize > 1) {
2202 if (*fmt == '\'' || *fmt == '$') {
2203 if (outsize < 3) {
2204 break;
2205 }
2206 *out++ = *fmt;
2207 *out++ = *fmt++;
2208 outsize -= 2;
2209 }
2210 else if (*fmt == '\n') {
2211 if (outsize < 7) {
2212 break;
2213 }
2214 strcpy (out, "$crlf()");
2215 out += 7;
2216 outsize -= 7;
2217 fmt++;
2218 }
2219 else if (*fmt == '[') {
2220 if (outsize < 3) {
2221 break;
2222 }
2223 strcpy (out, "'['");
2224 out += 3;
2225 outsize -= 3;
2226 fmt++;
2227 }
2228 else if (*fmt == ']') {
2229 if (outsize < 3) {
2230 break;
2231 }
2232 strcpy (out, "']'");
2233 out += 3;
2234 outsize -= 3;
2235 fmt++;
2236 }
2237 else if (*fmt != '%') {
2238 *out++ = *fmt++;
2239 outsize--;
2240 }
2241 else {
2242 fmt++;
2243 if (!*fmt) {
2244 break;
2245 }
2246 if (*fmt == '@') {
2247 const char *e = fmt;
2248 e++;
2249 while (*e && *e != '@') {
2250 e++;
2251 }
2252 #define APPEND(x) {size_t size = strlen (x); if (size >= outsize-1) break; memcpy (out, x, size); out += size; outsize -= size;}
2253 if (*e == '@') {
2254 char nm[100];
2255 size_t l = e-fmt-1;
2256 l = min (l, sizeof (nm)-1);
2257 strncpy (nm+1, fmt+1, l);
2258 nm[l+2] = 0;
2259 nm[0] = '%';
2260 nm[l+1] = '%';
2261
2262 APPEND (nm);
2263 fmt = e+1;
2264 }
2265 continue;
2266 }
2267 else if (*fmt == 'a') {
2268 APPEND ("%artist%");
2269 }
2270 else if (*fmt == 't') {
2271 APPEND ("%title%");
2272 }
2273 else if (*fmt == 'b') {
2274 APPEND ("%album%");
2275 }
2276 else if (*fmt == 'B') {
2277 APPEND ("%album artist%");
2278 }
2279 else if (*fmt == 'C') {
2280 APPEND ("%composer%");
2281 }
2282 else if (*fmt == 'n') {
2283 APPEND ("%tracknumber%");
2284 }
2285 else if (*fmt == 'N') {
2286 APPEND ("%numtracks%");
2287 }
2288 else if (*fmt == 'y') {
2289 APPEND ("%date%");
2290 }
2291 else if (*fmt == 'Y') {
2292 APPEND ("%original_release_time%");
2293 }
2294 else if (*fmt == 'g') {
2295 APPEND ("%genre%");
2296 }
2297 else if (*fmt == 'c') {
2298 APPEND ("%comment%");
2299 }
2300 else if (*fmt == 'r') {
2301 APPEND ("%copyright%");
2302 }
2303 else if (*fmt == 'l') {
2304 APPEND ("%length%");
2305 }
2306 else if (*fmt == 'e') {
2307 APPEND ("%playback_time%");
2308 }
2309 else if (*fmt == 'f') {
2310 APPEND ("%filename_ext%");
2311 }
2312 else if (*fmt == 'F') {
2313 APPEND ("%_path_raw%");
2314 }
2315 else if (*fmt == 'T') {
2316 APPEND ("$info(tagtype)");
2317 }
2318 else if (*fmt == 'd') {
2319 APPEND ("%directoryname%");
2320 }
2321 else if (*fmt == 'D') {
2322 APPEND ("$directory_path(%_path_raw%)");
2323 }
2324 else if (*fmt == 'L') {
2325 APPEND ("%list_length%"); // TODO
2326 }
2327 else if (*fmt == 'X') {
2328 APPEND ("%list_selected%"); // TODO
2329 }
2330 else if (*fmt == 'Z') {
2331 APPEND ("$channels()");
2332 }
2333 else if (*fmt == 'V') {
2334 APPEND ("%_deadbeef_version%");
2335 }
2336 else if (*fmt == '%') {
2337 APPEND ("%%");
2338 }
2339 else {
2340 *out++ = *fmt;
2341 outsize--;
2342 }
2343 #undef APPEND
2344 fmt++;
2345 }
2346 }
2347 *out = 0;
2348 }
2349