Logo Search packages:      
Sourcecode: libmpdclient version File versions  Download package

song.c

/* 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/song.h>
#include <mpd/pair.h>
#include <mpd/recv.h>
#include "internal.h"
#include "iso8601.h"

#include <assert.h>
#include <stdlib.h>
#include <string.h>

struct mpd_tag_value {
      struct mpd_tag_value *next;

      char *value;
};

00049 struct mpd_song {
      char *uri;

      struct mpd_tag_value tags[MPD_TAG_COUNT];

      /**
       * Duration of the song in seconds, or 0 for unknown.
       */
00057       unsigned duration;

      /**
       * The POSIX UTC time stamp of the last modification, or 0 if
       * that is unknown.
       */
00063       time_t last_modified;

      /**
       * The position of this song within the queue.
       */
00068       unsigned pos;

      /**
       * The id of this song within the queue.
       */
00073       unsigned id;

#ifndef NDEBUG
      /**
       * This flag is used in an assertion: when it is set, you must
       * not call mpd_song_feed() again.  It is a safeguard for
       * buggy callers.
       */
00081       bool finished;
#endif
};

static struct mpd_song *
mpd_song_new(const char *uri)
{
      struct mpd_song *song;

      assert(uri != NULL);

      song = malloc(sizeof(*song));
      if (song == NULL)
            /* out of memory */
            return NULL;

      song->uri = strdup(uri);
      if (song->uri == NULL) {
            free(song);
            return NULL;
      }

      for (unsigned i = 0; i < MPD_TAG_COUNT; ++i)
            song->tags[i].value = NULL;

      song->duration = 0;
      song->last_modified = 0;
      song->pos = 0;
      song->id = 0;

#ifndef NDEBUG
      song->finished = false;
#endif

      return song;
}

00118 void mpd_song_free(struct mpd_song *song) {
      assert(song != NULL);

      free(song->uri);

      for (unsigned i = 0; i < MPD_TAG_COUNT; ++i) {
            struct mpd_tag_value *tag = &song->tags[i], *next;

            if (tag->value == NULL)
                  continue;

            free(tag->value);

            tag = tag->next;

            while (tag != NULL) {
                  assert(tag->value != NULL);
                  free(tag->value);

                  next = tag->next;
                  free(tag);
                  tag = next;
            }
      }

      free(song);
}

static bool
mpd_song_add_tag(struct mpd_song *song,
             enum mpd_tag_type type, const char *value);

struct mpd_song *
00151 mpd_song_dup(const struct mpd_song *song)
{
      struct mpd_song *ret;
      bool success;

      assert(song != NULL);

      ret = mpd_song_new(song->uri);
      if (ret == NULL)
            /* out of memory */
            return NULL;

      for (unsigned i = 0; i < MPD_TAG_COUNT; ++i) {
            const struct mpd_tag_value *src_tag = &song->tags[i];

            if (src_tag->value == NULL)
                  continue;

            do {
                  success = mpd_song_add_tag(ret, i, src_tag->value);
                  if (!success) {
                        mpd_song_free(ret);
                        return NULL;
                  }

                  src_tag = src_tag->next;
            } while (src_tag != NULL);
      }

      ret->duration = song->duration;
      ret->last_modified = song->last_modified;
      ret->pos = song->pos;
      ret->id = song->id;

#ifndef NDEBUG
      ret->finished = true;
#endif

      return ret;
}

const char *
00193 mpd_song_get_uri(const struct mpd_song *song)
{
      return song->uri;
}


/**
 * Adds a tag value to the song.
 *
 * @return true on success, false if the tag is not supported or if no
 * memory could be allocated
 */
static bool
mpd_song_add_tag(struct mpd_song *song,
             enum mpd_tag_type type, const char *value)
{
      struct mpd_tag_value *tag = &song->tags[type], *prev;

      if ((int)type < 0 || type >= MPD_TAG_COUNT)
            return false;

      if (tag->value == NULL) {
            tag->next = NULL;
            tag->value = strdup(value);
            if (tag->value == NULL)
                  return false;
      } else {
            while (tag->next != NULL)
                  tag = tag->next;

            prev = tag;
            tag = malloc(sizeof(*tag));
            if (tag == NULL)
                  return NULL;

            tag->value = strdup(value);
            if (tag->value == NULL) {
                  free(tag);
                  return false;
            }

            tag->next = NULL;
            prev->next = tag;
      }

      return true;
}

#ifdef UNUSED_CODE
/**
 * Removes all values of the specified tag.
 */
static void
mpd_song_clear_tag(struct mpd_song *song, enum mpd_tag_type type)
{
      struct mpd_tag_value *tag = &song->tags[type];

      if ((unsigned)type >= MPD_TAG_COUNT)
            return;

      if (tag->value == NULL)
            /* this tag type is empty */
            return;

      /* free and clear the first value */
      free(tag->value);
      tag->value = NULL;

      /* free all other values; no need to clear the "next" pointer,
         because it is "undefined" as long as value==NULL */
      while ((tag = tag->next) != NULL)
            free(tag->value);
}
#endif

const char *
00269 mpd_song_get_tag(const struct mpd_song *song,
             enum mpd_tag_type type, unsigned idx)
{
      const struct mpd_tag_value *tag = &song->tags[type];

      if ((int)type < 0)
            return NULL;

      if (tag->value == NULL)
            return NULL;

      while (idx-- > 0) {
            tag = tag->next;
            if (tag == NULL)
                  return NULL;
      }

      return tag->value;
}

static void
mpd_song_set_duration(struct mpd_song *song, unsigned duration)
{
      song->duration = duration;
}

unsigned
00296 mpd_song_get_duration(const struct mpd_song *song)
{
      return song->duration;
}

static void
mpd_song_set_last_modified(struct mpd_song *song, time_t mtime)
{
      song->last_modified = mtime;
}

time_t
00308 mpd_song_get_last_modified(const struct mpd_song *song)
{
      return song->last_modified;
}

void
00314 mpd_song_set_pos(struct mpd_song *song, unsigned pos)
{
      song->pos = pos;
}

unsigned
00320 mpd_song_get_pos(const struct mpd_song *song)
{
      return song->pos;
}

static void
mpd_song_set_id(struct mpd_song *song, unsigned id)
{
      song->id = id;
}

unsigned
00332 mpd_song_get_id(const struct mpd_song *song)
{
      return song->id;
}

struct mpd_song *
00338 mpd_song_begin(const struct mpd_pair *pair)
{
      assert(pair != NULL);
      assert(pair->name != NULL);
      assert(pair->value != NULL);

      if (strcmp(pair->name, "file") != 0)
            return NULL;

      return mpd_song_new(pair->value);
}

bool
00351 mpd_song_feed(struct mpd_song *song, const struct mpd_pair *pair)
{
      enum mpd_tag_type tag_type;

      assert(song != NULL);
      assert(!song->finished);
      assert(pair != NULL);
      assert(pair->name != NULL);
      assert(pair->value != NULL);

      if (strcmp(pair->name, "file") == 0) {
#ifndef NDEBUG
            song->finished = true;
#endif
            return false;
      }

      if (*pair->value == 0)
            return true;

      tag_type = mpd_tag_name_parse(pair->name);
      if (tag_type != MPD_TAG_UNKNOWN) {
            mpd_song_add_tag(song, tag_type, pair->value);
            return true;
      }

      if (strcmp(pair->name, "Time") == 0)
            mpd_song_set_duration(song, atoi(pair->value));
      else if (strcmp(pair->name, "Last-Modified") == 0)
            mpd_song_set_last_modified(song, iso8601_datetime_parse(pair->value));
      else if (strcmp(pair->name, "Pos") == 0)
            mpd_song_set_pos(song, atoi(pair->value));
      else if (strcmp(pair->name, "Id") == 0)
            mpd_song_set_id(song, atoi(pair->value));

      return true;
}

struct mpd_song *
00390 mpd_recv_song(struct mpd_connection *connection)
{
      struct mpd_pair *pair;
      struct mpd_song *song;

      pair = mpd_recv_pair_named(connection, "file");
      if (pair == NULL)
            return NULL;

      song = mpd_song_begin(pair);
      mpd_return_pair(connection, pair);
      if (song == NULL) {
            mpd_error_code(&connection->error, MPD_ERROR_OOM);
            return NULL;
      }

      while ((pair = mpd_recv_pair(connection)) != NULL &&
             mpd_song_feed(song, pair))
            mpd_return_pair(connection, pair);

      if (mpd_error_is_defined(&connection->error)) {
            mpd_song_free(song);
            return NULL;
      }

      /* unread this pair for the next mpd_recv_song() call */
      mpd_enqueue_pair(connection, pair);

      return song;
}

Generated by  Doxygen 1.6.0   Back to index