aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--c/data.cpp107
-rw-r--r--c/data.h89
-rw-r--r--c/ext.cpp8
-rw-r--r--c/util.h56
4 files changed, 141 insertions, 119 deletions
diff --git a/c/data.cpp b/c/data.cpp
index 7df8bda..4db294c 100644
--- a/c/data.cpp
+++ b/c/data.cpp
@@ -11,72 +11,65 @@
#include "util.h"
#include "win.h"
-struct XmlError : public std::exception
-{
- const char* msg;
- XmlError()
- {
- msg = xmlGetLastError()->message;
- }
- virtual const char* what() const noexcept
- {
- return msg;
- }
-};
-
-static inline void XmlFree(void* p) { xmlFree(p); }
-
-/* RAII types for WinINet and libxml2. */
-using InternetHandle = Managed<HINTERNET, InternetCloseHandle, InternetError>;
-using XmlXPathContextPtr = Managed<xmlXPathContextPtr, xmlXPathFreeContext, XmlError>;
-using XmlXPathObjectPtr = Managed<xmlXPathObjectPtr, xmlXPathFreeObject, XmlError>;
-using XmlCharPtr = Managed<xmlChar*, XmlFree, XmlError>;
-
-static InternetHandle s_hi = InternetOpenW(L"Episode Browser", INTERNET_OPEN_TYPE_DIRECT, nullptr, nullptr, 0);
+static Unique<HINTERNET, InternetCloseHandle> s_hi =
+ InternetOpenW(L"Episode Browser", INTERNET_OPEN_TYPE_DIRECT, nullptr, nullptr, 0);
/* ParsedDoc downloads and parses an HTML document. */
struct ParsedDoc
{
- using HtmlParserCtxtPtr = Managed<htmlParserCtxtPtr, xmlFreeParserCtxt, XmlError>;
-
- InternetHandle hiUrl;
- HtmlParserCtxtPtr ctxt;
+ Unique<HINTERNET, InternetCloseHandle> hiUrl;
+ Unique<htmlParserCtxtPtr, xmlFreeParserCtxt> ctxt;
char bufI[1024];
char bufX[1024];
ParsedDoc(const wchar_t* wszUrl, const char* szUrl)
- : hiUrl(InternetOpenUrlW(s_hi, wszUrl, nullptr, 0, INTERNET_FLAG_NO_UI, 0)),
- ctxt(htmlCreatePushParserCtxt(nullptr, nullptr, bufX, sizeof(bufX), szUrl, XML_CHAR_ENCODING_UTF8))
{
- htmlCtxtUseOptions(ctxt, HTML_PARSE_RECOVER|HTML_PARSE_NOERROR|HTML_PARSE_NOWARNING);
+ if (!s_hi.Not(0))
+ throw Win32Error();
+
+ hiUrl = InternetOpenUrlW(s_hi.v, wszUrl, nullptr, 0, INTERNET_FLAG_NO_UI, 0);
+ if (!hiUrl.Not(0))
+ throw InternetError();
+
+ ctxt = htmlCreatePushParserCtxt(nullptr, nullptr, bufX, sizeof(bufX), szUrl, XML_CHAR_ENCODING_UTF8);
+ if (!ctxt.Not(0))
+ throw XmlError();
+
+ htmlCtxtUseOptions(ctxt.v, HTML_PARSE_RECOVER|HTML_PARSE_NOERROR|HTML_PARSE_NOWARNING);
BOOL r;
DWORD cbRead;
- while (r = InternetReadFile(hiUrl, bufI, sizeof(bufI), &cbRead), cbRead) {
+ while (r = InternetReadFile(hiUrl.v, bufI, sizeof(bufI), &cbRead), cbRead) {
if (!r)
throw InternetError();
- if (!htmlParseChunk(ctxt, bufI, cbRead, 0))
+ if (!htmlParseChunk(ctxt.v, bufI, cbRead, 0))
throw XmlError();
}
- htmlParseChunk(ctxt, bufI, 0, 1); /* Stop parsing. */
+ htmlParseChunk(ctxt.v, bufI, 0, 1); /* Stop parsing. */
}
- operator htmlDocPtr() { return ctxt->myDoc; }
+ operator htmlDocPtr() { return ctxt.v->myDoc; }
};
+static inline void XmlFree(void* p) { xmlFree(p); }
+
template <size_t N>
-bool WcharsFromXmlchars(wchar_t (&dst)[N], XmlCharPtr utf8) noexcept
+bool WcharsFromXmlchars(wchar_t (&dst)[N], xmlChar* utf8_)
{
+ Unique<xmlChar*, XmlFree> utf8 = utf8_;
+ if (!utf8.Not(0))
+ throw XmlError();
+
/* Truncate if source is larger than destination. */
- int lenUtf8 = xmlStrlen(utf8);
- utf8[Min(N, static_cast<size_t>(lenUtf8))] = 0;
+ int lenUtf8 = xmlStrlen(utf8.v);
+ utf8.v[Min(N, static_cast<size_t>(lenUtf8))] = 0;
/* Convert internal representation from UTF-8 to Latin-1,
* which seems to actually convert the string to proper UTF-8
* (???). */
unsigned char lat1[N];
int lenLat1 = N-1;
- if (UTF8Toisolat1(lat1, &lenLat1, utf8, &lenUtf8) <= 0)
+ if (UTF8Toisolat1(lat1, &lenLat1, utf8.v, &lenUtf8) <= 0)
return false;
lat1[lenLat1] = 0;
@@ -177,11 +170,17 @@ void FetchData(unsigned char* sig)
ParsedDoc doc(L"https://www.detectiveconanworld.com/wiki/Anime",
"https://www.detectiveconanworld.com/wiki/Anime");
- XmlXPathContextPtr xpathCtx = xmlXPathNewContext(doc);
- XmlXPathObjectPtr xpathObj = xmlXPathEvalExpression(
+ Unique<xmlXPathContextPtr, xmlXPathFreeContext> xpathCtx = xmlXPathNewContext(doc);
+ if (!xpathCtx.Not(0))
+ throw XmlError();
+
+ Unique<xmlXPathObjectPtr, xmlXPathFreeObject> xpathObj = xmlXPathEvalExpression(
reinterpret_cast<const xmlChar*>("//tr[./td[1] != '' and ./td[3][@style='background:#f2fde9;']]"),
- xpathCtx);
- xmlNodeSetPtr nodes = xpathObj->nodesetval;
+ xpathCtx.v);
+ if (!xpathObj.Not(0))
+ throw XmlError();
+
+ xmlNodeSetPtr nodes = xpathObj.v->nodesetval;
if (!nodes || !nodes->nodeNr)
throw std::runtime_error("could not find remote episode information");
@@ -223,7 +222,8 @@ void FetchData(unsigned char* sig)
* of the title node. */
const xmlNodePtr nodeLink = xmlFirstElementChild(nodeTitle);
if (nodeLink)
- WcharsFromXmlchars(d.wiki, xmlGetProp(nodeLink, reinterpret_cast<const xmlChar*>("href")));
+ WcharsFromXmlchars(d.wiki,
+ xmlGetProp(nodeLink, reinterpret_cast<const xmlChar*>("href")));
}
}
@@ -270,14 +270,19 @@ void FetchScreenwriters(unsigned char* sig)
/* Retrieve screenwriter from HTML. */
ParsedDoc doc(url, nullptr);
- XmlXPathContextPtr xpathCtx = xmlXPathNewContext(doc);
- XmlXPathObjectPtr xpathObj = xmlXPathEvalExpression(reinterpret_cast<const xmlChar*>(
- "//th[contains(text(), 'Screenplay:')]/following-sibling::td"),
- xpathCtx);
- xmlNodeSetPtr nodes = xpathObj->nodesetval;
- if (nodes && nodes->nodeNr) {
- xmlChar* s = xmlNodeGetContent(nodes->nodeTab[0]);
- WcharsFromXmlchars(d.screenwriter, s);
- }
+ Unique<xmlXPathContextPtr, xmlXPathFreeContext> xpathCtx = xmlXPathNewContext(doc);
+ if (!xpathCtx.Not(0))
+ throw XmlError();
+
+ Unique<xmlXPathObjectPtr, xmlXPathFreeObject> xpathObj =
+ xmlXPathEvalExpression(reinterpret_cast<const xmlChar*>(
+ "//th[contains(text(), 'Screenplay:')]/following-sibling::td"),
+ xpathCtx.v);
+ if (!xpathObj.Not(0))
+ throw XmlError();
+
+ xmlNodeSetPtr nodes = xpathObj.v->nodesetval;
+ if (nodes && nodes->nodeNr)
+ WcharsFromXmlchars(d.screenwriter, xmlNodeGetContent(nodes->nodeTab[0]));
}
}
diff --git a/c/data.h b/c/data.h
index 7ca5e6c..478182d 100644
--- a/c/data.h
+++ b/c/data.h
@@ -3,10 +3,24 @@
#include <stdexcept>
#include <windows.h>
+#include <libxml/xmlerror.h>
#include "util.h"
#include "win.h"
+struct XmlError : public std::exception
+{
+ const char* msg;
+ inline XmlError()
+ {
+ msg = xmlGetLastError()->message;
+ }
+ inline virtual const char* what() const noexcept
+ {
+ return msg;
+ }
+};
+
/* Fetch data from the web. */
void FetchData(unsigned char* sig);
void FetchScreenwriters(unsigned char* sig);
@@ -71,15 +85,21 @@ constexpr inline bool HasVersion = false;
template <typename T>
constexpr inline bool HasVersion<T, decltype((void) T::version, 0)> = true;
+inline void FreeView(void* view)
+{
+ FlushViewOfFile(view, 0);
+ UnmapViewOfFile(view);
+}
+
/* FileView objects manage a memory-mapped file. The view buffer may
* be treated as an array of a given type T. Note that reading and
* writing a view can throw structured exceptions. We ignore these. */
template <typename T>
struct FileView
{
- HANDLE hf;
- HANDLE hm;
- T* view;
+ Unique<HANDLE, CloseHandle> hf;
+ Unique<HANDLE, CloseHandle> hm;
+ Unique<T*, FreeView> view;
size_t c;
static FileView Initialized(const wchar_t* filename, size_t c, size_t cInit = 0)
@@ -91,41 +111,20 @@ struct FileView
cInit = cInit? cInit: c;
for (size_t i = 0; i < cInit; i++)
memcpy(fv+i, &t, sizeof(T));
- }
-
- return {filename, c};
- }
-
- FileView(FileView<T>&) = delete;
- FileView<T>& operator =(const FileView<unsigned char>&) = delete;
-
- FileView(FileView<T>&& other)
- {
- hf = other.hf;
- hm = other.hf;
- view = other.view;
- c = other.c;
- other.view = nullptr;
- }
- FileView<T>& operator =(FileView<unsigned char>&& other)
- {
- hf = other.hf;
- hm = other.hf;
- view = other.view;
- c = other.c;
- other.view = nullptr;
- return *this;
+ return fv;
+ } else
+ return {filename, c};
}
FileView(const wchar_t* filename, size_t c) : c(c)
{
hf = CreateFile(filename, GENERIC_READ|GENERIC_WRITE,
0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
- if (hf == INVALID_HANDLE_VALUE) {
+ if (!hf.Not(INVALID_HANDLE_VALUE)) {
if (GetLastError() == ERROR_FILE_NOT_FOUND) {
hf = CreateFile(filename, GENERIC_READ|GENERIC_WRITE,
0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
- if (hf == INVALID_HANDLE_VALUE)
+ if (!hf.Not(INVALID_HANDLE_VALUE))
throw Win32Error();
} else
throw Win32Error();
@@ -133,28 +132,14 @@ struct FileView
LARGE_INTEGER cbMap;
cbMap.QuadPart = c*sizeof(T);
- hm = CreateFileMapping(hf, nullptr, PAGE_READWRITE,
+ hm = CreateFileMapping(hf.v, nullptr, PAGE_READWRITE,
cbMap.HighPart, cbMap.LowPart, nullptr);
- if (!hm) {
- CloseHandle(hf);
+ if (!hm.Not(0))
throw Win32Error();
- }
- view = reinterpret_cast<T*>(MapViewOfFile(hm, FILE_MAP_ALL_ACCESS, 0, 0, 0));
- if (!view) {
- CloseHandle(hm);
+ view = reinterpret_cast<T*>(MapViewOfFile(hm.v, FILE_MAP_ALL_ACCESS, 0, 0, 0));
+ if (!view.Not(0))
throw Win32Error();
- }
- }
-
- ~FileView()
- {
- if (view) {
- FlushViewOfFile(view, 0);
- UnmapViewOfFile(view);
- CloseHandle(hm);
- CloseHandle(hf);
- }
}
/* Access element by index, performing bounds check and, if
@@ -165,7 +150,7 @@ struct FileView
if (i >= c)
throw std::out_of_range("index larger than buffer");
- T& t = view[i];
+ T& t = view.v[i];
if constexpr (HasVersion<T>) {
if (t.version == 0) {
@@ -181,17 +166,17 @@ struct FileView
return t;
}
- T* begin() { return *view; }
- T* end() { return view[c-1]; }
+ T* begin() noexcept { return *view; }
+ T* end() noexcept { return view[c-1]; }
inline operator T*() noexcept
{
- return view;
+ return view.v;
}
inline T* operator ->() noexcept
{
- return view;
+ return view.v;
}
};
diff --git a/c/ext.cpp b/c/ext.cpp
index 272f4a3..398804e 100644
--- a/c/ext.cpp
+++ b/c/ext.cpp
@@ -78,8 +78,6 @@ static inline bool MatchFileName(wchar_t (&file)[MAX_PATH], const wchar_t* const
static bool FindMatchingFile(wchar_t (&file)[MAX_PATH], const wchar_t* const root,
const wchar_t* const siEp, const int level = 0)
{
- using FindHandle = Managed<HANDLE, FindClose, Win32Error, -1>;
-
/* Don't recurse too much. */
if (level > 3)
return false;
@@ -88,7 +86,9 @@ static bool FindMatchingFile(wchar_t (&file)[MAX_PATH], const wchar_t* const roo
Swprintf(pat, L"%s\\*", root);
WIN32_FIND_DATA fdata;
- FindHandle h = FindFirstFileW(pat, &fdata);
+ Unique<HANDLE, FindClose> h = FindFirstFileW(pat, &fdata);
+ if (!h.Not(INVALID_HANDLE_VALUE))
+ throw Win32Error();
do
if (fdata.cFileName[0] == L'.')
@@ -103,7 +103,7 @@ static bool FindMatchingFile(wchar_t (&file)[MAX_PATH], const wchar_t* const roo
Swprintf(file, L"%s\\%s", root, fdata.cFileName);
return true;
}
- while (FindNextFileW(h, &fdata));
+ while (FindNextFileW(h.v, &fdata));
if (GetLastError() != ERROR_NO_MORE_FILES)
throw Win32Error();
diff --git a/c/util.h b/c/util.h
index 0d8a835..ffc6b57 100644
--- a/c/util.h
+++ b/c/util.h
@@ -28,21 +28,53 @@ struct Finally
};
#define FINALLY Finally _ = [=]()
-/* Generic RAII type. */
-template <typename T, auto F, typename U, auto E = 0>
-struct Managed
+template <typename T>
+inline void Delete(T v)
+{
+ delete v;
+}
+
+template <typename T, auto F = Delete<T>>
+struct Unique
{
- T obj;
- Managed(T obj) : obj(obj)
+ T v;
+ bool ok = true;
+ Unique() : ok(false) {}
+ Unique(T v) : v(v) {}
+ Unique& operator =(T v_)
+ {
+ if (ok)
+ F(v);
+ v = v_;
+ ok = true;
+ return *this;
+ }
+ Unique(Unique&& other)
+ {
+ v = std::move(other.v);
+ other.ok = false;
+ }
+ Unique& operator =(Unique&& other)
+ {
+ if (ok)
+ F(v);
+ v = std::move(other.v);
+ ok = other.ok;
+ other.ok = false;
+ return *this;
+ }
+ bool Not(T u)
+ {
+ if (v == u)
+ return ok = false;
+ else
+ return true;
+ }
+ ~Unique()
{
- if (obj == reinterpret_cast<T>(E))
- throw U();
+ if (ok)
+ F(v);
}
- ~Managed() { F(obj); }
- operator T() { return obj; }
- auto& operator *() { return *obj; }
- auto& operator ->() { return obj; }
- auto& operator [](size_t i) { return obj[i]; }
};
/* Buf is a span-like structure of a buffer and its size. */