]> git.lyx.org Git - lyx.git/blobdiff - src/support/filetools.cpp
Let paragraph::requestSpellcheck() consider contained insets
[lyx.git] / src / support / filetools.cpp
index bca242d376427d2489c79a4f642ac4a965984f12..cfb245cd549e301d2de7a3edf6b220a6dad71c97 100644 (file)
 
 #include <config.h>
 
+#include "support/filetools.h"
+
 #include "LyX.h"
 #include "LyXRC.h"
 
-#include "support/filetools.h"
-
 #include "support/convert.h"
 #include "support/debug.h"
 #include "support/environment.h"
@@ -42,7 +42,6 @@
 #include <QDir>
 
 #include "support/lassert.h"
-#include "support/regex.h"
 
 #include <fcntl.h>
 #ifdef HAVE_MAGIC_H
 
 #include <utility>
 #include <fstream>
+#include <regex>
 #include <sstream>
 #include <vector>
 
+#include <QCryptographicHash>
+
 #if defined (_WIN32)
 #include <io.h>
 #include <windows.h>
@@ -110,7 +112,7 @@ bool isBinaryFile(FileName const & filename)
        magic_t magic_cookie = magic_open(MAGIC_MIME_ENCODING);
        if (magic_cookie) {
                bool detected = true;
-               if (magic_load(magic_cookie, NULL) != 0) {
+               if (magic_load(magic_cookie, nullptr) != 0) {
                        LYXERR(Debug::FILES, "isBinaryFile: "
                                "Could not load magic database - "
                                << magic_error(magic_cookie));
@@ -335,12 +337,16 @@ 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, search_mode mode)
+                          string const & ext, search_mode mode,
+                          bool const only_global)
 {
-       FileName fullname = fileSearch(addPath(package().user_support().absFileName(), dir),
-                                    name, ext, mode);
-       if (!fullname.empty())
-               return fullname;
+       FileName fullname;
+       if (!only_global) {
+               fullname = fileSearch(addPath(package().user_support().absFileName(), dir),
+                                            name, ext, mode);
+               if (!fullname.empty())
+                       return fullname;
+       }
 
        if (!package().build_support().empty())
                fullname = fileSearch(addPath(package().build_support().absFileName(), dir),
@@ -442,9 +448,20 @@ string const commandPrep(string const & command_in)
 }
 
 
+FileName const tempFileName(FileName const & tempdir, string const & mask, bool const dir)
+{
+       return tempFileName(TempFile(tempdir, mask).name(), dir);
+}
+
+
 FileName const tempFileName(string const & mask, bool const dir)
 {
-       FileName tempfile = TempFile(mask).name();
+       return tempFileName(TempFile(mask).name(), dir);
+}
+
+
+FileName const tempFileName(FileName tempfile, bool const dir)
+{
        // 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.
@@ -486,8 +503,7 @@ void removeTempFile(FileName const & fn)
                return;
 
        string const abs = fn.absFileName();
-       if (tmp_names_.find(abs) != tmp_names_.end())
-               tmp_names_.erase(abs);
+       tmp_names_.erase(abs);
        fn.removeFile();
 }
 
@@ -499,7 +515,8 @@ static FileName createTmpDir(FileName const & tempdir, string const & mask)
 
        QFileInfo tmp_fi(QDir(toqstr(tempdir.absFileName())), toqstr(mask));
        FileName const tmpfl =
-               tempFileName(fromqstr(tmp_fi.absoluteFilePath()) + ".XXXXXXXXXXXX", true);
+               tempFileName(FileName(fromqstr(tmp_fi.absolutePath())),
+                            fromqstr(tmp_fi.fileName()) + ".XXXXXXXXXXXX", true);
 
        if (tmpfl.empty() || !tmpfl.createDirectory(0700)) {
                LYXERR0("LyX could not create temporary directory in " << tempdir
@@ -643,6 +660,19 @@ string const addName(string const & path, string const & fname)
 }
 
 
+string const addPathName(std::string const & path, std::string const & fname)
+{
+       string const pathpart = onlyPath(fname);
+       string const namepart = onlyFileName(fname);
+       string newpath = path;
+       if (!pathpart.empty())
+               newpath = addPath(newpath, pathpart);
+       if (!namepart.empty())
+               newpath = addName(newpath, namepart);
+       return newpath;
+}
+
+
 // Strips path from filename
 string const onlyFileName(string const & fname)
 {
@@ -659,8 +689,12 @@ string const onlyFileName(string const & fname)
 
 
 // Search the string for ${VAR} and $VAR and replace VAR using getenv.
+// If VAR does not exist, ${VAR} and $VAR are left as is in the string.
 string const replaceEnvironmentPath(string const & path)
 {
+       if (!contains(path, '$'))
+               return path;
+
        // ${VAR} is defined as
        // $\{[A-Za-z_][A-Za-z_0-9]*\}
        static string const envvar_br = "[$]\\{([A-Za-z_][A-Za-z_0-9]*)\\}";
@@ -678,14 +712,23 @@ string const replaceEnvironmentPath(string const & path)
                string result = path;
                while (1) {
                        smatch what;
+                       bool brackets = true;
                        if (!regex_match(result, what, envvar_br_re)) {
+                               brackets = false;
                                if (!regex_match(result, what, envvar_re))
                                        break;
                        }
                        string env_var = getEnv(what.str(2));
+                       if (env_var.empty()) {
+                               // temporarily use alert/bell (0x07) in place of $
+                               if (brackets)
+                                       env_var = "\a{" + what.str(2) + '}';
+                               else
+                                       env_var = "\a" + what.str(2);
+                       }
                        result = what.str(1) + env_var + what.str(3);
                }
-               return result;
+               return subst(result, '\a', '$');
        } catch (exception const & e) {
                LYXERR0("Something is very wrong: " << e.what());
                return path;
@@ -1039,7 +1082,7 @@ cmd_ret const runCommand(string const & cmd)
                command = rtrim(command, "2>&1");
                err2out = true;
        }
-       string const cmdarg = "/d /c \"" + command+"\"";
+       string const cmdarg = "/d /c \"" + command + "\"";
        string const comspec = getEnv("COMSPEC");
 
        security.nLength = sizeof(SECURITY_ATTRIBUTES);
@@ -1082,46 +1125,48 @@ cmd_ret const runCommand(string const & cmd)
 
        // (Claus Hentschel) Check if popen was successful ;-)
        if (!inf) {
-               lyxerr << "RunCommand:: could not start child process" << endl;
-               return make_pair(-1, string());
+               lyxerr << "RunCommand: could not start child process" << endl;
+               return { false, string() };
        }
 
-       string ret;
+       string result;
        int c = fgetc(inf);
        while (c != EOF) {
-               ret += static_cast<char>(c);
+               result += static_cast<char>(c);
                c = fgetc(inf);
        }
 
 #if defined (_WIN32)
        WaitForSingleObject(process.hProcess, INFINITE);
        DWORD pret;
-       if (!GetExitCodeProcess(process.hProcess, &pret))
-               pret = -1;
+       BOOL success = GetExitCodeProcess(process.hProcess, &pret);
+       bool valid = (pret == 0) && success;
        if (!infile.empty())
                CloseHandle(startup.hStdInput);
        CloseHandle(process.hProcess);
        if (fclose(inf) != 0)
-               pret = -1;
+               valid = false;
 #elif defined (HAVE_PCLOSE)
        int const pret = pclose(inf);
+       bool const valid = (pret != -1);
 #elif defined (HAVE__PCLOSE)
        int const pret = _pclose(inf);
+       bool const valid = (pret != -1);
 #else
 #error No pclose() function.
 #endif
 
-       if (pret == -1)
-               perror("RunCommand:: could not terminate child process");
+       if (!valid)
+               perror("RunCommand: could not terminate child process");
 
-       return make_pair(pret, ret);
+       return { valid, result };
 }
 
 
 FileName const findtexfile(string const & fil, string const & /*format*/,
                                                   bool const onlykpse)
 {
-       /* There is no problem to extend this function too use other
+       /* There is no problem to extend this function to use other
           methods to look for files. It could be setup to look
           in environment paths and also if wanted as a last resort
           to a recursive find. One of the easier extensions would
@@ -1147,7 +1192,7 @@ FileName const findtexfile(string const & fil, string const & /*format*/,
        // is used."
        // However, we want to take advantage of the format sine almost all
        // the different formats has environment variables that can be used
-       // to controll which paths to search. f.ex. bib looks in
+       // to control which paths to search. f.ex. bib looks in
        // BIBINPUTS and TEXBIB. Small list follows:
        // bib - BIBINPUTS, TEXBIB
        // bst - BSTINPUTS
@@ -1162,10 +1207,10 @@ FileName const findtexfile(string const & fil, string const & /*format*/,
 
        cmd_ret const c = runCommand(kpsecmd);
 
-       LYXERR(Debug::LATEX, "kpse status = " << c.first << '\n'
-                << "kpse result = `" << rtrim(c.second, "\n\r") << '\'');
-       if (c.first != -1)
-               return FileName(rtrim(to_utf8(from_filesystem8bit(c.second)), "\n\r"));
+       LYXERR(Debug::LATEX, "kpse status = " << c.valid << '\n'
+                << "kpse result = `" << rtrim(c.result, "\n\r") << '\'');
+       if (c.valid)
+               return FileName(rtrim(to_utf8(from_filesystem8bit(c.result)), "\n\r"));
        else
                return FileName();
 }
@@ -1211,7 +1256,7 @@ bool prefs2prefs(FileName const & filename, FileName const & tempfile, bool lfun
        LYXERR(Debug::FILES, "Running `" << command_str << '\'');
 
        cmd_ret const ret = runCommand(command_str);
-       if (ret.first != 0) {
+       if (!ret.valid) {
                LYXERR0("Could not run file conversion script prefs2prefs.py.");
                return false;
        }
@@ -1266,5 +1311,40 @@ void fileUnlock(int fd, const char * /* lock_file*/)
 #endif
 }
 
-} //namespace support
+
+std::string toHexHash(const std::string & str)
+{
+       // Use the best available hashing algorithm. Qt 5 proposes SHA-2, but Qt 4 is limited to SHA-1.
+#if QT_VERSION >= 0x050000
+       auto hashAlgo = QCryptographicHash::Sha256;
+#else
+       auto hashAlgo = QCryptographicHash::Sha1;
+#endif
+
+       QByteArray hash = QCryptographicHash::hash(toqstr(str).toLocal8Bit(), hashAlgo);
+       return fromqstr(QString(hash.toHex()));
+}
+
+
+std::string sanitizeFileName(const std::string & str)
+{
+       // 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 std::string const keep = "abcdefghijklmnopqrstuvwxyz"
+                                  "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+                                  "+-0123456789;=";
+
+       std::string name = str;
+       string::size_type pos = 0;
+       while ((pos = name.find_first_not_of(keep, pos)) != string::npos)
+               name[pos++] = '_';
+
+       return name;
+}
+
+} // namespace support
 } // namespace lyx