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