Move functions only needed by audiod from stat.c to audiod_command.c.
[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 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 struct mm_year_data {
191         /** Comparator and year given at the mood line. */
192         struct mm_compare_num_data *cnd;
193         /** Used to detect Y2K issues. */
194         int32_t current_year;
195 };
196
197 static int mm_year_parser(int argc, char **argv, void **private)
198 {
199         int ret = -E_MOOD_SYNTAX;
200         struct mm_year_data *mmyd = para_malloc(sizeof(*mmyd));
201         time_t current_time;
202         struct tm *gmt;
203
204         ret = mm_compare_num_parser(argc, argv, (void **)&mmyd->cnd);
205         if (ret < 0)
206                 goto err;
207         current_time = time(NULL);
208         gmt = gmtime(&current_time);
209         /* tm_year is the number of years since 1900 */
210         mmyd->current_year = gmt->tm_year + 1900;
211         *private = mmyd;
212         return 1;
213 err:
214         free(mmyd);
215         return ret;
216 }
217
218 static int mm_year_score_function(__a_unused const char *path,
219                 __a_unused const struct afs_info *afsi,
220                 const struct afh_info *afhi,
221                 const void *private)
222 {
223         const struct mm_year_data *mmyd = private;
224         int32_t tag_year;
225         int ret = para_atoi32(afhi->tags.year, &tag_year);
226
227         if (ret < 0) /* year tag not present or not a number */
228                 return -100;
229         if (tag_year < 0)
230                 return -100;
231         /* try to work around Y2K issues */
232         if (tag_year < 100) {
233                 tag_year += 1900;
234                 if (tag_year + 100 <= mmyd->current_year)
235                         tag_year += 100; /* assume tag_year >= 2000 */
236         }
237         return mm_compare_num_score_function(tag_year, mmyd->cnd);
238 }
239
240 static void mm_year_cleanup(void *private)
241 {
242         struct mm_year_data *mmyd = private;
243
244         free(mmyd->cnd);
245         free(mmyd);
246 }
247
248 static int mm_no_attributes_set_parser(int argc, __a_unused char **argv,
249                 __a_unused void **ignored)
250 {
251         return argc? -E_MOOD_SYNTAX : 1;
252 }
253
254 static int mm_no_attributes_set_score_function(__a_unused const char *path,
255                 const struct afs_info *afsi,
256                 __a_unused const struct afh_info *afhi,
257                 __a_unused const void *data)
258 {
259         if (!afsi->attributes)
260                 return 100;
261         return -100;
262 }
263
264 static int mm_path_matches_score_function(const char *path,
265                 __a_unused const struct afs_info *afsi,
266                 __a_unused const struct afh_info *afhi,
267                 const void *data)
268 {
269         if (fnmatch(data, path, 0))
270                 return -100;
271         return 100;
272 }
273
274 static int mm_path_matches_parser(int argc, char **argv, void **data)
275 {
276         if (argc != 1)
277                 return -E_MOOD_SYNTAX;
278         *data = para_strdup(argv[1]);
279         return 1;
280 }
281
282 static void mm_path_matches_cleanup(void *data)
283 {
284         free(data);
285 }
286
287 static int mm_is_set_parser(int argc, char **argv, void **bitnum)
288 {
289         int ret;
290         unsigned char c, *res;
291
292         if (argc != 1)
293                 return -E_MOOD_SYNTAX;
294         ret = get_attribute_bitnum_by_name(argv[1], &c);
295         if (ret < 0)
296                 return ret;
297         res = para_malloc(1);
298         *res = c;
299         *bitnum = res;
300         return 1;
301 }
302
303 static int mm_is_set_score_function(__a_unused const char *path,
304                 __a_unused const struct afs_info *afsi,
305                 __a_unused const struct afh_info *afhi,
306                 const void *data)
307 {
308         const unsigned char *bn = data;
309         if (afsi->attributes & (1ULL << *bn))
310                 return 100;
311         return -100;
312 }
313
314 #define DEFINE_MOOD_METHOD(_name) \
315 .parser = mm_ ## _name ## _parser, \
316 .score_function = mm_ ## _name ## _score_function, \
317 .name = #_name
318
319 #define DEFINE_MOOD_METHOD_WITH_CLEANUP(_name) \
320         DEFINE_MOOD_METHOD(_name), \
321         .cleanup = mm_ ## _name ## _cleanup
322
323 #define DEFINE_REGEX_MOOD_METHOD(_name) \
324         .name = #_name, \
325         .parser = mm_regex_parser, \
326         .score_function = mm_ ## _name ## _score_function, \
327         .cleanup = mm_regex_cleanup
328
329 #define DEFINE_COMPARE_NUM_MOOD_METHOD(_name) \
330         .name = #_name, \
331         .parser = mm_compare_num_parser, \
332         .score_function = mm_ ## _name ## _score_function
333
334 const struct mood_method mood_methods[] = {
335         {DEFINE_MOOD_METHOD(no_attributes_set)},
336         {DEFINE_MOOD_METHOD(is_set)},
337         {DEFINE_MOOD_METHOD_WITH_CLEANUP(path_matches)},
338         {DEFINE_MOOD_METHOD_WITH_CLEANUP(year)},
339         {DEFINE_REGEX_MOOD_METHOD(artist_matches)},
340         {DEFINE_REGEX_MOOD_METHOD(title_matches)},
341         {DEFINE_REGEX_MOOD_METHOD(album_matches)},
342         {DEFINE_REGEX_MOOD_METHOD(comment_matches)},
343         {DEFINE_COMPARE_NUM_MOOD_METHOD(bitrate)},
344         {DEFINE_COMPARE_NUM_MOOD_METHOD(frequency)},
345         {DEFINE_COMPARE_NUM_MOOD_METHOD(channels)},
346         {.parser = NULL}
347 };