Merge branch 'master' into next
[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 static int compare_int32(int32_t a, int32_t b, enum mood_comparator_id id)
49 {
50         int res;
51
52         switch (id) {
53         case MC_LESS:
54                 res = a < b; break;
55         case MC_LESS_OR_EQUAL:
56                 res = a <= b; break;
57         case MC_EQUAL:
58         case MC_EQUAL2:
59                 res = a == b; break;
60         case MC_NOT_EQUAL:
61         case MC_NOT_EQUAL2:
62                 res = a != b; break;
63         case MC_GREATER:
64                 res = a > b; break;
65         case MC_GREATER_OR_EQUAL:
66                 res = a >= b; break;
67         default:
68                 PARA_EMERG_LOG("BUG: invalid mood comparator\n");
69                 exit(EXIT_FAILURE);
70         }
71         return res? 100 : -100;
72 }
73
74 struct mm_year_data {
75         /** The year given at the mood line. */
76         int32_t year;
77         /** Used to detect Y2K issues. */
78         int32_t current_year;
79         /** <, <=, =, !=, >=, or >. */
80         enum mood_comparator_id id;
81 };
82
83 static int mm_year_parser(int argc, char **argv, void **private)
84 {
85         int ret = -E_MOOD_SYNTAX;
86         struct mm_year_data *mmyd = para_malloc(sizeof(*mmyd));
87         time_t current_time;
88         struct tm *gmt;
89
90         if (argc != 2)
91                 goto err;
92         ret = parse_mood_comparator(argv[1]);
93         mmyd->id = ret;
94         if (ret < 0)
95                 goto err;
96         ret = para_atoi32(argv[2], &mmyd->year);
97         if (ret < 0)
98                 goto err;
99         current_time = time(NULL);
100         gmt = gmtime(&current_time);
101         /* tm_year is the number of years since 1900 */
102         mmyd->current_year = gmt->tm_year + 1900;
103         *private = mmyd;
104         return 1;
105 err:
106         free(mmyd);
107         return ret;
108 }
109
110 static int mm_year_score_function(__a_unused const char *path,
111                 __a_unused const struct afs_info *afsi,
112                 const struct afh_info *afhi,
113                 const void *private)
114 {
115         const struct mm_year_data *mmyd = private;
116         int32_t tag_year;
117         int ret = para_atoi32(afhi->tags.year, &tag_year);
118
119         if (ret < 0) /* year tag not present or not a number */
120                 return -100;
121         if (tag_year < 0)
122                 return -100;
123         /* try to work around Y2K issues */
124         if (tag_year < 100) {
125                 tag_year += 1900;
126                 if (tag_year + 100 <= mmyd->current_year)
127                         tag_year += 100; /* assume tag_year >= 2000 */
128         }
129         return compare_int32(tag_year, mmyd->year, mmyd->id);
130 }
131
132 static void mm_year_cleanup(void *private)
133 {
134         free(private);
135 }
136
137 static int mm_no_attributes_set_parser(int argc, __a_unused char **argv,
138                 __a_unused void **ignored)
139 {
140         return argc? -E_MOOD_SYNTAX : 1;
141 }
142
143 static int mm_no_attributes_set_score_function(__a_unused const char *path,
144                 const struct afs_info *afsi,
145                 __a_unused const struct afh_info *afhi,
146                 __a_unused const void *data)
147 {
148         if (!afsi->attributes)
149                 return 100;
150         return -100;
151 }
152
153 static int mm_path_matches_score_function(const char *path,
154                 __a_unused const struct afs_info *afsi,
155                 __a_unused const struct afh_info *afhi,
156                 const void *data)
157 {
158         if (fnmatch(data, path, 0))
159                 return -100;
160         return 100;
161 }
162
163 static int mm_path_matches_parser(int argc, char **argv, void **data)
164 {
165         if (argc != 1)
166                 return -E_MOOD_SYNTAX;
167         *data = para_strdup(argv[1]);
168         return 1;
169 }
170
171 static void mm_path_matches_cleanup(void *data)
172 {
173         free(data);
174 }
175
176 static int mm_is_set_parser(int argc, char **argv, void **bitnum)
177 {
178         int ret;
179         unsigned char c, *res;
180
181         if (argc != 1)
182                 return -E_MOOD_SYNTAX;
183         ret = get_attribute_bitnum_by_name(argv[1], &c);
184         if (ret < 0)
185                 return ret;
186         res = para_malloc(1);
187         *res = c;
188         *bitnum = res;
189         return 1;
190 }
191
192 static int mm_is_set_score_function(__a_unused const char *path,
193                 __a_unused const struct afs_info *afsi,
194                 __a_unused const struct afh_info *afhi,
195                 const void *data)
196 {
197         const unsigned char *bn = data;
198         if (afsi->attributes & (1ULL << *bn))
199                 return 100;
200         return -100;
201 }
202
203 #define DEFINE_MOOD_METHOD(_name) \
204 .parser = mm_ ## _name ## _parser, \
205 .score_function = mm_ ## _name ## _score_function, \
206 .name = #_name
207
208 #define DEFINE_MOOD_METHOD_WITH_CLEANUP(_name) \
209         DEFINE_MOOD_METHOD(_name), \
210         .cleanup = mm_ ## _name ## _cleanup
211
212 const struct mood_method mood_methods[] = {
213         {DEFINE_MOOD_METHOD(no_attributes_set)},
214         {DEFINE_MOOD_METHOD(is_set)},
215         {DEFINE_MOOD_METHOD_WITH_CLEANUP(path_matches)},
216         {DEFINE_MOOD_METHOD_WITH_CLEANUP(year)},
217         {.parser = NULL}
218 };
219