1 /* Copyright (C) 2007 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
3 /** \file mm.c Paraslash's mood methods. */
17 /** The comparators for numeric mood methods (year, bitrate, ...). */
18 #define MOOD_COMPARATORS \
20 MC(LESS_OR_EQUAL, <=) \
26 MC(GREATER_OR_EQUAL, >=) \
28 /** Prefix mood comparator name with "_MC", example: MC_LESS. */
29 #define MC(a, b) MC_ ## a,
30 /** Each mood comparator is identified by an integer of this type. */
31 enum mood_comparator_id {MOOD_COMPARATORS NUM_MOOD_COMPARATORS};
33 /** Stringfied mood comparator, example: "<". */
35 /** Array of mood comparators represented as C strings ("<", "<=", ...). */
36 static const char *mood_comparators[] = {MOOD_COMPARATORS};
39 static int parse_mood_comparator(const char *word)
43 for (i = 0; i < NUM_MOOD_COMPARATORS; i++)
44 if (!strcmp(word, mood_comparators[i]))
46 return -E_MOOD_SYNTAX;
49 struct mm_compare_num_data {
50 /** <, <=, =, !=, >=, or >. */
51 enum mood_comparator_id id;
52 /** The value given at the mood line. */
56 static int mm_compare_num_score_function(int32_t val,
57 const struct mm_compare_num_data *cnd)
60 int32_t arg = cnd->arg;
64 res = val < arg; break;
65 case MC_LESS_OR_EQUAL:
66 res = val <= arg; break;
69 res = val == arg; break;
72 res = val != arg; break;
74 res = val > arg; break;
75 case MC_GREATER_OR_EQUAL:
76 res = val >= arg; break;
78 PARA_EMERG_LOG("BUG: invalid mood comparator\n");
81 return res? 100 : -100;
84 static int mm_compare_num_parser(int argc, char **argv, void **private)
87 enum mood_comparator_id id;
89 struct mm_compare_num_data *cnd;
91 return -E_MOOD_SYNTAX;
92 ret = parse_mood_comparator(argv[1]);
96 ret = para_atoi32(argv[2], &arg);
99 cnd = para_malloc(sizeof(struct mm_compare_num_data));
106 static int mm_regex_parser(int argc, char **argv, void **private)
112 return -E_MOOD_SYNTAX;
113 preg = para_malloc(sizeof(*preg));
114 ret = para_regcomp(preg, argv[1], REG_EXTENDED | REG_NOSUB);
123 static int mm_regex_score_function(const regex_t *preg, const char *pattern)
125 return regexec(preg, pattern, 0, NULL, 0) == 0? 100 : -100;
128 static void mm_regex_cleanup(void *private)
130 regex_t *preg = private;
135 static int mm_artist_matches_score_function(__a_unused const char *path,
136 __a_unused const struct afs_info *afsi,
137 const struct afh_info *afhi,
140 return mm_regex_score_function(private, afhi->tags.artist);
143 static int mm_title_matches_score_function(__a_unused const char *path,
144 __a_unused const struct afs_info *afsi,
145 const struct afh_info *afhi,
148 return mm_regex_score_function(private, afhi->tags.title);
151 static int mm_album_matches_score_function(__a_unused const char *path,
152 __a_unused const struct afs_info *afsi,
153 const struct afh_info *afhi,
156 return mm_regex_score_function(private, afhi->tags.album);
159 static int mm_comment_matches_score_function(__a_unused const char *path,
160 __a_unused const struct afs_info *afsi,
161 const struct afh_info *afhi,
164 return mm_regex_score_function(private, afhi->tags.comment);
167 static int mm_bitrate_score_function(__a_unused const char *path,
168 __a_unused const struct afs_info *afsi,
169 const struct afh_info *afhi,
172 return mm_compare_num_score_function(afhi->bitrate, private);
175 static int mm_frequency_score_function(__a_unused const char *path,
176 __a_unused const struct afs_info *afsi,
177 const struct afh_info *afhi,
180 return mm_compare_num_score_function(afhi->frequency, private);
183 static int mm_channels_score_function(__a_unused const char *path,
184 __a_unused const struct afs_info *afsi,
185 const struct afh_info *afhi,
188 return mm_compare_num_score_function(afhi->channels, private);
191 static int mm_image_id_score_function(__a_unused const char *path,
192 const struct afs_info *afsi,
193 __a_unused const struct afh_info *afhi,
196 return mm_compare_num_score_function(afsi->image_id, private);
199 static int mm_lyrics_id_score_function(__a_unused const char *path,
200 const struct afs_info *afsi,
201 __a_unused const struct afh_info *afhi,
204 return mm_compare_num_score_function(afsi->lyrics_id, private);
207 static int mm_num_played_score_function(__a_unused const char *path,
208 const struct afs_info *afsi,
209 __a_unused const struct afh_info *afhi,
212 return mm_compare_num_score_function(afsi->num_played, private);
215 struct mm_year_data {
216 /** Comparator and year given at the mood line. */
217 struct mm_compare_num_data *cnd;
218 /** Used to detect Y2K issues. */
219 int32_t current_year;
222 static int mm_year_parser(int argc, char **argv, void **private)
225 struct mm_year_data *mmyd = para_malloc(sizeof(*mmyd));
229 ret = mm_compare_num_parser(argc, argv, (void **)&mmyd->cnd);
232 current_time = time(NULL);
233 gmt = gmtime(¤t_time);
234 /* tm_year is the number of years since 1900 */
235 mmyd->current_year = gmt->tm_year + 1900;
243 static int mm_year_score_function(__a_unused const char *path,
244 __a_unused const struct afs_info *afsi,
245 const struct afh_info *afhi,
248 const struct mm_year_data *mmyd = private;
250 int ret = para_atoi32(afhi->tags.year, &tag_year);
252 if (ret < 0) /* year tag not present or not a number */
256 /* try to work around Y2K issues */
257 if (tag_year < 100) {
259 if (tag_year + 100 <= mmyd->current_year)
260 tag_year += 100; /* assume tag_year >= 2000 */
262 return mm_compare_num_score_function(tag_year, mmyd->cnd);
265 static void mm_year_cleanup(void *private)
267 struct mm_year_data *mmyd = private;
273 static int mm_no_attributes_set_parser(int argc, __a_unused char **argv,
274 __a_unused void **ignored)
276 return argc? -E_MOOD_SYNTAX : 1;
279 static int mm_no_attributes_set_score_function(__a_unused const char *path,
280 const struct afs_info *afsi,
281 __a_unused const struct afh_info *afhi,
282 __a_unused const void *data)
284 if (!afsi->attributes)
289 static int mm_path_matches_score_function(const char *path,
290 __a_unused const struct afs_info *afsi,
291 __a_unused const struct afh_info *afhi,
294 if (fnmatch(data, path, 0))
299 static int mm_path_matches_parser(int argc, char **argv, void **data)
302 return -E_MOOD_SYNTAX;
303 *data = para_strdup(argv[1]);
307 static void mm_path_matches_cleanup(void *data)
312 static int mm_is_set_parser(int argc, char **argv, void **bitnum)
315 unsigned char c, *res;
318 return -E_MOOD_SYNTAX;
319 ret = get_attribute_bitnum_by_name(argv[1], &c);
322 res = para_malloc(1);
328 static int mm_is_set_score_function(__a_unused const char *path,
329 __a_unused const struct afs_info *afsi,
330 __a_unused const struct afh_info *afhi,
333 const unsigned char *bn = data;
334 if (afsi->attributes & (1ULL << *bn))
339 #define DEFINE_MOOD_METHOD(_name) \
340 .parser = mm_ ## _name ## _parser, \
341 .score_function = mm_ ## _name ## _score_function, \
344 #define DEFINE_MOOD_METHOD_WITH_CLEANUP(_name) \
345 DEFINE_MOOD_METHOD(_name), \
346 .cleanup = mm_ ## _name ## _cleanup
348 #define DEFINE_REGEX_MOOD_METHOD(_name) \
350 .parser = mm_regex_parser, \
351 .score_function = mm_ ## _name ## _score_function, \
352 .cleanup = mm_regex_cleanup
354 #define DEFINE_COMPARE_NUM_MOOD_METHOD(_name) \
356 .parser = mm_compare_num_parser, \
357 .score_function = mm_ ## _name ## _score_function
359 const struct mood_method mood_methods[] = {
360 {DEFINE_MOOD_METHOD(no_attributes_set)},
361 {DEFINE_MOOD_METHOD(is_set)},
362 {DEFINE_MOOD_METHOD_WITH_CLEANUP(path_matches)},
363 {DEFINE_MOOD_METHOD_WITH_CLEANUP(year)},
364 {DEFINE_REGEX_MOOD_METHOD(artist_matches)},
365 {DEFINE_REGEX_MOOD_METHOD(title_matches)},
366 {DEFINE_REGEX_MOOD_METHOD(album_matches)},
367 {DEFINE_REGEX_MOOD_METHOD(comment_matches)},
368 {DEFINE_COMPARE_NUM_MOOD_METHOD(bitrate)},
369 {DEFINE_COMPARE_NUM_MOOD_METHOD(frequency)},
370 {DEFINE_COMPARE_NUM_MOOD_METHOD(channels)},
371 {DEFINE_COMPARE_NUM_MOOD_METHOD(num_played)},
372 {DEFINE_COMPARE_NUM_MOOD_METHOD(image_id)},
373 {DEFINE_COMPARE_NUM_MOOD_METHOD(lyrics_id)},