3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * parts Copyright 1985, 1990, 1993 Free Software Foundation, Inc.
8 * \author Ivan Schreter
9 * \author Dirk Niggemann
10 * \author Asger Alstrup
11 * \author Lars Gullik Bjønnes
12 * \author Jean-Marc Lasgouttes
13 * \author Angus Leeming
17 * Full author contact details are available in file CREDITS.
19 * General path-mangling functions
26 #include "support/filetools.h"
28 #include "support/debug.h"
29 #include "support/environment.h"
30 #include "support/gettext.h"
31 #include "support/lstrings.h"
32 #include "support/os.h"
33 #include "support/Messages.h"
34 #include "support/Package.h"
35 #include "support/PathChanger.h"
36 #include "support/Systemcall.h"
37 #include "support/qstring_helpers.h"
41 #include "support/lassert.h"
42 #include "support/regex.h"
67 bool isLyXFileName(string const & filename)
69 return suffixIs(ascii_lowercase(filename), ".lyx");
73 bool isSGMLFileName(string const & filename)
75 return suffixIs(ascii_lowercase(filename), ".sgml");
79 bool isValidLaTeXFileName(string const & filename)
81 string const invalid_chars("#%\"");
82 return filename.find_first_of(invalid_chars) == string::npos;
86 bool isValidDVIFileName(string const & filename)
88 string const invalid_chars("${}()[]^");
89 return filename.find_first_of(invalid_chars) == string::npos;
93 string const latex_path(string const & original_path,
94 latex_path_extension extension,
97 // On cygwin, we may need windows or posix style paths.
98 string path = os::latex_path(original_path);
99 path = subst(path, "~", "\\string~");
100 if (path.find(' ') != string::npos) {
101 // We can't use '"' because " is sometimes active (e.g. if
102 // babel is loaded with the "german" option)
103 if (extension == EXCLUDE_EXTENSION) {
104 // changeExtension calls os::internal_path internally
105 // so don't use it to remove the extension.
106 string const ext = getExtension(path);
107 string const base = ext.empty() ?
109 path.substr(0, path.length() - ext.length() - 1);
110 // changeExtension calls os::internal_path internally
111 // so don't use it to re-add the extension.
112 path = "\\string\"" + base + "\\string\"." + ext;
114 path = "\\string\"" + path + "\\string\"";
118 if (dots != ESCAPE_DOTS)
121 // Replace dots with the lyxdot macro, but only in the file name,
122 // not the directory part.
123 // addName etc call os::internal_path internally
124 // so don't use them for path manipulation
125 // The directory separator is always '/' for LaTeX.
126 string::size_type pos = path.rfind('/');
127 if (pos == string::npos)
128 return subst(path, ".", "\\lyxdot ");
129 return path.substr(0, pos) + subst(path.substr(pos), ".", "\\lyxdot ");
133 // Substitutes spaces with underscores in filename (and path)
134 FileName const makeLatexName(FileName const & file)
136 string name = file.onlyFileName();
137 string const path = file.onlyPath().absFileName() + "/";
139 // ok so we scan through the string twice, but who cares.
140 // FIXME: in Unicode time this will break for sure! There is
141 // a non-latin world out there...
142 string const keep = "abcdefghijklmnopqrstuvwxyz"
143 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
144 "@!'()*+,-./0123456789:;<=>?[]`|";
146 string::size_type pos = 0;
147 while ((pos = name.find_first_not_of(keep, pos)) != string::npos)
150 FileName latex_name(path + name);
151 latex_name.changeExtension(".tex");
156 string const quoteName(string const & name, quote_style style)
160 // This does not work on native Windows for filenames
161 // containing the following characters < > : " / \ | ? *
162 // Moreover, it can't be made to work, as, according to
163 // http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx
164 // those are reserved characters, and thus are forbidden.
165 // Please, also note that the command-line parser in
166 // ForkedCall::generateChild cannot deal with filenames
167 // containing " or ', therefore we don't pass user filenames
168 // to child processes if possible. We store them in a python
169 // script instead, where we don't have these limitations.
171 return (os::shell() == os::UNIX) ?
172 '\'' + subst(name, "'", "\'\\\'\'") + '\'' :
175 // According to the QProcess parser, a single double
176 // quote is represented by three consecutive ones.
177 // Here we simply escape the double quote and let our
178 // simple parser in Systemcall.cpp do the substitution.
179 return '"' + subst(name, "\"", "\\\"") + '"';
182 return "\"" + subst(subst(name, "\\", "\\\\"), "\"", "\\\"")
185 // shut up stupid compiler
191 // Uses a string of paths separated by ";"s to find a file to open.
192 // Can't cope with pathnames with a ';' in them. Returns full path to file.
193 // If path entry begins with $$LyX/, use system_lyxdir
194 // If path entry begins with $$User/, use user_lyxdir
195 // Example: "$$User/doc;$$LyX/doc"
196 FileName const fileOpenSearch(string const & path, string const & name,
201 bool notfound = true;
202 string tmppath = split(path, path_element, ';');
204 while (notfound && !path_element.empty()) {
205 path_element = os::internal_path(path_element);
206 if (!suffixIs(path_element, '/'))
208 path_element = subst(path_element, "$$LyX",
209 package().system_support().absFileName());
210 path_element = subst(path_element, "$$User",
211 package().user_support().absFileName());
213 real_file = fileSearch(path_element, name, ext);
215 if (real_file.empty()) {
217 tmppath = split(tmppath, path_element, ';');
218 } while (!tmppath.empty() && path_element.empty());
228 // Returns the real name of file name in directory path, with optional
230 FileName const fileSearch(string const & path, string const & name,
231 string const & ext, search_mode mode)
233 // if `name' is an absolute path, we ignore the setting of `path'
234 // Expand Environmentvariables in 'name'
235 string const tmpname = replaceEnvironmentPath(name);
236 FileName fullname(makeAbsPath(tmpname, path));
237 // search first without extension, then with it.
238 if (fullname.isReadableFile())
242 return mode == may_not_exist ? fullname : FileName();
243 // Only add the extension if it is not already the extension of
245 if (getExtension(fullname.absFileName()) != ext)
246 fullname = FileName(addExtension(fullname.absFileName(), ext));
247 if (fullname.isReadableFile() || mode == may_not_exist)
253 // Search the file name.ext in the subdirectory dir of
255 // 2) build_lyxdir (if not empty)
257 FileName const libFileSearch(string const & dir, string const & name,
260 FileName fullname = fileSearch(addPath(package().user_support().absFileName(), dir),
262 if (!fullname.empty())
265 if (!package().build_support().empty())
266 fullname = fileSearch(addPath(package().build_support().absFileName(), dir),
268 if (!fullname.empty())
271 return fileSearch(addPath(package().system_support().absFileName(), dir), name, ext);
275 FileName const i18nLibFileSearch(string const & dir, string const & name,
278 /* The highest priority value is the `LANGUAGE' environment
279 variable. But we don't use the value if the currently
280 selected locale is the C locale. This is a GNU extension.
282 Otherwise, w use a trick to guess what support/gettext.has done:
283 each po file is able to tell us its name. (JMarc)
286 string lang = getGuiMessages().language();
287 string const language = getEnv("LANGUAGE");
288 if (!lang.empty() && !language.empty())
292 lang = split(lang, l, ':');
295 // First try with the full name
296 tmp = libFileSearch(addPath(dir, l), name, ext);
300 // Then the name without country code
301 string const shortl = token(l, '_', 0);
303 tmp = libFileSearch(addPath(dir, shortl), name, ext);
309 // For compatibility, to be removed later (JMarc)
310 tmp = libFileSearch(dir, token(l, '_', 0) + '_' + name,
313 lyxerr << "i18nLibFileSearch: File `" << tmp
314 << "' has been found by the old method" <<endl;
318 lang = split(lang, l, ':');
321 return libFileSearch(dir, name, ext);
325 FileName const imageLibFileSearch(string & dir, string const & name,
328 if (!lyx::lyxrc.icon_set.empty()) {
329 string const imagedir = addPath(dir, lyx::lyxrc.icon_set);
330 FileName const fn = libFileSearch(imagedir, name, ext);
336 return libFileSearch(dir, name, ext);
340 string const libScriptSearch(string const & command_in)
342 static string const token_scriptpath = "$$s/";
344 string command = command_in;
345 // Find the starting position of "$$s/"
346 string::size_type const pos1 = command.find(token_scriptpath);
347 if (pos1 == string::npos)
349 // Find the end of the "$$s/some_subdir/some_script" word within
350 // command. Assumes that the script name does not contain spaces.
351 string::size_type const start_script = pos1 + 4;
352 string::size_type const pos2 = command.find(' ', start_script);
353 string::size_type const size_script = pos2 == string::npos?
354 (command.size() - start_script) : pos2 - start_script;
356 // Does this script file exist?
357 string const script =
358 libFileSearch(".", command.substr(start_script, size_script)).absFileName();
360 if (script.empty()) {
361 // Replace "$$s/" with ""
362 command.erase(pos1, 4);
364 quote_style style = quote_shell;
365 string const python_call = "python -tt";
366 if (prefixIs(command, python_call) || prefixIs(command, os::python()))
367 style = quote_python;
369 // Replace "$$s/foo/some_script" with "<path to>/some_script".
370 string::size_type const size_replace = size_script + 4;
371 command.replace(pos1, size_replace, quoteName(script, style));
378 static FileName createTmpDir(FileName const & tempdir, string const & mask)
380 LYXERR(Debug::FILES, "createTmpDir: tempdir=`" << tempdir << "'\n"
381 << "createTmpDir: mask=`" << mask << '\'');
383 FileName const tmpfl = FileName::tempName(tempdir, mask);
385 if (tmpfl.empty() || !tmpfl.createDirectory(0700)) {
386 LYXERR0("LyX could not create temporary directory in " << tempdir
395 FileName const createLyXTmpDir(FileName const & deflt)
397 if (deflt.empty() || deflt == package().system_temp_dir())
398 return createTmpDir(package().system_temp_dir(), "lyx_tmpdir");
400 if (deflt.createDirectory(0777))
403 if (deflt.isDirWritable()) {
404 // deflt could not be created because it
405 // did exist already, so let's create our own
407 return createTmpDir(deflt, "lyx_tmpdir");
409 // some other error occured.
410 return createTmpDir(package().system_temp_dir(), "lyx_tmpdir");
415 // Strip filename from path name
416 string const onlyPath(string const & filename)
418 // If empty filename, return empty
419 if (filename.empty())
422 // Find last / or start of filename
423 size_t j = filename.rfind('/');
424 return j == string::npos ? "./" : filename.substr(0, j + 1);
428 // Convert relative path into absolute path based on a basepath.
429 // If relpath is absolute, just use that.
430 // If basepath is empty, use CWD as base.
431 // Note that basePath can be a relative path, in the sense that it may
432 // not begin with "/" (e.g.), but it should NOT contain such constructs
434 // FIXME It might be nice if the code didn't simply assume that.
435 FileName const makeAbsPath(string const & relPath, string const & basePath)
437 // checks for already absolute path
438 if (FileName::isAbsolute(relPath))
439 return FileName(relPath);
441 // Copies given paths
442 string tempRel = os::internal_path(relPath);
443 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
444 tempRel = subst(tempRel, "//", "/");
448 if (FileName::isAbsolute(basePath))
451 tempBase = addPath(FileName::getcwd().absFileName(), basePath);
453 // Handle /./ at the end of the path
454 while (suffixIs(tempBase, "/./"))
455 tempBase.erase(tempBase.length() - 2);
457 // processes relative path
458 string rTemp = tempRel;
461 // Check for a leading "~"
463 rTemp = split(rTemp, temp, '/');
465 tempBase = Package::get_home_dir().absFileName();
470 while (!rTemp.empty()) {
472 rTemp = split(rTemp, temp, '/');
474 if (temp == ".") continue;
476 // Remove one level of TempBase
477 if (tempBase.length() <= 1) {
478 //this is supposed to be an absolute path, so...
482 //erase a trailing slash if there is one
483 if (suffixIs(tempBase, "/"))
484 tempBase.erase(tempBase.length() - 1, string::npos);
486 string::size_type i = tempBase.length() - 1;
487 while (i > 0 && tempBase[i] != '/')
490 tempBase.erase(i, string::npos);
493 } else if (temp.empty() && !rTemp.empty()) {
494 tempBase = os::current_root() + rTemp;
497 // Add this piece to TempBase
498 if (!suffixIs(tempBase, '/'))
504 // returns absolute path
505 return FileName(tempBase);
509 // Correctly append filename to the pathname.
510 // If pathname is '.', then don't use pathname.
511 // Chops any path of filename.
512 string const addName(string const & path, string const & fname)
514 string const basename = onlyFileName(fname);
517 if (path != "." && path != "./" && !path.empty()) {
518 buf = os::internal_path(path);
519 if (!suffixIs(path, '/'))
523 return buf + basename;
527 // Strips path from filename
528 string const onlyFileName(string const & fname)
533 string::size_type j = fname.rfind('/');
534 if (j == string::npos) // no '/' in fname
538 return fname.substr(j + 1);
542 // Create absolute path. If impossible, don't do anything
543 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
544 string const expandPath(string const & path)
546 // checks for already absolute path
547 string rTemp = replaceEnvironmentPath(path);
548 if (FileName::isAbsolute(rTemp))
552 string const copy = rTemp;
555 rTemp = split(rTemp, temp, '/');
558 return FileName::getcwd().absFileName() + '/' + rTemp;
561 return Package::get_home_dir().absFileName() + '/' + rTemp;
564 return makeAbsPath(copy).absFileName();
566 // Don't know how to handle this
571 // Search the string for ${VAR} and $VAR and replace VAR using getenv.
572 string const replaceEnvironmentPath(string const & path)
574 // ${VAR} is defined as
575 // $\{[A-Za-z_][A-Za-z_0-9]*\}
576 static string const envvar_br = "[$]\\{([A-Za-z_][A-Za-z_0-9]*)\\}";
578 // $VAR is defined as:
579 // $[A-Za-z_][A-Za-z_0-9]*
580 static string const envvar = "[$]([A-Za-z_][A-Za-z_0-9]*)";
582 static regex const envvar_br_re("(.*)" + envvar_br + "(.*)");
583 static regex const envvar_re("(.*)" + envvar + "(.*)");
584 string result = path;
587 if (!regex_match(result, what, envvar_br_re)) {
588 if (!regex_match(result, what, envvar_re))
591 string env_var = getEnv(what.str(2));
592 result = what.str(1) + env_var + what.str(3);
598 // Return a command prefix for setting the environment of the TeX engine.
599 string latexEnvCmdPrefix(string const & path)
601 if (path.empty() || lyxrc.texinputs_prefix.empty())
604 string const texinputs_prefix = os::latex_path_list(
605 replaceCurdirPath(path, lyxrc.texinputs_prefix));
606 string const sep = string(1, os::path_separator(os::TEXENGINE));
607 string const texinputs = getEnv("TEXINPUTS");
609 if (os::shell() == os::UNIX)
610 return "env TEXINPUTS=\"." + sep + texinputs_prefix
611 + sep + texinputs + "\" ";
613 return "cmd /d /c set TEXINPUTS=." + sep + texinputs_prefix
614 + sep + texinputs + "&";
618 // Replace current directory in all elements of a path list with a given path.
619 string const replaceCurdirPath(string const & path, string const & pathlist)
621 string const oldpathlist = replaceEnvironmentPath(pathlist);
622 char const sep = os::path_separator();
625 for (size_t i = 0, k = 0; i != string::npos; k = i) {
626 i = oldpathlist.find(sep, i);
627 string p = oldpathlist.substr(k, i - k);
628 if (FileName::isAbsolute(p)) {
634 } else if (prefixIs(p, "./")) {
636 while (p[offset] == '/')
639 newpathlist += addPath(path, p.substr(offset));
640 if (suffixIs(p, "//"))
643 if (i != string::npos) {
645 // Stop here if the last element is empty
646 if (++i == oldpathlist.length())
654 // Make relative path out of two absolute paths
655 docstring const makeRelPath(docstring const & abspath, docstring const & basepath)
656 // Makes relative path out of absolute path. If it is deeper than basepath,
657 // it's easy. If basepath and abspath share something (they are all deeper
658 // than some directory), it'll be rendered using ..'s. If they are completely
659 // different, then the absolute path will be used as relative path.
661 docstring::size_type const abslen = abspath.length();
662 docstring::size_type const baselen = basepath.length();
664 docstring::size_type i = os::common_path(abspath, basepath);
667 // actually no match - cannot make it relative
671 // Count how many dirs there are in basepath above match
672 // and append as many '..''s into relpath
674 docstring::size_type j = i;
675 while (j < baselen) {
676 if (basepath[j] == '/') {
677 if (j + 1 == baselen)
684 // Append relative stuff from common directory to abspath
685 if (abspath[i] == '/')
687 for (; i < abslen; ++i)
690 if (suffixIs(buf, '/'))
691 buf.erase(buf.length() - 1);
692 // Substitute empty with .
699 // Append sub-directory(ies) to a path in an intelligent way
700 string const addPath(string const & path, string const & path_2)
703 string const path2 = os::internal_path(path_2);
705 if (!path.empty() && path != "." && path != "./") {
706 buf = os::internal_path(path);
707 if (path[path.length() - 1] != '/')
711 if (!path2.empty()) {
712 string::size_type const p2start = path2.find_first_not_of('/');
713 string::size_type const p2end = path2.find_last_not_of('/');
714 string const tmp = path2.substr(p2start, p2end - p2start + 1);
721 string const changeExtension(string const & oldname, string const & extension)
723 string::size_type const last_slash = oldname.rfind('/');
724 string::size_type last_dot = oldname.rfind('.');
725 if (last_dot < last_slash && last_slash != string::npos)
726 last_dot = string::npos;
729 // Make sure the extension starts with a dot
730 if (!extension.empty() && extension[0] != '.')
731 ext= '.' + extension;
735 return os::internal_path(oldname.substr(0, last_dot) + ext);
739 string const removeExtension(string const & name)
741 return changeExtension(name, string());
745 string const addExtension(string const & name, string const & extension)
747 if (!extension.empty() && extension[0] != '.')
748 return name + '.' + extension;
749 return name + extension;
753 /// Return the extension of the file (not including the .)
754 string const getExtension(string const & name)
756 string::size_type const last_slash = name.rfind('/');
757 string::size_type const last_dot = name.rfind('.');
758 if (last_dot != string::npos &&
759 (last_slash == string::npos || last_dot > last_slash))
760 return name.substr(last_dot + 1,
761 name.length() - (last_dot + 1));
767 string const unzippedFileName(string const & zipped_file)
769 string const ext = getExtension(zipped_file);
770 if (ext == "gz" || ext == "z" || ext == "Z")
771 return changeExtension(zipped_file, string());
772 return onlyPath(zipped_file) + "unzipped_" + onlyFileName(zipped_file);
776 FileName const unzipFile(FileName const & zipped_file, string const & unzipped_file)
778 FileName const tempfile = FileName(unzipped_file.empty() ?
779 unzippedFileName(zipped_file.toFilesystemEncoding()) :
782 string const command = "gunzip -c " +
783 zipped_file.toFilesystemEncoding() + " > " +
784 tempfile.toFilesystemEncoding();
786 one.startscript(Systemcall::Wait, command);
787 // test that command was executed successfully (anon)
788 // yes, please do. (Lgb)
793 docstring const makeDisplayPath(string const & path, unsigned int threshold)
797 // If file is from LyXDir, display it as if it were relative.
798 string const system = package().system_support().absFileName();
799 if (prefixIs(str, system) && str != system)
800 return from_utf8("[" + str.erase(0, system.length()) + "]");
802 // replace /home/blah with ~/
803 string const home = Package::get_home_dir().absFileName();
804 if (!home.empty() && prefixIs(str, home))
805 str = subst(str, home, "~");
807 if (str.length() <= threshold)
808 return from_utf8(os::external_path(str));
810 string const prefix = ".../";
811 docstring dstr = from_utf8(str);
814 while (dstr.length() > threshold)
815 dstr = split(dstr, temp, '/');
817 // Did we shorten everything away?
819 // Yes, filename itself is too long.
820 // Pick the start and the end of the filename.
821 dstr = from_utf8(onlyFileName(path));
822 docstring const head = dstr.substr(0, threshold / 2 - 3);
824 docstring::size_type len = dstr.length();
825 docstring const tail =
826 dstr.substr(len - threshold / 2 - 2, len - 1);
827 dstr = head + from_ascii("...") + tail;
830 return from_utf8(os::external_path(prefix + to_utf8(dstr)));
835 bool readLink(FileName const & file, FileName & link)
837 string const encoded = file.toFilesystemEncoding();
838 #ifdef HAVE_DEF_PATH_MAX
839 char linkbuffer[PATH_MAX + 1];
840 int const nRead = ::readlink(encoded.c_str(),
841 linkbuffer, sizeof(linkbuffer) - 1);
844 linkbuffer[nRead] = '\0'; // terminator
846 vector<char> buf(1024);
850 nRead = ::readlink(encoded.c_str(), &buf[0], buf.size() - 1);
854 if (nRead < buf.size() - 1) {
857 buf.resize(buf.size() * 2);
859 buf[nRead] = '\0'; // terminator
860 const char * linkbuffer = &buf[0];
862 link = makeAbsPath(linkbuffer, onlyPath(file.absFileName()));
866 bool readLink(FileName const &, FileName &)
873 cmd_ret const runCommand(string const & cmd)
875 // FIXME: replace all calls to RunCommand with ForkedCall
876 // (if the output is not needed) or the code in ISpell.cpp
877 // (if the output is needed).
879 // One question is if we should use popen or
880 // create our own popen based on fork, exec, pipe
881 // of course the best would be to have a
882 // pstream (process stream), with the
883 // variants ipstream, opstream
888 PROCESS_INFORMATION process;
889 SECURITY_ATTRIBUTES security;
892 bool err2out = false;
894 string const infile = trim(split(cmd, command, '<'), " \"");
895 command = rtrim(command);
896 if (suffixIs(command, "2>&1")) {
897 command = rtrim(command, "2>&1");
900 string const cmdarg = "/d /c " + command;
901 string const comspec = getEnv("COMSPEC");
903 security.nLength = sizeof(SECURITY_ATTRIBUTES);
904 security.bInheritHandle = TRUE;
905 security.lpSecurityDescriptor = NULL;
907 if (CreatePipe(&in, &out, &security, 0)) {
908 memset(&startup, 0, sizeof(STARTUPINFO));
909 memset(&process, 0, sizeof(PROCESS_INFORMATION));
911 startup.cb = sizeof(STARTUPINFO);
912 startup.dwFlags = STARTF_USESTDHANDLES;
914 startup.hStdError = err2out ? out : GetStdHandle(STD_ERROR_HANDLE);
915 startup.hStdInput = infile.empty()
916 ? GetStdHandle(STD_INPUT_HANDLE)
917 : CreateFile(infile.c_str(), GENERIC_READ,
918 FILE_SHARE_READ, &security, OPEN_EXISTING,
919 FILE_ATTRIBUTE_NORMAL, NULL);
920 startup.hStdOutput = out;
922 if (startup.hStdInput != INVALID_HANDLE_VALUE &&
923 CreateProcess(comspec.c_str(), (LPTSTR)cmdarg.c_str(),
924 &security, &security, TRUE, CREATE_NO_WINDOW,
925 0, 0, &startup, &process)) {
927 CloseHandle(process.hThread);
928 fno = _open_osfhandle((long)in, _O_RDONLY);
930 inf = _fdopen(fno, "r");
933 #elif defined (HAVE_POPEN)
934 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
935 #elif defined (HAVE__POPEN)
936 FILE * inf = ::_popen(cmd.c_str(), os::popen_read_mode());
938 #error No popen() function.
941 // (Claus Hentschel) Check if popen was successful ;-)
943 lyxerr << "RunCommand:: could not start child process" << endl;
944 return make_pair(-1, string());
950 ret += static_cast<char>(c);
955 WaitForSingleObject(process.hProcess, INFINITE);
957 CloseHandle(startup.hStdInput);
958 CloseHandle(process.hProcess);
959 int const pret = fclose(inf);
960 #elif defined (HAVE_PCLOSE)
961 int const pret = pclose(inf);
962 #elif defined (HAVE__PCLOSE)
963 int const pret = _pclose(inf);
965 #error No pclose() function.
969 perror("RunCommand:: could not terminate child process");
971 return make_pair(pret, ret);
975 FileName const findtexfile(string const & fil, string const & /*format*/)
977 /* There is no problem to extend this function too use other
978 methods to look for files. It could be setup to look
979 in environment paths and also if wanted as a last resort
980 to a recursive find. One of the easier extensions would
981 perhaps be to use the LyX file lookup methods. But! I am
982 going to implement this until I see some demand for it.
986 // If the file can be found directly, we just return a
987 // absolute path version of it.
988 FileName const absfile(makeAbsPath(fil));
989 if (absfile.exists())
992 // Now we try to find it using kpsewhich.
993 // It seems from the kpsewhich manual page that it is safe to use
994 // kpsewhich without --format: "When the --format option is not
995 // given, the search path used when looking for a file is inferred
996 // from the name given, by looking for a known extension. If no
997 // known extension is found, the search path for TeX source files
999 // However, we want to take advantage of the format sine almost all
1000 // the different formats has environment variables that can be used
1001 // to controll which paths to search. f.ex. bib looks in
1002 // BIBINPUTS and TEXBIB. Small list follows:
1003 // bib - BIBINPUTS, TEXBIB
1005 // graphic/figure - TEXPICTS, TEXINPUTS
1006 // ist - TEXINDEXSTYLE, INDEXSTYLE
1007 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1009 // tfm - TFMFONTS, TEXFONTS
1010 // This means that to use kpsewhich in the best possible way we
1011 // should help it by setting additional path in the approp. envir.var.
1012 string const kpsecmd = "kpsewhich " + fil;
1014 cmd_ret const c = runCommand(kpsecmd);
1016 LYXERR(Debug::LATEX, "kpse status = " << c.first << '\n'
1017 << "kpse result = `" << rtrim(c.second, "\n\r") << '\'');
1019 return FileName(rtrim(to_utf8(from_filesystem8bit(c.second)), "\n\r"));
1025 int compare_timestamps(FileName const & file1, FileName const & file2)
1027 // If the original is newer than the copy, then copy the original
1028 // to the new directory.
1031 if (file1.exists() && file2.exists()) {
1032 double const tmp = difftime(file1.lastModified(), file2.lastModified());
1034 cmp = tmp > 0 ? 1 : -1;
1036 } else if (file1.exists()) {
1038 } else if (file2.exists()) {
1046 bool prefs2prefs(FileName const & filename, FileName const & tempfile, bool lfuns)
1048 FileName const script = libFileSearch("scripts", "prefs2prefs.py");
1049 if (script.empty()) {
1050 LYXERR0("Could not find bind file conversion "
1051 "script prefs2prefs.py.");
1055 ostringstream command;
1056 command << os::python() << ' ' << quoteName(script.toFilesystemEncoding())
1057 << ' ' << (lfuns ? "-l" : "-p") << ' '
1058 << quoteName(filename.toFilesystemEncoding())
1059 << ' ' << quoteName(tempfile.toFilesystemEncoding());
1060 string const command_str = command.str();
1062 LYXERR(Debug::FILES, "Running `" << command_str << '\'');
1064 cmd_ret const ret = runCommand(command_str);
1065 if (ret.first != 0) {
1066 LYXERR0("Could not run file conversion script prefs2prefs.py.");
1072 int fileLock(const char * lock_file)
1075 #if defined(HAVE_LOCKF)
1076 fd = open(lock_file, O_CREAT|O_APPEND|O_SYNC|O_RDWR, 0666);
1077 if (lockf(fd, F_LOCK, 0) != 0) {
1085 void fileUnlock(int fd, const char * /* lock_file*/)
1087 #if defined(HAVE_LOCKF)
1089 if (lockf(fd, F_ULOCK, 0))
1090 LYXERR0("Can't unlock the file.");
1096 } //namespace support