X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Fsupport%2FFileName.cpp;h=32a5862fdb6568bc91cd22670ce6e4b1c0780445;hb=b198a36a363bb6a084407d476942d68ef5fb5e86;hp=08d726e0c6347de476fa0950798ffa5527574b69;hpb=bf782ee02ac35d575247bf63eabbd38bd31c53af;p=lyx.git diff --git a/src/support/FileName.cpp b/src/support/FileName.cpp index 08d726e0c6..32a5862fdb 100644 --- a/src/support/FileName.cpp +++ b/src/support/FileName.cpp @@ -17,7 +17,7 @@ #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" @@ -28,10 +28,13 @@ #include #include #include -#include +#include -#include -#include +#ifdef _WIN32 +#include +#endif + +#include "support/checksum.h" #include #include @@ -68,15 +71,6 @@ 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 { @@ -90,18 +84,18 @@ struct FileName::Private { 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() { fi.refresh(); } - static bool isFilesystemEqual(QString const & lhs, QString const & rhs) { @@ -109,6 +103,21 @@ struct FileName::Private 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; /// @@ -172,7 +181,7 @@ bool FileName::empty() const bool FileName::isAbsolute(string const & name) { - QFileInfo fi(toqstr(name)); + QFileInfo fi(toqstr(Private::handleTildeName(name))); return fi.isAbsolute(); } @@ -191,7 +200,7 @@ string FileName::realPath() const 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 << ')'); LATTEST(empty() || isAbsolute(d->name)); @@ -220,7 +229,6 @@ void FileName::erase() bool FileName::copyTo(FileName const & name, bool keepsymlink) const { FileNameSet visited; - visited.insert(*this); return copyTo(name, keepsymlink, visited); } @@ -230,6 +238,7 @@ bool FileName::copyTo(FileName const & name, bool keepsymlink, { 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); @@ -248,8 +257,9 @@ bool FileName::copyTo(FileName const & name, bool keepsymlink, bool FileName::renameTo(FileName const & name) const { - LYXERR(Debug::FILES, "Renaming " << name); + 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; @@ -258,11 +268,26 @@ bool FileName::renameTo(FileName const & name) const bool FileName::moveTo(FileName const & name) const { - LYXERR(Debug::FILES, "Moving " << name); + 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; @@ -277,10 +302,20 @@ bool FileName::changePermission(unsigned long int mode) const << 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 { @@ -319,6 +354,9 @@ bool FileName::isSymLink() 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); @@ -454,40 +492,6 @@ FileNameList FileName::dirList(string const & ext) const } -static string createTempFile(QString const & mask) -{ - // FIXME: This is not safe. QTemporaryFile creates a file in open(), - // but the file is deleted when qt_tmp goes out of scope. - // Therefore the next call to createTempFile() may create the - // same file again. To make this safe the QTemporaryFile object - // needs to be kept for the whole life time of the temp file name. - // This can be achieved by using the TempFile class. - QTemporaryFile qt_tmp(mask + ".XXXXXXXXXXXX"); - 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 @@ -514,7 +518,12 @@ time_t FileName::lastModified() const // been touched between the object creation and now, we refresh the file // information. d->refresh(); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)) + return d->fi.lastModified().toSecsSinceEpoch(); +#else return d->fi.lastModified().toTime_t(); +#endif + } @@ -524,59 +533,57 @@ bool FileName::chdir() const } +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 beg(ifs); - istreambuf_iterator 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 @@ -588,7 +595,7 @@ unsigned long FileName::checksum() const int fd = open(file, O_RDONLY); if (!fd) - return result; + return 0; struct stat info; if (fstat(fd, &info)){ @@ -602,15 +609,13 @@ unsigned long FileName::checksum() const // Some platforms have the wrong type for MAP_FAILED (compaq cxx). if (mm == reinterpret_cast(MAP_FAILED)) { close(fd); - return result; + return 0; } - char * beg = static_cast(mm); - char * end = beg + info.st_size; + unsigned char * beg = static_cast(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); @@ -648,12 +653,12 @@ static bool rmdir(QFileInfo const & fi) 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()); } @@ -662,7 +667,7 @@ static bool rmdir(QFileInfo const & fi) LYXERR0("Could not delete " << fromqstr(list.at(i).absoluteFilePath())); } - } + } QDir parent = fi.absolutePath(); success &= parent.rmdir(fi.fileName()); return success; @@ -680,30 +685,31 @@ bool FileName::destroyDirectory() const // 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 @@ -711,6 +717,7 @@ 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; @@ -771,11 +778,7 @@ docstring FileName::fileContents(string const & encoding) const 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") @@ -805,6 +808,19 @@ void FileName::changeExtension(string const & extension) } +void FileName::ensureExtension(string const & extension) +{ + string ext; + // Make sure the extension starts with a dot + if (!extension.empty() && extension[0] != '.') + ext= '.' + extension; + else + ext = extension; + if (!suffixIs(ascii_lowercase(absFileName()), ext)) + set(absFileName() + ext); +} + + docstring const FileName::relPath(string const & path) const { // FIXME UNICODE @@ -944,45 +960,45 @@ string DocFileName::outputFileName(string const & path) const string DocFileName::mangledFileName(string const & dir) const { - // FIXME THREAD + 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 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) + mname = "export_" + onlyFileName() + "_" + toHexHash(mname); + // 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. - // Apart from non-ASCII characters, at least the following characters - // are forbidden: '/', '.', ' ', and ':'. - // On windows it is not possible to create files with '<', '>' or '?' - // in the name. - static string const keep = "abcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "+-0123456789;="; - string::size_type pos = 0; - while ((pos = mname.find_first_not_of(keep, pos)) != string::npos) - mname[pos++] = '_'; + mname = sanitizeFileName(mname); // Add the extension back on mname = support::changeExtension(mname, getExtension(name)); - // FIXME THREAD // 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