12fe336ef682823df8de17ead3669f1b7d120ec6
[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 }