1#include <errno.h>
2#include <stdlib.h>
3#include <string.h>
4#include <strings.h>
5
6#include <sys/types.h>
7
8#include "fns.h"
9#include "mpc.h"
10#include "mpdserver.h"
11
12static mpc_parser_t *
13mpd_command(void)
14{
15 return mpc_or(2, mpd_list_cmds(), mpd_command_primitive());
16}
17
18static int
19mpd_check_quote(mpc_val_t **val)
20{
21 char *ptr;
22
23 ptr = (char *)*val;
24 return *ptr != '"';
25}
26
27int
28mpd_check_array(mpc_val_t **val, void *ptr)
29{
30 size_t i;
31 int inset;
32 char *str;
33 mpd_string_array_t *ary;
34
35 str = *(char **)val;
36 ary = (mpd_string_array_t *)ptr;
37
38 inset = 0;
39 for (i = 0; i < ary->len; i++) {
40 if (!strcasecmp(str, ary->ptr[i]))
41 inset = 1;
42 }
43
44 return inset;
45}
46
47mpc_parser_t *
48mpd_whitespace(void)
49{
50 return mpc_expect(mpc_oneof(" \t"), "whitespace");
51}
52
53mpc_val_t *
54mpdf_lowercase(mpc_val_t *val)
55{
56 return lowercase((char *)val);
57}
58
59mpc_val_t *
60mpdf_unescape(mpc_val_t *val)
61{
62 unsigned short esc;
63 size_t i, oldlen, newlen;
64 char *oldstr, *newstr;
65
66 oldstr = val;
67 oldlen = strlen(oldstr);
68
69 newlen = 0;
70 newstr = xmalloc(sizeof(char) * oldlen);
71
72 for (esc = 0, i = 0; i < oldlen; i++) {
73 if (oldstr[i] == '\\' && !esc) {
74 esc = 1;
75 continue;
76 }
77
78 newstr[newlen++] = oldstr[i];
79 esc = 0;
80 }
81
82 free(val);
83 newstr = xrealloc(newstr, newlen + 1);
84 newstr[newlen] = '\0';
85 return newstr;
86}
87
88static mpc_val_t *
89mpdf_command_noarg(mpc_val_t *val)
90{
91 mpd_command_t *cmd;
92 char *str;
93
94 str = (char *)val;
95 cmd = mpd_new_command(str, 0);
96
97 return cmd;
98}
99
100static mpc_val_t *
101mpdf_fold_range(int n, mpc_val_t **xs)
102{
103 int i;
104 size_t start;
105 ssize_t end;
106
107 assert(n == 3);
108 assert(*(char *)xs[1] == ':');
109
110 /* TODO: We can't signal an error error condition from an apply
111 * function and strtoull(3) might potentially overflow. */
112 start = (size_t)strtoull((char *)xs[0], NULL, 10);
113 end = (xs[2]) ? (ssize_t)strtoull((char *)xs[2], NULL, 10) : -1;
114
115 for (i = 0; i < n; i++)
116 free(xs[i]);
117
118 return mpd_new_range(start, end);
119}
120
121static mpc_val_t *
122mpdf_range(mpc_val_t *val)
123{
124 unsigned int pos;
125
126 pos = *(unsigned int *)val;
127 free(val);
128
129 return mpd_new_range((size_t)pos, (ssize_t)pos);
130}
131
132static mpc_val_t *
133mpdf_uint(mpc_val_t *val)
134{
135 unsigned int *uval;
136
137 uval = xmalloc(sizeof(*uval));
138 /* TODO: We can't signal an error error condition from an apply
139 * function and strtoul(3) might potentially overflow. */
140 *uval = (unsigned int)strtoul((char *)val, NULL, 10);
141
142 free(val);
143 return uval;
144}
145
146mpc_parser_t *
147mpd_cmd_noarg(char *cmdstr)
148{
149 return mpc_apply(mpc_string(cmdstr), mpdf_command_noarg);
150}
151
152mpc_parser_t *
153mpd_argument(mpc_parser_t *a)
154{
155 mpc_parser_t *sep, *spaces, *quoted;
156
157 spaces = mpc_many1(mpcf_freefold, mpd_whitespace());
158 sep = mpc_expect(spaces, "argument");
159
160 /* All MPD arguments can be enclosed in quotes. However, some
161 * argument parsers, e.g. mpd_string, handle quotes explicitly,
162 * thus they may have already been parsed at this point and are
163 * only optional. */
164
165 /* TODO: unescape all arguments before doing further parsing */
166
167 quoted = mpc_between(mpc_copy(a), free, "\"", "\"");
168 return mpc_and(2, mpcf_snd_free, sep, mpc_or(2, a, quoted), free);
169}
170
171mpc_parser_t *
172mpd_int(void)
173{
174 mpc_parser_t *neg, *val;
175
176 neg = mpc_and(2, mpcf_strfold, mpc_char('-'), mpc_digits(), free);
177 val = mpc_or(2, neg, mpc_digits());
178
179 return mpc_expect(mpc_apply(val, mpcf_int), "signed integer");
180}
181
182mpc_parser_t *
183mpd_uint(void)
184{
185 return mpc_expect(mpc_apply(mpc_digits(), mpdf_uint),
186 "unsigned integer");
187}
188
189mpc_parser_t *
190mpd_binary(void)
191{
192 return mpc_expect(mpc_apply(mpc_oneof("01"), mpdf_uint), "binary");
193}
194
195mpc_parser_t *
196mpd_float_digits(void)
197{
198 mpc_parser_t *pre, *sep, *fra;
199
200 pre = mpc_maybe_lift(mpc_digits(), mpcf_ctor_str);
201 sep = mpc_char('.');
202
203 fra = mpc_and(3, mpcf_strfold, pre, sep, mpc_digits(), free, free);
204 return mpc_or(2, fra, mpc_digits(), free);
205}
206
207mpc_parser_t *
208mpd_float(void)
209{
210 return mpc_expect(mpc_apply(mpd_float_digits(), mpcf_float), "float");
211}
212
213mpc_parser_t *
214mpd_range(void)
215{
216 return mpc_and(3, mpdf_fold_range, mpc_digits(), mpc_char(':'),
217 mpc_maybe(mpc_digits()), free, free);
218}
219
220mpc_parser_t *
221mpd_range_with_single(void)
222{
223 mpc_parser_t *single;
224
225 single = mpc_apply(mpd_uint(), mpdf_range);
226 return mpc_or(2, mpd_range(), single);
227}
228
229mpc_parser_t *
230mpd_string(void)
231{
232 mpc_parser_t *str, *strcheck;
233
234 str = mpc_many(mpcf_strfold, mpc_noneof(" \t\n"));
235 strcheck =
236 mpc_check(str, free, mpd_check_quote, "missing closing '\"'");
237
238 return mpc_or(2, mpc_apply(mpc_string_lit(), mpdf_unescape), strcheck);
239}
240
241mpc_parser_t *
242mpd_string_case(void)
243{
244 return mpc_apply(mpd_string(), mpdf_lowercase);
245}
246
247mpc_parser_t *
248mpd_uri(void)
249{
250 /* Unfortunately, a URI argument is either a URI or a file name.
251 * While a URI is properly defined in RFC 3986 a file name can
252 * be pretty much anything though mpd itself doesn't seem to
253 * allow `.` and `..` as path elements in file names.
254 * Nonetheless, it doesn't make sense to validate URIs properly
255 * since every invalid URI is likely a valid file name. */
256 return mpd_string();
257}
258
259mpc_parser_t *
260mpd_command_primitive(void)
261{
262 mpc_parser_t *cmd;
263
264 /* TODO: mpc_or all parsers */
265 cmd = mpc_or(6, mpd_playback_cmds(), mpd_status_cmds(),
266 mpd_queue_cmds(), mpd_control_cmds(), mpd_database_cmds(),
267 mpd_connection_cmds());
268
269 return mpc_and(2, mpcf_fst_free, cmd, mpc_newline(), mpd_free_command);
270}
271
272mpd_command_t *
273mpd_parse(char *input)
274{
275 size_t i;
276 mpd_expression_t *expr;
277 mpd_argument_t *c;
278 mpd_command_t *cmd;
279 mpc_parser_t *par;
280 mpc_result_t r;
281
282 cmd = NULL;
283 par = mpd_command();
284
285 if (mpc_parse("", input, par, &r)) {
286 cmd = (mpd_command_t *)r.output;
287 } else {
288#ifdef MPC_PARSER_DEBUG
289 mpc_err_print(r.error);
290#endif
291 mpc_err_delete(r.error);
292 goto ret;
293 }
294
295 /* TODO: There is now easy way to unescape the input string
296 * before processing it further. Woraround: call mpc_parse twice. */
297 for (i = 0; i < cmd->argc; i++) {
298 c = cmd->argv[i];
299 if (c->type != MPD_VAL_EXPR_STR)
300 continue;
301
302 if (!(expr = mpd_expression(c->v.sval))) {
303 mpd_free_command(cmd);
304 cmd = NULL;
305 goto ret;
306 }
307
308 free(c->v.sval);
309 c->v.eval = expr;
310 c->type = MPD_VAL_EXPR;
311 }
312
313ret:
314 mpc_cleanup(1, par);
315 return cmd;
316}