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