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
24 #include "support/convert.h"
25 #include "support/environment.h"
26 #include "support/filetools.h"
27 #include "support/fs_extras.h"
28 #include "support/lstrings.h"
29 #include "support/lyxlib.h"
30 #include "support/os.h"
31 #include "support/package.h"
32 #include "support/path.h"
33 #include "support/systemcall.h"
35 // FIXME Interface violation
39 #include <boost/assert.hpp>
40 #include <boost/filesystem/operations.hpp>
41 #include <boost/regex.hpp>
54 #ifndef CXX_GLOBAL_CSTD
65 using std::ostringstream;
68 namespace fs = boost::filesystem;
73 bool isLyXFilename(string const & filename)
75 return suffixIs(ascii_lowercase(filename), ".lyx");
79 bool isSGMLFilename(string const & filename)
81 return suffixIs(ascii_lowercase(filename), ".sgml");
85 string const latex_path(string const & original_path,
86 latex_path_extension extension,
89 // On cygwin, we may need windows or posix style paths.
90 string path = os::latex_path(original_path);
91 path = subst(path, "~", "\\string~");
92 if (path.find(' ') != string::npos) {
93 // We can't use '"' because " is sometimes active (e.g. if
94 // babel is loaded with the "german" option)
95 if (extension == EXCLUDE_EXTENSION) {
96 // ChangeExtension calls os::internal_path internally
97 // so don't use it to remove the extension.
98 string const ext = getExtension(path);
99 string const base = ext.empty() ?
101 path.substr(0, path.length() - ext.length() - 1);
102 // ChangeExtension calls os::internal_path internally
103 // so don't use it to re-add the extension.
104 path = "\\string\"" + base + "\\string\"." + ext;
106 path = "\\string\"" + path + "\\string\"";
110 return dots == ESCAPE_DOTS ? subst(path, ".", "\\lyxdot ") : path;
114 // Substitutes spaces with underscores in filename (and path)
115 string const makeLatexName(string const & file)
117 string name = onlyFilename(file);
118 string const path = onlyPath(file);
120 // ok so we scan through the string twice, but who cares.
121 string const keep = "abcdefghijklmnopqrstuvwxyz"
122 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
123 "@!'()*+,-./0123456789:;<=>?[]`|";
125 string::size_type pos = 0;
126 while ((pos = name.find_first_not_of(keep, pos)) != string::npos)
129 return addName(path, name);
133 string const quoteName(string const & name, quote_style style)
137 // This does not work for filenames containing " (windows)
138 // or ' (all other OSes). This can't be changed easily, since
139 // we would need to adapt the command line parser in
140 // Forkedcall::generateChild. Therefore we don't pass user
141 // filenames to child processes if possible. We store them in
142 // a python script instead, where we don't have these
144 return (os::shell() == os::UNIX) ?
148 return "\"" + subst(subst(name, "\\", "\\\\"), "\"", "\\\"")
151 // shut up stupid compiler
156 bool isFileReadable(FileName const & filename)
158 std::string const path = filename.toFilesystemEncoding();
159 return fs::exists(path) && !fs::is_directory(path) && fs::is_readable(path);
163 //returns true: dir writeable
164 // false: not writeable
165 bool isDirWriteable(FileName const & path)
167 lyxerr[Debug::FILES] << "isDirWriteable: " << path << endl;
169 FileName const tmpfl(tempName(path, "lyxwritetest"));
180 // Uses a string of paths separated by ";"s to find a file to open.
181 // Can't cope with pathnames with a ';' in them. Returns full path to file.
182 // If path entry begins with $$LyX/, use system_lyxdir
183 // If path entry begins with $$User/, use user_lyxdir
184 // Example: "$$User/doc;$$LyX/doc"
185 FileName const fileOpenSearch(string const & path, string const & name,
190 bool notfound = true;
191 string tmppath = split(path, path_element, ';');
193 while (notfound && !path_element.empty()) {
194 path_element = os::internal_path(path_element);
195 if (!suffixIs(path_element, '/'))
197 path_element = subst(path_element, "$$LyX",
198 package().system_support());
199 path_element = subst(path_element, "$$User",
200 package().user_support());
202 real_file = fileSearch(path_element, name, ext);
204 if (real_file.empty()) {
206 tmppath = split(tmppath, path_element, ';');
207 } while (!tmppath.empty() && path_element.empty());
217 /// Returns a vector of all files in directory dir having extension ext.
218 vector<FileName> const dirList(FileName const & dir, string const & ext)
220 // EXCEPTIONS FIXME. Rewrite needed when we turn on exceptions. (Lgb)
221 vector<FileName> dirlist;
223 string const encoded_dir = dir.toFilesystemEncoding();
224 if (!(fs::exists(encoded_dir) && fs::is_directory(encoded_dir))) {
226 << "Directory \"" << dir
227 << "\" does not exist to DirList." << endl;
232 if (!ext.empty() && ext[0] != '.')
236 fs::directory_iterator dit(encoded_dir);
237 fs::directory_iterator end;
238 for (; dit != end; ++dit) {
239 string const & fil = dit->leaf();
240 if (suffixIs(fil, extension))
241 dirlist.push_back(FileName::fromFilesystemEncoding(
242 encoded_dir + '/' + fil));
248 // Returns the real name of file name in directory path, with optional
250 FileName const fileSearch(string const & path, string const & name,
251 string const & ext, search_mode mode)
253 // if `name' is an absolute path, we ignore the setting of `path'
254 // Expand Environmentvariables in 'name'
255 string const tmpname = replaceEnvironmentPath(name);
256 FileName fullname(makeAbsPath(tmpname, path));
257 // search first without extension, then with it.
258 if (isFileReadable(fullname))
262 return mode == allow_unreadable ? fullname : FileName();
263 // Only add the extension if it is not already the extension of
265 if (getExtension(fullname.absFilename()) != ext)
266 fullname = FileName(addExtension(fullname.absFilename(), ext));
267 if (isFileReadable(fullname) || mode == allow_unreadable)
273 // Search the file name.ext in the subdirectory dir of
275 // 2) build_lyxdir (if not empty)
277 FileName const libFileSearch(string const & dir, string const & name,
280 FileName fullname = fileSearch(addPath(package().user_support(), dir),
282 if (!fullname.empty())
285 if (!package().build_support().empty())
286 fullname = fileSearch(addPath(package().build_support(), dir),
288 if (!fullname.empty())
291 return fileSearch(addPath(package().system_support(), dir), name, ext);
295 FileName const i18nLibFileSearch(string const & dir, string const & name,
298 // the following comments are from intl/dcigettext.c. We try
299 // to mimick this behaviour here.
300 /* The highest priority value is the `LANGUAGE' environment
301 variable. But we don't use the value if the currently
302 selected locale is the C locale. This is a GNU extension. */
303 /* [Otherwise] We have to proceed with the POSIX methods of
304 looking to `LC_ALL', `LC_xxx', and `LANG'. */
306 string lang = getEnv("LC_ALL");
308 lang = getEnv("LC_MESSAGES");
310 lang = getEnv("LANG");
316 string const language = getEnv("LANGUAGE");
317 if (lang != "C" && lang != "POSIX" && !language.empty())
321 lang = split(lang, l, ':');
322 while (!l.empty() && l != "C" && l != "POSIX") {
323 FileName const tmp = libFileSearch(dir,
324 token(l, '_', 0) + '_' + name,
328 lang = split(lang, l, ':');
331 return libFileSearch(dir, name, ext);
335 string const libScriptSearch(string const & command_in, quote_style style)
337 static string const token_scriptpath = "$$s/";
339 string command = command_in;
340 // Find the starting position of "$$s/"
341 string::size_type const pos1 = command.find(token_scriptpath);
342 if (pos1 == string::npos)
344 // Find the end of the "$$s/some_subdir/some_script" word within
345 // command. Assumes that the script name does not contain spaces.
346 string::size_type const start_script = pos1 + 4;
347 string::size_type const pos2 = command.find(' ', start_script);
348 string::size_type const size_script = pos2 == string::npos?
349 (command.size() - start_script) : pos2 - start_script;
351 // Does this script file exist?
352 string const script =
353 libFileSearch(".", command.substr(start_script, size_script)).absFilename();
355 if (script.empty()) {
356 // Replace "$$s/" with ""
357 command.erase(pos1, 4);
359 // Replace "$$s/foo/some_script" with "<path to>/some_script".
360 string::size_type const size_replace = size_script + 4;
361 command.replace(pos1, size_replace, quoteName(script, style));
370 FileName const createTmpDir(FileName const & tempdir, string const & mask)
373 << "createTmpDir: tempdir=`" << tempdir << "'\n"
374 << "createTmpDir: mask=`" << mask << '\'' << endl;
376 FileName const tmpfl(tempName(tempdir, mask));
377 // lyx::tempName actually creates a file to make sure that it
378 // stays unique. So we have to delete it before we can create
379 // a dir with the same name. Note also that we are not thread
380 // safe because of the gap between unlink and mkdir. (Lgb)
383 if (tmpfl.empty() || mkdir(tmpfl, 0700)) {
384 lyxerr << "LyX could not create the temporary directory '"
385 << tmpfl << "'" << endl;
395 bool destroyDir(FileName const & tmpdir)
398 return fs::remove_all(tmpdir.toFilesystemEncoding()) > 0;
399 } catch (fs::filesystem_error const & fe){
400 lyxerr << "Could not delete " << tmpdir << ". (" << fe.what() << ")" << std::endl;
406 string const createBufferTmpDir()
409 // We are in our own directory. Why bother to mangle name?
410 // In fact I wrote this code to circumvent a problematic behaviour
411 // (bug?) of EMX mkstemp().
413 package().temp_dir() + "/lyx_tmpbuf" +
414 convert<string>(count++);
416 if (mkdir(FileName(tmpfl), 0777)) {
417 lyxerr << "LyX could not create the temporary directory '"
418 << tmpfl << "'" << endl;
425 FileName const createLyXTmpDir(FileName const & deflt)
427 if (!deflt.empty() && deflt.absFilename() != "/tmp") {
428 if (mkdir(deflt, 0777)) {
429 if (isDirWriteable(deflt)) {
430 // deflt could not be created because it
431 // did exist already, so let's create our own
433 return createTmpDir(deflt, "lyx_tmpdir");
435 // some other error occured.
436 return createTmpDir(FileName("/tmp"), "lyx_tmpdir");
441 return createTmpDir(FileName("/tmp"), "lyx_tmpdir");
446 bool createDirectory(string const & path, int permission)
448 string temp = rtrim(os::internal_path(path), "/");
449 BOOST_ASSERT(!temp.empty());
450 return mkdir(FileName(temp), permission) == 0;
454 // Strip filename from path name
455 string const onlyPath(string const & filename)
457 // If empty filename, return empty
458 if (filename.empty())
461 // Find last / or start of filename
462 string::size_type j = filename.rfind('/');
463 return j == string::npos ? "./" : filename.substr(0, j + 1);
467 // Convert relative path into absolute path based on a basepath.
468 // If relpath is absolute, just use that.
469 // If basepath is empty, use CWD as base.
470 FileName const makeAbsPath(string const & relPath, string const & basePath)
472 // checks for already absolute path
473 if (os::is_absolute_path(relPath))
474 return FileName(relPath);
476 // Copies given paths
477 string tempRel = os::internal_path(relPath);
478 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
479 tempRel = subst(tempRel, "//", "/");
483 if (os::is_absolute_path(basePath))
486 tempBase = addPath(getcwd().absFilename(), basePath);
488 // Handle /./ at the end of the path
489 while (suffixIs(tempBase, "/./"))
490 tempBase.erase(tempBase.length() - 2);
492 // processes relative path
493 string rTemp = tempRel;
496 while (!rTemp.empty()) {
498 rTemp = split(rTemp, temp, '/');
500 if (temp == ".") continue;
502 // Remove one level of TempBase
503 string::difference_type i = tempBase.length() - 2;
506 while (i > 0 && tempBase[i] != '/')
509 tempBase.erase(i, string::npos);
512 } else if (temp.empty() && !rTemp.empty()) {
513 tempBase = os::current_root() + rTemp;
516 // Add this piece to TempBase
517 if (!suffixIs(tempBase, '/'))
523 // returns absolute path
524 return FileName(os::internal_path(tempBase));
528 // Correctly append filename to the pathname.
529 // If pathname is '.', then don't use pathname.
530 // Chops any path of filename.
531 string const addName(string const & path, string const & fname)
533 string const basename = onlyFilename(fname);
536 if (path != "." && path != "./" && !path.empty()) {
537 buf = os::internal_path(path);
538 if (!suffixIs(path, '/'))
542 return buf + basename;
546 // Strips path from filename
547 string const onlyFilename(string const & fname)
552 string::size_type j = fname.rfind('/');
553 if (j == string::npos) // no '/' in fname
557 return fname.substr(j + 1);
561 /// Returns true is path is absolute
562 bool absolutePath(string const & path)
564 return os::is_absolute_path(path);
568 // Create absolute path. If impossible, don't do anything
569 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
570 string const expandPath(string const & path)
572 // checks for already absolute path
573 string rTemp = replaceEnvironmentPath(path);
574 if (os::is_absolute_path(rTemp))
578 string const copy = rTemp;
581 rTemp = split(rTemp, temp, '/');
584 return getcwd().absFilename() + '/' + rTemp;
587 return package().home_dir() + '/' + rTemp;
590 return makeAbsPath(copy).absFilename();
592 // Don't know how to handle this
597 // Normalize a path. Constracts path/../path
598 // Can't handle "../../" or "/../" (Asger)
599 // Also converts paths like /foo//bar ==> /foo/bar
600 string const normalizePath(string const & path)
602 // Normalize paths like /foo//bar ==> /foo/bar
603 static boost::regex regex("/{2,}");
604 string const tmppath = boost::regex_merge(path, regex, "/");
606 fs::path const npath = fs::path(tmppath, fs::no_check).normalize();
608 if (!npath.is_complete())
609 return "./" + npath.string() + '/';
611 return npath.string() + '/';
615 string const getFileContents(FileName const & fname)
617 string const encodedname = fname.toFilesystemEncoding();
618 if (fs::exists(encodedname)) {
619 ifstream ifs(encodedname.c_str());
627 lyxerr << "LyX was not able to read file '" << fname << '\'' << endl;
632 // Search the string for ${VAR} and $VAR and replace VAR using getenv.
633 string const replaceEnvironmentPath(string const & path)
635 // ${VAR} is defined as
636 // $\{[A-Za-z_][A-Za-z_0-9]*\}
637 static string const envvar_br = "[$]\\{([A-Za-z_][A-Za-z_0-9]*)\\}";
639 // $VAR is defined as:
640 // $\{[A-Za-z_][A-Za-z_0-9]*\}
641 static string const envvar = "[$]([A-Za-z_][A-Za-z_0-9]*)";
643 static boost::regex envvar_br_re("(.*)" + envvar_br + "(.*)");
644 static boost::regex envvar_re("(.*)" + envvar + "(.*)");
647 string result = path;
649 regex_match(result, what, envvar_br_re);
650 if (!what[0].matched) {
651 regex_match(result, what, envvar_re);
652 if (!what[0].matched)
655 result = what.str(1) + getEnv(what.str(2)) + what.str(3);
661 // Make relative path out of two absolute paths
662 string const makeRelPath(string const & abspath, string const & basepath)
663 // Makes relative path out of absolute path. If it is deeper than basepath,
664 // it's easy. If basepath and abspath share something (they are all deeper
665 // than some directory), it'll be rendered using ..'s. If they are completely
666 // different, then the absolute path will be used as relative path.
668 string::size_type const abslen = abspath.length();
669 string::size_type const baselen = basepath.length();
671 string::size_type i = os::common_path(abspath, basepath);
674 // actually no match - cannot make it relative
678 // Count how many dirs there are in basepath above match
679 // and append as many '..''s into relpath
681 string::size_type j = i;
682 while (j < baselen) {
683 if (basepath[j] == '/') {
684 if (j + 1 == baselen)
691 // Append relative stuff from common directory to abspath
692 if (abspath[i] == '/')
694 for (; i < abslen; ++i)
697 if (suffixIs(buf, '/'))
698 buf.erase(buf.length() - 1);
699 // Substitute empty with .
706 // Append sub-directory(ies) to a path in an intelligent way
707 string const addPath(string const & path, string const & path_2)
710 string const path2 = os::internal_path(path_2);
712 if (!path.empty() && path != "." && path != "./") {
713 buf = os::internal_path(path);
714 if (path[path.length() - 1] != '/')
718 if (!path2.empty()) {
719 string::size_type const p2start = path2.find_first_not_of('/');
720 string::size_type const p2end = path2.find_last_not_of('/');
721 string const tmp = path2.substr(p2start, p2end - p2start + 1);
728 string const changeExtension(string const & oldname, string const & extension)
730 string::size_type const last_slash = oldname.rfind('/');
731 string::size_type last_dot = oldname.rfind('.');
732 if (last_dot < last_slash && last_slash != string::npos)
733 last_dot = string::npos;
736 // Make sure the extension starts with a dot
737 if (!extension.empty() && extension[0] != '.')
738 ext= '.' + extension;
742 return os::internal_path(oldname.substr(0, last_dot) + ext);
746 string const removeExtension(string const & name)
748 return changeExtension(name, string());
752 string const addExtension(string const & name, string const & extension)
754 if (!extension.empty() && extension[0] != '.')
755 return name + '.' + extension;
756 return name + extension;
760 /// Return the extension of the file (not including the .)
761 string const getExtension(string const & name)
763 string::size_type const last_slash = name.rfind('/');
764 string::size_type const last_dot = name.rfind('.');
765 if (last_dot != string::npos &&
766 (last_slash == string::npos || last_dot > last_slash))
767 return name.substr(last_dot + 1,
768 name.length() - (last_dot + 1));
774 // the different filetypes and what they contain in one of the first lines
775 // (dots are any characters). (Herbert 20020131)
778 // EPS %!PS-Adobe-3.0 EPSF...
785 // PBM P1... or P4 (B/W)
786 // PGM P2... or P5 (Grayscale)
787 // PPM P3... or P6 (color)
788 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
789 // SGI \001\332... (decimal 474)
791 // TIFF II... or MM...
793 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
794 // ...static char *...
795 // XWD \000\000\000\151 (0x00006900) decimal 105
797 // GZIP \037\213 http://www.ietf.org/rfc/rfc1952.txt
798 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
799 // Z \037\235 UNIX compress
801 string const getFormatFromContents(FileName const & filename)
804 if (filename.empty() || !isFileReadable(filename))
807 ifstream ifs(filename.toFilesystemEncoding().c_str());
809 // Couldn't open file...
813 static string const gzipStamp = "\037\213";
816 static string const zipStamp = "PK";
819 static string const compressStamp = "\037\235";
821 // Maximum strings to read
822 int const max_count = 50;
827 bool firstLine = true;
828 while ((count++ < max_count) && format.empty()) {
830 lyxerr[Debug::GRAPHICS]
831 << "filetools(getFormatFromContents)\n"
832 << "\tFile type not recognised before EOF!"
838 string const stamp = str.substr(0, 2);
839 if (firstLine && str.size() >= 2) {
840 // at first we check for a zipped file, because this
841 // information is saved in the first bytes of the file!
842 // also some graphic formats which save the information
843 // in the first line, too.
844 if (prefixIs(str, gzipStamp)) {
847 } else if (stamp == zipStamp) {
850 } else if (stamp == compressStamp) {
854 } else if (stamp == "BM") {
857 } else if (stamp == "\001\332") {
861 // Don't need to use str.at(0), str.at(1) because
862 // we already know that str.size() >= 2
863 } else if (str[0] == 'P') {
879 } else if ((stamp == "II") || (stamp == "MM")) {
882 } else if (prefixIs(str,"%TGIF")) {
885 } else if (prefixIs(str,"#FIG")) {
888 } else if (prefixIs(str,"GIF")) {
891 } else if (str.size() > 3) {
892 int const c = ((str[0] << 24) & (str[1] << 16) &
893 (str[2] << 8) & str[3]);
904 else if (contains(str,"EPSF"))
905 // dummy, if we have wrong file description like
906 // %!PS-Adobe-2.0EPSF"
909 else if (contains(str,"Grace"))
912 else if (contains(str,"JFIF"))
915 else if (contains(str,"%PDF"))
918 else if (contains(str,"PNG"))
921 else if (contains(str,"%!PS-Adobe")) {
924 if (contains(str,"EPSF"))
930 else if (contains(str,"_bits[]"))
933 else if (contains(str,"XPM") || contains(str, "static char *"))
936 else if (contains(str,"BITPIX"))
940 if (!format.empty()) {
941 lyxerr[Debug::GRAPHICS]
942 << "Recognised Fileformat: " << format << endl;
946 lyxerr[Debug::GRAPHICS]
947 << "filetools(getFormatFromContents)\n"
948 << "\tCouldn't find a known format!\n";
953 /// check for zipped file
954 bool zippedFile(FileName const & name)
956 string const type = getFormatFromContents(name);
957 if (contains("gzip zip compress", type) && !type.empty())
963 string const unzippedFileName(string const & zipped_file)
965 string const ext = getExtension(zipped_file);
966 if (ext == "gz" || ext == "z" || ext == "Z")
967 return changeExtension(zipped_file, string());
968 return "unzipped_" + zipped_file;
972 FileName const unzipFile(FileName const & zipped_file, string const & unzipped_file)
974 FileName const tempfile = FileName(unzipped_file.empty() ?
975 unzippedFileName(zipped_file.toFilesystemEncoding()) :
978 string const command = "gunzip -c " +
979 zipped_file.toFilesystemEncoding() + " > " +
980 tempfile.toFilesystemEncoding();
982 one.startscript(Systemcall::Wait, command);
983 // test that command was executed successfully (anon)
984 // yes, please do. (Lgb)
989 docstring const makeDisplayPath(string const & path, unsigned int threshold)
992 string const home = package().home_dir();
994 // replace /home/blah with ~/
995 if (!home.empty() && prefixIs(str, home))
996 str = subst(str, home, "~");
998 if (str.length() <= threshold)
999 return from_utf8(os::external_path(str));
1001 string const prefix = ".../";
1004 while (str.length() > threshold)
1005 str = split(str, temp, '/');
1007 // Did we shorten everything away?
1009 // Yes, filename itself is too long.
1010 // Pick the start and the end of the filename.
1011 str = onlyFilename(path);
1012 string const head = str.substr(0, threshold / 2 - 3);
1014 string::size_type len = str.length();
1016 str.substr(len - threshold / 2 - 2, len - 1);
1017 str = head + "..." + tail;
1020 return from_utf8(os::external_path(prefix + str));
1024 bool readLink(string const & file, string & link, bool resolve)
1026 #ifdef HAVE_READLINK
1027 char linkbuffer[512];
1028 // Should be PATH_MAX but that needs autconf support
1029 int const nRead = ::readlink(file.c_str(),
1030 linkbuffer, sizeof(linkbuffer) - 1);
1033 linkbuffer[nRead] = '\0'; // terminator
1035 link = makeAbsPath(linkbuffer, onlyPath(file)).absFilename();
1045 cmd_ret const runCommand(string const & cmd)
1047 // FIXME: replace all calls to RunCommand with ForkedCall
1048 // (if the output is not needed) or the code in ispell.C
1049 // (if the output is needed).
1051 // One question is if we should use popen or
1052 // create our own popen based on fork, exec, pipe
1053 // of course the best would be to have a
1054 // pstream (process stream), with the
1055 // variants ipstream, opstream
1057 #if defined (HAVE_POPEN)
1058 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1059 #elif defined (HAVE__POPEN)
1060 FILE * inf = ::_popen(cmd.c_str(), os::popen_read_mode());
1062 #error No popen() function.
1065 // (Claus Hentschel) Check if popen was succesful ;-)
1067 lyxerr << "RunCommand:: could not start child process" << endl;
1068 return make_pair(-1, string());
1074 ret += static_cast<char>(c);
1078 #if defined (HAVE_PCLOSE)
1079 int const pret = pclose(inf);
1080 #elif defined (HAVE__PCLOSE)
1081 int const pret = _pclose(inf);
1083 #error No pclose() function.
1087 perror("RunCommand:: could not terminate child process");
1089 return make_pair(pret, ret);
1093 FileName const findtexfile(string const & fil, string const & /*format*/)
1095 /* There is no problem to extend this function too use other
1096 methods to look for files. It could be setup to look
1097 in environment paths and also if wanted as a last resort
1098 to a recursive find. One of the easier extensions would
1099 perhaps be to use the LyX file lookup methods. But! I am
1100 going to implement this until I see some demand for it.
1104 // If the file can be found directly, we just return a
1105 // absolute path version of it.
1106 FileName const absfile(makeAbsPath(fil));
1107 if (fs::exists(absfile.toFilesystemEncoding()))
1110 // No we try to find it using kpsewhich.
1111 // It seems from the kpsewhich manual page that it is safe to use
1112 // kpsewhich without --format: "When the --format option is not
1113 // given, the search path used when looking for a file is inferred
1114 // from the name given, by looking for a known extension. If no
1115 // known extension is found, the search path for TeX source files
1117 // However, we want to take advantage of the format sine almost all
1118 // the different formats has environment variables that can be used
1119 // to controll which paths to search. f.ex. bib looks in
1120 // BIBINPUTS and TEXBIB. Small list follows:
1121 // bib - BIBINPUTS, TEXBIB
1123 // graphic/figure - TEXPICTS, TEXINPUTS
1124 // ist - TEXINDEXSTYLE, INDEXSTYLE
1125 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1127 // tfm - TFMFONTS, TEXFONTS
1128 // This means that to use kpsewhich in the best possible way we
1129 // should help it by setting additional path in the approp. envir.var.
1130 string const kpsecmd = "kpsewhich " + fil;
1132 cmd_ret const c = runCommand(kpsecmd);
1134 lyxerr[Debug::LATEX] << "kpse status = " << c.first << '\n'
1135 << "kpse result = `" << rtrim(c.second, "\n\r")
1138 return FileName(os::internal_path(rtrim(to_utf8(from_filesystem8bit(c.second)),
1145 void removeAutosaveFile(string const & filename)
1147 string a = onlyPath(filename);
1149 a += onlyFilename(filename);
1151 FileName const autosave(a);
1152 if (fs::exists(autosave.toFilesystemEncoding()))
1157 void readBB_lyxerrMessage(FileName const & file, bool & zipped,
1158 string const & message)
1160 lyxerr[Debug::GRAPHICS] << "[readBB_from_PSFile] "
1161 << message << std::endl;
1162 #ifdef WITH_WARNINGS
1163 #warning Why is this func deleting a file? (Lgb)
1170 string const readBB_from_PSFile(FileName const & file)
1172 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1173 // It seems that every command in the header has an own line,
1174 // getline() should work for all files.
1175 // On the other hand some plot programs write the bb at the
1176 // end of the file. Than we have in the header:
1177 // %%BoundingBox: (atend)
1178 // In this case we must check the end.
1179 bool zipped = zippedFile(file);
1180 FileName const file_ = zipped ? unzipFile(file) : file;
1181 string const format = getFormatFromContents(file_);
1183 if (format != "eps" && format != "ps") {
1184 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1188 static boost::regex bbox_re(
1189 "^%%BoundingBox:\\s*([[:digit:]]+)\\s+([[:digit:]]+)\\s+([[:digit:]]+)\\s+([[:digit:]]+)");
1190 std::ifstream is(file_.toFilesystemEncoding().c_str());
1195 if (regex_match(s, what, bbox_re)) {
1196 // Our callers expect the tokens in the string
1197 // separated by single spaces.
1198 // FIXME: change return type from string to something
1201 os << what.str(1) << ' ' << what.str(2) << ' '
1202 << what.str(3) << ' ' << what.str(4);
1203 string const bb = os.str();
1204 readBB_lyxerrMessage(file_, zipped, bb);
1208 readBB_lyxerrMessage(file_, zipped, "no bb found");
1213 int compare_timestamps(FileName const & filename1, FileName const & filename2)
1215 // If the original is newer than the copy, then copy the original
1216 // to the new directory.
1218 string const file1 = filename1.toFilesystemEncoding();
1219 string const file2 = filename2.toFilesystemEncoding();
1221 if (fs::exists(file1) && fs::exists(file2)) {
1222 double const tmp = difftime(fs::last_write_time(file1),
1223 fs::last_write_time(file2));
1225 cmp = tmp > 0 ? 1 : -1;
1227 } else if (fs::exists(file1)) {
1229 } else if (fs::exists(file2)) {
1236 } //namespace support