Move mood methods to a separate file.
[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