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

vob_list.cpp

// Copyright 2006 Ben Hutchings <ben@decadent.org.uk>.
// See the file "COPYING" for licence details.

#include <cerrno>
#include <cstring>
#include <sstream>

#include <unistd.h>
#include <fcntl.h>

#include <expat.h>

#include <glibmm/fileutils.h>
#include <glibmm/miscutils.h>

#include "auto_fd.hpp"
#include "auto_handle.hpp"
#include "vob_list.hpp"

namespace
{
    struct xml_parser_closer
    {
      void operator()(XML_Parser parser) const
          {
            if (parser)
                XML_ParserFree(parser);
          }
    };

    struct xml_parser_factory
    {
      XML_Parser operator()() const
          {
            return NULL;
          }
    };

    typedef auto_handle<XML_Parser, xml_parser_closer, xml_parser_factory>
    auto_xml_parser;

    struct parse_context
    {
      parse_context(XML_Parser parser,
                  const std::string & list_path,
                  vob_list & list)
            : parser(parser),
              list_path(list_path),
              base_path(Glib::path_get_dirname(list_path)),
              list(list),
              level(0)
          {}
      XML_Parser parser;
      const std::string & list_path;
      std::string base_path;
      vob_list & list;
      std::auto_ptr<xml_error> parse_error;
      unsigned level;
    };

    void start_element_handler(void * user_data,
                         const char * name,
                         const char ** attributes)
    {
      parse_context & context = *static_cast<parse_context *>(user_data);

      if (context.level == 0 && std::strcmp(name, "vob-list") == 0)
      {
          // We don't expect (and will ignore) any attributes.
      }
      else if (context.level == 1 && std::strcmp(name, "vob") == 0)
      {
          vob_ref ref;

          while (attributes[0] != NULL)
          {
            if (std::strcmp(attributes[0], "file") == 0)
            {
                ref.file = attributes[1];
                if (!Glib::path_is_absolute(ref.file))
                  ref.file = Glib::build_filename(context.base_path,
                                          ref.file);
            }
            else if (std::strcmp(attributes[0], "chapters") == 0)
            {
                ref.chapters = attributes[1];
            }
            else if (std::strcmp(attributes[0], "pause") == 0)
            {
                ref.pause = attributes[1];
            }

            attributes += 2;
          }

          if (ref.file.empty())
          {
            context.parse_error.reset(
                new xml_error(
                  context.list_path,
                  XML_GetCurrentLineNumber(context.parser),
                  "<vob> element missing file attribute"));
          }
          else
          {
            context.list.push_back(ref);
          }
      }
      else // not root <vob-list> or child <vob>
      {
          context.parse_error.reset(
            new xml_error(
                context.list_path,
                XML_GetCurrentLineNumber(context.parser),
                std::string("unexpected element: <").append(name)
                .append(">")));
      }

      ++context.level;
    }

    void end_element_handler(void * user_data,
                       const char * /*name*/)
    {
      parse_context & context = *static_cast<parse_context *>(user_data);
      --context.level;
    }
}

vob_list read_vob_list(const std::string & path)
{
    vob_list result;

    auto_fd fd(open(path.c_str(), O_RDONLY, 0));
    if (fd.get() < 0)
      // FIXME: look up proper error code
      throw Glib::FileError(Glib::FileError::FAILED,
                        std::strerror(errno));

    auto_xml_parser parser(XML_ParserCreate(NULL));
    if (parser.get() == NULL)
      throw std::bad_alloc(); // any other reason?

    parse_context context(parser.get(), path, result);
    XML_SetUserData(parser.get(), &context);
    XML_SetStartElementHandler(parser.get(), start_element_handler);
    XML_SetEndElementHandler(parser.get(), end_element_handler);

    for (;;)
    {
      static const int buffer_size = 1024;
      void * buffer = XML_GetBuffer(parser.get(), buffer_size);
      if (buffer == NULL)
          throw std::bad_alloc();
      int read_size = read(fd.get(), buffer, buffer_size);
      if (read_size < 0)
          // FIXME: look up proper error code
          throw Glib::FileError(Glib::FileError::FAILED,
                          std::strerror(errno));
      bool is_final = read_size < buffer_size;
      if (XML_ParseBuffer(parser.get(), read_size, is_final)
          == XML_STATUS_ERROR)
          throw xml_error(path,
                      XML_GetCurrentLineNumber(parser.get()),
                      XML_ErrorString(XML_GetErrorCode(parser.get())));
      if (context.parse_error.get())
          throw *context.parse_error;
      if (is_final)
          break;
    }

    return result;
}

namespace
{
    std::string make_xml_error_message(const std::string & path, int line,
                               const std::string & message)
    {
      std::ostringstream os;
      os << path << ":" << line << ": " << message;
      return os.str();
    }
}

xml_error::xml_error(const std::string & path, int line,
                 const std::string & message)
      : std::runtime_error(make_xml_error_message(path, line, message))
{}

Generated by  Doxygen 1.6.0   Back to index