X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Fsupport%2Ffiletools.cpp;h=37fd14f54d425b2d28c61826132adc5d7385ac86;hb=279e656d6a7c4ee3647752cba8b9aa7d6ec6e0c2;hp=d241fcc495a6131fce7d99692717b327792b6077;hpb=fdbe775b9f5468e8f53dc83a66583f412b5970fb;p=lyx.git diff --git a/src/support/filetools.cpp b/src/support/filetools.cpp index d241fcc495..37fd14f54d 100644 --- a/src/support/filetools.cpp +++ b/src/support/filetools.cpp @@ -21,10 +21,12 @@ #include +#include "LyX.h" #include "LyXRC.h" #include "support/filetools.h" +#include "support/convert.h" #include "support/debug.h" #include "support/environment.h" #include "support/gettext.h" @@ -35,15 +37,24 @@ #include "support/PathChanger.h" #include "support/Systemcall.h" #include "support/qstring_helpers.h" +#include "support/TempFile.h" #include +#include #include "support/lassert.h" #include "support/regex.h" #include +#ifdef HAVE_MAGIC_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif #include +#include #include #include @@ -90,6 +101,60 @@ bool isValidDVIFileName(string const & filename) } +bool isBinaryFile(FileName const & filename) +{ + bool isbinary = false; + if (filename.empty() || !filename.exists()) + return isbinary; + +#ifdef HAVE_MAGIC_H + magic_t magic_cookie = magic_open(MAGIC_MIME_ENCODING); + if (magic_cookie) { + bool detected = true; + if (magic_load(magic_cookie, NULL) != 0) { + LYXERR(Debug::FILES, "isBinaryFile: " + "Could not load magic database - " + << magic_error(magic_cookie)); + detected = false; + } else { + char const *charset = magic_file(magic_cookie, + filename.toFilesystemEncoding().c_str()); + isbinary = contains(charset, "binary"); + } + magic_close(magic_cookie); + if (detected) + return isbinary; + } +#endif + // Try by looking for binary chars at the beginning of the file. + // Note that this is formally not correct, since count_bin_chars + // expects utf8, and the passed string can be anything: plain text + // in any encoding, or really binary data. In practice it works, + // since QString::fromUtf8() drops invalid utf8 sequences, and + // while the exact number may not be correct, we still get a high + // number for truly binary files. + + ifstream ifs(filename.toFilesystemEncoding().c_str()); + if (!ifs) + return isbinary; + + // Maximum strings to read + int const max_count = 50; + + // Maximum number of binary chars allowed + int const max_bin = 5; + + int count = 0; + int binchars = 0; + string str; + while (count++ < max_count && !ifs.eof()) { + getline(ifs, str); + binchars += count_bin_chars(str); + } + return binchars > max_bin; +} + + string const latex_path(string const & original_path, latex_path_extension extension, latex_path_dots dots) @@ -178,6 +243,8 @@ string const quoteName(string const & name, quote_style style) // simple parser in Systemcall.cpp do the substitution. return '"' + subst(name, "\"", "\\\"") + '"'; #endif + case quote_shell_filename: + return quoteName(os::external_path(name), quote_shell); case quote_python: return "\"" + subst(subst(name, "\\", "\\\\"), "\"", "\\\"") + "\""; @@ -228,7 +295,7 @@ FileName const fileOpenSearch(string const & path, string const & name, // Returns the real name of file name in directory path, with optional // extension ext. FileName const fileSearch(string const & path, string const & name, - string const & ext, search_mode mode) + string const & exts, search_mode mode) { // if `name' is an absolute path, we ignore the setting of `path' // Expand Environmentvariables in 'name' @@ -237,15 +304,29 @@ FileName const fileSearch(string const & path, string const & name, // search first without extension, then with it. if (fullname.isReadableFile()) return fullname; - if (ext.empty()) + if (exts.empty()) // We are done. return mode == may_not_exist ? fullname : FileName(); - // Only add the extension if it is not already the extension of - // fullname. - if (getExtension(fullname.absFileName()) != ext) - fullname = FileName(addExtension(fullname.absFileName(), ext)); - if (fullname.isReadableFile() || mode == may_not_exist) - return fullname; + int n = 0; + string ext = token(exts, ',', n); + while (!ext.empty()) { + // Only add the extension if it is not already the extension of + // fullname. + bool addext = getExtension(fullname.absFileName()) != ext; + if (addext) { + if (mode == check_hidpi) { + FileName fullname2x = FileName(addExtension(fullname.absFileName() + "@2x", ext)); + if (fullname2x.isReadableFile()) + return fullname2x; + } + fullname = FileName(addExtension(fullname.absFileName(), ext)); + } + if (fullname.isReadableFile() || mode == may_not_exist) + return fullname; + if (addext) + fullname.changeExtension(""); + ext = token(exts, ',', ++n); + } return FileName(); } @@ -255,20 +336,21 @@ FileName const fileSearch(string const & path, string const & name, // 2) build_lyxdir (if not empty) // 3) system_lyxdir FileName const libFileSearch(string const & dir, string const & name, - string const & ext) + string const & ext, search_mode mode) { FileName fullname = fileSearch(addPath(package().user_support().absFileName(), dir), - name, ext); + name, ext, mode); if (!fullname.empty()) return fullname; if (!package().build_support().empty()) fullname = fileSearch(addPath(package().build_support().absFileName(), dir), - name, ext); + name, ext, mode); if (!fullname.empty()) return fullname; - return fileSearch(addPath(package().system_support().absFileName(), dir), name, ext); + return fileSearch(addPath(package().system_support().absFileName(), dir), + name, ext, mode); } @@ -323,25 +405,29 @@ FileName const i18nLibFileSearch(string const & dir, string const & name, FileName const imageLibFileSearch(string & dir, string const & name, - string const & ext) + string const & ext, search_mode mode) { if (!lyx::lyxrc.icon_set.empty()) { string const imagedir = addPath(dir, lyx::lyxrc.icon_set); - FileName const fn = libFileSearch(imagedir, name, ext); + FileName const fn = libFileSearch(imagedir, name, ext, mode); if (fn.exists()) { dir = imagedir; return fn; } } - return libFileSearch(dir, name, ext); + return libFileSearch(dir, name, ext, mode); } -string const libScriptSearch(string const & command_in, quote_style style) +string const commandPrep(string const & command_in) { static string const token_scriptpath = "$$s/"; + string const python_call = "python -tt"; string command = command_in; + if (prefixIs(command_in, python_call)) + command = os::python() + command_in.substr(python_call.length()); + // Find the starting position of "$$s/" string::size_type const pos1 = command.find(token_scriptpath); if (pos1 == string::npos) @@ -361,6 +447,10 @@ string const libScriptSearch(string const & command_in, quote_style style) // Replace "$$s/" with "" command.erase(pos1, 4); } else { + quote_style style = quote_shell; + if (prefixIs(command, os::python())) + style = quote_python; + // Replace "$$s/foo/some_script" with "/some_script". string::size_type const size_replace = size_script + 4; command.replace(pos1, size_replace, quoteName(script, style)); @@ -370,12 +460,80 @@ string const libScriptSearch(string const & command_in, quote_style style) } +FileName const tempFileName(string const & mask) +{ + FileName tempfile = TempFile(mask).name(); + // Since the QTemporaryFile object is destroyed at function return + // (which is what is intended here), the next call to this function + // may return the same file name again. + // Thus, in order to prevent race conditions, we track returned names + // and create our own unique names if QTemporaryFile returns a name again. + if (tmp_names_.find(tempfile.absFileName()) == tmp_names_.end()) { + tmp_names_.insert(tempfile.absFileName()); + return tempfile; + } + + // OK, we need another name. Simply append digits. + FileName tmp = tempfile; + tmp.changeExtension(""); + for (int i = 1; i < INT_MAX ;++i) { + // Append digit to filename and re-add extension + string const new_fn = tmp.absFileName() + convert(i) + + "." + tempfile.extension(); + if (tmp_names_.find(new_fn) == tmp_names_.end()) { + tmp_names_.insert(new_fn); + tempfile.set(new_fn); + return tempfile; + } + } + + // This should not happen! + LYXERR0("tempFileName(): Could not create unique temp file name!"); + return tempfile; +} + + +void removeTempFile(FileName const & fn) +{ + if (!fn.exists()) + return; + + string const abs = fn.absFileName(); + if (tmp_names_.find(abs) != tmp_names_.end()) + tmp_names_.erase(abs); + fn.removeFile(); +} + + +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 could be achieved by creating a class TempDir (like + // TempFile, but using a currently non-existing + // QTemporaryDirectory object). + 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(); +} + + static FileName createTmpDir(FileName const & tempdir, string const & mask) { LYXERR(Debug::FILES, "createTmpDir: tempdir=`" << tempdir << "'\n" << "createTmpDir: mask=`" << mask << '\''); - FileName const tmpfl = FileName::tempName(tempdir, mask); + QFileInfo tmp_fi(QDir(toqstr(tempdir.absFileName())), toqstr(mask)); + FileName const tmpfl(createTempFile(tmp_fi.absoluteFilePath())); if (tmpfl.empty() || !tmpfl.createDirectory(0700)) { LYXERR0("LyX could not create temporary directory in " << tempdir @@ -401,7 +559,7 @@ FileName const createLyXTmpDir(FileName const & deflt) // dir inside deflt. return createTmpDir(deflt, "lyx_tmpdir"); } else { - // some other error occured. + // some other error occurred. return createTmpDir(package().system_temp_dir(), "lyx_tmpdir"); } } @@ -534,35 +692,6 @@ string const onlyFileName(string const & fname) } -// Create absolute path. If impossible, don't do anything -// Supports ./ and ~/. Later we can add support for ~logname/. (Asger) -string const expandPath(string const & path) -{ - // checks for already absolute path - string rTemp = replaceEnvironmentPath(path); - if (FileName::isAbsolute(rTemp)) - return rTemp; - - string temp; - string const copy = rTemp; - - // Split by next / - rTemp = split(rTemp, temp, '/'); - - if (temp == ".") - return FileName::getcwd().absFileName() + '/' + rTemp; - - if (temp == "~") - return Package::get_home_dir().absFileName() + '/' + rTemp; - - if (temp == "..") - return makeAbsPath(copy).absFileName(); - - // Don't know how to handle this - return copy; -} - - // Search the string for ${VAR} and $VAR and replace VAR using getenv. string const replaceEnvironmentPath(string const & path) { @@ -591,22 +720,59 @@ string const replaceEnvironmentPath(string const & path) // Return a command prefix for setting the environment of the TeX engine. -string latexEnvCmdPrefix(string const & path) +string latexEnvCmdPrefix(string const & path, string const & lpath) { - if (path.empty() || lyxrc.texinputs_prefix.empty()) + bool use_lpath = !(lpath.empty() || lpath == "." || lpath == "./"); + + if (path.empty() || (lyxrc.texinputs_prefix.empty() && !use_lpath)) return string(); - string const texinputs_prefix = os::latex_path_list( + string texinputs_prefix = lyxrc.texinputs_prefix.empty() ? string() + : os::latex_path_list( replaceCurdirPath(path, lyxrc.texinputs_prefix)); + string const allother_prefix = os::latex_path_list(path); string const sep = string(1, os::path_separator(os::TEXENGINE)); string const texinputs = getEnv("TEXINPUTS"); + string const bibinputs = getEnv("BIBINPUTS"); + string const bstinputs = getEnv("BSTINPUTS"); + string const texfonts = getEnv("TEXFONTS"); + + if (use_lpath) { + string const abslpath = FileName::isAbsolute(lpath) + ? os::latex_path(lpath) + : os::latex_path(FileName(path + "/" + lpath).realPath()); + if (texinputs_prefix.empty()) + texinputs_prefix = abslpath; + else if (suffixIs(texinputs_prefix, sep)) + texinputs_prefix.append(abslpath + sep); + else + texinputs_prefix.append(sep + abslpath); + } if (os::shell() == os::UNIX) return "env TEXINPUTS=\"." + sep + texinputs_prefix - + sep + texinputs + "\" "; + + sep + texinputs + "\" " + + "BIBINPUTS=\"." + sep + allother_prefix + + sep + bibinputs + "\" " + + "BSTINPUTS=\"." + sep + allother_prefix + + sep + bstinputs + "\" " + + "TEXFONTS=\"." + sep + allother_prefix + + sep + texfonts + "\" "; else - return "cmd /d /c set TEXINPUTS=." + sep + texinputs_prefix - + sep + texinputs + "&"; + // NOTE: the dummy blank dirs are necessary to force the + // QProcess parser to quote the argument (see bug 9453) + return "cmd /d /c set \"TEXINPUTS=." + sep + " " + + sep + texinputs_prefix + + sep + texinputs + "\" & " + + "set \"BIBINPUTS=." + sep + " " + + sep + allother_prefix + + sep + bibinputs + "\" & " + + "set \"BSTINPUTS=." + sep + " " + + sep + allother_prefix + + sep + bstinputs + "\" & " + + "set \"TEXFONTS=." + sep + " " + + sep + allother_prefix + + sep + texfonts + "\" & "; } @@ -637,7 +803,7 @@ string const replaceCurdirPath(string const & path, string const & pathlist) } if (i != string::npos) { newpathlist += sep; - // Stop here if the last element is empty + // Stop here if the last element is empty if (++i == oldpathlist.length()) break; } @@ -764,6 +930,8 @@ string const unzippedFileName(string const & zipped_file) string const ext = getExtension(zipped_file); if (ext == "gz" || ext == "z" || ext == "Z") return changeExtension(zipped_file, string()); + else if (ext == "svgz") + return changeExtension(zipped_file, "svg"); return onlyPath(zipped_file) + "unzipped_" + onlyFileName(zipped_file); } @@ -813,13 +981,11 @@ docstring const makeDisplayPath(string const & path, unsigned int threshold) if (dstr.empty()) { // Yes, filename itself is too long. // Pick the start and the end of the filename. - dstr = from_utf8(onlyFileName(path)); - docstring const head = dstr.substr(0, threshold / 2 - 3); - - docstring::size_type len = dstr.length(); - docstring const tail = - dstr.substr(len - threshold / 2 - 2, len - 1); - dstr = head + from_ascii("...") + tail; + docstring fstr = from_utf8(onlyFileName(path)); + dstr = fstr; + if (support::truncateWithEllipsis(dstr, threshold / 2)) + dstr += fstr.substr(fstr.length() - threshold / 2 - 2, + docstring::npos); } return from_utf8(os::external_path(prefix + to_utf8(dstr))); @@ -832,7 +998,7 @@ bool readLink(FileName const & file, FileName & link) string const encoded = file.toFilesystemEncoding(); #ifdef HAVE_DEF_PATH_MAX char linkbuffer[PATH_MAX + 1]; - int const nRead = ::readlink(encoded.c_str(), + ssize_t const nRead = ::readlink(encoded.c_str(), linkbuffer, sizeof(linkbuffer) - 1); if (nRead <= 0) return false; @@ -846,7 +1012,7 @@ bool readLink(FileName const & file, FileName & link) if (nRead < 0) { return false; } - if (nRead < buf.size() - 1) { + if (static_cast(nRead) < buf.size() - 1) { break; } buf.resize(buf.size() * 2); @@ -877,8 +1043,12 @@ cmd_ret const runCommand(string const & cmd) // pstream (process stream), with the // variants ipstream, opstream + if (verbose) + lyxerr << "\nRunning: " << cmd << endl; + else + LYXERR(Debug::INFO,"Running: " << cmd); + #if defined (_WIN32) - int fno; STARTUPINFO startup; PROCESS_INFORMATION process; SECURITY_ATTRIBUTES security; @@ -920,7 +1090,7 @@ cmd_ret const runCommand(string const & cmd) 0, 0, &startup, &process)) { CloseHandle(process.hThread); - fno = _open_osfhandle((long)in, _O_RDONLY); + int fno = _open_osfhandle((intptr_t)in, _O_RDONLY); CloseHandle(out); inf = _fdopen(fno, "r"); } @@ -948,10 +1118,14 @@ cmd_ret const runCommand(string const & cmd) #if defined (_WIN32) WaitForSingleObject(process.hProcess, INFINITE); + DWORD pret; + if (!GetExitCodeProcess(process.hProcess, &pret)) + pret = -1; if (!infile.empty()) CloseHandle(startup.hStdInput); CloseHandle(process.hProcess); - int const pret = fclose(inf); + if (fclose(inf) != 0) + pret = -1; #elif defined (HAVE_PCLOSE) int const pret = pclose(inf); #elif defined (HAVE__PCLOSE) @@ -967,7 +1141,8 @@ cmd_ret const runCommand(string const & cmd) } -FileName const findtexfile(string const & fil, string const & /*format*/) +FileName const findtexfile(string const & fil, string const & /*format*/, + bool const onlykpse) { /* There is no problem to extend this function too use other methods to look for files. It could be setup to look @@ -980,9 +1155,11 @@ FileName const findtexfile(string const & fil, string const & /*format*/) // If the file can be found directly, we just return a // absolute path version of it. - FileName const absfile(makeAbsPath(fil)); - if (absfile.exists()) - return absfile; + if (!onlykpse) { + FileName const absfile(makeAbsPath(fil)); + if (absfile.exists()) + return absfile; + } // Now we try to find it using kpsewhich. // It seems from the kpsewhich manual page that it is safe to use @@ -1064,19 +1241,43 @@ bool prefs2prefs(FileName const & filename, FileName const & tempfile, bool lfun return true; } + +bool configFileNeedsUpdate(string const & file) +{ + // We cannot initialize configure_script directly because the package + // is not initialized yet when static objects are constructed. + static FileName configure_script; + static bool firstrun = true; + if (firstrun) { + configure_script = + FileName(addName(package().system_support().absFileName(), + "configure.py")); + firstrun = false; + } + + FileName absfile = + FileName(addName(package().user_support().absFileName(), file)); + return !absfile.exists() + || configure_script.lastModified() > absfile.lastModified(); +} + + int fileLock(const char * lock_file) { int fd = -1; #if defined(HAVE_LOCKF) fd = open(lock_file, O_CREAT|O_APPEND|O_SYNC|O_RDWR, 0666); + if (fd == -1) + return -1; if (lockf(fd, F_LOCK, 0) != 0) { close(fd); - return(-1); + return -1; } #endif - return(fd); + return fd; } + void fileUnlock(int fd, const char * /* lock_file*/) { #if defined(HAVE_LOCKF)