X-Git-Url: https://git.decadent.org.uk/gitweb/?p=videolink.git;a=blobdiff_plain;f=vob_list.cpp;fp=vob_list.cpp;h=086e1a032138f3e399a868c2bdc2a65af99b2b79;hp=0000000000000000000000000000000000000000;hb=c13714f6498df33e02635421354f5fb88a60eb3d;hpb=d8d5c01a14ab3808a9c823996eb1b72d5c624807 diff --git a/vob_list.cpp b/vob_list.cpp new file mode 100644 index 0000000..086e1a0 --- /dev/null +++ b/vob_list.cpp @@ -0,0 +1,189 @@ +// Copyright 2006 Ben Hutchings . +// See the file "COPYING" for licence details. + +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#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 + 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 parse_error; + unsigned level; + }; + + void start_element_handler(void * user_data, + const char * name, + const char ** attributes) + { + parse_context & context = *static_cast(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), + " element missing file attribute")); + } + else + { + context.list.push_back(ref); + } + } + else // not root or child + { + 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(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)) +{}