#include "support/filetools.h"
#include "support/lassert.h"
#include "support/lstrings.h"
-#include "support/qstring_helpers.h"
+#include "support/mutex.h"
#include "support/os.h"
#include "support/Package.h"
#include "support/qstring_helpers.h"
+#include <QCryptographicHash>
#include <QDateTime>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QList>
#include <QTemporaryFile>
-#include <QTime>
+#include <QElapsedTimer>
+
+#ifdef _WIN32
+#include <QThread>
+#endif
-#include <boost/crc.hpp>
-#include <boost/scoped_array.hpp>
+#include "support/checksum.h"
#include <algorithm>
#include <iterator>
using namespace std;
using namespace lyx::support;
-// OK, this is ugly, but it is the only workaround I found to compile
-// with gcc (any version) on a system which uses a non-GNU toolchain.
-// The problem is that gcc uses a weak symbol for a particular
-// instantiation and that the system linker usually does not
-// understand those weak symbols (seen on HP-UX, tru64, AIX and
-// others). Thus we force an explicit instanciation of this particular
-// template (JMarc)
-template struct boost::detail::crc_table_t<32, 0x04C11DB7, true>;
-
namespace lyx {
namespace support {
{
Private() {}
- Private(string const & abs_filename) : fi(toqstr(abs_filename))
+ explicit Private(string const & abs_filename)
+ : fi(toqstr(handleTildeName(abs_filename)))
{
name = fromqstr(fi.absoluteFilePath());
- fi.setCaching(fi.exists() ? true : false);
+ fi.setCaching(fi.exists());
}
///
- inline void refresh()
+ inline void refresh()
{
-// There seems to be a bug in Qt >= 4.2.0 and < 4.5.0, that causes problems with
-// QFileInfo::refresh() on *nix. So we recreate the object in that case.
-#if defined(_WIN32) || (QT_VERSION >= 0x040500)
fi.refresh();
-#else
- fi = QFileInfo(fi.absoluteFilePath());
-#endif
}
-
static
bool isFilesystemEqual(QString const & lhs, QString const & rhs)
{
Qt::CaseSensitive : Qt::CaseInsensitive) == 0;
}
+ static
+ string const handleTildeName(string const & name)
+ {
+ string resname;
+ if ( name == "~" )
+ resname = Package::get_home_dir().absFileName();
+ else if ( prefixIs(name, "~/"))
+ resname = Package::get_home_dir().absFileName() + name.substr(1);
+ else if ( prefixIs(name, "~:s/"))
+ resname = package().system_support().absFileName() + name.substr(3);
+ else
+ resname = name;
+ return resname;
+ }
+
/// The absolute file name in UTF-8 encoding.
std::string name;
///
: d(abs_filename.empty() ? new Private : new Private(abs_filename))
{
//LYXERR(Debug::FILES, "FileName(" << abs_filename << ')');
- LASSERT(empty() || isAbsolute(d->name), /**/);
+ LATTEST(empty() || isAbsolute(d->name));
}
bool FileName::isAbsolute(string const & name)
{
- QFileInfo fi(toqstr(name));
+ QFileInfo fi(toqstr(Private::handleTildeName(name)));
return fi.isAbsolute();
}
void FileName::set(string const & name)
{
- d->fi.setFile(toqstr(name));
+ d->fi.setFile(toqstr(Private::handleTildeName(name)));
d->name = fromqstr(d->fi.absoluteFilePath());
//LYXERR(Debug::FILES, "FileName::set(" << name << ')');
- LASSERT(empty() || isAbsolute(d->name), /**/);
+ LATTEST(empty() || isAbsolute(d->name));
}
d->fi.setFile(QDir(rhs.d->fi.absoluteFilePath()), toqstr(suffix));
d->name = fromqstr(d->fi.absoluteFilePath());
//LYXERR(Debug::FILES, "FileName::set(" << d->name << ')');
- LASSERT(empty() || isAbsolute(d->name), /**/);
+ LATTEST(empty() || isAbsolute(d->name));
}
}
-bool FileName::copyTo(FileName const & name) const
+bool FileName::copyTo(FileName const & name, bool keepsymlink) const
+{
+ FileNameSet visited;
+ return copyTo(name, keepsymlink, visited);
+}
+
+
+bool FileName::copyTo(FileName const & name, bool keepsymlink,
+ FileName::FileNameSet & visited) const
{
- LYXERR(Debug::FILES, "Copying " << name);
+ LYXERR(Debug::FILES, "Copying " << name << " keep symlink: " << keepsymlink);
+ if (keepsymlink && name.isSymLink()) {
+ visited.insert(*this);
+ FileName const target(fromqstr(name.d->fi.symLinkTarget()));
+ if (visited.find(target) != visited.end()) {
+ LYXERR(Debug::FILES, "Found circular symlink: " << target);
+ return false;
+ }
+ return copyTo(target, true);
+ }
QFile::remove(name.d->fi.absoluteFilePath());
bool success = QFile::copy(d->fi.absoluteFilePath(), name.d->fi.absoluteFilePath());
if (!success)
bool FileName::renameTo(FileName const & name) const
{
+ LYXERR(Debug::FILES, "Renaming " << name << " as " << *this);
bool success = QFile::rename(d->fi.absoluteFilePath(), name.d->fi.absoluteFilePath());
+ d->refresh();
if (!success)
LYXERR0("Could not rename file " << *this << " to " << name);
return success;
bool FileName::moveTo(FileName const & name) const
{
+ LYXERR(Debug::FILES, "Moving " << *this << " to " << name);
+#ifdef _WIN32
+ // there's a locking problem on Windows sometimes, so
+ // we will keep trying for five seconds, in the hope
+ // that clears.
+ name.refresh();
+ if (name.exists()) {
+ bool removed = name.removeFile();
+ int tries = 1;
+ while (!removed && tries < 6) {
+ QThread::sleep(1);
+ removed = name.removeFile();
+ tries++;
+ }
+ }
+#else
QFile::remove(name.d->fi.absoluteFilePath());
+#endif
- bool success = QFile::rename(d->fi.absoluteFilePath(),
- name.d->fi.absoluteFilePath());
+ bool const success = renameTo(name);
if (!success)
LYXERR0("Could not move file " << *this << " to " << name);
return success;
<< mode << ".");
return false;
}
+#else
+ // squash warning
+ (void) mode;
#endif
return true;
}
+bool FileName::clonePermissions(FileName const & source)
+{
+ QFile fin(toqstr(source.absFileName()));
+ QFile f(toqstr(absFileName()));
+
+ return f.setPermissions(fin.permissions());
+}
string FileName::toFilesystemEncoding() const
{
}
+//QFileInfo caching info might fool this test if file was changed meanwhile.
+//refresh() helps, but we don't want to put it blindly here, because it might
+//trigger slowdown on networked file systems.
bool FileName::isFileEmpty() const
{
LASSERT(!empty(), return true);
}
+FileName FileName::parentPath() const
+{
+ FileName path;
+ // return empty path for parent of root dir
+ // parent of empty path is empty too
+ if (empty() || d->fi.isRoot())
+ return path;
+ path.d->fi.setFile(d->fi.path());
+ path.d->name = fromqstr(path.d->fi.absoluteFilePath());
+ return path;
+}
+
+
bool FileName::isReadableFile() const
{
return !empty() && d->fi.isFile() && d->fi.isReadable();
}
-static string createTempFile(QString const & mask)
-{
- QTemporaryFile qt_tmp(mask);
- if (qt_tmp.open()) {
- string const temp_file = fromqstr(qt_tmp.fileName());
- LYXERR(Debug::FILES, "Temporary file `" << temp_file << "' created.");
- return temp_file;
- }
- LYXERR(Debug::FILES, "Unable to create temporary file with following template: "
- << qt_tmp.fileTemplate());
- return string();
-}
-
-
-FileName FileName::tempName(FileName const & temp_dir, string const & mask)
-{
- QFileInfo tmp_fi(QDir(temp_dir.d->fi.absoluteFilePath()), toqstr(mask));
- LYXERR(Debug::FILES, "Temporary file in " << tmp_fi.absoluteFilePath());
- return FileName(createTempFile(tmp_fi.absoluteFilePath()));
-}
-
-
-FileName FileName::tempName(string const & mask)
-{
- return tempName(package().temp_dir(), mask);
-}
-
-
FileName FileName::getcwd()
{
// return makeAbsPath("."); would create an infinite loop
}
+bool FileName::link(FileName const & name) const
+{
+ return QFile::link(toqstr(absFileName()), toqstr(name.absFileName()));
+}
+
+
unsigned long checksum_ifstream_fallback(char const * file)
{
- unsigned long result = 0;
//LYXERR(Debug::FILES, "lyx::sum() using istreambuf_iterator (fast)");
ifstream ifs(file, ios_base::in | ios_base::binary);
if (!ifs)
- return result;
-
- istreambuf_iterator<char> beg(ifs);
- istreambuf_iterator<char> end;
- boost::crc_32_type crc;
- crc = for_each(beg, end, crc);
- result = crc.checksum();
- return result;
+ return 0;
+ return support::checksum(ifs);
}
+
unsigned long FileName::checksum() const
{
- unsigned long result = 0;
-
if (!exists()) {
//LYXERR0("File \"" << absFileName() << "\" does not exist!");
- return result;
+ return 0;
}
// a directory may be passed here so we need to test it. (bug 3622)
if (isDirectory()) {
LYXERR0('"' << absFileName() << "\" is a directory!");
- return result;
+ return 0;
}
// This is used in the debug output at the end of the method.
- static QTime t;
+ static QElapsedTimer t;
if (lyxerr.debugging(Debug::FILES))
t.restart();
+ unsigned long result = 0;
+
#if QT_VERSION >= 0x999999
// First version of checksum uses Qt4.4 mmap support.
// FIXME: This code is not ready with Qt4.4.2,
// see http://www.lyx.org/trac/ticket/5293
// FIXME: should we check if the MapExtension extension is supported?
- // see QAbstractFileEngine::supportsExtension() and
+ // see QAbstractFileEngine::supportsExtension() and
// QAbstractFileEngine::MapExtension)
QFile qf(fi.filePath());
if (!qf.open(QIODevice::ReadOnly))
- return result;
+ return 0;
qint64 size = fi.size();
uchar * ubeg = qf.map(0, size);
uchar * uend = ubeg + size;
- boost::crc_32_type ucrc;
- ucrc.process_block(ubeg, uend);
+ result = support::checksum(ubeg, uend);
qf.unmap(ubeg);
qf.close();
- result = ucrc.checksum();
#else // QT_VERSION
int fd = open(file, O_RDONLY);
if (!fd)
- return result;
+ return 0;
struct stat info;
if (fstat(fd, &info)){
// Some platforms have the wrong type for MAP_FAILED (compaq cxx).
if (mm == reinterpret_cast<void*>(MAP_FAILED)) {
close(fd);
- return result;
+ return 0;
}
- char * beg = static_cast<char*>(mm);
- char * end = beg + info.st_size;
+ unsigned char * beg = static_cast<unsigned char*>(mm);
+ unsigned char * end = beg + info.st_size;
- boost::crc_32_type crc;
- crc.process_block(beg, end);
- result = crc.checksum();
+ result = support::checksum(beg, end);
munmap(mm, info.st_size);
close(fd);
continue;
bool removed;
if (list.at(i).isDir()) {
- LYXERR(Debug::FILES, "Removing dir "
+ LYXERR(Debug::FILES, "Removing dir "
<< fromqstr(list.at(i).absoluteFilePath()));
removed = rmdir(list.at(i));
}
else {
- LYXERR(Debug::FILES, "Removing file "
+ LYXERR(Debug::FILES, "Removing file "
<< fromqstr(list.at(i).absoluteFilePath()));
removed = dir.remove(list.at(i).fileName());
}
LYXERR0("Could not delete "
<< fromqstr(list.at(i).absoluteFilePath()));
}
- }
+ }
QDir parent = fi.absolutePath();
success &= parent.rmdir(fi.fileName());
return success;
// Only used in non Win32 platforms
+#ifndef Q_OS_WIN32
static int mymkdir(char const * pathname, unsigned long int mode)
{
// FIXME: why don't we have mode_t in lyx::mkdir prototype ??
-#if HAVE_MKDIR
-# if MKDIR_TAKES_ONE_ARG
+# if HAVE_MKDIR
+# if MKDIR_TAKES_ONE_ARG
// MinGW32
return ::mkdir(pathname);
// FIXME: "Permissions of created directories are ignored on this system."
-# else
+# else
// POSIX
return ::mkdir(pathname, mode_t(mode));
-# endif
-#elif defined(_WIN32)
+# endif
+# elif defined(_WIN32)
// plain Windows 32
return CreateDirectory(pathname, 0) != 0 ? 0 : -1;
// FIXME: "Permissions of created directories are ignored on this system."
-#elif HAVE__MKDIR
+# elif HAVE__MKDIR
return ::_mkdir(pathname);
// FIXME: "Permissions of created directories are ignored on this system."
-#else
+# else
# error "Don't know how to create a directory on this system."
-#endif
-
+# endif
}
+#endif
bool FileName::createDirectory(int permission) const
LASSERT(!empty(), return false);
#ifdef Q_OS_WIN32
// FIXME: "Permissions of created directories are ignored on this system."
+ (void) permission;
return createPath();
#else
return mymkdir(toFilesystemEncoding().c_str(), permission) == 0;
docstring FileName::fileContents(string const & encoding) const
{
if (!isReadableFile()) {
- LYXERR0("File '" << *this << "' is not redable!");
+ LYXERR0("File '" << *this << "' is not readable!");
return docstring();
}
if (encoding.empty() || encoding == "UTF-8")
s = QString::fromUtf8(contents.data());
else if (encoding == "ascii")
+#if (QT_VERSION < 0x050000)
s = QString::fromAscii(contents.data());
+#else
+ s = QString::fromLatin1(contents.data());
+#endif
else if (encoding == "local8bit")
s = QString::fromLocal8Bit(contents.data());
else if (encoding == "latin1")
string DocFileName::mangledFileName(string const & dir) const
{
+ return mangledFileName(dir, true, false);
+}
+
+string DocFileName::mangledFileName(string const & dir, bool use_counter, bool encrypt_path) const
+{
+ // Concurrent access to these variables is possible.
+
// We need to make sure that every DocFileName instance for a given
// filename returns the same mangled name.
typedef map<string, string> MangledMap;
static MangledMap mangledNames;
+ static Mutex mangledMutex;
+ // this locks both access to mangledNames and counter below
+ Mutex::Locker lock(&mangledMutex);
MangledMap::const_iterator const it = mangledNames.find(absFileName());
if (it != mangledNames.end())
return (*it).second;
string const name = absFileName();
- // Now the real work
- string mname = os::internal_path(name);
- // Remove the extension.
- mname = support::changeExtension(name, string());
+ // Now the real work. Remove the extension.
+ string mname = support::changeExtension(name, string());
+
+ if (encrypt_path) {
+ QString qname = toqstr(mname);
+#if QT_VERSION >= 0x050000
+ QByteArray hash = QCryptographicHash::hash(qname.toLocal8Bit(),QCryptographicHash::Sha256);
+#else
+ QByteArray hash = QCryptographicHash::hash(qname.toLocal8Bit(),QCryptographicHash::Sha1);
+#endif
+ hash = hash.toHex();
+ mname = fromqstr(QString(hash));
+ mname = mname + "_" + onlyFileName();
+ }
+
// The mangled name must be a valid LaTeX name.
// The list of characters to keep is probably over-restrictive,
// but it is not really a problem.
// Prepend a counter to the filename. This is necessary to make
// the mangled name unique.
static int counter = 0;
- ostringstream s;
- s << counter++ << mname;
- mname = s.str();
+
+ if (use_counter) {
+ ostringstream s;
+ s << counter++ << mname;
+ mname = s.str();
+ }
// MiKTeX's YAP (version 2.4.1803) crashes if the file name
// is longer than about 160 characters. MiKTeX's pdflatex