1#include <errno.h>2#include <stdlib.h>3#include <string.h>4#include <strings.h>56#include <sys/types.h>78#include "fns.h"9#include "mpc.h"10#include "mpdserver.h"1112static mpc_parser_t *13mpd_command(void)14{15 return mpc_or(2, mpd_list_cmds(), mpd_command_primitive());16}1718static int19mpd_check_quote(mpc_val_t **val)20{21 char *ptr;2223 ptr = (char *)*val;24 return *ptr != '"';25}2627int28mpd_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;3435 str = *(char **)val;36 ary = (mpd_string_array_t *)ptr;3738 inset = 0;39 for (i = 0; i < ary->len; i++) {40 if (!strcasecmp(str, ary->ptr[i]))41 inset = 1;42 }4344 return inset;45}4647mpc_parser_t *48mpd_whitespace(void)49{50 return mpc_expect(mpc_oneof(" \t"), "whitespace");51}5253mpc_val_t *54mpdf_lowercase(mpc_val_t *val)55{56 return lowercase((char *)val);57}5859mpc_val_t *60mpdf_unescape(mpc_val_t *val)61{62 unsigned short esc;63 size_t i, oldlen, newlen;64 char *oldstr, *newstr;6566 oldstr = val;67 oldlen = strlen(oldstr);6869 newlen = 0;70 newstr = xmalloc(sizeof(char) * oldlen);7172 for (esc = 0, i = 0; i < oldlen; i++) {73 if (oldstr[i] == '\\' && !esc) {74 esc = 1;75 continue;76 }7778 newstr[newlen++] = oldstr[i];79 esc = 0;80 }8182 free(val);83 newstr = xrealloc(newstr, newlen + 1);84 newstr[newlen] = '\0';85 return newstr;86}8788static mpc_val_t *89mpdf_command_noarg(mpc_val_t *val)90{91 mpd_command_t *cmd;92 char *str;9394 str = (char *)val;95 cmd = mpd_new_command(str, 0);9697 return cmd;98}99100static mpc_val_t *101mpdf_fold_range(int n, mpc_val_t **xs)102{103 int i;104 size_t start;105 ssize_t end;106107 assert(n == 3);108 assert(*(char *)xs[1] == ':');109110 /* TODO: We can't signal an error error condition from an apply111 * 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;114115 for (i = 0; i < n; i++)116 free(xs[i]);117118 return mpd_new_range(start, end);119}120121static mpc_val_t *122mpdf_range(mpc_val_t *val)123{124 unsigned int pos;125126 pos = *(unsigned int *)val;127 free(val);128129 return mpd_new_range((size_t)pos, (ssize_t)pos);130}131132static mpc_val_t *133mpdf_uint(mpc_val_t *val)134{135 unsigned int *uval;136137 uval = xmalloc(sizeof(*uval));138 /* TODO: We can't signal an error error condition from an apply139 * function and strtoul(3) might potentially overflow. */140 *uval = (unsigned int)strtoul((char *)val, NULL, 10);141142 free(val);143 return uval;144}145146mpc_parser_t *147mpd_cmd_noarg(char *cmdstr)148{149 return mpc_apply(mpc_string(cmdstr), mpdf_command_noarg);150}151152mpc_parser_t *153mpd_argument(mpc_parser_t *a)154{155 mpc_parser_t *sep, *spaces, *quoted;156157 spaces = mpc_many1(mpcf_freefold, mpd_whitespace());158 sep = mpc_expect(spaces, "argument");159160 /* All MPD arguments can be enclosed in quotes. However, some161 * argument parsers, e.g. mpd_string, handle quotes explicitly,162 * thus they may have already been parsed at this point and are163 * only optional. */164165 /* TODO: unescape all arguments before doing further parsing */166167 quoted = mpc_between(mpc_copy(a), free, "\"", "\"");168 return mpc_and(2, mpcf_snd_free, sep, mpc_or(2, a, quoted), free);169}170171mpc_parser_t *172mpd_int(void)173{174 mpc_parser_t *neg, *val;175176 neg = mpc_and(2, mpcf_strfold, mpc_char('-'), mpc_digits(), free);177 val = mpc_or(2, neg, mpc_digits());178179 return mpc_expect(mpc_apply(val, mpcf_int), "signed integer");180}181182mpc_parser_t *183mpd_uint(void)184{185 return mpc_expect(mpc_apply(mpc_digits(), mpdf_uint),186 "unsigned integer");187}188189mpc_parser_t *190mpd_binary(void)191{192 return mpc_expect(mpc_apply(mpc_oneof("01"), mpdf_uint), "binary");193}194195mpc_parser_t *196mpd_float_digits(void)197{198 mpc_parser_t *pre, *sep, *fra;199200 pre = mpc_maybe_lift(mpc_digits(), mpcf_ctor_str);201 sep = mpc_char('.');202203 fra = mpc_and(3, mpcf_strfold, pre, sep, mpc_digits(), free, free);204 return mpc_or(2, fra, mpc_digits(), free);205}206207mpc_parser_t *208mpd_float(void)209{210 return mpc_expect(mpc_apply(mpd_float_digits(), mpcf_float), "float");211}212213mpc_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}219220mpc_parser_t *221mpd_range_with_single(void)222{223 mpc_parser_t *single;224225 single = mpc_apply(mpd_uint(), mpdf_range);226 return mpc_or(2, mpd_range(), single);227}228229mpc_parser_t *230mpd_string(void)231{232 mpc_parser_t *str, *strcheck;233234 str = mpc_many(mpcf_strfold, mpc_noneof(" \t\n"));235 strcheck =236 mpc_check(str, free, mpd_check_quote, "missing closing '\"'");237238 return mpc_or(2, mpc_apply(mpc_string_lit(), mpdf_unescape), strcheck);239}240241mpc_parser_t *242mpd_string_case(void)243{244 return mpc_apply(mpd_string(), mpdf_lowercase);245}246247mpc_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 can252 * be pretty much anything though mpd itself doesn't seem to253 * allow `.` and `..` as path elements in file names.254 * Nonetheless, it doesn't make sense to validate URIs properly255 * since every invalid URI is likely a valid file name. */256 return mpd_string();257}258259mpc_parser_t *260mpd_command_primitive(void)261{262 mpc_parser_t *cmd;263264 /* 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());268269 return mpc_and(2, mpcf_fst_free, cmd, mpc_newline(), mpd_free_command);270}271272mpd_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;281282 cmd = NULL;283 par = mpd_command();284285 if (mpc_parse("", input, par, &r)) {286 cmd = (mpd_command_t *)r.output;287 } else {288#ifdef MPC_PARSER_DEBUG289 mpc_err_print(r.error);290#endif291 mpc_err_delete(r.error);292 goto ret;293 }294295 /* TODO: There is now easy way to unescape the input string296 * 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;301302 if (!(expr = mpd_expression(c->v.sval))) {303 mpd_free_command(cmd);304 cmd = NULL;305 goto ret;306 }307308 free(c->v.sval);309 c->v.eval = expr;310 c->type = MPD_VAL_EXPR;311 }312313ret:314 mpc_cleanup(1, par);315 return cmd;316}