Permalink
Browse files

parser: added a response parser library

This sub-library consumes response lines obtained by
mpd_async_recv_line() and parses them.
  • Loading branch information...
1 parent d13e387 commit b877bc3026e9bf9dabd053a0a999a441cbd8f54d @MaxKellermann MaxKellermann committed Jun 10, 2009
Showing with 355 additions and 0 deletions.
  1. +2 −0 Makefile.am
  2. +134 −0 include/mpd/parser.h
  3. +11 −0 libmpdclient.ld
  4. +208 −0 src/parser.c
View
@@ -14,6 +14,7 @@ mpdinclude_HEADERS = \
include/mpd/entity.h \
include/mpd/error.h \
include/mpd/idle.h \
+ include/mpd/parser.h \
include/mpd/protocol.h \
include/mpd/send.h \
include/mpd/status.h \
@@ -43,6 +44,7 @@ src_libmpdclient_la_SOURCES = \
src/pair.c \
src/entity.c \
src/idle.c \
+ src/parser.c \
src/quote.c src/quote.h \
src/search.c \
src/send.c \
View
@@ -0,0 +1,134 @@
+/* libmpdclient
+ (c) 2003-2009 The Music Player Daemon Project
+ This project's homepage is: http://www.musicpd.org
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ - Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ - Neither the name of the Music Player Daemon nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef LIBMPDCLIENT_PARSER_H
+#define LIBMPDCLIENT_PARSER_H
+
+#include <mpd/protocol.h>
+
+#include <stdbool.h>
+
+enum mpd_parser_result {
+ /**
+ * Response line was not understood.
+ */
+ MPD_PARSER_MALFORMED,
+
+ /**
+ * MPD has returned "OK" or "list_OK" (check with
+ * mpd_parser_is_partial()).
+ */
+ MPD_PARSER_SUCCESS,
+
+ /**
+ * MPD has returned "ACK" with an error code. Call
+ * mpd_parser_get_reason() to get the error code.
+ */
+ MPD_PARSER_ERROR,
+
+ /**
+ * MPD has returned a name-value pair. Call
+ * mpd_parser_get_name() and mpd_parser_get_value().
+ */
+ MPD_PARSER_PAIR,
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Allocates a new mpd_parser object. Returns NULL on error (out of
+ * memory).
+ */
+struct mpd_parser *
+mpd_parser_new(void);
+
+/**
+ * Frees a mpd_parser object.
+ */
+void
+mpd_parser_free(struct mpd_parser *parser);
+
+/**
+ * Feeds a line (without the trailing newline character) received from
+ * MPD / mpd_async_recv_line() into the parser.
+ */
+enum mpd_parser_result
+mpd_parser_feed(struct mpd_parser *parser, char *line);
+
+/**
+ * Call this when mpd_parser_feed() has returned #MPD_PARSER_SUCCESS
+ * to find out whether this is an "OK" (false) or a "list_OK" (true)
+ * response.
+ */
+bool
+mpd_parser_is_partial(const struct mpd_parser *parser);
+
+/**
+ * Call this when mpd_parser_feed() has returned #MPD_PARSER_ERROR to
+ * obtain the reason for the error.
+ */
+enum mpd_ack
+mpd_parser_get_ack(const struct mpd_parser *parser);
+
+/**
+ * On #MPD_PARSER_ERROR, this returns the number of the list command
+ * which failed, or -1 if that information is not available.
+ */
+int
+mpd_parser_get_at(const struct mpd_parser *parser);
+
+/**
+ * On #MPD_PARSER_ERROR, this returns the human readable error message
+ * returned by MPD (UTF-8).
+ */
+const char *
+mpd_parser_get_message(const struct mpd_parser *parser);
+
+/**
+ * On #MPD_PARSER_PAIR, this returns the name.
+ */
+const char *
+mpd_parser_get_name(const struct mpd_parser *parser);
+
+/**
+ * On #MPD_PARSER_PAIR, this returns the value.
+ */
+const char *
+mpd_parser_get_value(const struct mpd_parser *parser);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
View
@@ -13,6 +13,17 @@ global:
mpd_async_send_command;
mpd_async_recv_line;
+ /* mpd/parser.h */
+ mpd_parser_new;
+ mpd_parser_free;
+ mpd_parser_feed;
+ mpd_parser_is_partial;
+ mpd_parser_get_ack;
+ mpd_parser_get_at;
+ mpd_parser_get_message;
+ mpd_parser_get_name;
+ mpd_parser_get_value;
+
/* mpd/send.h */
mpd_send_command;
View
@@ -0,0 +1,208 @@
+/* libmpdclient
+ (c) 2003-2009 The Music Player Daemon Project
+ This project's homepage is: http://www.musicpd.org
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ - Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ - Neither the name of the Music Player Daemon nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <mpd/parser.h>
+#include <mpd/protocol.h>
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct mpd_parser {
+#ifndef NDEBUG
+ enum mpd_parser_result result;
+#endif
+
+ union {
+ bool partial;
+
+ struct {
+ enum mpd_ack ack;
+ int at;
+ const char *message;
+ } error;
+
+ struct {
+ const char *name, *value;
+ } pair;
+ } u;
+};
+
+struct mpd_parser *
+mpd_parser_new(void)
+{
+ struct mpd_parser *parser = malloc(sizeof(*parser));
+ if (parser == NULL)
+ return NULL;
+
+#ifndef NDEBUG
+ parser->result = MPD_PARSER_MALFORMED;
+#endif
+
+ return parser;
+}
+
+void
+mpd_parser_free(struct mpd_parser *parser)
+{
+ free(parser);
+}
+
+static inline enum mpd_parser_result
+set_result(struct mpd_parser *parser, enum mpd_parser_result result)
+{
+#ifndef NDEBUG
+ /* this value exists only in the debug build, and is used by
+ assertions in the "get" functions below */
+ parser->result = result;
+#else
+ /* suppress "unused" warning */
+ (void)parser;
+#endif
+
+ return result;
+}
+
+enum mpd_parser_result
+mpd_parser_feed(struct mpd_parser *parser, char *line)
+{
+ if (strcmp(line, "OK") == 0) {
+ parser->u.partial = false;
+ return set_result(parser, MPD_PARSER_SUCCESS);
+ } else if (strcmp(line, "list_OK") == 0) {
+ parser->u.partial = true;
+ return set_result(parser, MPD_PARSER_SUCCESS);
+ } else if (memcmp(line, "ACK", 3) == 0) {
+ char *p, *q;
+
+ parser->u.error.ack = MPD_ACK_ERROR_UNK;
+ parser->u.error.at = -1;
+ parser->u.error.message = NULL;
+
+ /* parse [ACK@AT] */
+
+ p = strchr(line + 3, '[');
+ if (p == NULL)
+ return set_result(parser, MPD_PARSER_ERROR);
+
+ parser->u.error.ack = strtol(p + 1, &p, 10);
+ if (*p == '@')
+ parser->u.error.at = strtol(p + 1, &p, 10);
+
+ q = strchr(p, ']');
+ if (q == NULL)
+ return set_result(parser, MPD_PARSER_MALFORMED);
+
+ /* skip the {COMMAND} */
+
+ p = q + 1;
+ q = strchr(p, '{');
+ if (q != NULL) {
+ q = strchr(p, '}');
+ if (q != NULL)
+ p = q + 1;
+ }
+
+ /* obtain error message */
+
+ while (*p == ' ')
+ ++p;
+
+ if (*p != 0)
+ parser->u.error.message = p;
+
+ return set_result(parser, MPD_PARSER_ERROR);
+ } else {
+ /* so this must be a name-value pair */
+
+ char *p;
+
+ p = strchr(line, ':');
+ if (p == NULL || p[1] != ' ')
+ return set_result(parser, MPD_PARSER_MALFORMED);
+
+ *p = 0;
+
+ parser->u.pair.name = line;
+ parser->u.pair.value = p + 2;
+
+ return set_result(parser, MPD_PARSER_PAIR);
+ }
+}
+
+bool
+mpd_parser_is_partial(const struct mpd_parser *parser)
+{
+ assert(parser->result == MPD_PARSER_SUCCESS);
+
+ return parser->u.partial;
+}
+
+enum mpd_ack
+mpd_parser_get_ack(const struct mpd_parser *parser)
+{
+ assert(parser->result == MPD_PARSER_ERROR);
+
+ return parser->u.error.ack;
+}
+
+int
+mpd_parser_get_at(const struct mpd_parser *parser)
+{
+ assert(parser->result == MPD_PARSER_ERROR);
+
+ return parser->u.error.at;
+}
+
+const char *
+mpd_parser_get_message(const struct mpd_parser *parser)
+{
+ assert(parser->result == MPD_PARSER_ERROR);
+
+ return parser->u.error.message;
+}
+
+const char *
+mpd_parser_get_name(const struct mpd_parser *parser)
+{
+ assert(parser->result == MPD_PARSER_PAIR);
+
+ return parser->u.pair.name;
+}
+
+const char *
+mpd_parser_get_value(const struct mpd_parser *parser)
+{
+ assert(parser->result == MPD_PARSER_PAIR);
+
+ return parser->u.pair.value;
+}

0 comments on commit b877bc3

Please sign in to comment.