From 049ba1e7e40616ef3add12500fa3b5a0a398abe9 Mon Sep 17 00:00:00 2001 From: Jean-Francois Dockes Date: Sun, 21 Jul 2019 16:23:16 +0200 Subject: [PATCH] Windows: build with UNICODE, get rid of all TCHAR/TEXT(), use explicit xxA() interfaces and wchar_t in some places. Add a static cleanup retry method to TempFile, called after clearing the MimeHandler cache (killing the subprocesses which might hold an open file). --- src/common/rclinit.cpp | 12 +- src/internfile/mimehandler.cpp | 234 +++++++++++++------------- src/testmains/trrclutil.cpp | 51 ++++++ src/utils/pathut.cpp | 4 +- src/utils/rclutil.cpp | 185 +++++++++----------- src/utils/rclutil.h | 5 + src/windows/execmd_w.cpp | 20 +-- src/windows/qmkrecoll/librecoll.pro | 4 +- src/windows/qmkrecoll/rclstartw.pro | 4 +- src/windows/qmkrecoll/recollindex.pro | 4 +- src/windows/qmkrecoll/recollq.pro | 4 +- 11 files changed, 280 insertions(+), 247 deletions(-) create mode 100644 src/testmains/trrclutil.cpp diff --git a/src/common/rclinit.cpp b/src/common/rclinit.cpp index e1bf8e98..45ba5741 100644 --- a/src/common/rclinit.cpp +++ b/src/common/rclinit.cpp @@ -205,16 +205,16 @@ LRESULT CALLBACK MainWndProc(HWND hwnd , UINT msg , WPARAM wParam, bool CreateInvisibleWindow() { HWND hwnd; - WNDCLASS wc = {0}; + WNDCLASS wc = {0,0,0,0,0,0,0,0,0,0}; wc.lpfnWndProc = (WNDPROC)MainWndProc; wc.hInstance = GetModuleHandle(NULL); - wc.hIcon = LoadIcon(GetModuleHandle(NULL), "TestWClass"); - wc.lpszClassName = "TestWClass"; + wc.hIcon = LoadIcon(GetModuleHandle(NULL), L"TestWClass"); + wc.lpszClassName = L"TestWClass"; RegisterClass(&wc); hwnd = - CreateWindowEx(0, "TestWClass", "TestWClass", WS_OVERLAPPEDWINDOW, + CreateWindowEx(0, L"TestWClass", L"TestWClass", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, (HWND) NULL, (HMENU) NULL, GetModuleHandle(NULL), (LPVOID) NULL); @@ -248,8 +248,8 @@ void initAsyncSigs(void (*sigcleanup)(int)) } } } - HANDLE hInvisiblethread = - CreateThread(NULL, 0, RunInvisibleWindowThread, NULL, 0, &tid); + + CreateThread(NULL, 0, RunInvisibleWindowThread, NULL, 0, &tid); SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE); eWorkFinished = CreateEvent(NULL, TRUE, FALSE, NULL); if (eWorkFinished == INVALID_HANDLE_VALUE) { diff --git a/src/internfile/mimehandler.cpp b/src/internfile/mimehandler.cpp index 3f1a89c7..6754ca10 100644 --- a/src/internfile/mimehandler.cpp +++ b/src/internfile/mimehandler.cpp @@ -43,6 +43,7 @@ using namespace std; #include "mh_null.h" #include "mh_xslt.h" #include "rcldoc.h" +#include "rclutil.h" // Performance help: we use a pool of already known and created // handlers. There can be several instances for a given mime type @@ -67,17 +68,17 @@ static RecollFilter *getMimeHandlerFromCache(const string& key) multimap::iterator it = o_handlers.find(key); if (it != o_handlers.end()) { - RecollFilter *h = it->second; - hlruit_tp it1 = find(o_hlru.begin(), o_hlru.end(), it); - if (it1 != o_hlru.end()) { - o_hlru.erase(it1); - } else { - LOGERR("getMimeHandlerFromCache: lru position not found\n"); - } - o_handlers.erase(it); - LOGDEB("getMimeHandlerFromCache: " << xdigest << " found size " << + RecollFilter *h = it->second; + hlruit_tp it1 = find(o_hlru.begin(), o_hlru.end(), it); + if (it1 != o_hlru.end()) { + o_hlru.erase(it1); + } else { + LOGERR("getMimeHandlerFromCache: lru position not found\n"); + } + o_handlers.erase(it); + LOGDEB("getMimeHandlerFromCache: " << xdigest << " found size " << o_handlers.size() << "\n"); - return h; + return h; } LOGDEB("getMimeHandlerFromCache: " << xdigest << " not found\n"); return 0; @@ -89,8 +90,8 @@ void returnMimeHandler(RecollFilter *handler) typedef multimap::value_type value_type; if (handler == 0) { - LOGERR("returnMimeHandler: bad parameter\n"); - return; + LOGERR("returnMimeHandler: bad parameter\n"); + return; } handler->clear(); @@ -107,20 +108,20 @@ void returnMimeHandler(RecollFilter *handler) // are processing the same mime type at the same time. multimap::iterator it; if (o_handlers.size() >= max_handlers_cache_size) { - static int once = 1; - if (once) { - once = 0; - for (it = o_handlers.begin(); it != o_handlers.end(); it++) { - LOGDEB1("Cache full. key: " << it->first << "\n"); - } - LOGDEB1("Cache LRU size: " << o_hlru.size() << "\n"); - } - if (o_hlru.size() > 0) { - it = o_hlru.back(); - o_hlru.pop_back(); - delete it->second; - o_handlers.erase(it); - } + static int once = 1; + if (once) { + once = 0; + for (it = o_handlers.begin(); it != o_handlers.end(); it++) { + LOGDEB1("Cache full. key: " << it->first << "\n"); + } + LOGDEB1("Cache LRU size: " << o_hlru.size() << "\n"); + } + if (o_hlru.size() > 0) { + it = o_hlru.back(); + o_hlru.pop_back(); + delete it->second; + o_handlers.erase(it); + } } it = o_handlers.insert(value_type(handler->get_id(), handler)); o_hlru.push_front(it); @@ -132,15 +133,16 @@ void clearMimeHandlerCache() multimap::iterator it; std::unique_lock locker(o_handlers_mutex); for (it = o_handlers.begin(); it != o_handlers.end(); it++) { - delete it->second; + delete it->second; } o_handlers.clear(); + TempFile::tryRemoveAgain(); } /** For mime types set as "internal" in mimeconf: - * create appropriate handler object. */ + * create appropriate handler object. */ static RecollFilter *mhFactory(RclConfig *config, const string &mimeOrParams, - bool nobuild, string& id) + bool nobuild, string& id) { LOGDEB1("mhFactory(" << mimeOrParams << ")\n"); vector lparams; @@ -152,50 +154,50 @@ static RecollFilter *mhFactory(RclConfig *config, const string &mimeOrParams, string lmime(lparams[0]); stringtolower(lmime); if (cstr_textplain == lmime) { - LOGDEB2("mhFactory(" << mime << "): returning MimeHandlerText\n"); - MD5String("MimeHandlerText", id); - return nobuild ? 0 : new MimeHandlerText(config, id); + LOGDEB2("mhFactory(" << mime << "): returning MimeHandlerText\n"); + MD5String("MimeHandlerText", id); + return nobuild ? 0 : new MimeHandlerText(config, id); } else if (cstr_texthtml == lmime) { - LOGDEB2("mhFactory(" << mime << "): returning MimeHandlerHtml\n"); - MD5String("MimeHandlerHtml", id); - return nobuild ? 0 : new MimeHandlerHtml(config, id); + LOGDEB2("mhFactory(" << mime << "): returning MimeHandlerHtml\n"); + MD5String("MimeHandlerHtml", id); + return nobuild ? 0 : new MimeHandlerHtml(config, id); } else if ("text/x-mail" == lmime) { - LOGDEB2("mhFactory(" << mime << "): returning MimeHandlerMbox\n"); - MD5String("MimeHandlerMbox", id); - return nobuild ? 0 : new MimeHandlerMbox(config, id); + LOGDEB2("mhFactory(" << mime << "): returning MimeHandlerMbox\n"); + MD5String("MimeHandlerMbox", id); + return nobuild ? 0 : new MimeHandlerMbox(config, id); } else if ("message/rfc822" == lmime) { - LOGDEB2("mhFactory(" << mime << "): returning MimeHandlerMail\n"); - MD5String("MimeHandlerMail", id); - return nobuild ? 0 : new MimeHandlerMail(config, id); + LOGDEB2("mhFactory(" << mime << "): returning MimeHandlerMail\n"); + MD5String("MimeHandlerMail", id); + return nobuild ? 0 : new MimeHandlerMail(config, id); } else if ("inode/symlink" == lmime) { - LOGDEB2("mhFactory(" << mime << "): returning MimeHandlerSymlink\n"); - MD5String("MimeHandlerSymlink", id); - return nobuild ? 0 : new MimeHandlerSymlink(config, id); + LOGDEB2("mhFactory(" << mime << "): returning MimeHandlerSymlink\n"); + MD5String("MimeHandlerSymlink", id); + return nobuild ? 0 : new MimeHandlerSymlink(config, id); } else if ("application/x-zerosize" == lmime) { - LOGDEB("mhFactory(" << lmime << "): returning MimeHandlerNull\n"); - MD5String("MimeHandlerNull", id); - return nobuild ? 0 : new MimeHandlerNull(config, id); + LOGDEB("mhFactory(" << lmime << "): returning MimeHandlerNull\n"); + MD5String("MimeHandlerNull", id); + return nobuild ? 0 : new MimeHandlerNull(config, id); } else if (lmime.find("text/") == 0) { // Try to handle unknown text/xx as text/plain. This // only happen if the text/xx was defined as "internal" in // mimeconf, not at random. For programs, for example this // allows indexing and previewing as text/plain (no filter // exec) but still opening with a specific editor. - LOGDEB2("mhFactory(" << mime << "): returning MimeHandlerText(x)\n"); - MD5String("MimeHandlerText", id); + LOGDEB2("mhFactory(" << mime << "): returning MimeHandlerText(x)\n"); + MD5String("MimeHandlerText", id); return nobuild ? 0 : new MimeHandlerText(config, id); } else if ("xsltproc" == lmime) { // XML Types processed with one or several xslt style sheets. MD5String(mimeOrParams, id); return nobuild ? 0 : new MimeHandlerXslt(config, id, lparams); } else { - // We should not get there. It means that "internal" was set - // as a handler in mimeconf for a mime type we actually can't - // handle. - LOGERR("mhFactory: mime type [" << lmime << + // We should not get there. It means that "internal" was set + // as a handler in mimeconf for a mime type we actually can't + // handle. + LOGERR("mhFactory: mime type [" << lmime << "] set as internal but unknown\n"); - MD5String("MimeHandlerUnknown", id); - return nobuild ? 0 : new MimeHandlerUnknown(config, id); + MD5String("MimeHandlerUnknown", id); + return nobuild ? 0 : new MimeHandlerUnknown(config, id); } } @@ -216,7 +218,7 @@ MimeHandlerExec *mhExecFactory(RclConfig *cfg, const string& mtype, string& hs, string cmdstr; if (!cfg->valueSplitAttributes(hs, cmdstr, attrs)) { - LOGERR("mhExecFactory: bad config line for [" << + LOGERR("mhExecFactory: bad config line for [" << mtype << "]: [" << hs << "]\n"); return 0; } @@ -225,12 +227,12 @@ MimeHandlerExec *mhExecFactory(RclConfig *cfg, const string& mtype, string& hs, vector cmdtoks; stringToStrings(cmdstr, cmdtoks); if (cmdtoks.empty()) { - LOGERR("mhExecFactory: bad config line for [" << mtype << + LOGERR("mhExecFactory: bad config line for [" << mtype << "]: [" << hs << "]\n"); - return 0; + return 0; } MimeHandlerExec *h = multiple ? - new MimeHandlerExecMultiple(cfg, id) : + new MimeHandlerExecMultiple(cfg, id) : new MimeHandlerExec(cfg, id); vector::iterator it = cmdtoks.begin(); @@ -262,7 +264,7 @@ MimeHandlerExec *mhExecFactory(RclConfig *cfg, const string& mtype, string& hs, #if 0 string scmd; for (it = h->params.begin(); it != h->params.end(); it++) { - scmd += string("[") + *it + "] "; + scmd += string("[") + *it + "] "; } LOGDEB("mhExecFactory:mt [" << mtype << "] cfgmt [" << h->cfgFilterOutputMtype << "] cfgcs [" << @@ -291,83 +293,83 @@ RecollFilter *getMimeHandler(const string &mtype, RclConfig *cfg, string id; if (!hs.empty()) { - // Got a handler definition line - // Break definition into type (internal/exec/execm) - // and name/command string + // Got a handler definition line + // Break definition into type (internal/exec/execm) + // and name/command string string::size_type b1 = hs.find_first_of(" \t"); string handlertype = hs.substr(0, b1); - string cmdstr; - if (b1 != string::npos) { - cmdstr = hs.substr(b1); + string cmdstr; + if (b1 != string::npos) { + cmdstr = hs.substr(b1); trimstring(cmdstr); - } - bool internal = !stringlowercmp("internal", handlertype); - if (internal) { - // For internal types let the factory compute the cache id - mhFactory(cfg, cmdstr.empty() ? mtype : cmdstr, true, id); - } else { - // exec/execm: use the md5 of the def line - MD5String(hs, id); - } + } + bool internal = !stringlowercmp("internal", handlertype); + if (internal) { + // For internal types let the factory compute the cache id + mhFactory(cfg, cmdstr.empty() ? mtype : cmdstr, true, id); + } else { + // exec/execm: use the md5 of the def line + MD5String(hs, id); + } // Do we already have a handler object in the cache ? - h = getMimeHandlerFromCache(id); - if (h != 0) - goto out; + h = getMimeHandlerFromCache(id); + if (h != 0) + goto out; - LOGDEB2("getMimeHandler: " << mtype << " not in cache\n"); - if (internal) { - // If there is a parameter after "internal" it's the mime - // type to use, or the further qualifier (e.g. style sheet - // name for xslt types). This is so that we can have bogus - // mime types like text/x-purple-html-log (for ie: - // specific icon) and still use the html filter on - // them. This is partly redundant with the - // localfields/rclaptg, but better? (and the latter will - // probably go away at some point in the future?). - LOGDEB2("handlertype internal, cmdstr [" << cmdstr << "]\n"); - h = mhFactory(cfg, cmdstr.empty() ? mtype : cmdstr, false, id); - goto out; - } else if (!stringlowercmp("dll", handlertype)) { - } else { + LOGDEB2("getMimeHandler: " << mtype << " not in cache\n"); + if (internal) { + // If there is a parameter after "internal" it's the mime + // type to use, or the further qualifier (e.g. style sheet + // name for xslt types). This is so that we can have bogus + // mime types like text/x-purple-html-log (for ie: + // specific icon) and still use the html filter on + // them. This is partly redundant with the + // localfields/rclaptg, but better? (and the latter will + // probably go away at some point in the future?). + LOGDEB2("handlertype internal, cmdstr [" << cmdstr << "]\n"); + h = mhFactory(cfg, cmdstr.empty() ? mtype : cmdstr, false, id); + goto out; + } else if (!stringlowercmp("dll", handlertype)) { + } else { if (cmdstr.empty()) { - LOGERR("getMimeHandler: bad line for " << mtype << ": " << + LOGERR("getMimeHandler: bad line for " << mtype << ": " << hs << "\n"); - goto out; - } + goto out; + } if (!stringlowercmp("exec", handlertype)) { h = mhExecFactory(cfg, mtype, cmdstr, false, id); - goto out; + goto out; } else if (!stringlowercmp("execm", handlertype)) { h = mhExecFactory(cfg, mtype, cmdstr, true, id); - goto out; + goto out; } else { - LOGERR("getMimeHandler: bad line for " << mtype << ": " << + LOGERR("getMimeHandler: bad line for " << mtype << ": " << hs << "\n"); - goto out; + goto out; } - } + } } else { // No identified mime type, or no handler associated. // Unhandled files are either ignored or their name and // generic metadata is indexed, depending on configuration - bool indexunknown = false; - cfg->getConfParam("indexallfilenames", &indexunknown); - if (indexunknown) { - MD5String("MimeHandlerUnknown", id); - if ((h = getMimeHandlerFromCache(id)) == 0) - h = new MimeHandlerUnknown(cfg, id); - } - goto out; + bool indexunknown = false; + cfg->getConfParam("indexallfilenames", &indexunknown); + if (indexunknown) { + MD5String("MimeHandlerUnknown", id); + if ((h = getMimeHandlerFromCache(id)) == 0) + h = new MimeHandlerUnknown(cfg, id); + } + goto out; } out: if (h) { - h->set_property(RecollFilter::DEFAULT_CHARSET, cfg->getDefCharset()); - // In multithread context, and in case this handler is out - // from the cache, it may have a config pointer belonging to - // another thread. Fix it. - h->setConfig(cfg); + h->set_property(RecollFilter::DEFAULT_CHARSET, cfg->getDefCharset()); + // In multithread context, and in case this handler is out + // from the cache, it may have a config pointer belonging to + // another thread. Fix it. + h->setConfig(cfg); } return h; } @@ -376,10 +378,10 @@ out: bool canIntern(const std::string mtype, RclConfig *cfg) { if (mtype.empty()) - return false; + return false; string hs = cfg->getMimeHandlerDef(mtype); if (hs.empty()) - return false; + return false; return true; } /// Same, getting MIME from doc diff --git a/src/testmains/trrclutil.cpp b/src/testmains/trrclutil.cpp new file mode 100644 index 00000000..6a180236 --- /dev/null +++ b/src/testmains/trrclutil.cpp @@ -0,0 +1,51 @@ + +#include "rclutil.h" + + +void path_to_thumb(const string& _input) +{ + string input(_input); + // Make absolute path if needed + if (input[0] != '/') { + input = path_absolute(input); + } + + input = string("file://") + path_canon(input); + + string path; + //path = url_encode(input, 7); + thumbPathForUrl(input, 7, path); + cout << path << endl; +} + +const char *thisprog; + +int main(int argc, const char **argv) +{ + thisprog = *argv++; + argc--; + + string s; + vector::const_iterator it; + +#if 0 + if (argc > 1) { + cerr << "Usage: thumbpath " << endl; + exit(1); + } + string input; + if (argc == 1) { + input = *argv++; + if (input.empty()) { + cerr << "Usage: thumbpath " << endl; + exit(1); + } + path_to_thumb(input); + } else { + while (getline(cin, input)) { + path_to_thumb(input); + } + } + exit(0); +#endif +} diff --git a/src/utils/pathut.cpp b/src/utils/pathut.cpp index 453dc2fa..57704417 100644 --- a/src/utils/pathut.cpp +++ b/src/utils/pathut.cpp @@ -274,8 +274,8 @@ bool fsocc(const string& path, int *pc, long long *avmbs) #ifdef _WIN32 ULARGE_INTEGER freebytesavail; ULARGE_INTEGER totalbytes; - if (!GetDiskFreeSpaceEx(path.c_str(), &freebytesavail, - &totalbytes, NULL)) { + if (!GetDiskFreeSpaceExA(path.c_str(), &freebytesavail, + &totalbytes, NULL)) { return false; } if (pc) { diff --git a/src/utils/rclutil.cpp b/src/utils/rclutil.cpp index 08fa522e..3cae3bd7 100644 --- a/src/utils/rclutil.cpp +++ b/src/utils/rclutil.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2016 J.F.Dockes +/* Copyright (C) 2016-2019 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -14,7 +14,7 @@ * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef TEST_RCLUTIL + #include "autoconfig.h" #include @@ -39,6 +39,7 @@ #include #include #include +#include #include "rclutil.h" #include "pathut.h" @@ -50,7 +51,6 @@ using namespace std; - template void map_ss_cp_noshr(T s, T *d) { for (const auto& ent : s) { @@ -83,54 +83,48 @@ static bool path_isdriveabs(const string& s) #include #pragma comment(lib, "shlwapi.lib") -string path_tchartoutf8(TCHAR *text) -{ -#ifdef UNICODE - // Simple C - // const size_t size = ( wcslen(text) + 1 ) * sizeof(wchar_t); - // wcstombs(&buffer[0], text, size); - // std::vector buffer(size); - // Or: - // Windows API - std::vector buffer; - int size = WideCharToMultiByte(CP_UTF8, 0, text, -1, NULL, 0, NULL, NULL); - if (size > 0) { - buffer.resize(size); - WideCharToMultiByte(CP_UTF8, 0, text, -1, - &buffer[0], int(buffer.size()), NULL, NULL); - } else { - return string(); - } - return string(&buffer[0]); -#else - return text; -#endif -} string path_thisexecpath() { - TCHAR text[MAX_PATH]; - GetModuleFileName(NULL, text, MAX_PATH); + wchar_t text[MAX_PATH]; + GetModuleFileNameW(NULL, text, MAX_PATH); #ifdef NTDDI_WIN8_future PathCchRemoveFileSpec(text, MAX_PATH); #else - PathRemoveFileSpec(text); + PathRemoveFileSpecW(text); #endif - string path = path_tchartoutf8(text); + string path; + wchartoutf8(text, path); if (path.empty()) { path = "c:/"; } return path; } -string path_wingettempfilename(TCHAR *pref) +string path_wingettempfilename(wchar_t *pref) { - TCHAR buf[(MAX_PATH + 1)*sizeof(TCHAR)]; - TCHAR dbuf[(MAX_PATH + 1)*sizeof(TCHAR)]; - GetTempPath(MAX_PATH + 1, dbuf); - GetTempFileName(dbuf, pref, 0, buf); + // Use a subdirectory named "rcltmp" inside the windows temp + // location. + wchar_t dbuf[MAX_PATH + 1]; + GetTempPathW(MAX_PATH + 1, dbuf); + string tdir; + wchartoutf8(dbuf, tdir); + tdir = path_cat(tdir, "rcltmp");; + if (!path_exists(tdir)) { + if (path_makepath(tdir, 0700)) { + LOGSYSERR("path_wingettempfilename", "path_makepath", tdir); + } + } + utf8towchar(tdir, dbuf, MAX_PATH); + wchar_t buf[MAX_PATH + 1]; + GetTempFileNameW(dbuf, pref, 0, buf); // Windows will have created a temp file, we delete it. - string filename = path_tchartoutf8(buf); - unlink(filename.c_str()); + if (!DeleteFileW(buf)) { + LOGSYSERR("path_wingettempfilename", "DeleteFileW", filename); + } else { + LOGDEB1("path_wingettempfilename: DeleteFile " << filename << " Ok\n"); + } + string filename; + wchartoutf8(buf, filename); path_slashize(filename); return filename; } @@ -235,9 +229,9 @@ const string& tmplocation() } if (tmpdir == 0) { #ifdef _WIN32 - TCHAR bufw[(MAX_PATH + 1)*sizeof(TCHAR)]; - GetTempPath(MAX_PATH + 1, bufw); - stmpdir = path_tchartoutf8(bufw); + wchar_t bufw[MAX_PATH + 1]; + GetTempPathW(MAX_PATH + 1, bufw); + wchartoutf8(bufw, stmpdir); #else stmpdir = "/tmp"; #endif @@ -267,19 +261,19 @@ bool maketmpdir(string& tdir, string& reason) // in the foot #if !defined(HAVE_MKDTEMP) || defined(_WIN32) static std::mutex mmutex; - std::unique_lock lock(mmutex); + std::unique_lock lock(mmutex); #endif if (! #ifdef HAVE_MKDTEMP - mkdtemp(cp) + mkdtemp(cp) #else - mktemp(cp) + mktemp(cp) #endif // HAVE_MKDTEMP - ) { + ) { free(cp); reason = "maketmpdir: mktemp failed for [" + tdir + "] : " + - strerror(errno); + strerror(errno); tdir.erase(); return false; } @@ -291,7 +285,8 @@ bool maketmpdir(string& tdir, string& reason) // in the foot static std::mutex mmutex; std::unique_lock lock(mmutex); - tdir = path_wingettempfilename(TEXT("rcltmp")); + static wchar_t tmpbasename[]{L"rcltmp"}; + tdir = path_wingettempfilename(tmpbasename); #endif // At this point the directory does not exist yet except if we used @@ -382,10 +377,12 @@ TempFile::Internal::Internal(const string& suffix) filename = cp; free(cp); #else - string filename = path_wingettempfilename(TEXT("recoll")); + static wchar_t tmpbasename[]{L"rcl"}; + string filename = path_wingettempfilename(tmpbasename); #endif m_filename = filename + suffix; + LOGDEB1("TempFile: filename: " << m_filename << endl); int fd1 = open(m_filename.c_str(), O_CREAT | O_EXCL, 0600); if (fd1 < 0) { m_reason = string("Open/create error. errno : ") + @@ -396,13 +393,51 @@ TempFile::Internal::Internal(const string& suffix) } } +#ifdef _WIN32 +static list remainingTempFileNames; +static std::mutex remTmpFNMutex; +#endif + TempFile::Internal::~Internal() { if (!m_filename.empty() && !m_noremove) { - unlink(m_filename.c_str()); + LOGDEB1("TempFile:~: unlinking " << m_filename << endl); + if (unlink(m_filename.c_str()) != 0) { + LOGSYSERR("TempFile:~", "unlink", m_filename); +#ifdef _WIN32 + { + std::unique_lock lock(remTmpFNMutex); + remainingTempFileNames.push_back(m_filename); + } +#endif + } else { + LOGDEB1("TempFile:~: unlink " << m_filename << " Ok\n"); + } } } +// On Windows we sometimes fail to remove temporary files because +// they are open. It's difficult to make sure this does not +// happen, so we add a cleaning pass after clearing the input +// handlers cache (which should kill subprocesses etc.) +void TempFile::tryRemoveAgain() +{ +#ifdef _WIN32 + LOGDEB1("TempFile::tryRemoveAgain. List size: " << + remainingTempFileNames.size() << endl); + std::unique_lock lock(remTmpFNMutex); + std::list::iterator pos = remainingTempFileNames.begin(); + while (pos != remainingTempFileNames.end()) { + if (unlink(pos->c_str()) != 0) { + LOGSYSERR("TempFile::tryRemoveAgain", "unlink", *pos); + pos++; + } else { + pos = remainingTempFileNames.erase(pos); + } + } +#endif +} + TempDir::TempDir() { if (!maketmpdir(m_dirname, m_reason)) { @@ -448,6 +483,7 @@ static const string& xdgcachedir() } return xdgcache; } + static const string& thumbnailsdir() { static string thumbnailsd; @@ -506,56 +542,3 @@ void rclutil_init_mt() tmplocation(); thumbnailsdir(); } - -#else // TEST_RCLUTIL - -void path_to_thumb(const string& _input) -{ - string input(_input); - // Make absolute path if needed - if (input[0] != '/') { - input = path_absolute(input); - } - - input = string("file://") + path_canon(input); - - string path; - //path = url_encode(input, 7); - thumbPathForUrl(input, 7, path); - cout << path << endl; -} - -const char *thisprog; - -int main(int argc, const char **argv) -{ - thisprog = *argv++; - argc--; - - string s; - vector::const_iterator it; - -#if 0 - if (argc > 1) { - cerr << "Usage: thumbpath " << endl; - exit(1); - } - string input; - if (argc == 1) { - input = *argv++; - if (input.empty()) { - cerr << "Usage: thumbpath " << endl; - exit(1); - } - path_to_thumb(input); - } else { - while (getline(cin, input)) { - path_to_thumb(input); - } - } - exit(0); -#endif -} - -#endif // TEST_RCLUTIL - diff --git a/src/utils/rclutil.h b/src/utils/rclutil.h index e5c9e5fc..19de72e4 100644 --- a/src/utils/rclutil.h +++ b/src/utils/rclutil.h @@ -58,6 +58,11 @@ public: const std::string& getreason() const; void setnoremove(bool onoff); bool ok() const; + // Attempt to delete all files which could not be deleted on the + // first try (typically on Windows: because they are open by some + // process). Called after clearing the mimeHandler cache. Does + // nothing if not _WIN32 + static void tryRemoveAgain(); class Internal; private: std::shared_ptr m; diff --git a/src/windows/execmd_w.cpp b/src/windows/execmd_w.cpp index 3c729e3e..d22ff8b3 100644 --- a/src/windows/execmd_w.cpp +++ b/src/windows/execmd_w.cpp @@ -108,6 +108,13 @@ static string argvToCmdLine(const string& cmd, const vector& args) return cmdline; } +// Because we build with UNICODE defined, GetEnvironmentStrings is +// defined as GetEnvironmentStringsW. Because of a Windows problem teh +// GetEnvironmentStrings function is really a GetEnvironmentStringsA, +// which is what we want. +// See: https://devblogs.microsoft.com/oldnewthing/20130117-00/?p=5533 +#undef GetEnvironmentStrings + // Merge the father environment with the variable specified in m_env static char *mergeEnvironment(const std::unordered_map& addenv) { @@ -131,7 +138,7 @@ static char *mergeEnvironment(const std::unordered_map& addenv) } } - FreeEnvironmentStrings(envir); + FreeEnvironmentStringsA(envir); // Merge our values for (auto it = addenv.begin(); it != addenv.end(); it++) { @@ -614,7 +621,7 @@ bool ExecCmd::Internal::preparePipes(bool has_input,HANDLE *hChildInput, // attribute, and the options are important. // use CreateFile to open a new handle to the existing pipe... sa.bInheritHandle = TRUE; - hOutputWrite = CreateFile( + hOutputWrite = CreateFileA( pipeName.c_str(), FILE_WRITE_DATA | SYNCHRONIZE, 0, &sa, OPEN_EXISTING, // very important flag! @@ -659,7 +666,7 @@ bool ExecCmd::Internal::preparePipes(bool has_input,HANDLE *hChildInput, } sa.bInheritHandle = TRUE; - hInputRead = CreateFile( + hInputRead = CreateFileA( pipeName.c_str(), FILE_READ_DATA | SYNCHRONIZE, 0, &sa, OPEN_EXISTING, // very important flag! @@ -782,13 +789,7 @@ int ExecCmd::startExec(const string &cmd, const vector& args, char *envir = mergeEnvironment(m->m_env); // Create the child process. - // Need a writable buffer for the command line, for some reason. LOGDEB("ExecCmd:startExec: cmdline [" << cmdline << "]\n"); -#if 0 - LPSTR buf = (LPSTR)malloc(cmdline.size() + 1); - memcpy(buf, cmdline.c_str(), cmdline.size()); - buf[cmdline.size()] = 0; -#endif SYSPATH(cmdline, wcmdline); bSuccess = CreateProcessW(NULL, wcmdline, // command line @@ -805,7 +806,6 @@ int ExecCmd::startExec(const string &cmd, const vector& args, } free(envir); -// free(buf); // Close child-side handles else we'll never see eofs if (!CloseHandle(hOutputWrite)) printError("CloseHandle"); diff --git a/src/windows/qmkrecoll/librecoll.pro b/src/windows/qmkrecoll/librecoll.pro index f3efe35d..7a1affa4 100644 --- a/src/windows/qmkrecoll/librecoll.pro +++ b/src/windows/qmkrecoll/librecoll.pro @@ -10,9 +10,7 @@ TARGET = librecoll TEMPLATE = lib DEFINES += LIBRECOLL_LIBRARY BUILDING_RECOLL -DEFINES -= UNICODE -DEFINES -= _UNICODE -DEFINES += _MBCS +DEFINES += UNICODE DEFINES += PSAPI_VERSION=1 DEFINES += READFILE_ENABLE_MINIZ DEFINES += READFILE_ENABLE_MD5 diff --git a/src/windows/qmkrecoll/rclstartw.pro b/src/windows/qmkrecoll/rclstartw.pro index 5af52100..9c2c1cb3 100644 --- a/src/windows/qmkrecoll/rclstartw.pro +++ b/src/windows/qmkrecoll/rclstartw.pro @@ -5,9 +5,7 @@ TARGET = rclstartw TEMPLATE = app DEFINES += BUILDING_RECOLL -DEFINES -= UNICODE -DEFINES -= _UNICODE -DEFINES += _MBCS +DEFINES += UNICODE DEFINES += PSAPI_VERSION=1 diff --git a/src/windows/qmkrecoll/recollindex.pro b/src/windows/qmkrecoll/recollindex.pro index 81f1800a..3508266d 100644 --- a/src/windows/qmkrecoll/recollindex.pro +++ b/src/windows/qmkrecoll/recollindex.pro @@ -7,9 +7,7 @@ CONFIG -= app_bundle TEMPLATE = app DEFINES += BUILDING_RECOLL -DEFINES -= UNICODE -DEFINES -= _UNICODE -DEFINES += _MBCS +DEFINES += UNICODE DEFINES += PSAPI_VERSION=1 DEFINES += RCL_MONITOR diff --git a/src/windows/qmkrecoll/recollq.pro b/src/windows/qmkrecoll/recollq.pro index a233aaf3..d4b2f0ec 100644 --- a/src/windows/qmkrecoll/recollq.pro +++ b/src/windows/qmkrecoll/recollq.pro @@ -7,9 +7,7 @@ CONFIG -= app_bundle TEMPLATE = app DEFINES += BUILDING_RECOLL -DEFINES -= UNICODE -DEFINES -= _UNICODE -DEFINES += _MBCS +DEFINES += UNICODE DEFINES += PSAPI_VERSION=1