2 * Copyright (C) 2017 Andre Noll <maan@tuebingen.mpg.de>
4 * Licensed under the GPL v2. For licencing details see COPYING.
8 * \file mp.c Mood parser helper functions.
10 * This file contains the public and the private API of the flex/bison based
13 * The public API (at the bottom of the file) allows to parse the same mood
14 * definition many times in an efficient manner.
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.
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.
26 * If the mood is no longer needed, \ref mp_shutdown() should be called to free
29 * The internal API is described in \ref mp.h.
49 struct mp_ast_node *ast;
50 /* per audio file context */
51 const struct osl_row *aft_row;
60 * Parse a (generalized) string literal.
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.
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.
73 * The function strips off the opening and leading quote characters, replaces
74 * double backslashes by single backslashes and handles the usual escapes like
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
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.
86 * \sa \ref mp_parse_regex_pattern(), \ref mp_parse_wildcard_pattern().
88 unsigned parse_quoted_string(const char *src, const char quote_chars[2],
91 size_t n, len = strlen(src);
96 assert(src[0] == quote_chars[0]);
97 p = dst = para_malloc(len - 1);
108 if (c == quote_chars[1])
113 if (c == quote_chars[1])
114 *p++ = quote_chars[1];
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);
128 assert(src[n] == quote_chars[1]);
135 * Parse and compile an extended regular expression pattern, including flags.
137 * \param src The pattern to parse.
138 * \param result C-string and flags are returned here.
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.
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
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
158 * \sa \ref mp_parse_wildcard_pattern(), \ref parse_quoted_string(),
159 * \ref para_regcomp(), regex(3).
161 int mp_parse_regex_pattern(const char *src, struct mp_re_pattern *result)
165 unsigned n = parse_quoted_string(src, "//", &pat);
168 for (; src[n]; n++) {
170 case 'i': result->flags |= REG_ICASE; break;
171 case 'n': result->flags |= REG_NEWLINE; break;
172 default: assert(false);
175 ret = para_regcomp(&result->preg, pat, result->flags);
181 * Parse a wildcard pattern, including flags.
183 * \param src The pattern to parse.
184 * \param result C-string and flags are returned here.
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
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.
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.
199 * \sa \ref parse_quoted_string(), \ref mp_parse_regex_pattern(), fnmatch(3).
201 void mp_parse_wildcard_pattern(const char *src, struct mp_wc_pattern *result)
203 unsigned n = parse_quoted_string(src, "||", &result->pat);
206 for (; src[n]; 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;
215 #ifdef HAVE_FNM_EXTMATCH
216 case 'e': result->flags |= FNM_EXTMATCH; break;
217 #else /* silently ignore extglob flag */
220 default: assert(false);
226 * Set the error bit in the parser context and log a message.
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.
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").
235 __printf_3_4 void mp_parse_error(int line, struct mp_context *ctx,
236 const char *fmt, ...)
241 if (ctx->errmsg) /* we already printed an error message */
244 xvasprintf(&tmp, fmt, ap);
246 xasprintf(&ctx->errmsg, "line %d: %s", line, tmp);
248 PARA_WARNING_LOG("%s\n", ctx->errmsg);
251 static int get_afsi(struct mp_context *ctx)
257 ret = get_afsi_of_row(ctx->aft_row, &ctx->afsi);
260 ctx->have_afsi = true;
264 static int get_afhi(struct mp_context *ctx)
270 ret = get_afhi_of_row(ctx->aft_row, &ctx->afhi);
273 ctx->have_afhi = true;
278 * Return the full path to the audio file.
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.
285 * \return A reference to the path. Must not be freed by the caller.
287 * \sa \ref get_audio_file_path_of_row().
289 char *mp_path(struct mp_context *ctx)
292 get_audio_file_path_of_row(ctx->aft_row, &ctx->path);
297 * Check whether the given attribute is set for the current audio file.
299 * \param attr The string to look up in the attribute table.
300 * \param ctx See \ref mp_path().
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
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.
309 * \sa \ref get_attribute_bitnum_by_name().
311 bool mp_is_set(const char *attr, struct mp_context *ctx)
314 unsigned char bitnum;
315 const uint64_t one = 1;
317 ret = get_attribute_bitnum_by_name(attr, &bitnum);
318 if (ret < 0) /* treat invalid attributes as not set */
323 return (one << bitnum) & ctx->afsi.attributes;
327 * Count the number of attributes set.
329 * \param ctx See \ref mp_path().
331 * \return The number of bits which are set in the ->attributes field of the
332 * afs_info structure of the current audio file.
334 int64_t mp_num_attributes_set(struct mp_context *ctx)
336 const uint64_t m = ~(uint64_t)0;
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;
355 * Define a function which returns a field of the afs_info structure.
357 * \param _name The name of the field.
359 * The defined function casts the value to int64_t. On errors, zero is returned.
361 #define MP_AFSI(_name) \
362 int64_t mp_ ## _name(struct mp_context *ctx) \
364 int ret = get_afsi(ctx); \
367 return ctx->afsi._name; \
376 * Define a function which returns a field of the afh_info structure.
378 * \param _name The name of the field.
380 * The defined function casts the value to int64_t. On errors, zero is returned.
382 #define MP_AFHI(_name) \
383 int64_t mp_ ## _name(struct mp_context *ctx) \
385 int ret = get_afhi(ctx); \
388 return ctx->afhi._name; \
397 * Define a function which extracts and returns the value of a meta tag.
399 * \param _name The name of the tag (artist, title, ...).
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.
406 #define MP_TAG(_name) \
407 char *mp_ ## _name (struct mp_context *ctx) \
409 int ret = get_afhi(ctx); \
412 return ctx->afhi.tags._name; \
422 * Parse and return the value of the year tag.
424 * \param ctx See \ref mp_path().
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
430 int64_t mp_year(struct mp_context *ctx)
433 int ret = get_afhi(ctx);
437 assert(ctx->afhi.tags.year);
438 ret = para_atoi64(ctx->afhi.tags.year, &year);
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
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);
467 * Initialize the mood parser.
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.
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.
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.
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
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.
488 * \return Standard. On success *errmsg is set to NULL.
490 int mp_init(const char *definition, int nbytes, struct mp_context **result,
495 struct mp_context *ctx;
496 struct yy_buffer_state *buffer_state;
498 if (!definition || nbytes == 0) { /* dummy mood */
504 ctx = para_calloc(sizeof(*ctx));
508 ret = mp_yylex_init(&scanner);
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 */
518 *errmsg = ctx->errmsg;
522 return -E_MOOD_PARSE;
531 * Determine whether the given audio file is admissible.
533 * \param aft_row The audio file to check for admissibility.
534 * \param ctx As returned from \ref mp_init().
536 * \return Whether the audio file is admissible.
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.
542 * \sa \ref change_current_mood(), \ref mp_eval_ast().
544 bool mp_eval_row(const struct osl_row *aft_row, struct mp_context *ctx)
546 if (!ctx) /* dummy mood */
549 ctx->aft_row = aft_row;
550 ctx->have_afsi = false;
551 ctx->have_afhi = false;
553 return mp_eval_ast(ctx->ast, ctx);
557 * Deallocate the resources of a mood parser.
559 * This function frees the abstract syntax tree which was created by \ref
562 * \param ctx As returned from \ref mp_init().
564 * It's OK to pass a NULL pointer, in which case the function does nothing.
566 void mp_shutdown(struct mp_context *ctx)
570 mp_free_ast(ctx->ast);