diff options
author | John Ankarström <john@ankarstrom.se> | 2022-08-28 04:02:24 +0200 |
---|---|---|
committer | John Ankarström <john@ankarstrom.se> | 2022-08-28 04:02:24 +0200 |
commit | e327e4469ac2847615f7c847facd5879b5c2e5bc (patch) | |
tree | b7db3fb2383be3364ea2692769a8d02e708e3478 | |
parent | a8a73a21495bf54f720088cf789d8cf6cf682661 (diff) | |
download | EpisodeBrowser-e327e4469ac2847615f7c847facd5879b5c2e5bc.tar.gz |
Replace Managed with Unique.
-rw-r--r-- | c/data.cpp | 107 | ||||
-rw-r--r-- | c/data.h | 89 | ||||
-rw-r--r-- | c/ext.cpp | 8 | ||||
-rw-r--r-- | c/util.h | 56 |
4 files changed, 141 insertions, 119 deletions
@@ -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])); } } @@ -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; } }; @@ -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(); @@ -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. */ |