2 * Copyright (C) 2007 Andre Noll <maan@tuebingen.mpg.de>
4 * Licensed under the GPL v2. For licencing details see COPYING.
7 /** \file mm.c Paraslash's mood methods. */
21 /** The comparators for numeric mood methods (year, bitrate, ...). */
22 #define MOOD_COMPARATORS \
24 MC(LESS_OR_EQUAL, <=) \
30 MC(GREATER_OR_EQUAL, >=) \
32 /** Prefix mood comparator name with "_MC", example: MC_LESS. */
33 #define MC(a, b) MC_ ## a,
34 /** Each mood comparator is identified by an integer of this type. */
35 enum mood_comparator_id {MOOD_COMPARATORS NUM_MOOD_COMPARATORS};
37 /** Stringfied mood comparator, example: "<". */
39 /** Array of mood comparators represented as C strings ("<", "<=", ...). */
40 static const char *mood_comparators[] = {MOOD_COMPARATORS};
43 static int parse_mood_comparator(const char *word)
47 for (i = 0; i < NUM_MOOD_COMPARATORS; i++)
48 if (!strcmp(word, mood_comparators[i]))
50 return -E_MOOD_SYNTAX;
53 struct mm_compare_num_data {
54 /** <, <=, =, !=, >=, or >. */
55 enum mood_comparator_id id;
56 /** The value given at the mood line. */
60 static int mm_compare_num_score_function(int32_t val,
61 const struct mm_compare_num_data *cnd)
64 int32_t arg = cnd->arg;
68 res = val < arg; break;
69 case MC_LESS_OR_EQUAL:
70 res = val <= arg; break;
73 res = val == arg; break;
76 res = val != arg; break;
78 res = val > arg; break;
79 case MC_GREATER_OR_EQUAL:
80 res = val >= arg; break;
82 PARA_EMERG_LOG("BUG: invalid mood comparator\n");
85 return res? 100 : -100;
88 static int mm_compare_num_parser(int argc, char **argv, void **private)
91 enum mood_comparator_id id;
93 struct mm_compare_num_data *cnd;
95 return -E_MOOD_SYNTAX;
96 ret = parse_mood_comparator(argv[1]);
100 ret = para_atoi32(argv[2], &arg);
103 cnd = para_malloc(sizeof(struct mm_compare_num_data));
110 static int mm_regex_parser(int argc, char **argv, void **private)
116 return -E_MOOD_SYNTAX;
117 preg = para_malloc(sizeof(*preg));
118 ret = para_regcomp(preg, argv[1], REG_EXTENDED | REG_NOSUB);
127 static int mm_regex_score_function(const regex_t *preg, const char *pattern)
129 return regexec(preg, pattern, 0, NULL, 0) == 0? 100 : -100;
132 static void mm_regex_cleanup(void *private)
134 regex_t *preg = private;
139 static int mm_artist_matches_score_function(__a_unused const char *path,
140 __a_unused const struct afs_info *afsi,
141 const struct afh_info *afhi,
144 return mm_regex_score_function(private, afhi->tags.artist);
147 static int mm_title_matches_score_function(__a_unused const char *path,
148 __a_unused const struct afs_info *afsi,
149 const struct afh_info *afhi,
152 return mm_regex_score_function(private, afhi->tags.title);
155 static int mm_album_matches_score_function(__a_unused const char *path,
156 __a_unused const struct afs_info *afsi,
157 const struct afh_info *afhi,
160 return mm_regex_score_function(private, afhi->tags.album);
163 static int mm_comment_matches_score_function(__a_unused const char *path,
164 __a_unused const struct afs_info *afsi,
165 const struct afh_info *afhi,
168 return mm_regex_score_function(private, afhi->tags.comment);
171 static int mm_bitrate_score_function(__a_unused const char *path,
172 __a_unused const struct afs_info *afsi,
173 const struct afh_info *afhi,
176 return mm_compare_num_score_function(afhi->bitrate, private);
179 static int mm_frequency_score_function(__a_unused const char *path,
180 __a_unused const struct afs_info *afsi,
181 const struct afh_info *afhi,
184 return mm_compare_num_score_function(afhi->frequency, private);
187 static int mm_channels_score_function(__a_unused const char *path,
188 __a_unused const struct afs_info *afsi,
189 const struct afh_info *afhi,
192 return mm_compare_num_score_function(afhi->channels, private);
195 static int mm_image_id_score_function(__a_unused const char *path,
196 const struct afs_info *afsi,
197 __a_unused const struct afh_info *afhi,
200 return mm_compare_num_score_function(afsi->image_id, private);
203 static int mm_lyrics_id_score_function(__a_unused const char *path,
204 const struct afs_info *afsi,
205 __a_unused const struct afh_info *afhi,
208 return mm_compare_num_score_function(afsi->lyrics_id, private);
211 static int mm_num_played_score_function(__a_unused const char *path,
212 const struct afs_info *afsi,
213 __a_unused const struct afh_info *afhi,
216 return mm_compare_num_score_function(afsi->num_played, private);
219 struct mm_year_data {
220 /** Comparator and year given at the mood line. */
221 struct mm_compare_num_data *cnd;
222 /** Used to detect Y2K issues. */
223 int32_t current_year;
226 static int mm_year_parser(int argc, char **argv, void **private)
229 struct mm_year_data *mmyd = para_malloc(sizeof(*mmyd));
233 ret = mm_compare_num_parser(argc, argv, (void **)&mmyd->cnd);
236 current_time = time(NULL);
237 gmt = gmtime(¤t_time);
238 /* tm_year is the number of years since 1900 */
239 mmyd->current_year = gmt->tm_year + 1900;
247 static int mm_year_score_function(__a_unused const char *path,
248 __a_unused const struct afs_info *afsi,
249 const struct afh_info *afhi,
252 const struct mm_year_data *mmyd = private;
254 int ret = para_atoi32(afhi->tags.year, &tag_year);
256 if (ret < 0) /* year tag not present or not a number */
260 /* try to work around Y2K issues */
261 if (tag_year < 100) {
263 if (tag_year + 100 <= mmyd->current_year)
264 tag_year += 100; /* assume tag_year >= 2000 */
266 return mm_compare_num_score_function(tag_year, mmyd->cnd);
269 static void mm_year_cleanup(void *private)
271 struct mm_year_data *mmyd = private;
277 static int mm_no_attributes_set_parser(int argc, __a_unused char **argv,
278 __a_unused void **ignored)
280 return argc? -E_MOOD_SYNTAX : 1;
283 static int mm_no_attributes_set_score_function(__a_unused const char *path,
284 const struct afs_info *afsi,
285 __a_unused const struct afh_info *afhi,
286 __a_unused const void *data)
288 if (!afsi->attributes)
293 static int mm_path_matches_score_function(const char *path,
294 __a_unused const struct afs_info *afsi,
295 __a_unused const struct afh_info *afhi,
298 if (fnmatch(data, path, 0))
303 static int mm_path_matches_parser(int argc, char **argv, void **data)
306 return -E_MOOD_SYNTAX;
307 *data = para_strdup(argv[1]);
311 static void mm_path_matches_cleanup(void *data)
316 static int mm_is_set_parser(int argc, char **argv, void **bitnum)
319 unsigned char c, *res;
322 return -E_MOOD_SYNTAX;
323 ret = get_attribute_bitnum_by_name(argv[1], &c);
326 res = para_malloc(1);
332 static int mm_is_set_score_function(__a_unused const char *path,
333 __a_unused const struct afs_info *afsi,
334 __a_unused const struct afh_info *afhi,
337 const unsigned char *bn = data;
338 if (afsi->attributes & (1ULL << *bn))
343 #define DEFINE_MOOD_METHOD(_name) \
344 .parser = mm_ ## _name ## _parser, \
345 .score_function = mm_ ## _name ## _score_function, \
348 #define DEFINE_MOOD_METHOD_WITH_CLEANUP(_name) \
349 DEFINE_MOOD_METHOD(_name), \
350 .cleanup = mm_ ## _name ## _cleanup
352 #define DEFINE_REGEX_MOOD_METHOD(_name) \
354 .parser = mm_regex_parser, \
355 .score_function = mm_ ## _name ## _score_function, \
356 .cleanup = mm_regex_cleanup
358 #define DEFINE_COMPARE_NUM_MOOD_METHOD(_name) \
360 .parser = mm_compare_num_parser, \
361 .score_function = mm_ ## _name ## _score_function
363 const struct mood_method mood_methods[] = {
364 {DEFINE_MOOD_METHOD(no_attributes_set)},
365 {DEFINE_MOOD_METHOD(is_set)},
366 {DEFINE_MOOD_METHOD_WITH_CLEANUP(path_matches)},
367 {DEFINE_MOOD_METHOD_WITH_CLEANUP(year)},
368 {DEFINE_REGEX_MOOD_METHOD(artist_matches)},
369 {DEFINE_REGEX_MOOD_METHOD(title_matches)},
370 {DEFINE_REGEX_MOOD_METHOD(album_matches)},
371 {DEFINE_REGEX_MOOD_METHOD(comment_matches)},
372 {DEFINE_COMPARE_NUM_MOOD_METHOD(bitrate)},
373 {DEFINE_COMPARE_NUM_MOOD_METHOD(frequency)},
374 {DEFINE_COMPARE_NUM_MOOD_METHOD(channels)},
375 {DEFINE_COMPARE_NUM_MOOD_METHOD(num_played)},
376 {DEFINE_COMPARE_NUM_MOOD_METHOD(image_id)},
377 {DEFINE_COMPARE_NUM_MOOD_METHOD(lyrics_id)},