Windows: deal with non-ASCII user login, non-ascii paths in confdir etc.

This commit is contained in:
Jean-Francois Dockes
2020-04-15 14:03:04 +01:00
parent 7c39eff719
commit 12ebb7ac6e
10 changed files with 201 additions and 92 deletions
+27 -22
View File
@@ -235,31 +235,27 @@ ConfSimple::ConfSimple(const char *fname, int readonly, bool tildexp,
m_fmtime(0), m_holdWrites(false)
{
status = readonly ? STATUS_RO : STATUS_RW;
int mode = readonly ? ios::in : ios::in | ios::out;
if (!readonly && !path_exists(fname)) {
mode |= ios::trunc;
}
fstream input = path_open(fname, mode);
if (!input.is_open()) {
LOGERR("ConfSimple::ConfSimple: fstream(w)("<<fname<<", "<< mode <<
") errno" << errno << "\n");
}
ifstream input;
if (readonly) {
input.open(fname, ios::in);
} else {
ios::openmode mode = ios::in | ios::out;
// It seems that there is no separate 'create if not exists'
// open flag. Have to truncate to create, but dont want to do
// this to an existing file !
if (!path_exists(fname)) {
mode |= ios::trunc;
}
input.open(fname, mode);
if (input.is_open()) {
status = STATUS_RW;
} else {
input.clear();
input.open(fname, ios::in);
if (input.is_open()) {
status = STATUS_RO;
}
}
if (!readonly && !input.is_open()) {
// reset errors
input.clear();
status = STATUS_RO;
// open readonly
input = path_open(fname, ios::in);
}
if (!input.is_open()) {
LOGERR("ConfSimple::ConfSimple: fstream("<<fname<<", "<<ios::in<<
") errno" << errno << "\n");
status = STATUS_ERROR;
return;
}
@@ -581,7 +577,16 @@ bool ConfSimple::write()
return true;
}
if (m_filename.length()) {
ofstream output(m_filename.c_str(), ios::out | ios::trunc);
fstream output;
#if (defined(BUILDING_RECOLL) && defined(_MSC_VER))
// msc has support for using wide chars for opening files. We may
// need this if, e.g. the user name/home directory is not ascii
wchar_t wfname[MAX_PATH + 1];
utf8towchar(m_filename, wfname, MAX_PATH);
output = fstream(wfname, ios::out | ios::trunc);
#else
output = fstream(m_filename, ios::out | ios::trunc);
#endif
if (!output.is_open()) {
return 0;
}
+89 -22
View File
@@ -49,13 +49,16 @@
#include <math.h>
#include <errno.h>
#include <dirent.h>
#include <fstream>
#ifdef _WIN32
#include "safefcntl.h"
#include "safeunistd.h"
#include "safewindows.h"
#include "safesysstat.h"
#include "transcode.h"
#include "log.h"
#define STAT _wstati64
#define LSTAT _wstati64
@@ -66,8 +69,12 @@
#define READDIR _wreaddir
#define DIRENT _wdirent
#define DIRHDL _WDIR
#define MKDIR(a,b) _wmkdir(a)
#define OPEN ::_wopen
#define UNLINK _wunlink
#else /* !_WIN32 -> */
#else // Not windows ->
#include <fcntl.h>
#include <unistd.h>
#include <sys/param.h>
@@ -86,7 +93,12 @@
#define READDIR readdir
#define DIRENT dirent
#define DIRHDL DIR
#endif
#define MKDIR(a,b) mkdir(a,b)
#define O_BINARY 0
#define OPEN ::open
#define UNLINK unlink
#endif /* !_WIN32 */
#include <cstdlib>
#include <cstring>
@@ -100,9 +112,11 @@
#include "pathut.h"
#include "smallut.h"
using namespace std;
#ifdef _WIN32
#include <shlobj_core.h>
/// Convert \ separators to /
void path_slashize(string& s)
{
@@ -276,7 +290,25 @@ flock (int fd, int operation)
return 0;
}
#endif // Win32 only section
std::string path_shortpath(const std::string& path)
{
SYSPATH(path, syspath);
wchar_t wspath[MAX_PATH];
int ret = GetShortPathNameW(syspath, wspath, MAX_PATH);
if (ret == 0) {
LOGERR("GetShortPathNameW failed for [" << path << "]\n");
return path;
} else if (ret >= MAX_PATH) {
LOGERR("GetShortPathNameW [" << path << "] too long " <<
path.size() << " MAX_PATH " << MAX_PATH << "\n");
return path;
}
string shortpath;
wchartoutf8(wspath, shortpath);
return shortpath;
}
#endif /* _WIN32 */
bool fsocc(const string& path, int *pc, long long *avmbs)
{
@@ -284,8 +316,8 @@ bool fsocc(const string& path, int *pc, long long *avmbs)
#ifdef _WIN32
ULARGE_INTEGER freebytesavail;
ULARGE_INTEGER totalbytes;
if (!GetDiskFreeSpaceExA(path.c_str(), &freebytesavail,
&totalbytes, NULL)) {
SYSPATH(path, syspath);
if (!GetDiskFreeSpaceExW(syspath, &freebytesavail, &totalbytes, NULL)) {
return false;
}
if (pc) {
@@ -295,7 +327,7 @@ bool fsocc(const string& path, int *pc, long long *avmbs)
*avmbs = int(totalbytes.QuadPart / FSOCC_MB);
}
return true;
#else // not windows ->
#else /* !_WIN32 */
struct statvfs buf;
if (statvfs(path.c_str(), &buf) != 0) {
@@ -323,7 +355,7 @@ bool fsocc(const string& path, int *pc, long long *avmbs)
}
}
return true;
#endif
#endif /* !_WIN32 */
}
@@ -433,21 +465,30 @@ string path_home()
{
#ifdef _WIN32
string dir;
const char *cp = getenv("USERPROFILE");
// Using wgetenv does not work well, depending on the
// environment I get wrong values for the accented chars (works
// with recollindex started from msys command window, does not
// work when started from recoll. SHGet... fixes this
//const wchar_t *cp = _wgetenv(L"USERPROFILE");
wchar_t *cp;
SHGetKnownFolderPath(FOLDERID_Profile, 0, nullptr, &cp);
if (cp != 0) {
dir = cp;
wchartoutf8(cp, dir);
}
if (dir.empty()) {
cp = getenv("HOMEDRIVE");
cp = _wgetenv(L"HOMEDRIVE");
wchartoutf8(cp, dir);
if (cp != 0) {
const char *cp1 = getenv("HOMEPATH");
string dir1;
const wchar_t *cp1 = _wgetenv(L"HOMEPATH");
wchartoutf8(cp1, dir1);
if (cp1 != 0) {
dir = string(cp) + string(cp1);
dir = path_cat(dir, dir1);
}
}
}
if (dir.empty()) {
dir = "C:\\";
dir = "C:/";
}
dir = path_canon(dir);
path_catslash(dir);
@@ -475,12 +516,15 @@ string path_home()
string path_homedata()
{
#ifdef _WIN32
const char *cp = getenv("LOCALAPPDATA");
wchar_t *cp;
SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &cp);
string dir;
if (cp != 0) {
dir = path_canon(cp);
wchartoutf8(cp, dir);
}
if (dir.empty()) {
if (!dir.empty()) {
dir = path_canon(dir);
} else {
dir = path_cat(path_home(), "AppData/Local/");
}
return dir;
@@ -673,8 +717,11 @@ bool path_makepath(const string& ipath, int mode)
path += elem;
// Not using path_isdir() here, because this cant grok symlinks
// If we hit an existing file, no worry, mkdir will just fail.
if (access(path.c_str(), 0) != 0) {
if (mkdir(path.c_str(), mode) != 0) {
LOGDEB1("path_makepath: testing existence: [" << path << "]\n");
if (!path_exists(path)) {
LOGDEB1("path_makepath: creating directory [" << path << "]\n");
SYSPATH(path, syspath);
if (MKDIR(syspath, mode) != 0) {
//cerr << "mkdir " << path << " failed, errno " << errno << endl;
return false;
}
@@ -684,6 +731,24 @@ bool path_makepath(const string& ipath, int mode)
return true;
}
std::fstream path_open(const std::string& path, int mode)
{
#if defined(_WIN32) && defined (_MSC_VER)
// MSC STL has support for using wide chars in fstream
// constructor. We need this if, e.g. the user name/home directory
// is not ASCII. Actually don't know how to do this with gcc
wchar_t wpath[MAX_PATH + 1];
utf8towchar(path, wpath, MAX_PATH);
std::fstream ret(wpath, mode);
if (!ret.is_open()) {
LOGERR("path_open("<< path << ", "<< mode <<") errno " << errno <<"\n");
}
return ret;
#else
return std::fstream(path, mode);
#endif
}
bool path_isdir(const string& path, bool follow)
{
struct STATBUF st;
@@ -1098,7 +1163,8 @@ Pidfile::~Pidfile()
int Pidfile::read_pid()
{
int fd = ::open(m_path.c_str(), O_RDONLY);
SYSPATH(m_path, syspath);
int fd = OPEN(syspath, O_RDONLY);
if (fd == -1) {
return -1;
}
@@ -1120,8 +1186,8 @@ int Pidfile::read_pid()
int Pidfile::flopen()
{
const char *path = m_path.c_str();
if ((m_fd = ::open(path, O_RDWR | O_CREAT, 0644)) == -1) {
SYSPATH(m_path, syspath);
if ((m_fd = OPEN(syspath, O_RDWR | O_CREAT, 0644)) == -1) {
m_reason = "Open failed: [" + m_path + "]: " + strerror(errno);
return -1;
}
@@ -1198,7 +1264,8 @@ int Pidfile::close()
int Pidfile::remove()
{
return unlink(m_path.c_str());
SYSPATH(m_path, syspath);
return UNLINK(syspath);
}
// Call funcs that need static init (not initially reentrant)
+20
View File
@@ -21,6 +21,7 @@
#include <vector>
#include <set>
#include <cstdint>
#include <fstream>
// Must be called in main thread before starting other threads
extern void pathut_init_mt();
@@ -129,6 +130,22 @@ bool fsocc(const std::string& path, int *pc, long long *avmbs = 0);
/// mkdir -p
extern bool path_makepath(const std::string& path, int mode);
/* Open file, trying to do the right thing with non-ASCII paths on
* Windows, where it only works with MSVC at the moment if the path is
* not ASCII, because it uses fstream(wchar_t*), which is an MSVC
* extension. On other OSes, just builds the fstream. We'd need to
* find a way to make this work with g++. It would be easier in this
* case to use a FILE (_openw(), then fdopen()), but conftree really
* depends on std::iostream. One possible workaround for g++ would be
* to use shortpaths (which we already use to pass file names to
* xapian and aspell). Most of the problems are caused by the home
* directory name being non-ASCII, so returning a short path in
* path_home() would probably solve everything (but not pretty).
*
* @param path an utf-8 file path.
* @param mode is an std::fstream mode (ios::in etc.) */
extern std::fstream path_open(const std::string& path, int mode);
/// Where we create the user data subdirs
extern std::string path_homedata();
/// Test if path is absolute
@@ -164,6 +181,9 @@ public:
/// Convert \ separators to /
void path_slashize(std::string& s);
void path_backslashize(std::string& s);
extern std::string path_shortpath(const std::string& path);
#else
#define path_shortpath(path) (path)
#endif
/// Lock/pid file class. This is quite close to the pidfile_xxx
+4
View File
@@ -118,6 +118,10 @@ static const string& path_wingetrcltmpdir()
LOGSYSERR("path_wingettempfilename", "path_makepath", tdir);
}
}
// Try to use a short path to avoid issues in case the user
// login name is not ascii, especially with command line
// parameter passing
tdir = path_shortpath(tdir);
}
return tdir;
}