#include <config.h>
-#include "LyXRC.h"
-
#include "support/filetools.h"
+#include "LyX.h"
+#include "LyXRC.h"
+
+#include "support/convert.h"
#include "support/debug.h"
#include "support/environment.h"
#include "support/gettext.h"
#include "support/PathChanger.h"
#include "support/Systemcall.h"
#include "support/qstring_helpers.h"
+#include "support/TempFile.h"
#include <QDir>
-#include <QImage>
-#include <QTemporaryFile>
#include "support/lassert.h"
-#include "support/regex.h"
#include <fcntl.h>
#ifdef HAVE_MAGIC_H
#endif
#include <cerrno>
+#include <climits>
#include <cstdlib>
#include <cstdio>
#include <utility>
#include <fstream>
+#include <regex>
#include <sstream>
#include <vector>
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));
// 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),
FileName const i18nLibFileSearch(string const & dir, string const & name,
string const & ext)
{
- /* The highest priority value is the `LANGUAGE' environment
- variable. But we don't use the value if the currently
- selected locale is the C locale. This is a GNU extension.
-
- Otherwise, w use a trick to guess what support/gettext.has done:
- each po file is able to tell us its name. (JMarc)
- */
-
+ // if the LANGUAGE variable is set, use it as a fallback for searching for files.
string lang = getGuiMessages().language();
string const language = getEnv("LANGUAGE");
- if (!lang.empty() && !language.empty())
- lang = language;
+ if (!language.empty())
+ lang += ":" + language;
- string l;
- lang = split(lang, l, ':');
- while (!l.empty()) {
+ for (auto const & l : getVectorFromString(lang, ":")) {
FileName tmp;
// First try with the full name
- tmp = libFileSearch(addPath(dir, l), name, ext);
+ // `en' files are not in a subdirectory
+ if (l == "en")
+ tmp = libFileSearch(dir, name, ext);
+ else
+ tmp = libFileSearch(addPath(dir, l), name, ext);
if (!tmp.empty())
return tmp;
if (!tmp.empty())
return tmp;
}
-
-#if 1
- // For compatibility, to be removed later (JMarc)
- tmp = libFileSearch(dir, token(l, '_', 0) + '_' + name,
- ext);
- if (!tmp.empty()) {
- lyxerr << "i18nLibFileSearch: File `" << tmp
- << "' has been found by the old method" <<endl;
- return tmp;
- }
-#endif
- lang = split(lang, l, ':');
}
return libFileSearch(dir, name, ext);
}
-int iconScaleFactor(FileName const & image)
-{
- int imgsize = QImage(toqstr(image.absFileName())).height();
- if (imgsize <= 0)
- return 100;
-
- // default icon size
- int iconsize = 20;
-
- string dir = "images";
- FileName const fn = imageLibFileSearch(dir, "iconsize.png");
- if (!fn.empty())
- iconsize = QImage(toqstr(fn.absFileName())).height();
-
- return (100 * iconsize + imgsize / 2)/imgsize;
-}
-
-
string const commandPrep(string const & command_in)
{
static string const token_scriptpath = "$$s/";
}
-static string createTempFile(QString const & mask)
+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)
+{
+ return tempFileName(TempFile(mask).name(), dir);
+}
+
+
+FileName const tempFileName(FileName tempfile, bool const dir)
{
- // 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 currentlky 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;
+ // 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;
}
- LYXERR(Debug::FILES, "Unable to create temporary file with following template: "
- << qt_tmp.fileTemplate());
- return string();
+
+ // OK, we need another name. Simply append digits.
+ FileName tmp = tempfile;
+ string ext;
+ if (!dir) {
+ // Store and remove extensions
+ ext = "." + tempfile.extension();
+ 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<string>(i) + ext;
+ 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();
+ tmp_names_.erase(abs);
+ fn.removeFile();
}
<< "createTmpDir: mask=`" << mask << '\'');
QFileInfo tmp_fi(QDir(toqstr(tempdir.absFileName())), toqstr(mask));
- FileName const tmpfl(createTempFile(tmp_fi.absoluteFilePath()));
+ FileName const tmpfl =
+ 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
if (path != "." && path != "./" && !path.empty()) {
buf = os::internal_path(path);
- if (!suffixIs(path, '/'))
+ if (!suffixIs(buf, '/'))
buf += '/';
}
}
+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)
{
}
-// 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.
+// 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]*)\\}";
// $[A-Za-z_][A-Za-z_0-9]*
static string const envvar = "[$]([A-Za-z_][A-Za-z_0-9]*)";
- static regex const envvar_br_re("(.*)" + envvar_br + "(.*)");
- static regex const envvar_re("(.*)" + envvar + "(.*)");
- string result = path;
- while (1) {
- smatch what;
- if (!regex_match(result, what, envvar_br_re)) {
- if (!regex_match(result, what, envvar_re))
- break;
+ // Coverity thinks that the regex constructor can return an
+ // exception. We know that it is not true since our regex are
+ // hardcoded, but we have to protect against that nevertheless.
+ try {
+ static regex const envvar_br_re("(.*)" + envvar_br + "(.*)");
+ static regex const envvar_re("(.*)" + envvar + "(.*)");
+ 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);
}
- string env_var = getEnv(what.str(2));
- result = what.str(1) + env_var + what.str(3);
+ return subst(result, '\a', '$');
+ } catch (exception const & e) {
+ LYXERR0("Something is very wrong: " << e.what());
+ return path;
}
- return result;
}
// 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
-#ifndef USE_QPROCESS
- return "cmd /d /c set \"TEXINPUTS=."
- + sep + texinputs_prefix
- + sep + texinputs + "\"&";
-#else
- return "cmd /d /c set \"\"\"TEXINPUTS=."
- + sep + texinputs_prefix
- + sep + texinputs + "\"\"\"&";
-#endif
+ // 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 + "\" & ";
}
unzippedFileName(zipped_file.toFilesystemEncoding()) :
unzipped_file);
// Run gunzip
- string const command = "gunzip -c " +
- zipped_file.toFilesystemEncoding() + " > " +
- tempfile.toFilesystemEncoding();
+ string const command = "gunzip -c \"" +
+ zipped_file.toFilesystemEncoding() + "\" > \"" +
+ tempfile.toFilesystemEncoding() + "\"";
Systemcall one;
one.startscript(Systemcall::Wait, command);
// test that command was executed successfully (anon)
{
string str = path;
+ // Recode URL encoded chars.
+ str = from_percent_encoding(str);
+
// If file is from LyXDir, display it as if it were relative.
string const system = package().system_support().absFileName();
if (prefixIs(str, system) && str != system)
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)));
// 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;
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);
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");
}
// (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;
+ BOOL success = GetExitCodeProcess(process.hProcess, &pret);
+ bool valid = (pret == 0) && success;
if (!infile.empty())
CloseHandle(startup.hStdInput);
CloseHandle(process.hProcess);
- int const pret = fclose(inf);
+ if (fclose(inf) != 0)
+ 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*/)
+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
// 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
// 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
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();
}
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;
}
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)