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