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, quote_style style)
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 // Replace "$$s/foo/some_script" with "<path to>/some_script".
365 string::size_type const size_replace = size_script + 4;
366 command.replace(pos1, size_replace, quoteName(script, style));
373 static FileName createTmpDir(FileName const & tempdir, string const & mask)
375 LYXERR(Debug::FILES, "createTmpDir: tempdir=`" << tempdir << "'\n"
376 << "createTmpDir: mask=`" << mask << '\'');
378 FileName const tmpfl = FileName::tempName(tempdir, mask);
380 if (tmpfl.empty() || !tmpfl.createDirectory(0700)) {
381 LYXERR0("LyX could not create temporary directory in " << tempdir
390 FileName const createLyXTmpDir(FileName const & deflt)
392 if (deflt.empty() || deflt == package().system_temp_dir())
393 return createTmpDir(package().system_temp_dir(), "lyx_tmpdir");
395 if (deflt.createDirectory(0777))
398 if (deflt.isDirWritable()) {
399 // deflt could not be created because it
400 // did exist already, so let's create our own
402 return createTmpDir(deflt, "lyx_tmpdir");
404 // some other error occured.
405 return createTmpDir(package().system_temp_dir(), "lyx_tmpdir");
410 // Strip filename from path name
411 string const onlyPath(string const & filename)
413 // If empty filename, return empty
414 if (filename.empty())
417 // Find last / or start of filename
418 size_t j = filename.rfind('/');
419 return j == string::npos ? "./" : filename.substr(0, j + 1);
423 // Convert relative path into absolute path based on a basepath.
424 // If relpath is absolute, just use that.
425 // If basepath is empty, use CWD as base.
426 // Note that basePath can be a relative path, in the sense that it may
427 // not begin with "/" (e.g.), but it should NOT contain such constructs
429 // FIXME It might be nice if the code didn't simply assume that.
430 FileName const makeAbsPath(string const & relPath, string const & basePath)
432 // checks for already absolute path
433 if (FileName::isAbsolute(relPath))
434 return FileName(relPath);
436 // Copies given paths
437 string tempRel = os::internal_path(relPath);
438 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
439 tempRel = subst(tempRel, "//", "/");
443 if (FileName::isAbsolute(basePath))
446 tempBase = addPath(FileName::getcwd().absFileName(), basePath);
448 // Handle /./ at the end of the path
449 while (suffixIs(tempBase, "/./"))
450 tempBase.erase(tempBase.length() - 2);
452 // processes relative path
453 string rTemp = tempRel;
456 // Check for a leading "~"
458 rTemp = split(rTemp, temp, '/');
460 tempBase = Package::get_home_dir().absFileName();
465 while (!rTemp.empty()) {
467 rTemp = split(rTemp, temp, '/');
469 if (temp == ".") continue;
471 // Remove one level of TempBase
472 if (tempBase.length() <= 1) {
473 //this is supposed to be an absolute path, so...
477 //erase a trailing slash if there is one
478 if (suffixIs(tempBase, "/"))
479 tempBase.erase(tempBase.length() - 1, string::npos);
481 string::size_type i = tempBase.length() - 1;
482 while (i > 0 && tempBase[i] != '/')
485 tempBase.erase(i, string::npos);
488 } else if (temp.empty() && !rTemp.empty()) {
489 tempBase = os::current_root() + rTemp;
492 // Add this piece to TempBase
493 if (!suffixIs(tempBase, '/'))
499 // returns absolute path
500 return FileName(tempBase);
504 // Correctly append filename to the pathname.
505 // If pathname is '.', then don't use pathname.
506 // Chops any path of filename.
507 string const addName(string const & path, string const & fname)
509 string const basename = onlyFileName(fname);
512 if (path != "." && path != "./" && !path.empty()) {
513 buf = os::internal_path(path);
514 if (!suffixIs(path, '/'))
518 return buf + basename;
522 // Strips path from filename
523 string const onlyFileName(string const & fname)
528 string::size_type j = fname.rfind('/');
529 if (j == string::npos) // no '/' in fname
533 return fname.substr(j + 1);
537 // Create absolute path. If impossible, don't do anything
538 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
539 string const expandPath(string const & path)
541 // checks for already absolute path
542 string rTemp = replaceEnvironmentPath(path);
543 if (FileName::isAbsolute(rTemp))
547 string const copy = rTemp;
550 rTemp = split(rTemp, temp, '/');
553 return FileName::getcwd().absFileName() + '/' + rTemp;
556 return Package::get_home_dir().absFileName() + '/' + rTemp;
559 return makeAbsPath(copy).absFileName();
561 // Don't know how to handle this
566 // Search the string for ${VAR} and $VAR and replace VAR using getenv.
567 string const replaceEnvironmentPath(string const & path)
569 // ${VAR} is defined as
570 // $\{[A-Za-z_][A-Za-z_0-9]*\}
571 static string const envvar_br = "[$]\\{([A-Za-z_][A-Za-z_0-9]*)\\}";
573 // $VAR is defined as:
574 // $[A-Za-z_][A-Za-z_0-9]*
575 static string const envvar = "[$]([A-Za-z_][A-Za-z_0-9]*)";
577 static regex envvar_br_re("(.*)" + envvar_br + "(.*)");
578 static regex envvar_re("(.*)" + envvar + "(.*)");
579 string result = path;
582 if (!regex_match(result, what, envvar_br_re)) {
583 if (!regex_match(result, what, envvar_re))
586 string env_var = getEnv(what.str(2));
587 result = what.str(1) + env_var + what.str(3);
593 // Return a command prefix for setting the environment of the TeX engine.
594 string latexEnvCmdPrefix(string const & path)
596 if (path.empty() || lyxrc.texinputs_prefix.empty())
599 string const texinputs_prefix = os::latex_path_list(
600 replaceCurdirPath(path, lyxrc.texinputs_prefix));
601 string const sep = string(1, os::path_separator(os::TEXENGINE));
602 string const texinputs = getEnv("TEXINPUTS");
604 if (os::shell() == os::UNIX)
605 return "env TEXINPUTS=\"." + sep + texinputs_prefix
606 + sep + texinputs + "\" ";
608 return "cmd /d /c set TEXINPUTS=." + sep + texinputs_prefix
609 + sep + texinputs + "&";
613 // Replace current directory in all elements of a path list with a given path.
614 string const replaceCurdirPath(string const & path, string const & pathlist)
616 string const oldpathlist = replaceEnvironmentPath(pathlist);
617 char const sep = os::path_separator();
620 for (size_t i = 0, k = 0; i != string::npos; k = i) {
621 i = oldpathlist.find(sep, i);
622 string p = oldpathlist.substr(k, i - k);
623 if (FileName::isAbsolute(p)) {
629 } else if (prefixIs(p, "./")) {
631 while (p[offset] == '/')
634 newpathlist += addPath(path, p.substr(offset));
635 if (suffixIs(p, "//"))
638 if (i != string::npos) {
640 // Stop here if the last element is empty
641 if (++i == oldpathlist.length())
649 // Make relative path out of two absolute paths
650 docstring const makeRelPath(docstring const & abspath, docstring const & basepath)
651 // Makes relative path out of absolute path. If it is deeper than basepath,
652 // it's easy. If basepath and abspath share something (they are all deeper
653 // than some directory), it'll be rendered using ..'s. If they are completely
654 // different, then the absolute path will be used as relative path.
656 docstring::size_type const abslen = abspath.length();
657 docstring::size_type const baselen = basepath.length();
659 docstring::size_type i = os::common_path(abspath, basepath);
662 // actually no match - cannot make it relative
666 // Count how many dirs there are in basepath above match
667 // and append as many '..''s into relpath
669 docstring::size_type j = i;
670 while (j < baselen) {
671 if (basepath[j] == '/') {
672 if (j + 1 == baselen)
679 // Append relative stuff from common directory to abspath
680 if (abspath[i] == '/')
682 for (; i < abslen; ++i)
685 if (suffixIs(buf, '/'))
686 buf.erase(buf.length() - 1);
687 // Substitute empty with .
694 // Append sub-directory(ies) to a path in an intelligent way
695 string const addPath(string const & path, string const & path_2)
698 string const path2 = os::internal_path(path_2);
700 if (!path.empty() && path != "." && path != "./") {
701 buf = os::internal_path(path);
702 if (path[path.length() - 1] != '/')
706 if (!path2.empty()) {
707 string::size_type const p2start = path2.find_first_not_of('/');
708 string::size_type const p2end = path2.find_last_not_of('/');
709 string const tmp = path2.substr(p2start, p2end - p2start + 1);
716 string const changeExtension(string const & oldname, string const & extension)
718 string::size_type const last_slash = oldname.rfind('/');
719 string::size_type last_dot = oldname.rfind('.');
720 if (last_dot < last_slash && last_slash != string::npos)
721 last_dot = string::npos;
724 // Make sure the extension starts with a dot
725 if (!extension.empty() && extension[0] != '.')
726 ext= '.' + extension;
730 return os::internal_path(oldname.substr(0, last_dot) + ext);
734 string const removeExtension(string const & name)
736 return changeExtension(name, string());
740 string const addExtension(string const & name, string const & extension)
742 if (!extension.empty() && extension[0] != '.')
743 return name + '.' + extension;
744 return name + extension;
748 /// Return the extension of the file (not including the .)
749 string const getExtension(string const & name)
751 string::size_type const last_slash = name.rfind('/');
752 string::size_type const last_dot = name.rfind('.');
753 if (last_dot != string::npos &&
754 (last_slash == string::npos || last_dot > last_slash))
755 return name.substr(last_dot + 1,
756 name.length() - (last_dot + 1));
762 string const unzippedFileName(string const & zipped_file)
764 string const ext = getExtension(zipped_file);
765 if (ext == "gz" || ext == "z" || ext == "Z")
766 return changeExtension(zipped_file, string());
767 return onlyPath(zipped_file) + "unzipped_" + onlyFileName(zipped_file);
771 FileName const unzipFile(FileName const & zipped_file, string const & unzipped_file)
773 FileName const tempfile = FileName(unzipped_file.empty() ?
774 unzippedFileName(zipped_file.toFilesystemEncoding()) :
777 string const command = "gunzip -c " +
778 zipped_file.toFilesystemEncoding() + " > " +
779 tempfile.toFilesystemEncoding();
781 one.startscript(Systemcall::Wait, command);
782 // test that command was executed successfully (anon)
783 // yes, please do. (Lgb)
788 docstring const makeDisplayPath(string const & path, unsigned int threshold)
792 // If file is from LyXDir, display it as if it were relative.
793 string const system = package().system_support().absFileName();
794 if (prefixIs(str, system) && str != system)
795 return from_utf8("[" + str.erase(0, system.length()) + "]");
797 // replace /home/blah with ~/
798 string const home = Package::get_home_dir().absFileName();
799 if (!home.empty() && prefixIs(str, home))
800 str = subst(str, home, "~");
802 if (str.length() <= threshold)
803 return from_utf8(os::external_path(str));
805 string const prefix = ".../";
806 docstring dstr = from_utf8(str);
809 while (dstr.length() > threshold)
810 dstr = split(dstr, temp, '/');
812 // Did we shorten everything away?
814 // Yes, filename itself is too long.
815 // Pick the start and the end of the filename.
816 dstr = from_utf8(onlyFileName(path));
817 docstring const head = dstr.substr(0, threshold / 2 - 3);
819 docstring::size_type len = dstr.length();
820 docstring const tail =
821 dstr.substr(len - threshold / 2 - 2, len - 1);
822 dstr = head + from_ascii("...") + tail;
825 return from_utf8(os::external_path(prefix + to_utf8(dstr)));
830 bool readLink(FileName const & file, FileName & link)
832 string const encoded = file.toFilesystemEncoding();
833 #ifdef HAVE_DEF_PATH_MAX
834 char linkbuffer[PATH_MAX + 1];
835 int const nRead = ::readlink(encoded.c_str(),
836 linkbuffer, sizeof(linkbuffer) - 1);
839 linkbuffer[nRead] = '\0'; // terminator
841 vector<char> buf(1024);
845 nRead = ::readlink(encoded.c_str(), &buf[0], buf.size() - 1);
849 if (nRead < buf.size() - 1) {
852 buf.resize(buf.size() * 2);
854 buf[nRead] = '\0'; // terminator
855 const char * linkbuffer = &buf[0];
857 link = makeAbsPath(linkbuffer, onlyPath(file.absFileName()));
861 bool readLink(FileName const &, FileName &)
868 cmd_ret const runCommand(string const & cmd)
870 // FIXME: replace all calls to RunCommand with ForkedCall
871 // (if the output is not needed) or the code in ISpell.cpp
872 // (if the output is needed).
874 // One question is if we should use popen or
875 // create our own popen based on fork, exec, pipe
876 // of course the best would be to have a
877 // pstream (process stream), with the
878 // variants ipstream, opstream
883 PROCESS_INFORMATION process;
884 SECURITY_ATTRIBUTES security;
887 bool err2out = false;
889 string const infile = trim(split(cmd, command, '<'), " \"");
890 command = rtrim(command);
891 if (suffixIs(command, "2>&1")) {
892 command = rtrim(command, "2>&1");
895 string const cmdarg = "/d /c " + command;
896 string const comspec = getEnv("COMSPEC");
898 security.nLength = sizeof(SECURITY_ATTRIBUTES);
899 security.bInheritHandle = TRUE;
900 security.lpSecurityDescriptor = NULL;
902 if (CreatePipe(&in, &out, &security, 0)) {
903 memset(&startup, 0, sizeof(STARTUPINFO));
904 memset(&process, 0, sizeof(PROCESS_INFORMATION));
906 startup.cb = sizeof(STARTUPINFO);
907 startup.dwFlags = STARTF_USESTDHANDLES;
909 startup.hStdError = err2out ? out : GetStdHandle(STD_ERROR_HANDLE);
910 startup.hStdInput = infile.empty()
911 ? GetStdHandle(STD_INPUT_HANDLE)
912 : CreateFile(infile.c_str(), GENERIC_READ,
913 FILE_SHARE_READ, &security, OPEN_EXISTING,
914 FILE_ATTRIBUTE_NORMAL, NULL);
915 startup.hStdOutput = out;
917 if (startup.hStdInput != INVALID_HANDLE_VALUE &&
918 CreateProcess(comspec.c_str(), (LPTSTR)cmdarg.c_str(),
919 &security, &security, TRUE, CREATE_NO_WINDOW,
920 0, 0, &startup, &process)) {
922 CloseHandle(process.hThread);
923 fno = _open_osfhandle((long)in, _O_RDONLY);
925 inf = _fdopen(fno, "r");
928 #elif defined (HAVE_POPEN)
929 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
930 #elif defined (HAVE__POPEN)
931 FILE * inf = ::_popen(cmd.c_str(), os::popen_read_mode());
933 #error No popen() function.
936 // (Claus Hentschel) Check if popen was successful ;-)
938 lyxerr << "RunCommand:: could not start child process" << endl;
939 return make_pair(-1, string());
945 ret += static_cast<char>(c);
950 WaitForSingleObject(process.hProcess, INFINITE);
952 CloseHandle(startup.hStdInput);
953 CloseHandle(process.hProcess);
954 int const pret = fclose(inf);
955 #elif defined (HAVE_PCLOSE)
956 int const pret = pclose(inf);
957 #elif defined (HAVE__PCLOSE)
958 int const pret = _pclose(inf);
960 #error No pclose() function.
964 perror("RunCommand:: could not terminate child process");
966 return make_pair(pret, ret);
970 FileName const findtexfile(string const & fil, string const & /*format*/)
972 /* There is no problem to extend this function too use other
973 methods to look for files. It could be setup to look
974 in environment paths and also if wanted as a last resort
975 to a recursive find. One of the easier extensions would
976 perhaps be to use the LyX file lookup methods. But! I am
977 going to implement this until I see some demand for it.
981 // If the file can be found directly, we just return a
982 // absolute path version of it.
983 FileName const absfile(makeAbsPath(fil));
984 if (absfile.exists())
987 // Now we try to find it using kpsewhich.
988 // It seems from the kpsewhich manual page that it is safe to use
989 // kpsewhich without --format: "When the --format option is not
990 // given, the search path used when looking for a file is inferred
991 // from the name given, by looking for a known extension. If no
992 // known extension is found, the search path for TeX source files
994 // However, we want to take advantage of the format sine almost all
995 // the different formats has environment variables that can be used
996 // to controll which paths to search. f.ex. bib looks in
997 // BIBINPUTS and TEXBIB. Small list follows:
998 // bib - BIBINPUTS, TEXBIB
1000 // graphic/figure - TEXPICTS, TEXINPUTS
1001 // ist - TEXINDEXSTYLE, INDEXSTYLE
1002 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1004 // tfm - TFMFONTS, TEXFONTS
1005 // This means that to use kpsewhich in the best possible way we
1006 // should help it by setting additional path in the approp. envir.var.
1007 string const kpsecmd = "kpsewhich " + fil;
1009 cmd_ret const c = runCommand(kpsecmd);
1011 LYXERR(Debug::LATEX, "kpse status = " << c.first << '\n'
1012 << "kpse result = `" << rtrim(c.second, "\n\r") << '\'');
1014 return FileName(rtrim(to_utf8(from_filesystem8bit(c.second)), "\n\r"));
1020 int compare_timestamps(FileName const & file1, FileName const & file2)
1022 // If the original is newer than the copy, then copy the original
1023 // to the new directory.
1026 if (file1.exists() && file2.exists()) {
1027 double const tmp = difftime(file1.lastModified(), file2.lastModified());
1029 cmp = tmp > 0 ? 1 : -1;
1031 } else if (file1.exists()) {
1033 } else if (file2.exists()) {
1041 bool prefs2prefs(FileName const & filename, FileName const & tempfile, bool lfuns)
1043 FileName const script = libFileSearch("scripts", "prefs2prefs.py");
1044 if (script.empty()) {
1045 LYXERR0("Could not find bind file conversion "
1046 "script prefs2prefs.py.");
1050 ostringstream command;
1051 command << os::python() << ' ' << quoteName(script.toFilesystemEncoding())
1052 << ' ' << (lfuns ? "-l" : "-p") << ' '
1053 << quoteName(filename.toFilesystemEncoding())
1054 << ' ' << quoteName(tempfile.toFilesystemEncoding());
1055 string const command_str = command.str();
1057 LYXERR(Debug::FILES, "Running `" << command_str << '\'');
1059 cmd_ret const ret = runCommand(command_str);
1060 if (ret.first != 0) {
1061 LYXERR0("Could not run file conversion script prefs2prefs.py.");
1067 int fileLock(const char * lock_file)
1070 #if defined(HAVE_LOCKF)
1071 fd = open(lock_file, O_CREAT|O_APPEND|O_SYNC|O_RDWR, 0666);
1072 if (lockf(fd, F_LOCK, 0) != 0) {
1080 void fileUnlock(int fd, const char * lock_file)
1082 #if defined(HAVE_LOCKF)
1084 (void) lockf(fd, F_ULOCK, 0);
1090 } //namespace support