mood: Make "duration" a new keyword for the mood grammar.
[paraslash.git] / mp.c
1 /* Copyright (C) 2017 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
2
3 /**
4 * \file mp.c Mood parser helper functions.
5 *
6 * This file contains the public and the private API of the flex/bison based
7 * mood parser.
8 *
9 * The public API (at the bottom of the file) allows parsing the same mood
10 * definition many times in an efficient manner.
11 *
12 * The first function to call is \ref mp_init(), which analyzes the given mood
13 * definition syntactically. It returns the abstract syntax tree of the mood
14 * definition and pre-compiles all regular expression patterns to make later
15 * pattern matching efficient.
16 *
17 * Semantic analysis is performed in \ref mp_eval_row(). This function is
18 * called from \ref mood.c once for each file in the audio file table. It
19 * utilizes the abstract syntax tree and the pre-compiled regular expressions
20 * to determine the set of admissible audio files.
21 *
22 * If the mood is no longer needed, \ref mp_shutdown() should be called to free
23 * the resources.
24 *
25 * The internal API is described in \ref mp.h.
26 */
27
28 #include "para.h"
29
30 #include <regex.h>
31 #include <fnmatch.h>
32 #include <osl.h>
33 #include <lopsub.h>
34
35 #include "string.h"
36 #include "error.h"
37 #include "afh.h"
38 #include "afs.h"
39 #include "mp.h"
40 #include "mp.bison.h"
41
42 struct mp_context {
43 /* global context */
44 char *errmsg;
45 struct mp_ast_node *ast;
46 /* per audio file context */
47 const struct osl_row *aft_row;
48 char *path;
49 bool have_afsi;
50 struct afs_info afsi;
51 bool have_afhi;
52 struct afh_info afhi;
53 };
54
55 /**
56 * Parse a (generalized) string literal.
57 *
58 * \param src The string to parse.
59 * \param quote_chars Opening and closing quote characters.
60 * \param result The corresponding C string is returned here.
61 *
62 * This function turns a generalized C99 string literal like "xyz\n" into a C
63 * string (containing the three characters 'x', 'y' and 'z', followed by a
64 * newline character and the terminating zero byte). The function receives
65 * quote characters as an argument so that, for example, regular expression
66 * patterns enclosed in '/' can be parsed as well. To parse a proper string
67 * literal, one has to pass two double quotes as the second argument.
68 *
69 * The function strips off the opening and leading quote characters, replaces
70 * double backslashes by single backslashes and handles the usual escapes like
71 * \n and \".
72 *
73 * The caller must make sure that the input is well-formed. The function simply
74 * aborts if the input is not a valid C99 string literal (modulo the quote
75 * characters).
76 *
77 * \return Offset of the first character after the closing quote. For proper
78 * string literals this will be the terminating zero byte of the input string,
79 * for regular expression patterns it is the beginning of the flags which
80 * modify the matching behaviour.
81 *
82 * \sa \ref mp_parse_regex_pattern(), \ref mp_parse_wildcard_pattern().
83 */
84 unsigned parse_quoted_string(const char *src, const char quote_chars[2],
85 char **result)
86 {
87 size_t n, len = strlen(src);
88 char *dst, *p;
89 bool backslash;
90
91 assert(len >= 2);
92 assert(src[0] == quote_chars[0]);
93 p = dst = para_malloc(len - 1);
94 backslash = false;
95 for (n = 1;; n++) {
96 char c;
97 assert(n < len);
98 c = src[n];
99 if (!backslash) {
100 if (c == '\\') {
101 backslash = true;
102 continue;
103 }
104 if (c == quote_chars[1])
105 break;
106 *p++ = c;
107 continue;
108 }
109 if (c == quote_chars[1])
110 *p++ = quote_chars[1];
111 else switch (c) {
112 case '\\': *p++ = '\\'; break;
113 case 'a': *p++ = '\a'; break;
114 case 'b': *p++ = '\b'; break;
115 case 'f': *p++ = '\f'; break;
116 case 'n': *p++ = '\n'; break;
117 case 'r': *p++ = '\r'; break;
118 case 't': *p++ = '\t'; break;
119 case 'v': *p++ = '\v'; break;
120 default: assert(false);
121 }
122 backslash = false;
123 }
124 assert(src[n] == quote_chars[1]);
125 *p = '\0';
126 *result = dst;
127 return n + 1;
128 }
129
130 /**
131 * Parse and compile an extended regular expression pattern, including flags.
132 *
133 * \param src The pattern to parse.
134 * \param result C-string and flags are returned here.
135 *
136 * A regex pattern is identical to a C99 string literal except (a) it is
137 * enclosed in '/' characters rather than double quotes, (b) double quote
138 * characters which are part of the pattern do not need to be quoted with
139 * backslashes, but slashes must be quoted in this way, and (c) the closing
140 * slash may be followed by one or more flag characters which modify the
141 * matching behaviour.
142 *
143 * The only flags which are currently supported are 'i' to ignore case in match
144 * (REG_ICASE) and 'n' to change the handling of newline characters
145 * (REG_NEWLINE).
146 *
147 * \return Standard. This function calls \ref parse_quoted_string(), hence it
148 * aborts if the input string is malformed. However, errors from \ref
149 * para_regcomp are returned without aborting the process. The rationale behind
150 * this difference is that passing a malformed string must be considered an
151 * implementation bug because malformed strings should be rejected earlier by
152 * the lexer.
153 *
154 * \sa \ref mp_parse_wildcard_pattern(), \ref parse_quoted_string(),
155 * \ref para_regcomp(), regex(3).
156 */
157 int mp_parse_regex_pattern(const char *src, struct mp_re_pattern *result)
158 {
159 int ret;
160 char *pat;
161 unsigned n = parse_quoted_string(src, "//", &pat);
162
163 result->flags = 0;
164 for (; src[n]; n++) {
165 switch (src[n]) {
166 case 'i': result->flags |= REG_ICASE; break;
167 case 'n': result->flags |= REG_NEWLINE; break;
168 default: assert(false);
169 }
170 }
171 ret = para_regcomp(&result->preg, pat, result->flags);
172 free(pat);
173 return ret;
174 }
175
176 /**
177 * Parse a wildcard pattern, including flags.
178 *
179 * \param src The pattern to parse.
180 * \param result C-string and flags are returned here.
181 *
182 * This function parses a shell wildcard pattern. It is similar to \ref
183 * mp_parse_regex_pattern(), so the remarks mentioned there apply to this
184 * function as well.
185 *
186 * Wildcard patterns differ from regular expression patterns in that (a) they
187 * must be enclosed in '|' characters, (b) they support different flags for
188 * modifying matching behaviour, and (c) there is no cache for them.
189 *
190 * The following flags, whose meaning is explained in fnmatch(3), are currently
191 * supported: 'n' (FNM_NOESCAPE), 'p' (FNM_PATHNAME), 'P' (FNM_PERIOD), 'l'
192 * (FNM_LEADING_DIR), 'i' (FNM_CASEFOLD), 'e' (FNM_EXTMATCH). The last flag is
193 * a GNU extension. It is silently ignored on non GNU systems.
194 *
195 * \sa \ref parse_quoted_string(), \ref mp_parse_regex_pattern(), fnmatch(3).
196 */
197 void mp_parse_wildcard_pattern(const char *src, struct mp_wc_pattern *result)
198 {
199 unsigned n = parse_quoted_string(src, "||", &result->pat);
200
201 result->flags = 0;
202 for (; src[n]; n++) {
203 switch (src[n]) {
204 case 'n': result->flags |= FNM_NOESCAPE; break;
205 case 'p': result->flags |= FNM_PATHNAME; break;
206 case 'P': result->flags |= FNM_PERIOD; break;
207 /* not POSIX, but both FreeBSD and NetBSD have it */
208 case 'l': result->flags |= FNM_LEADING_DIR; break;
209 case 'i': result->flags |= FNM_CASEFOLD; break;
210 /* GNU only */
211 #ifdef HAVE_FNM_EXTMATCH
212 case 'e': result->flags |= FNM_EXTMATCH; break;
213 #else /* silently ignore extglob flag */
214 case 'e': break;
215 #endif
216 default: assert(false);
217 }
218 }
219 }
220
221 /**
222 * Set the error bit in the parser context and log a message.
223 *
224 * \param line The number of the input line which caused the error.
225 * \param ctx Contains the error bit.
226 * \param fmt Usual format string.
227 *
228 * This is called if the lexer or the parser detect an error in the mood
229 * definition. Only the first error is logged (with a severity of "warn").
230 */
231 __printf_3_4 void mp_parse_error(int line, struct mp_context *ctx,
232 const char *fmt, ...)
233 {
234 va_list ap;
235 char *tmp;
236
237 if (ctx->errmsg) /* we already printed an error message */
238 return;
239 va_start(ap, fmt);
240 xvasprintf(&tmp, fmt, ap);
241 va_end(ap);
242 xasprintf(&ctx->errmsg, "line %d: %s", line, tmp);
243 free(tmp);
244 PARA_WARNING_LOG("%s\n", ctx->errmsg);
245 }
246
247 static int get_afsi(struct mp_context *ctx)
248 {
249 int ret;
250
251 if (ctx->have_afsi)
252 return 0;
253 ret = get_afsi_of_row(ctx->aft_row, &ctx->afsi);
254 if (ret < 0)
255 return ret;
256 ctx->have_afsi = true;
257 return 1;
258 }
259
260 static int get_afhi(struct mp_context *ctx)
261 {
262 int ret;
263
264 if (ctx->have_afhi)
265 return 0;
266 ret = get_afhi_of_row(ctx->aft_row, &ctx->afhi);
267 if (ret < 0)
268 return ret;
269 ctx->have_afhi = true;
270 return 1;
271 }
272
273 /**
274 * Return the full path to the audio file.
275 *
276 * \param ctx Contains a reference to the row of the audio file table which
277 * corresponds to the current audio file. The path of the audio file, the
278 * afs_info and the afh_info structures (which contain the tag information) can
279 * be retrieved through this reference.
280 *
281 * \return A reference to the path. Must not be freed by the caller.
282 *
283 * \sa \ref get_audio_file_path_of_row().
284 */
285 char *mp_path(struct mp_context *ctx)
286 {
287 if (!ctx->path)
288 get_audio_file_path_of_row(ctx->aft_row, &ctx->path);
289 return ctx->path;
290 }
291
292 /**
293 * Check whether the given attribute is set for the current audio file.
294 *
295 * \param attr The string to look up in the attribute table.
296 * \param ctx See \ref mp_path().
297 *
298 * First, determine the bit number which corresponds to the attribute, then
299 * check if this bit is set in the ->attributes field of the afs_info structure
300 * of the audio file.
301 *
302 * \return True if the attribute is set, false if it is not. On errors, for
303 * example if the given string is no attribute, the function returns false.
304 *
305 * \sa \ref get_attribute_bitnum_by_name().
306 */
307 bool mp_is_set(const char *attr, struct mp_context *ctx)
308 {
309 int ret;
310 unsigned char bitnum;
311 const uint64_t one = 1;
312
313 ret = get_attribute_bitnum_by_name(attr, &bitnum);
314 if (ret < 0) /* treat invalid attributes as not set */
315 return false;
316 ret = get_afsi(ctx);
317 if (ret < 0)
318 return false;
319 return (one << bitnum) & ctx->afsi.attributes;
320 }
321
322 /**
323 * Count the number of attributes set.
324 *
325 * \param ctx See \ref mp_path().
326 *
327 * \return The number of bits which are set in the ->attributes field of the
328 * afs_info structure of the current audio file.
329 */
330 int64_t mp_num_attributes_set(struct mp_context *ctx)
331 {
332 const uint64_t m = ~(uint64_t)0;
333 int ret;
334 uint64_t v;
335
336 ret = get_afsi(ctx);
337 if (ret < 0)
338 return 0;
339
340 v = ctx->afsi.attributes;
341 /* taken from https://graphics.stanford.edu/~seander/bithacks.html */
342 v = v - ((v >> 1) & m / 3);
343 v = (v & m / 15 * 3) + ((v >> 2) & m / 15 * 3);
344 v = (v + (v >> 4)) & m / 255 * 15;
345 v = (v * (m / 255)) >> 56;
346 assert(v <= 64);
347 return v;
348 }
349
350 /**
351 * Define a function which returns a field of the afs_info structure.
352 *
353 * \param _name The name of the field.
354 *
355 * The defined function casts the value to int64_t. On errors, zero is returned.
356 */
357 #define MP_AFSI(_name) \
358 int64_t mp_ ## _name(struct mp_context *ctx) \
359 { \
360 int ret = get_afsi(ctx); \
361 if (ret < 0) \
362 return 0; \
363 return ctx->afsi._name; \
364 }
365 /** \cond MP_AFSI */
366 MP_AFSI(num_played)
367 MP_AFSI(image_id)
368 MP_AFSI(lyrics_id)
369 /** \endcond */
370
371 /**
372 * Define a function which returns a field of the afh_info structure.
373 *
374 * \param _name The name of the field.
375 *
376 * The defined function casts the value to int64_t. On errors, zero is returned.
377 */
378 #define MP_AFHI(_name) \
379 int64_t mp_ ## _name(struct mp_context *ctx) \
380 { \
381 int ret = get_afhi(ctx); \
382 if (ret < 0) \
383 return 0; \
384 return ctx->afhi._name; \
385 }
386 /** \cond MP_AFHI */
387 MP_AFHI(bitrate)
388 MP_AFHI(frequency)
389 MP_AFHI(channels)
390 /** \endcond */
391
392 /**
393 * Return the duration of the audio file from the afh info structure.
394 *
395 * \param ctx See \ref mp_path().
396 *
397 * The duration is computed by multiplying the number of chunks and the
398 * duration of one chunk.
399 *
400 * \return The approximate number of milliseconds.
401 */
402 int64_t mp_duration(struct mp_context *ctx)
403 {
404 struct timeval tmp;
405 int ret = get_afhi(ctx);
406
407 if (ret < 0)
408 return 0;
409 tv_scale(ctx->afhi.chunks_total, &ctx->afhi.chunk_tv, &tmp);
410 return tv2ms(&tmp);
411 }
412
413 /**
414 * Define a function which extracts and returns the value of a meta tag.
415 *
416 * \param _name The name of the tag (artist, title, ...).
417 *
418 * The function will return a pointer to memory owned by the audio file
419 * selector. On errors, or if the current audio file has no tag of the given
420 * name, the function returns the empty string. The caller must not attempt to
421 * free the returned string.
422 */
423 #define MP_TAG(_name) \
424 char *mp_ ## _name (struct mp_context *ctx) \
425 { \
426 int ret = get_afhi(ctx); \
427 if (ret < 0) \
428 return ""; \
429 return ctx->afhi.tags._name; \
430 }
431 /** \cond MP_TAG */
432 MP_TAG(artist)
433 MP_TAG(title)
434 MP_TAG(album)
435 MP_TAG(comment)
436 /** \endcond */
437
438 /**
439 * Parse and return the value of the year tag.
440 *
441 * \param ctx See \ref mp_path().
442 *
443 * \return If the year tag is not present, can not be parsed, or its value is
444 * less than zero, the function returns 0. If the value is less than 100, we
445 * add 1900.
446 */
447 int64_t mp_year(struct mp_context *ctx)
448 {
449 int64_t year;
450 int ret = get_afhi(ctx);
451
452 if (ret < 0)
453 return 0;
454 assert(ctx->afhi.tags.year);
455 ret = para_atoi64(ctx->afhi.tags.year, &year);
456 if (ret < 0)
457 return 0;
458 if (year < 0)
459 return 0;
460 if (year < 100)
461 year += 1900;
462 return year;
463 }
464
465 /*
466 * Ideally, these functions should be declared in a header file which is
467 * created by flex with the --header-file option. However, for flex-2.6.x
468 * (2017) this option is borken: if --reentrant is also given, the generated
469 * header file contains syntax errors. As a workaround we declare the functions
470 * here.
471 */
472 /** \cond flex_workaround */
473 int mp_yylex_init(mp_yyscan_t *yyscanner);
474 struct yy_buffer_state *mp_yy_scan_bytes(const char *buf, int len,
475 mp_yyscan_t yyscanner);
476 void mp_yy_delete_buffer(struct yy_buffer_state *bs, mp_yyscan_t yyscanner);
477 int mp_yylex_destroy(mp_yyscan_t yyscanner);
478 void mp_yyset_lineno(int lineno, mp_yyscan_t scanner);
479 /** \endcond */
480
481 /* Public API */
482
483 /**
484 * Initialize the mood parser.
485 *
486 * This allocates and sets up the internal structures of the mood parser
487 * and creates an abstract syntax tree from the given mood definition.
488 * It must be called before \ref mp_eval_row() can be called.
489 *
490 * The context pointer returned by this function may be passed to \ref
491 * mp_eval_row() to determine whether an audio file is admissible.
492 *
493 * \param definition A reference to the mood definition.
494 * \param nbytes The size of the mood definition.
495 * \param result Opaque context pointer is returned here.
496 * \param errmsg Optional error message is returned here.
497 *
498 * It's OK to pass a NULL pointer or a zero sized buffer as the mood
499 * definition. This corresponds to the "dummy" mood for which all audio files
500 * are admissible.
501 *
502 * The error message pointer may also be NULL in which case no error message
503 * is returned. Otherwise, the caller must free the returned string.
504 *
505 * \return Standard. On success *errmsg is set to NULL.
506 */
507 int mp_init(const char *definition, int nbytes, struct mp_context **result,
508 char **errmsg)
509 {
510 int ret;
511 mp_yyscan_t scanner;
512 struct mp_context *ctx;
513 struct yy_buffer_state *buffer_state;
514
515 *result = NULL;
516 if (!definition || nbytes == 0) { /* dummy mood */
517 if (errmsg)
518 *errmsg = NULL;
519 return 0;
520 }
521 ctx = para_calloc(sizeof(*ctx));
522 ctx->errmsg = NULL;
523 ctx->ast = NULL;
524
525 ret = mp_yylex_init(&scanner);
526 assert(ret == 0);
527 buffer_state = mp_yy_scan_bytes(definition, nbytes, scanner);
528 mp_yyset_lineno(1, scanner);
529 PARA_NOTICE_LOG("creating abstract syntax tree\n");
530 ret = mp_yyparse(ctx, &ctx->ast, scanner);
531 mp_yy_delete_buffer(buffer_state, scanner);
532 mp_yylex_destroy(scanner);
533 if (ctx->errmsg) { /* parse error */
534 if (errmsg)
535 *errmsg = ctx->errmsg;
536 else
537 free(ctx->errmsg);
538 free(ctx);
539 return -E_MOOD_PARSE;
540 }
541 if (errmsg)
542 *errmsg = NULL;
543 *result = ctx;
544 return 1;
545 }
546
547 /**
548 * Determine whether the given audio file is admissible.
549 *
550 * \param aft_row The audio file to check for admissibility.
551 * \param ctx As returned from \ref mp_init().
552 *
553 * \return Whether the audio file is admissible.
554 *
555 * If the mood parser was set up without an input buffer (dummy mood), this
556 * function returns true (without looking at the audio file metadata) to
557 * indicate that the given audio file should be considered admissible.
558 *
559 * \sa \ref change_current_mood(), \ref mp_eval_ast().
560 */
561 bool mp_eval_row(const struct osl_row *aft_row, struct mp_context *ctx)
562 {
563 if (!ctx) /* dummy mood */
564 return true;
565 if (!ctx->ast) /* empty mood */
566 return true;
567 assert(aft_row);
568 ctx->aft_row = aft_row;
569 ctx->have_afsi = false;
570 ctx->have_afhi = false;
571 ctx->path = NULL;
572 return mp_eval_ast(ctx->ast, ctx);
573 }
574
575 /**
576 * Deallocate the resources of a mood parser.
577 *
578 * This function frees the abstract syntax tree which was created by \ref
579 * mp_init().
580 *
581 * \param ctx As returned from \ref mp_init().
582 *
583 * It's OK to pass a NULL pointer, in which case the function does nothing.
584 */
585 void mp_shutdown(struct mp_context *ctx)
586 {
587 if (!ctx)
588 return;
589 mp_free_ast(ctx->ast);
590 free(ctx);
591 }