Change name of private_wmadec_data parameter of wmadec_cleanup().
[paraslash.git] / mm.c
1 /*
2  * Copyright (C) 2007-2009 Andre Noll <maan@systemlinux.org>
3  *
4  * Licensed under the GPL v2. For licencing details see COPYING.
5  */
6
7 /** \file mm.c Paraslash's mood methods. */
8
9 #include <time.h>
10 #include <regex.h>
11 #include <fnmatch.h>
12 #include <osl.h>
13
14 #include "para.h"
15 #include "error.h"
16 #include "string.h"
17 #include "afh.h"
18 #include "afs.h"
19 #include "mm.h"
20
21 #define MOOD_COMPARATORS \
22         MC(LESS, <) \
23         MC(LESS_OR_EQUAL, <=) \
24         MC(EQUAL, =) \
25         MC(EQUAL2, ==) \
26         MC(NOT_EQUAL, !=) \
27         MC(NOT_EQUAL2, <>) \
28         MC(GREATER, >) \
29         MC(GREATER_OR_EQUAL, >=) \
30
31 #define MC(a, b) MC_ ## a,
32 enum mood_comparator_id {MOOD_COMPARATORS NUM_MOOD_COMPARATORS};
33 #undef MC
34 #define MC(a, b) # b,
35 static const char const *mood_comparators[] = {MOOD_COMPARATORS};
36 #undef MC
37
38 static int parse_mood_comparator(const char *word)
39 {
40         int i;
41
42         for (i = 0; i < NUM_MOOD_COMPARATORS; i++)
43                 if (!strcmp(word, mood_comparators[i]))
44                         return i;
45         return -E_MOOD_SYNTAX;
46 }
47
48 struct mm_compare_num_data {
49         /** <, <=, =, !=, >=, or >. */
50         enum mood_comparator_id id;
51         /** The value given at the mood line. */
52         int32_t arg;
53 };
54
55 static int mm_compare_num_score_function(int32_t val,
56                 const struct mm_compare_num_data *cnd)
57 {
58         int res;
59         int32_t arg = cnd->arg;
60
61         switch (cnd->id) {
62         case MC_LESS:
63                 res = val < arg; break;
64         case MC_LESS_OR_EQUAL:
65                 res = val <= arg; break;
66         case MC_EQUAL:
67         case MC_EQUAL2:
68                 res = val == arg; break;
69         case MC_NOT_EQUAL:
70         case MC_NOT_EQUAL2:
71                 res = val != arg; break;
72         case MC_GREATER:
73                 res = val > arg; break;
74         case MC_GREATER_OR_EQUAL:
75                 res = val >= arg; break;
76         default:
77                 PARA_EMERG_LOG("BUG: invalid mood comparator\n");
78                 exit(EXIT_FAILURE);
79         }
80         return res? 100 : -100;
81 }
82
83 static int mm_compare_num_parser(int argc, char **argv, void **private)
84 {
85         int ret;
86         enum mood_comparator_id id;
87         int32_t arg;
88         struct mm_compare_num_data *cnd;
89         if (argc != 2)
90                 return -E_MOOD_SYNTAX;
91         ret = parse_mood_comparator(argv[1]);
92         if (ret < 0)
93                 return ret;
94         id = ret;
95         ret = para_atoi32(argv[2], &arg);
96         if (ret < 0)
97                 return ret;
98         cnd = para_malloc(sizeof(struct mm_compare_num_data));
99         cnd->id = id;
100         cnd->arg = arg;
101         *private = cnd;
102         return 1;
103 }
104
105 static int mm_regex_parser(int argc, char **argv, void **private)
106 {
107         regex_t *preg;
108         int ret;
109
110         if (argc != 1)
111                 return -E_MOOD_SYNTAX;
112         preg = para_malloc(sizeof(*preg));
113         ret = para_regcomp(preg, argv[1], REG_EXTENDED | REG_NOSUB);
114         if (ret < 0) {
115                 free(preg);
116                 return ret;
117         }
118         *private = preg;
119         return 1;
120 }
121
122 static int mm_regex_score_function(const regex_t *preg, const char *pattern)
123 {
124         return regexec(preg, pattern, 0, NULL, 0) == 0? 100 : -100;
125 }
126
127 static void mm_regex_cleanup(void *private)
128 {
129         regex_t *preg = private;
130         regfree(preg);
131         free(preg);
132 }
133
134 static int mm_artist_matches_score_function(__a_unused const char *path,
135                 __a_unused const struct afs_info *afsi,
136                 const struct afh_info *afhi,
137                 const void *private)
138 {
139         return mm_regex_score_function(private, afhi->tags.artist);
140 }
141
142 static int mm_title_matches_score_function(__a_unused const char *path,
143                 __a_unused const struct afs_info *afsi,
144                 const struct afh_info *afhi,
145                 const void *private)
146 {
147         return mm_regex_score_function(private, afhi->tags.title);
148 }
149
150 static int mm_album_matches_score_function(__a_unused const char *path,
151                 __a_unused const struct afs_info *afsi,
152                 const struct afh_info *afhi,
153                 const void *private)
154 {
155         return mm_regex_score_function(private, afhi->tags.album);
156 }
157
158 static int mm_comment_matches_score_function(__a_unused const char *path,
159                 __a_unused const struct afs_info *afsi,
160                 const struct afh_info *afhi,
161                 const void *private)
162 {
163         return mm_regex_score_function(private, afhi->tags.comment);
164 }
165
166 static int mm_bitrate_score_function(__a_unused const char *path,
167                 __a_unused const struct afs_info *afsi,
168                 const struct afh_info *afhi,
169                 const void *private)
170 {
171         return mm_compare_num_score_function(afhi->bitrate, private);
172 }
173
174 static int mm_frequency_score_function(__a_unused const char *path,
175                 __a_unused const struct afs_info *afsi,
176                 const struct afh_info *afhi,
177                 const void *private)
178 {
179         return mm_compare_num_score_function(afhi->frequency, private);
180 }
181
182 static int mm_channels_score_function(__a_unused const char *path,
183                 __a_unused const struct afs_info *afsi,
184                 const struct afh_info *afhi,
185                 const void *private)
186 {
187         return mm_compare_num_score_function(afhi->channels, private);
188 }
189
190 static int mm_num_played_score_function(__a_unused const char *path,
191                 const struct afs_info *afsi,
192                 __a_unused const struct afh_info *afhi,
193                 const void *private)
194 {
195         return mm_compare_num_score_function(afsi->num_played, private);
196 }
197
198 struct mm_year_data {
199         /** Comparator and year given at the mood line. */
200         struct mm_compare_num_data *cnd;
201         /** Used to detect Y2K issues. */
202         int32_t current_year;
203 };
204
205 static int mm_year_parser(int argc, char **argv, void **private)
206 {
207         int ret = -E_MOOD_SYNTAX;
208         struct mm_year_data *mmyd = para_malloc(sizeof(*mmyd));
209         time_t current_time;
210         struct tm *gmt;
211
212         ret = mm_compare_num_parser(argc, argv, (void **)&mmyd->cnd);
213         if (ret < 0)
214                 goto err;
215         current_time = time(NULL);
216         gmt = gmtime(&current_time);
217         /* tm_year is the number of years since 1900 */
218         mmyd->current_year = gmt->tm_year + 1900;
219         *private = mmyd;
220         return 1;
221 err:
222         free(mmyd);
223         return ret;
224 }
225
226 static int mm_year_score_function(__a_unused const char *path,
227                 __a_unused const struct afs_info *afsi,
228                 const struct afh_info *afhi,
229                 const void *private)
230 {
231         const struct mm_year_data *mmyd = private;
232         int32_t tag_year;
233         int ret = para_atoi32(afhi->tags.year, &tag_year);
234
235         if (ret < 0) /* year tag not present or not a number */
236                 return -100;
237         if (tag_year < 0)
238                 return -100;
239         /* try to work around Y2K issues */
240         if (tag_year < 100) {
241                 tag_year += 1900;
242                 if (tag_year + 100 <= mmyd->current_year)
243                         tag_year += 100; /* assume tag_year >= 2000 */
244         }
245         return mm_compare_num_score_function(tag_year, mmyd->cnd);
246 }
247
248 static void mm_year_cleanup(void *private)
249 {
250         struct mm_year_data *mmyd = private;
251
252         free(mmyd->cnd);
253         free(mmyd);
254 }
255
256 static int mm_no_attributes_set_parser(int argc, __a_unused char **argv,
257                 __a_unused void **ignored)
258 {
259         return argc? -E_MOOD_SYNTAX : 1;
260 }
261
262 static int mm_no_attributes_set_score_function(__a_unused const char *path,
263                 const struct afs_info *afsi,
264                 __a_unused const struct afh_info *afhi,
265                 __a_unused const void *data)
266 {
267         if (!afsi->attributes)
268                 return 100;
269         return -100;
270 }
271
272 static int mm_path_matches_score_function(const char *path,
273                 __a_unused const struct afs_info *afsi,
274                 __a_unused const struct afh_info *afhi,
275                 const void *data)
276 {
277         if (fnmatch(data, path, 0))
278                 return -100;
279         return 100;
280 }
281
282 static int mm_path_matches_parser(int argc, char **argv, void **data)
283 {
284         if (argc != 1)
285                 return -E_MOOD_SYNTAX;
286         *data = para_strdup(argv[1]);
287         return 1;
288 }
289
290 static void mm_path_matches_cleanup(void *data)
291 {
292         free(data);
293 }
294
295 static int mm_is_set_parser(int argc, char **argv, void **bitnum)
296 {
297         int ret;
298         unsigned char c, *res;
299
300         if (argc != 1)
301                 return -E_MOOD_SYNTAX;
302         ret = get_attribute_bitnum_by_name(argv[1], &c);
303         if (ret < 0)
304                 return ret;
305         res = para_malloc(1);
306         *res = c;
307         *bitnum = res;
308         return 1;
309 }
310
311 static int mm_is_set_score_function(__a_unused const char *path,
312                 __a_unused const struct afs_info *afsi,
313                 __a_unused const struct afh_info *afhi,
314                 const void *data)
315 {
316         const unsigned char *bn = data;
317         if (afsi->attributes & (1ULL << *bn))
318                 return 100;
319         return -100;
320 }
321
322 #define DEFINE_MOOD_METHOD(_name) \
323 .parser = mm_ ## _name ## _parser, \
324 .score_function = mm_ ## _name ## _score_function, \
325 .name = #_name
326
327 #define DEFINE_MOOD_METHOD_WITH_CLEANUP(_name) \
328         DEFINE_MOOD_METHOD(_name), \
329         .cleanup = mm_ ## _name ## _cleanup
330
331 #define DEFINE_REGEX_MOOD_METHOD(_name) \
332         .name = #_name, \
333         .parser = mm_regex_parser, \
334         .score_function = mm_ ## _name ## _score_function, \
335         .cleanup = mm_regex_cleanup
336
337 #define DEFINE_COMPARE_NUM_MOOD_METHOD(_name) \
338         .name = #_name, \
339         .parser = mm_compare_num_parser, \
340         .score_function = mm_ ## _name ## _score_function
341
342 const struct mood_method mood_methods[] = {
343         {DEFINE_MOOD_METHOD(no_attributes_set)},
344         {DEFINE_MOOD_METHOD(is_set)},
345         {DEFINE_MOOD_METHOD_WITH_CLEANUP(path_matches)},
346         {DEFINE_MOOD_METHOD_WITH_CLEANUP(year)},
347         {DEFINE_REGEX_MOOD_METHOD(artist_matches)},
348         {DEFINE_REGEX_MOOD_METHOD(title_matches)},
349         {DEFINE_REGEX_MOOD_METHOD(album_matches)},
350         {DEFINE_REGEX_MOOD_METHOD(comment_matches)},
351         {DEFINE_COMPARE_NUM_MOOD_METHOD(bitrate)},
352         {DEFINE_COMPARE_NUM_MOOD_METHOD(frequency)},
353         {DEFINE_COMPARE_NUM_MOOD_METHOD(channels)},
354         {DEFINE_COMPARE_NUM_MOOD_METHOD(num_played)},
355         {.parser = NULL}
356 };