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 commandPrep(string const & command_in)
342 static string const token_scriptpath = "$$s/";
343 string const python_call = "python -tt";
345 string command = command_in;
346 if (prefixIs(command_in, python_call))
347 command = os::python() + command_in.substr(python_call.length());
349 // Find the starting position of "$$s/"
350 string::size_type const pos1 = command.find(token_scriptpath);
351 if (pos1 == string::npos)
353 // Find the end of the "$$s/some_subdir/some_script" word within
354 // command. Assumes that the script name does not contain spaces.
355 string::size_type const start_script = pos1 + 4;
356 string::size_type const pos2 = command.find(' ', start_script);
357 string::size_type const size_script = pos2 == string::npos?
358 (command.size() - start_script) : pos2 - start_script;
360 // Does this script file exist?
361 string const script =
362 libFileSearch(".", command.substr(start_script, size_script)).absFileName();
364 if (script.empty()) {
365 // Replace "$$s/" with ""
366 command.erase(pos1, 4);
368 quote_style style = quote_shell;
369 if (prefixIs(command, os::python()))
370 style = quote_python;
372 // Replace "$$s/foo/some_script" with "<path to>/some_script".
373 string::size_type const size_replace = size_script + 4;
374 command.replace(pos1, size_replace, quoteName(script, style));
381 static FileName createTmpDir(FileName const & tempdir, string const & mask)
383 LYXERR(Debug::FILES, "createTmpDir: tempdir=`" << tempdir << "'\n"
384 << "createTmpDir: mask=`" << mask << '\'');
386 FileName const tmpfl = FileName::tempName(tempdir, mask);
388 if (tmpfl.empty() || !tmpfl.createDirectory(0700)) {
389 LYXERR0("LyX could not create temporary directory in " << tempdir
398 FileName const createLyXTmpDir(FileName const & deflt)
400 if (deflt.empty() || deflt == package().system_temp_dir())
401 return createTmpDir(package().system_temp_dir(), "lyx_tmpdir");
403 if (deflt.createDirectory(0777))
406 if (deflt.isDirWritable()) {
407 // deflt could not be created because it
408 // did exist already, so let's create our own
410 return createTmpDir(deflt, "lyx_tmpdir");
412 // some other error occured.
413 return createTmpDir(package().system_temp_dir(), "lyx_tmpdir");
418 // Strip filename from path name
419 string const onlyPath(string const & filename)
421 // If empty filename, return empty
422 if (filename.empty())
425 // Find last / or start of filename
426 size_t j = filename.rfind('/');
427 return j == string::npos ? "./" : filename.substr(0, j + 1);
431 // Convert relative path into absolute path based on a basepath.
432 // If relpath is absolute, just use that.
433 // If basepath is empty, use CWD as base.
434 // Note that basePath can be a relative path, in the sense that it may
435 // not begin with "/" (e.g.), but it should NOT contain such constructs
437 // FIXME It might be nice if the code didn't simply assume that.
438 FileName const makeAbsPath(string const & relPath, string const & basePath)
440 // checks for already absolute path
441 if (FileName::isAbsolute(relPath))
442 return FileName(relPath);
444 // Copies given paths
445 string tempRel = os::internal_path(relPath);
446 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
447 tempRel = subst(tempRel, "//", "/");
451 if (FileName::isAbsolute(basePath))
454 tempBase = addPath(FileName::getcwd().absFileName(), basePath);
456 // Handle /./ at the end of the path
457 while (suffixIs(tempBase, "/./"))
458 tempBase.erase(tempBase.length() - 2);
460 // processes relative path
461 string rTemp = tempRel;
464 // Check for a leading "~"
466 rTemp = split(rTemp, temp, '/');
468 tempBase = Package::get_home_dir().absFileName();
473 while (!rTemp.empty()) {
475 rTemp = split(rTemp, temp, '/');
477 if (temp == ".") continue;
479 // Remove one level of TempBase
480 if (tempBase.length() <= 1) {
481 //this is supposed to be an absolute path, so...
485 //erase a trailing slash if there is one
486 if (suffixIs(tempBase, "/"))
487 tempBase.erase(tempBase.length() - 1, string::npos);
489 string::size_type i = tempBase.length() - 1;
490 while (i > 0 && tempBase[i] != '/')
493 tempBase.erase(i, string::npos);
496 } else if (temp.empty() && !rTemp.empty()) {
497 tempBase = os::current_root() + rTemp;
500 // Add this piece to TempBase
501 if (!suffixIs(tempBase, '/'))
507 // returns absolute path
508 return FileName(tempBase);
512 // Correctly append filename to the pathname.
513 // If pathname is '.', then don't use pathname.
514 // Chops any path of filename.
515 string const addName(string const & path, string const & fname)
517 string const basename = onlyFileName(fname);
520 if (path != "." && path != "./" && !path.empty()) {
521 buf = os::internal_path(path);
522 if (!suffixIs(path, '/'))
526 return buf + basename;
530 // Strips path from filename
531 string const onlyFileName(string const & fname)
536 string::size_type j = fname.rfind('/');
537 if (j == string::npos) // no '/' in fname
541 return fname.substr(j + 1);
545 // Create absolute path. If impossible, don't do anything
546 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
547 string const expandPath(string const & path)
549 // checks for already absolute path
550 string rTemp = replaceEnvironmentPath(path);
551 if (FileName::isAbsolute(rTemp))
555 string const copy = rTemp;
558 rTemp = split(rTemp, temp, '/');
561 return FileName::getcwd().absFileName() + '/' + rTemp;
564 return Package::get_home_dir().absFileName() + '/' + rTemp;
567 return makeAbsPath(copy).absFileName();
569 // Don't know how to handle this
574 // Search the string for ${VAR} and $VAR and replace VAR using getenv.
575 string const replaceEnvironmentPath(string const & path)
577 // ${VAR} is defined as
578 // $\{[A-Za-z_][A-Za-z_0-9]*\}
579 static string const envvar_br = "[$]\\{([A-Za-z_][A-Za-z_0-9]*)\\}";
581 // $VAR is defined as:
582 // $[A-Za-z_][A-Za-z_0-9]*
583 static string const envvar = "[$]([A-Za-z_][A-Za-z_0-9]*)";
585 static regex const envvar_br_re("(.*)" + envvar_br + "(.*)");
586 static regex const envvar_re("(.*)" + envvar + "(.*)");
587 string result = path;
590 if (!regex_match(result, what, envvar_br_re)) {
591 if (!regex_match(result, what, envvar_re))
594 string env_var = getEnv(what.str(2));
595 result = what.str(1) + env_var + what.str(3);
601 // Return a command prefix for setting the environment of the TeX engine.
602 string latexEnvCmdPrefix(string const & path)
604 if (path.empty() || lyxrc.texinputs_prefix.empty())
607 string const texinputs_prefix = os::latex_path_list(
608 replaceCurdirPath(path, lyxrc.texinputs_prefix));
609 string const sep = string(1, os::path_separator(os::TEXENGINE));
610 string const texinputs = getEnv("TEXINPUTS");
612 if (os::shell() == os::UNIX)
613 return "env TEXINPUTS=\"." + sep + texinputs_prefix
614 + sep + texinputs + "\" ";
616 return "cmd /d /c set TEXINPUTS=." + sep + texinputs_prefix
617 + sep + texinputs + "&";
621 // Replace current directory in all elements of a path list with a given path.
622 string const replaceCurdirPath(string const & path, string const & pathlist)
624 string const oldpathlist = replaceEnvironmentPath(pathlist);
625 char const sep = os::path_separator();
628 for (size_t i = 0, k = 0; i != string::npos; k = i) {
629 i = oldpathlist.find(sep, i);
630 string p = oldpathlist.substr(k, i - k);
631 if (FileName::isAbsolute(p)) {
637 } else if (prefixIs(p, "./")) {
639 while (p[offset] == '/')
642 newpathlist += addPath(path, p.substr(offset));
643 if (suffixIs(p, "//"))
646 if (i != string::npos) {
648 // Stop here if the last element is empty
649 if (++i == oldpathlist.length())
657 // Make relative path out of two absolute paths
658 docstring const makeRelPath(docstring const & abspath, docstring const & basepath)
659 // Makes relative path out of absolute path. If it is deeper than basepath,
660 // it's easy. If basepath and abspath share something (they are all deeper
661 // than some directory), it'll be rendered using ..'s. If they are completely
662 // different, then the absolute path will be used as relative path.
664 docstring::size_type const abslen = abspath.length();
665 docstring::size_type const baselen = basepath.length();
667 docstring::size_type i = os::common_path(abspath, basepath);
670 // actually no match - cannot make it relative
674 // Count how many dirs there are in basepath above match
675 // and append as many '..''s into relpath
677 docstring::size_type j = i;
678 while (j < baselen) {
679 if (basepath[j] == '/') {
680 if (j + 1 == baselen)
687 // Append relative stuff from common directory to abspath
688 if (abspath[i] == '/')
690 for (; i < abslen; ++i)
693 if (suffixIs(buf, '/'))
694 buf.erase(buf.length() - 1);
695 // Substitute empty with .
702 // Append sub-directory(ies) to a path in an intelligent way
703 string const addPath(string const & path, string const & path_2)
706 string const path2 = os::internal_path(path_2);
708 if (!path.empty() && path != "." && path != "./") {
709 buf = os::internal_path(path);
710 if (path[path.length() - 1] != '/')
714 if (!path2.empty()) {
715 string::size_type const p2start = path2.find_first_not_of('/');
716 string::size_type const p2end = path2.find_last_not_of('/');
717 string const tmp = path2.substr(p2start, p2end - p2start + 1);
724 string const changeExtension(string const & oldname, string const & extension)
726 string::size_type const last_slash = oldname.rfind('/');
727 string::size_type last_dot = oldname.rfind('.');
728 if (last_dot < last_slash && last_slash != string::npos)
729 last_dot = string::npos;
732 // Make sure the extension starts with a dot
733 if (!extension.empty() && extension[0] != '.')
734 ext= '.' + extension;
738 return os::internal_path(oldname.substr(0, last_dot) + ext);
742 string const removeExtension(string const & name)
744 return changeExtension(name, string());
748 string const addExtension(string const & name, string const & extension)
750 if (!extension.empty() && extension[0] != '.')
751 return name + '.' + extension;
752 return name + extension;
756 /// Return the extension of the file (not including the .)
757 string const getExtension(string const & name)
759 string::size_type const last_slash = name.rfind('/');
760 string::size_type const last_dot = name.rfind('.');
761 if (last_dot != string::npos &&
762 (last_slash == string::npos || last_dot > last_slash))
763 return name.substr(last_dot + 1,
764 name.length() - (last_dot + 1));
770 string const unzippedFileName(string const & zipped_file)
772 string const ext = getExtension(zipped_file);
773 if (ext == "gz" || ext == "z" || ext == "Z")
774 return changeExtension(zipped_file, string());
775 return onlyPath(zipped_file) + "unzipped_" + onlyFileName(zipped_file);
779 FileName const unzipFile(FileName const & zipped_file, string const & unzipped_file)
781 FileName const tempfile = FileName(unzipped_file.empty() ?
782 unzippedFileName(zipped_file.toFilesystemEncoding()) :
785 string const command = "gunzip -c " +
786 zipped_file.toFilesystemEncoding() + " > " +
787 tempfile.toFilesystemEncoding();
789 one.startscript(Systemcall::Wait, command);
790 // test that command was executed successfully (anon)
791 // yes, please do. (Lgb)
796 docstring const makeDisplayPath(string const & path, unsigned int threshold)
800 // If file is from LyXDir, display it as if it were relative.
801 string const system = package().system_support().absFileName();
802 if (prefixIs(str, system) && str != system)
803 return from_utf8("[" + str.erase(0, system.length()) + "]");
805 // replace /home/blah with ~/
806 string const home = Package::get_home_dir().absFileName();
807 if (!home.empty() && prefixIs(str, home))
808 str = subst(str, home, "~");
810 if (str.length() <= threshold)
811 return from_utf8(os::external_path(str));
813 string const prefix = ".../";
814 docstring dstr = from_utf8(str);
817 while (dstr.length() > threshold)
818 dstr = split(dstr, temp, '/');
820 // Did we shorten everything away?
822 // Yes, filename itself is too long.
823 // Pick the start and the end of the filename.
824 dstr = from_utf8(onlyFileName(path));
825 docstring const head = dstr.substr(0, threshold / 2 - 3);
827 docstring::size_type len = dstr.length();
828 docstring const tail =
829 dstr.substr(len - threshold / 2 - 2, len - 1);
830 dstr = head + from_ascii("...") + tail;
833 return from_utf8(os::external_path(prefix + to_utf8(dstr)));
838 bool readLink(FileName const & file, FileName & link)
840 string const encoded = file.toFilesystemEncoding();
841 #ifdef HAVE_DEF_PATH_MAX
842 char linkbuffer[PATH_MAX + 1];
843 ssize_t const nRead = ::readlink(encoded.c_str(),
844 linkbuffer, sizeof(linkbuffer) - 1);
847 linkbuffer[nRead] = '\0'; // terminator
849 vector<char> buf(1024);
853 nRead = ::readlink(encoded.c_str(), &buf[0], buf.size() - 1);
857 if (static_cast<size_t>(nRead) < buf.size() - 1) {
860 buf.resize(buf.size() * 2);
862 buf[nRead] = '\0'; // terminator
863 const char * linkbuffer = &buf[0];
865 link = makeAbsPath(linkbuffer, onlyPath(file.absFileName()));
869 bool readLink(FileName const &, FileName &)
876 cmd_ret const runCommand(string const & cmd)
878 // FIXME: replace all calls to RunCommand with ForkedCall
879 // (if the output is not needed) or the code in ISpell.cpp
880 // (if the output is needed).
882 // One question is if we should use popen or
883 // create our own popen based on fork, exec, pipe
884 // of course the best would be to have a
885 // pstream (process stream), with the
886 // variants ipstream, opstream
891 PROCESS_INFORMATION process;
892 SECURITY_ATTRIBUTES security;
895 bool err2out = false;
897 string const infile = trim(split(cmd, command, '<'), " \"");
898 command = rtrim(command);
899 if (suffixIs(command, "2>&1")) {
900 command = rtrim(command, "2>&1");
903 string const cmdarg = "/d /c " + command;
904 string const comspec = getEnv("COMSPEC");
906 security.nLength = sizeof(SECURITY_ATTRIBUTES);
907 security.bInheritHandle = TRUE;
908 security.lpSecurityDescriptor = NULL;
910 if (CreatePipe(&in, &out, &security, 0)) {
911 memset(&startup, 0, sizeof(STARTUPINFO));
912 memset(&process, 0, sizeof(PROCESS_INFORMATION));
914 startup.cb = sizeof(STARTUPINFO);
915 startup.dwFlags = STARTF_USESTDHANDLES;
917 startup.hStdError = err2out ? out : GetStdHandle(STD_ERROR_HANDLE);
918 startup.hStdInput = infile.empty()
919 ? GetStdHandle(STD_INPUT_HANDLE)
920 : CreateFile(infile.c_str(), GENERIC_READ,
921 FILE_SHARE_READ, &security, OPEN_EXISTING,
922 FILE_ATTRIBUTE_NORMAL, NULL);
923 startup.hStdOutput = out;
925 if (startup.hStdInput != INVALID_HANDLE_VALUE &&
926 CreateProcess(comspec.c_str(), (LPTSTR)cmdarg.c_str(),
927 &security, &security, TRUE, CREATE_NO_WINDOW,
928 0, 0, &startup, &process)) {
930 CloseHandle(process.hThread);
931 fno = _open_osfhandle((long)in, _O_RDONLY);
933 inf = _fdopen(fno, "r");
936 #elif defined (HAVE_POPEN)
937 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
938 #elif defined (HAVE__POPEN)
939 FILE * inf = ::_popen(cmd.c_str(), os::popen_read_mode());
941 #error No popen() function.
944 // (Claus Hentschel) Check if popen was successful ;-)
946 lyxerr << "RunCommand:: could not start child process" << endl;
947 return make_pair(-1, string());
953 ret += static_cast<char>(c);
958 WaitForSingleObject(process.hProcess, INFINITE);
960 CloseHandle(startup.hStdInput);
961 CloseHandle(process.hProcess);
962 int const pret = fclose(inf);
963 #elif defined (HAVE_PCLOSE)
964 int const pret = pclose(inf);
965 #elif defined (HAVE__PCLOSE)
966 int const pret = _pclose(inf);
968 #error No pclose() function.
972 perror("RunCommand:: could not terminate child process");
974 return make_pair(pret, ret);
978 FileName const findtexfile(string const & fil, string const & /*format*/)
980 /* There is no problem to extend this function too use other
981 methods to look for files. It could be setup to look
982 in environment paths and also if wanted as a last resort
983 to a recursive find. One of the easier extensions would
984 perhaps be to use the LyX file lookup methods. But! I am
985 going to implement this until I see some demand for it.
989 // If the file can be found directly, we just return a
990 // absolute path version of it.
991 FileName const absfile(makeAbsPath(fil));
992 if (absfile.exists())
995 // Now we try to find it using kpsewhich.
996 // It seems from the kpsewhich manual page that it is safe to use
997 // kpsewhich without --format: "When the --format option is not
998 // given, the search path used when looking for a file is inferred
999 // from the name given, by looking for a known extension. If no
1000 // known extension is found, the search path for TeX source files
1002 // However, we want to take advantage of the format sine almost all
1003 // the different formats has environment variables that can be used
1004 // to controll which paths to search. f.ex. bib looks in
1005 // BIBINPUTS and TEXBIB. Small list follows:
1006 // bib - BIBINPUTS, TEXBIB
1008 // graphic/figure - TEXPICTS, TEXINPUTS
1009 // ist - TEXINDEXSTYLE, INDEXSTYLE
1010 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1012 // tfm - TFMFONTS, TEXFONTS
1013 // This means that to use kpsewhich in the best possible way we
1014 // should help it by setting additional path in the approp. envir.var.
1015 string const kpsecmd = "kpsewhich " + fil;
1017 cmd_ret const c = runCommand(kpsecmd);
1019 LYXERR(Debug::LATEX, "kpse status = " << c.first << '\n'
1020 << "kpse result = `" << rtrim(c.second, "\n\r") << '\'');
1022 return FileName(rtrim(to_utf8(from_filesystem8bit(c.second)), "\n\r"));
1028 int compare_timestamps(FileName const & file1, FileName const & file2)
1030 // If the original is newer than the copy, then copy the original
1031 // to the new directory.
1034 if (file1.exists() && file2.exists()) {
1035 double const tmp = difftime(file1.lastModified(), file2.lastModified());
1037 cmp = tmp > 0 ? 1 : -1;
1039 } else if (file1.exists()) {
1041 } else if (file2.exists()) {
1049 bool prefs2prefs(FileName const & filename, FileName const & tempfile, bool lfuns)
1051 FileName const script = libFileSearch("scripts", "prefs2prefs.py");
1052 if (script.empty()) {
1053 LYXERR0("Could not find bind file conversion "
1054 "script prefs2prefs.py.");
1058 ostringstream command;
1059 command << os::python() << ' ' << quoteName(script.toFilesystemEncoding())
1060 << ' ' << (lfuns ? "-l" : "-p") << ' '
1061 << quoteName(filename.toFilesystemEncoding())
1062 << ' ' << quoteName(tempfile.toFilesystemEncoding());
1063 string const command_str = command.str();
1065 LYXERR(Debug::FILES, "Running `" << command_str << '\'');
1067 cmd_ret const ret = runCommand(command_str);
1068 if (ret.first != 0) {
1069 LYXERR0("Could not run file conversion script prefs2prefs.py.");
1075 int fileLock(const char * lock_file)
1078 #if defined(HAVE_LOCKF)
1079 fd = open(lock_file, O_CREAT|O_APPEND|O_SYNC|O_RDWR, 0666);
1080 if (lockf(fd, F_LOCK, 0) != 0) {
1088 void fileUnlock(int fd, const char * /* lock_file*/)
1090 #if defined(HAVE_LOCKF)
1092 if (lockf(fd, F_ULOCK, 0))
1093 LYXERR0("Can't unlock the file.");
1099 } //namespace support