]> git.decadent.org.uk Git - videolink.git/blobdiff - vob_list.cpp
Moved xml_escape into a separate file.
[videolink.git] / vob_list.cpp
diff --git a/vob_list.cpp b/vob_list.cpp
new file mode 100644 (file)
index 0000000..086e1a0
--- /dev/null
@@ -0,0 +1,189 @@
+// Copyright 2006 Ben Hutchings <ben@decadentplace.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))
+{}