1#include <assert.h>
2#include <stddef.h>
3#include <string.h>
4#include <strings.h>
5
6#include "fns.h"
7#include "mpc.h"
8
9static char *mpd_tag_names[] = {
10 "any",
11 "artist",
12 "artistsort",
13 "album",
14 "albumsort",
15 "albumartist",
16 "albumartistsort",
17 "title",
18 "track",
19 "name",
20 "genre",
21 "date",
22 "composer",
23 "performer",
24 "grouping",
25 "comment",
26 "disc",
27 "label",
28 "musicbrainz_artistid",
29 "musicbrainz_albumid",
30 "musicbrainz_albumartistid",
31 "musicbrainz_trackid",
32 "musicbrainz_releasetrackid",
33 "musicbrainz_workid",
34};
35
36mpc_parser_t *
37mpd_tag_name(void)
38{
39 mpc_parser_t *str, *lstr;
40 static mpd_string_array_t a = MPD_STRING_ARY(mpd_tag_names);
41
42 str = mpc_many1(mpcf_strfold,
43 mpc_or(2, mpc_range('a', 'z'), mpc_range('A', 'Z')));
44 lstr = mpc_apply(str, mpdf_lowercase);
45
46 return mpc_check_with(lstr, free, mpd_check_array, &a, "invalid tag");
47}
48
49static mpc_parser_t *
50mpd_expr_string(void)
51{
52 mpc_parser_t *strch, *par;
53
54 strch = mpc_or(2, mpc_escape(), mpc_noneof("\'"));
55 par = mpc_between(mpc_many(mpcf_strfold, strch), free, "'", "'");
56
57 return mpc_apply(mpc_or(2, par, mpc_string_lit()), mpdf_unescape);
58}
59
60static mpc_val_t *
61mpdf_fold_seperator(int n, mpc_val_t **xs)
62{
63 if (!n)
64 return xstrdup("");
65
66 return mpcf_fst_free(n, xs);
67}
68
69static mpc_parser_t *
70mpd_seperator(void)
71{
72 return mpc_many(mpdf_fold_seperator, mpd_whitespace());
73}
74
75static mpc_parser_t *
76mpd_op(mpc_parser_t *par)
77{
78 return mpc_and(3, mpcf_snd_free, mpd_seperator(), par, mpd_seperator(),
79 free, free);
80}
81
82static mpd_operation_t
83mpd_str2op(char *str)
84{
85 if (!strcmp(str, "=="))
86 return MPD_OP_EQUAL;
87 else if (!strcmp(str, "!="))
88 return MPD_OP_NEQUAL;
89 else if (!strcmp(str, "contains"))
90 return MPD_OP_CONTAINS;
91 else if (!strcmp(str, "=~"))
92 return MPD_OP_MATCH;
93 else if (!strcmp(str, "!~"))
94 return MPD_OP_NMATCH;
95 else
96 return MPD_OP_NONE;
97}
98
99static mpc_val_t *
100mpdf_fold_expression(int n, mpc_val_t **xs)
101{
102 mpd_expression_t *expr;
103
104 assert(n == 3);
105
106 expr = xmalloc(sizeof(*expr));
107 expr->name = (char *)xs[0];
108 expr->op = mpd_str2op((char *)xs[1]);
109 expr->o1.str = (char *)xs[2];
110 expr->next = NULL;
111
112 free(xs[1]);
113 return expr;
114}
115
116static mpc_val_t *
117mpdf_fold_expressions(int n, mpc_val_t **xs)
118{
119 mpd_expression_t *expr;
120
121 expr = xmalloc(sizeof(*expr));
122 expr->op = MPD_OP_NONE;
123
124 if (n == 3) {
125 expr->name = (char *)xs[1];
126 assert(!strcmp(expr->name, "AND"));
127 expr->o1.expr = (mpd_expression_t *)xs[0];
128 expr->next = (mpd_expression_t *)xs[2];
129 } else if (n == 2) {
130 expr->name = xstrdup("NOT");
131 assert(!strcmp((char *)xs[0], "!"));
132 expr->o1.expr = (mpd_expression_t *)xs[1];
133 expr->next = NULL;
134 free(xs[0]);
135 } else {
136 assert(0);
137 }
138
139 return expr;
140}
141
142static mpc_parser_t *
143mpd_tag(void)
144{
145 mpc_parser_t *op;
146
147 op = mpc_or(5, mpc_string("=="), mpc_string("!="),
148 mpc_string("contains"), mpc_string("=~"), mpc_string("!~"));
149 return mpc_and(3, mpdf_fold_expression, mpd_tag_name(), mpd_op(op),
150 mpd_expr_string(), free, free);
151}
152
153static mpc_parser_t *
154mpd_file(void)
155{
156 return mpc_and(3, mpdf_fold_expression, mpc_string("file"),
157 mpd_op(mpc_string("==")), mpd_expr_string(), free, free);
158}
159
160static mpc_parser_t *
161mpd_base(void)
162{
163 return mpc_and(3, mpdf_fold_expression, mpc_string("base"),
164 mpd_seperator(), mpd_expr_string(), free, free);
165}
166
167static mpc_parser_t *
168mpd_modified_since(void)
169{
170 return mpc_and(3, mpdf_fold_expression, mpc_string("modified-since"),
171 mpd_seperator(), mpd_expr_string(), free, free);
172}
173
174static mpc_parser_t *
175mpd_audio_format(void)
176{
177 mpc_parser_t *ops;
178
179 ops = mpc_or(2, mpc_string("=="), mpc_string("=~"));
180 return mpc_and(3, mpdf_fold_expression, mpc_string("AudioFormat"),
181 mpd_op(ops), mpd_expr_string(), free, free);
182}
183
184static mpc_parser_t *
185mpd_base_exprs(void)
186{
187 return mpc_or(5, mpd_file(), mpd_base(), mpd_modified_since(),
188 mpd_audio_format(), mpd_tag());
189}
190
191void
192mpd_free_expression(void *ptr)
193{
194 mpd_expression_t *expr;
195
196 expr = (mpd_expression_t *)ptr;
197 if (!strcmp(expr->name, "AND") || !strcmp(expr->name, "NOT"))
198 mpd_free_expression(expr->o1.expr);
199 else
200 free(expr->o1.str);
201
202 if (expr->next)
203 mpd_free_expression(expr->next);
204
205 free(expr->name);
206 free(expr);
207}
208
209mpd_expression_t *
210mpd_expression(char *str)
211{
212 mpc_result_t r;
213 mpc_parser_t *expr;
214 mpd_expression_t *ret;
215
216 expr = mpc_new("expr");
217 mpc_define(expr,
218 mpc_between(mpc_or(3, mpd_base_exprs(),
219 mpc_and(3, mpdf_fold_expressions, expr,
220 mpd_op(mpc_string("AND")), expr,
221 mpd_free_expression, free),
222 mpc_and(2, mpdf_fold_expressions,
223 mpc_char('!'), expr, free)),
224 mpd_free_expression, "(", ")"));
225
226 if (mpc_parse("", str, expr, &r)) {
227 ret = (mpd_expression_t *)r.output;
228 } else {
229 mpc_err_delete(r.error);
230 ret = NULL;
231 }
232
233 mpc_cleanup(1, expr);
234 return ret;
235}