X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Fsupport%2FFileName.cpp;h=91b9a33cc4db809d17927a84737af8b7a5e7373d;hb=c23db901ece8b4e21121e43fc2562ac73e79fc98;hp=b946efd4ce1b4e7ac13c35f8b14331249a84520a;hpb=050b0f152fecc2db9d131cefc2dc430cac63c12b;p=lyx.git diff --git a/src/support/FileName.cpp b/src/support/FileName.cpp index b946efd4ce..91b9a33cc4 100644 --- a/src/support/FileName.cpp +++ b/src/support/FileName.cpp @@ -19,24 +19,55 @@ #include "debug.h" #include "lyxlib.h" +#include +#include #include #include +#include -#include -#include +#include #include #include +#include #include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#include +#include + using std::map; using std::string; +using std::ifstream; +using std::ostringstream; +using std::endl; namespace lyx { namespace support { +///////////////////////////////////////////////////////////////////// +// +// FileName::Private +// +///////////////////////////////////////////////////////////////////// + +struct FileName::Private +{ + Private() {} + + Private(string const & abs_filename) : fi(toqstr(abs_filename)) + {} + /// + QFileInfo fi; +}; + ///////////////////////////////////////////////////////////////////// // // FileName @@ -44,35 +75,70 @@ namespace support { ///////////////////////////////////////////////////////////////////// +FileName::FileName() : d(new Private) +{ +} + FileName::FileName(string const & abs_filename) - : name_(abs_filename) + : d(abs_filename.empty() ? new Private : new Private(abs_filename)) { - BOOST_ASSERT(empty() || absolutePath(name_)); + BOOST_ASSERT(empty() || d->fi.isAbsolute()); #if defined(_WIN32) - BOOST_ASSERT(!contains(name_, '\\')); + BOOST_ASSERT(!contains(abs_filename, '\\')); #endif } +FileName::FileName(FileName const & rhs) : d(new Private) +{ + d->fi = rhs.d->fi; +} + + +FileName & FileName::operator=(FileName const & rhs) +{ + d->fi = rhs.d->fi; + return *this; +} + + +bool FileName::empty() const +{ + return d->fi.absoluteFilePath().isEmpty(); +} + + +string FileName::absFilename() const +{ + return fromqstr(d->fi.absoluteFilePath()); +} + + void FileName::set(string const & name) { - name_ = name; - BOOST_ASSERT(absolutePath(name_)); + d->fi.setFile(toqstr(name)); + BOOST_ASSERT(d->fi.isAbsolute()); #if defined(_WIN32) - BOOST_ASSERT(!contains(name_, '\\')); + BOOST_ASSERT(!contains(name, '\\')); #endif } void FileName::erase() { - name_.erase(); + d->fi = QFileInfo(); +} + + +bool FileName::copyTo(FileName const & name) const +{ + return QFile::copy(d->fi.absoluteFilePath(), name.d->fi.absoluteFilePath()); } string FileName::toFilesystemEncoding() const { - QByteArray const encoded = QFile::encodeName(toqstr(name_)); + QByteArray const encoded = QFile::encodeName(d->fi.absoluteFilePath()); return string(encoded.begin(), encoded.end()); } @@ -86,47 +152,67 @@ FileName FileName::fromFilesystemEncoding(string const & name) bool FileName::exists() const { - return QFileInfo(toqstr(name_)).exists(); + return d->fi.exists(); +} + + +bool FileName::isSymLink() const +{ + return d->fi.isSymLink(); +} + + +bool FileName::isFileEmpty() const +{ + return d->fi.size() == 0; } bool FileName::isDirectory() const { - return QFileInfo(toqstr(name_)).isDir(); + return d->fi.isDir(); } bool FileName::isReadOnly() const { - QFileInfo const fi(toqstr(name_)); - return fi.isReadable() && !fi.isWritable(); + return d->fi.isReadable() && !d->fi.isWritable(); +} + + +bool FileName::isReadableDirectory() const +{ + return d->fi.isDir() && d->fi.isReadable(); +} + + +std::string FileName::onlyFileName() const +{ + return support::onlyFilename(absFilename()); } -bool FileName::isReadable() const +FileName FileName::onlyPath() const { - QFileInfo const fi(toqstr(name_)); - return fi.isReadable(); + return FileName(support::onlyPath(absFilename())); } -bool FileName::isFileReadable() const +bool FileName::isReadableFile() const { - QFileInfo const fi(toqstr(name_)); - return fi.isFile() && fi.isReadable(); + return d->fi.isFile() && d->fi.isReadable(); } bool FileName::isWritable() const { - QFileInfo const fi(toqstr(name_)); - return fi.isWritable(); + return d->fi.isWritable(); } bool FileName::isDirWritable() const { - LYXERR(Debug::FILES) << "isDirWriteable: " << *this << std::endl; + LYXERR(Debug::FILES, "isDirWriteable: " << *this); FileName const tmpfl(tempName(*this, "lyxwritetest")); @@ -146,7 +232,298 @@ FileName FileName::tempName(FileName const & dir, std::string const & mask) std::time_t FileName::lastModified() const { - return boost::filesystem::last_write_time(toFilesystemEncoding()); + return d->fi.lastModified().toTime_t(); +} + + +static bool rmdir(QFileInfo const & fi) +{ + QDir dir(fi.absoluteFilePath()); + QFileInfoList list = dir.entryInfoList(); + bool global_success = true; + for (int i = 0; i < list.size(); ++i) { + if (list.at(i).fileName() == ".") + continue; + if (list.at(i).fileName() == "..") + continue; + bool success; + if (list.at(i).isDir()) { + LYXERR(Debug::FILES, "Erasing dir " + << fromqstr(list.at(i).absoluteFilePath()) << endl); + success = rmdir(list.at(i)); + } + else { + LYXERR(Debug::FILES, "Erasing file " + << fromqstr(list.at(i).absoluteFilePath()) << endl); + success = dir.remove(list.at(i).fileName()); + } + if (!success) { + global_success = false; + lyxerr << "Could not delete " + << fromqstr(list.at(i).absoluteFilePath()) << "." << endl; + } + } + QDir parent = fi.absolutePath(); + global_success |= parent.rmdir(fi.fileName()); + return global_success; +} + + +bool FileName::destroyDirectory() const +{ + bool const success = rmdir(d->fi); + if (!success) + lyxerr << "Could not delete " << *this << "." << endl; + + return success; +} + + +bool FileName::createDirectory(int permission) const +{ + BOOST_ASSERT(!empty()); + return mkdir(*this, permission) == 0; +} + + +std::vector FileName::dirList(std::string const & ext) +{ + std::vector dirlist; + if (!exists() || !isDirectory()) { + lyxerr << "FileName::dirList(): Directory \"" << absFilename() + << "\" does not exist!" << endl; + return dirlist; + } + + QDir dir(d->fi.absoluteFilePath()); + + if (!ext.empty()) { + QString filter; + switch (ext[0]) { + case '.': filter = "*" + toqstr(ext); break; + case '*': filter = toqstr(ext); break; + default: filter = "*." + toqstr(ext); + } + dir.setNameFilters(QStringList(filter)); + LYXERR(Debug::FILES, "FileName::dirList(): filtering on extension " + << fromqstr(filter) << " is requested." << endl); + } + + QFileInfoList list = dir.entryInfoList(); + for (int i = 0; i < list.size(); ++i) { + FileName fi; + fi.d->fi = list.at(i); + dirlist.push_back(fi); + LYXERR(Debug::FILES, "FileName::dirList(): found file " + << fi.absFilename() << endl); + } + + return dirlist; +} + + +docstring FileName::displayName(int threshold) const +{ + return makeDisplayPath(absFilename(), threshold); +} + + +string FileName::fileContents() const +{ + if (exists()) { + string const encodedname = toFilesystemEncoding(); + ifstream ifs(encodedname.c_str()); + ostringstream ofs; + if (ifs && ofs) { + ofs << ifs.rdbuf(); + ifs.close(); + return ofs.str(); + } + } + lyxerr << "LyX was not able to read file '" << *this << '\'' << std::endl; + return string(); +} + + +string FileName::guessFormatFromContents() const +{ + // the different filetypes and what they contain in one of the first lines + // (dots are any characters). (Herbert 20020131) + // AGR Grace... + // BMP BM... + // EPS %!PS-Adobe-3.0 EPSF... + // FIG #FIG... + // FITS ...BITPIX... + // GIF GIF... + // JPG JFIF + // PDF %PDF-... + // PNG .PNG... + // PBM P1... or P4 (B/W) + // PGM P2... or P5 (Grayscale) + // PPM P3... or P6 (color) + // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"! + // SGI \001\332... (decimal 474) + // TGIF %TGIF... + // TIFF II... or MM... + // XBM ..._bits[]... + // XPM /* XPM */ sometimes missing (f.ex. tgif-export) + // ...static char *... + // XWD \000\000\000\151 (0x00006900) decimal 105 + // + // GZIP \037\213 http://www.ietf.org/rfc/rfc1952.txt + // ZIP PK... http://www.halyava.ru/document/ind_arch.htm + // Z \037\235 UNIX compress + // paranoia check + + if (empty() || !isReadableFile()) + return string(); + + ifstream ifs(toFilesystemEncoding().c_str()); + if (!ifs) + // Couldn't open file... + return string(); + + // gnuzip + static string const gzipStamp = "\037\213"; + + // PKZIP + static string const zipStamp = "PK"; + + // compress + static string const compressStamp = "\037\235"; + + // Maximum strings to read + int const max_count = 50; + int count = 0; + + string str; + string format; + bool firstLine = true; + while ((count++ < max_count) && format.empty()) { + if (ifs.eof()) { + LYXERR(Debug::GRAPHICS, "filetools(getFormatFromContents)\n" + << "\tFile type not recognised before EOF!"); + break; + } + + getline(ifs, str); + string const stamp = str.substr(0, 2); + if (firstLine && str.size() >= 2) { + // at first we check for a zipped file, because this + // information is saved in the first bytes of the file! + // also some graphic formats which save the information + // in the first line, too. + if (prefixIs(str, gzipStamp)) { + format = "gzip"; + + } else if (stamp == zipStamp) { + format = "zip"; + + } else if (stamp == compressStamp) { + format = "compress"; + + // the graphics part + } else if (stamp == "BM") { + format = "bmp"; + + } else if (stamp == "\001\332") { + format = "sgi"; + + // PBM family + // Don't need to use str.at(0), str.at(1) because + // we already know that str.size() >= 2 + } else if (str[0] == 'P') { + switch (str[1]) { + case '1': + case '4': + format = "pbm"; + break; + case '2': + case '5': + format = "pgm"; + break; + case '3': + case '6': + format = "ppm"; + } + break; + + } else if ((stamp == "II") || (stamp == "MM")) { + format = "tiff"; + + } else if (prefixIs(str,"%TGIF")) { + format = "tgif"; + + } else if (prefixIs(str,"#FIG")) { + format = "fig"; + + } else if (prefixIs(str,"GIF")) { + format = "gif"; + + } else if (str.size() > 3) { + int const c = ((str[0] << 24) & (str[1] << 16) & + (str[2] << 8) & str[3]); + if (c == 105) { + format = "xwd"; + } + } + + firstLine = false; + } + + if (!format.empty()) + break; + else if (contains(str,"EPSF")) + // dummy, if we have wrong file description like + // %!PS-Adobe-2.0EPSF" + format = "eps"; + + else if (contains(str, "Grace")) + format = "agr"; + + else if (contains(str, "JFIF")) + format = "jpg"; + + else if (contains(str, "%PDF")) + format = "pdf"; + + else if (contains(str, "PNG")) + format = "png"; + + else if (contains(str, "%!PS-Adobe")) { + // eps or ps + ifs >> str; + if (contains(str,"EPSF")) + format = "eps"; + else + format = "ps"; + } + + else if (contains(str, "_bits[]")) + format = "xbm"; + + else if (contains(str, "XPM") || contains(str, "static char *")) + format = "xpm"; + + else if (contains(str, "BITPIX")) + format = "fits"; + } + + if (!format.empty()) { + LYXERR(Debug::GRAPHICS, "Recognised Fileformat: " << format); + return format; + } + + LYXERR(Debug::GRAPHICS, "filetools(getFormatFromContents)\n" + << "\tCouldn't find a known format!"); + return string(); +} + + +bool FileName::isZippedFile() const +{ + string const type = guessFormatFromContents(); + return contains("gzip zip compress", type) && !type.empty(); } @@ -205,14 +582,14 @@ DocFileName::DocFileName(FileName const & abs_filename, bool save_abs) void DocFileName::set(string const & name, string const & buffer_path) { save_abs_path_ = absolutePath(name); - name_ = save_abs_path_ ? name : makeAbsPath(name, buffer_path).absFilename(); + FileName::set(save_abs_path_ ? name : makeAbsPath(name, buffer_path).absFilename()); zipped_valid_ = false; } void DocFileName::erase() { - name_.erase(); + FileName::erase(); zipped_valid_ = false; } @@ -220,14 +597,13 @@ void DocFileName::erase() string const DocFileName::relFilename(string const & path) const { // FIXME UNICODE - return to_utf8(makeRelPath(from_utf8(name_), from_utf8(path))); + return to_utf8(makeRelPath(qstring_to_ucs4(d->fi.absoluteFilePath()), from_utf8(path))); } string const DocFileName::outputFilename(string const & path) const { - // FIXME UNICODE - return save_abs_path_ ? name_ : to_utf8(makeRelPath(from_utf8(name_), from_utf8(path))); + return save_abs_path_ ? absFilename() : relFilename(path); } @@ -237,14 +613,15 @@ string const DocFileName::mangledFilename(std::string const & dir) const // filename returns the same mangled name. typedef map MangledMap; static MangledMap mangledNames; - MangledMap::const_iterator const it = mangledNames.find(name_); + 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_); + string mname = os::internal_path(name); // Remove the extension. - mname = changeExtension(name_, string()); + mname = changeExtension(name, string()); // 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. @@ -259,7 +636,7 @@ string const DocFileName::mangledFilename(std::string const & dir) const while ((pos = mname.find_first_not_of(keep, pos)) != string::npos) mname[pos++] = '_'; // Add the extension back on - mname = changeExtension(mname, getExtension(name_)); + mname = changeExtension(mname, getExtension(name)); // Prepend a counter to the filename. This is necessary to make // the mangled name unique. @@ -287,7 +664,7 @@ string const DocFileName::mangledFilename(std::string const & dir) const } } - mangledNames[name_] = mname; + mangledNames[absFilename()] = mname; return mname; } @@ -295,7 +672,7 @@ string const DocFileName::mangledFilename(std::string const & dir) const bool DocFileName::isZipped() const { if (!zipped_valid_) { - zipped_ = zippedFile(*this); + zipped_ = isZippedFile(); zipped_valid_ = true; } return zipped_; @@ -304,14 +681,14 @@ bool DocFileName::isZipped() const string const DocFileName::unzippedFilename() const { - return unzippedFileName(name_); + return unzippedFileName(absFilename()); } bool operator==(DocFileName const & lhs, DocFileName const & rhs) { - return lhs.absFilename() == rhs.absFilename() && - lhs.saveAbsPath() == rhs.saveAbsPath(); + return lhs.absFilename() == rhs.absFilename() + && lhs.saveAbsPath() == rhs.saveAbsPath(); }