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(addPath(dir, token(l, '_', 0)),
328 // to be removed later (JMarc)
329 FileName const tmpold = libFileSearch(dir,
330 token(l, '_', 0) + '_' + name,
332 if (!tmpold.empty()) {
333 lyxerr << "i18nLibFileSearch: File `" << tmpold
334 << "' has been found by the old method" <<endl;
338 lang = split(lang, l, ':');
341 return libFileSearch(dir, name, ext);
345 string const libScriptSearch(string const & command_in, quote_style style)
347 static string const token_scriptpath = "$$s/";
349 string command = command_in;
350 // Find the starting position of "$$s/"
351 string::size_type const pos1 = command.find(token_scriptpath);
352 if (pos1 == string::npos)
354 // Find the end of the "$$s/some_subdir/some_script" word within
355 // command. Assumes that the script name does not contain spaces.
356 string::size_type const start_script = pos1 + 4;
357 string::size_type const pos2 = command.find(' ', start_script);
358 string::size_type const size_script = pos2 == string::npos?
359 (command.size() - start_script) : pos2 - start_script;
361 // Does this script file exist?
362 string const script =
363 libFileSearch(".", command.substr(start_script, size_script)).absFilename();
365 if (script.empty()) {
366 // Replace "$$s/" with ""
367 command.erase(pos1, 4);
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));
380 FileName const createTmpDir(FileName const & tempdir, string const & mask)
383 << "createTmpDir: tempdir=`" << tempdir << "'\n"
384 << "createTmpDir: mask=`" << mask << '\'' << endl;
386 FileName const tmpfl(tempName(tempdir, mask));
387 // lyx::tempName actually creates a file to make sure that it
388 // stays unique. So we have to delete it before we can create
389 // a dir with the same name. Note also that we are not thread
390 // safe because of the gap between unlink and mkdir. (Lgb)
393 if (tmpfl.empty() || mkdir(tmpfl, 0700)) {
394 lyxerr << "LyX could not create the temporary directory '"
395 << tmpfl << "'" << endl;
405 bool destroyDir(FileName const & tmpdir)
408 return fs::remove_all(tmpdir.toFilesystemEncoding()) > 0;
409 } catch (fs::filesystem_error const & fe){
410 lyxerr << "Could not delete " << tmpdir << ". (" << fe.what() << ")" << std::endl;
416 string const createBufferTmpDir()
419 // We are in our own directory. Why bother to mangle name?
420 // In fact I wrote this code to circumvent a problematic behaviour
421 // (bug?) of EMX mkstemp().
423 package().temp_dir() + "/lyx_tmpbuf" +
424 convert<string>(count++);
426 if (mkdir(FileName(tmpfl), 0777)) {
427 lyxerr << "LyX could not create the temporary directory '"
428 << tmpfl << "'" << endl;
435 FileName const createLyXTmpDir(FileName const & deflt)
437 if (!deflt.empty() && deflt.absFilename() != "/tmp") {
438 if (mkdir(deflt, 0777)) {
439 if (isDirWriteable(deflt)) {
440 // deflt could not be created because it
441 // did exist already, so let's create our own
443 return createTmpDir(deflt, "lyx_tmpdir");
445 // some other error occured.
446 return createTmpDir(FileName("/tmp"), "lyx_tmpdir");
451 return createTmpDir(FileName("/tmp"), "lyx_tmpdir");
456 bool createDirectory(string const & path, int permission)
458 string temp = rtrim(os::internal_path(path), "/");
459 BOOST_ASSERT(!temp.empty());
460 return mkdir(FileName(temp), permission) == 0;
464 // Strip filename from path name
465 string const onlyPath(string const & filename)
467 // If empty filename, return empty
468 if (filename.empty())
471 // Find last / or start of filename
472 string::size_type j = filename.rfind('/');
473 return j == string::npos ? "./" : filename.substr(0, j + 1);
477 // Convert relative path into absolute path based on a basepath.
478 // If relpath is absolute, just use that.
479 // If basepath is empty, use CWD as base.
480 FileName const makeAbsPath(string const & relPath, string const & basePath)
482 // checks for already absolute path
483 if (os::is_absolute_path(relPath))
484 return FileName(relPath);
486 // Copies given paths
487 string tempRel = os::internal_path(relPath);
488 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
489 tempRel = subst(tempRel, "//", "/");
493 if (os::is_absolute_path(basePath))
496 tempBase = addPath(getcwd().absFilename(), basePath);
498 // Handle /./ at the end of the path
499 while (suffixIs(tempBase, "/./"))
500 tempBase.erase(tempBase.length() - 2);
502 // processes relative path
503 string rTemp = tempRel;
506 while (!rTemp.empty()) {
508 rTemp = split(rTemp, temp, '/');
510 if (temp == ".") continue;
512 // Remove one level of TempBase
513 string::difference_type i = tempBase.length() - 2;
516 while (i > 0 && tempBase[i] != '/')
519 tempBase.erase(i, string::npos);
522 } else if (temp.empty() && !rTemp.empty()) {
523 tempBase = os::current_root() + rTemp;
526 // Add this piece to TempBase
527 if (!suffixIs(tempBase, '/'))
533 // returns absolute path
534 return FileName(os::internal_path(tempBase));
538 // Correctly append filename to the pathname.
539 // If pathname is '.', then don't use pathname.
540 // Chops any path of filename.
541 string const addName(string const & path, string const & fname)
543 string const basename = onlyFilename(fname);
546 if (path != "." && path != "./" && !path.empty()) {
547 buf = os::internal_path(path);
548 if (!suffixIs(path, '/'))
552 return buf + basename;
556 // Strips path from filename
557 string const onlyFilename(string const & fname)
562 string::size_type j = fname.rfind('/');
563 if (j == string::npos) // no '/' in fname
567 return fname.substr(j + 1);
571 /// Returns true is path is absolute
572 bool absolutePath(string const & path)
574 return os::is_absolute_path(path);
578 // Create absolute path. If impossible, don't do anything
579 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
580 string const expandPath(string const & path)
582 // checks for already absolute path
583 string rTemp = replaceEnvironmentPath(path);
584 if (os::is_absolute_path(rTemp))
588 string const copy = rTemp;
591 rTemp = split(rTemp, temp, '/');
594 return getcwd().absFilename() + '/' + rTemp;
597 return package().home_dir() + '/' + rTemp;
600 return makeAbsPath(copy).absFilename();
602 // Don't know how to handle this
607 // Normalize a path. Constracts path/../path
608 // Can't handle "../../" or "/../" (Asger)
609 // Also converts paths like /foo//bar ==> /foo/bar
610 string const normalizePath(string const & path)
612 // Normalize paths like /foo//bar ==> /foo/bar
613 static boost::regex regex("/{2,}");
614 string const tmppath = boost::regex_merge(path, regex, "/");
616 fs::path const npath = fs::path(tmppath, fs::no_check).normalize();
618 if (!npath.is_complete())
619 return "./" + npath.string() + '/';
621 return npath.string() + '/';
625 string const getFileContents(FileName const & fname)
627 string const encodedname = fname.toFilesystemEncoding();
628 if (fs::exists(encodedname)) {
629 ifstream ifs(encodedname.c_str());
637 lyxerr << "LyX was not able to read file '" << fname << '\'' << endl;
642 // Search the string for ${VAR} and $VAR and replace VAR using getenv.
643 string const replaceEnvironmentPath(string const & path)
645 // ${VAR} is defined as
646 // $\{[A-Za-z_][A-Za-z_0-9]*\}
647 static string const envvar_br = "[$]\\{([A-Za-z_][A-Za-z_0-9]*)\\}";
649 // $VAR is defined as:
650 // $\{[A-Za-z_][A-Za-z_0-9]*\}
651 static string const envvar = "[$]([A-Za-z_][A-Za-z_0-9]*)";
653 static boost::regex envvar_br_re("(.*)" + envvar_br + "(.*)");
654 static boost::regex envvar_re("(.*)" + envvar + "(.*)");
657 string result = path;
659 regex_match(result, what, envvar_br_re);
660 if (!what[0].matched) {
661 regex_match(result, what, envvar_re);
662 if (!what[0].matched)
665 result = what.str(1) + getEnv(what.str(2)) + what.str(3);
671 // Make relative path out of two absolute paths
672 string const makeRelPath(string const & abspath, string const & basepath)
673 // Makes relative path out of absolute path. If it is deeper than basepath,
674 // it's easy. If basepath and abspath share something (they are all deeper
675 // than some directory), it'll be rendered using ..'s. If they are completely
676 // different, then the absolute path will be used as relative path.
678 string::size_type const abslen = abspath.length();
679 string::size_type const baselen = basepath.length();
681 string::size_type i = os::common_path(abspath, basepath);
684 // actually no match - cannot make it relative
688 // Count how many dirs there are in basepath above match
689 // and append as many '..''s into relpath
691 string::size_type j = i;
692 while (j < baselen) {
693 if (basepath[j] == '/') {
694 if (j + 1 == baselen)
701 // Append relative stuff from common directory to abspath
702 if (abspath[i] == '/')
704 for (; i < abslen; ++i)
707 if (suffixIs(buf, '/'))
708 buf.erase(buf.length() - 1);
709 // Substitute empty with .
716 // Append sub-directory(ies) to a path in an intelligent way
717 string const addPath(string const & path, string const & path_2)
720 string const path2 = os::internal_path(path_2);
722 if (!path.empty() && path != "." && path != "./") {
723 buf = os::internal_path(path);
724 if (path[path.length() - 1] != '/')
728 if (!path2.empty()) {
729 string::size_type const p2start = path2.find_first_not_of('/');
730 string::size_type const p2end = path2.find_last_not_of('/');
731 string const tmp = path2.substr(p2start, p2end - p2start + 1);
738 string const changeExtension(string const & oldname, string const & extension)
740 string::size_type const last_slash = oldname.rfind('/');
741 string::size_type last_dot = oldname.rfind('.');
742 if (last_dot < last_slash && last_slash != string::npos)
743 last_dot = string::npos;
746 // Make sure the extension starts with a dot
747 if (!extension.empty() && extension[0] != '.')
748 ext= '.' + extension;
752 return os::internal_path(oldname.substr(0, last_dot) + ext);
756 string const removeExtension(string const & name)
758 return changeExtension(name, string());
762 string const addExtension(string const & name, string const & extension)
764 if (!extension.empty() && extension[0] != '.')
765 return name + '.' + extension;
766 return name + extension;
770 /// Return the extension of the file (not including the .)
771 string const getExtension(string const & name)
773 string::size_type const last_slash = name.rfind('/');
774 string::size_type const last_dot = name.rfind('.');
775 if (last_dot != string::npos &&
776 (last_slash == string::npos || last_dot > last_slash))
777 return name.substr(last_dot + 1,
778 name.length() - (last_dot + 1));
784 // the different filetypes and what they contain in one of the first lines
785 // (dots are any characters). (Herbert 20020131)
788 // EPS %!PS-Adobe-3.0 EPSF...
795 // PBM P1... or P4 (B/W)
796 // PGM P2... or P5 (Grayscale)
797 // PPM P3... or P6 (color)
798 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
799 // SGI \001\332... (decimal 474)
801 // TIFF II... or MM...
803 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
804 // ...static char *...
805 // XWD \000\000\000\151 (0x00006900) decimal 105
807 // GZIP \037\213 http://www.ietf.org/rfc/rfc1952.txt
808 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
809 // Z \037\235 UNIX compress
811 string const getFormatFromContents(FileName const & filename)
814 if (filename.empty() || !isFileReadable(filename))
817 ifstream ifs(filename.toFilesystemEncoding().c_str());
819 // Couldn't open file...
823 static string const gzipStamp = "\037\213";
826 static string const zipStamp = "PK";
829 static string const compressStamp = "\037\235";
831 // Maximum strings to read
832 int const max_count = 50;
837 bool firstLine = true;
838 while ((count++ < max_count) && format.empty()) {
840 lyxerr[Debug::GRAPHICS]
841 << "filetools(getFormatFromContents)\n"
842 << "\tFile type not recognised before EOF!"
848 string const stamp = str.substr(0, 2);
849 if (firstLine && str.size() >= 2) {
850 // at first we check for a zipped file, because this
851 // information is saved in the first bytes of the file!
852 // also some graphic formats which save the information
853 // in the first line, too.
854 if (prefixIs(str, gzipStamp)) {
857 } else if (stamp == zipStamp) {
860 } else if (stamp == compressStamp) {
864 } else if (stamp == "BM") {
867 } else if (stamp == "\001\332") {
871 // Don't need to use str.at(0), str.at(1) because
872 // we already know that str.size() >= 2
873 } else if (str[0] == 'P') {
889 } else if ((stamp == "II") || (stamp == "MM")) {
892 } else if (prefixIs(str,"%TGIF")) {
895 } else if (prefixIs(str,"#FIG")) {
898 } else if (prefixIs(str,"GIF")) {
901 } else if (str.size() > 3) {
902 int const c = ((str[0] << 24) & (str[1] << 16) &
903 (str[2] << 8) & str[3]);
914 else if (contains(str,"EPSF"))
915 // dummy, if we have wrong file description like
916 // %!PS-Adobe-2.0EPSF"
919 else if (contains(str,"Grace"))
922 else if (contains(str,"JFIF"))
925 else if (contains(str,"%PDF"))
928 else if (contains(str,"PNG"))
931 else if (contains(str,"%!PS-Adobe")) {
934 if (contains(str,"EPSF"))
940 else if (contains(str,"_bits[]"))
943 else if (contains(str,"XPM") || contains(str, "static char *"))
946 else if (contains(str,"BITPIX"))
950 if (!format.empty()) {
951 lyxerr[Debug::GRAPHICS]
952 << "Recognised Fileformat: " << format << endl;
956 lyxerr[Debug::GRAPHICS]
957 << "filetools(getFormatFromContents)\n"
958 << "\tCouldn't find a known format!\n";
963 /// check for zipped file
964 bool zippedFile(FileName const & name)
966 string const type = getFormatFromContents(name);
967 if (contains("gzip zip compress", type) && !type.empty())
973 string const unzippedFileName(string const & zipped_file)
975 string const ext = getExtension(zipped_file);
976 if (ext == "gz" || ext == "z" || ext == "Z")
977 return changeExtension(zipped_file, string());
978 return "unzipped_" + zipped_file;
982 FileName const unzipFile(FileName const & zipped_file, string const & unzipped_file)
984 FileName const tempfile = FileName(unzipped_file.empty() ?
985 unzippedFileName(zipped_file.toFilesystemEncoding()) :
988 string const command = "gunzip -c " +
989 zipped_file.toFilesystemEncoding() + " > " +
990 tempfile.toFilesystemEncoding();
992 one.startscript(Systemcall::Wait, command);
993 // test that command was executed successfully (anon)
994 // yes, please do. (Lgb)
999 docstring const makeDisplayPath(string const & path, unsigned int threshold)
1002 string const home = package().home_dir();
1004 // replace /home/blah with ~/
1005 if (!home.empty() && prefixIs(str, home))
1006 str = subst(str, home, "~");
1008 if (str.length() <= threshold)
1009 return from_utf8(os::external_path(str));
1011 string const prefix = ".../";
1014 while (str.length() > threshold)
1015 str = split(str, temp, '/');
1017 // Did we shorten everything away?
1019 // Yes, filename itself is too long.
1020 // Pick the start and the end of the filename.
1021 str = onlyFilename(path);
1022 string const head = str.substr(0, threshold / 2 - 3);
1024 string::size_type len = str.length();
1026 str.substr(len - threshold / 2 - 2, len - 1);
1027 str = head + "..." + tail;
1030 return from_utf8(os::external_path(prefix + str));
1034 bool readLink(string const & file, string & link, bool resolve)
1036 #ifdef HAVE_READLINK
1037 char linkbuffer[512];
1038 // Should be PATH_MAX but that needs autconf support
1039 int const nRead = ::readlink(file.c_str(),
1040 linkbuffer, sizeof(linkbuffer) - 1);
1043 linkbuffer[nRead] = '\0'; // terminator
1045 link = makeAbsPath(linkbuffer, onlyPath(file)).absFilename();
1055 cmd_ret const runCommand(string const & cmd)
1057 // FIXME: replace all calls to RunCommand with ForkedCall
1058 // (if the output is not needed) or the code in ispell.C
1059 // (if the output is needed).
1061 // One question is if we should use popen or
1062 // create our own popen based on fork, exec, pipe
1063 // of course the best would be to have a
1064 // pstream (process stream), with the
1065 // variants ipstream, opstream
1067 #if defined (HAVE_POPEN)
1068 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1069 #elif defined (HAVE__POPEN)
1070 FILE * inf = ::_popen(cmd.c_str(), os::popen_read_mode());
1072 #error No popen() function.
1075 // (Claus Hentschel) Check if popen was succesful ;-)
1077 lyxerr << "RunCommand:: could not start child process" << endl;
1078 return make_pair(-1, string());
1084 ret += static_cast<char>(c);
1088 #if defined (HAVE_PCLOSE)
1089 int const pret = pclose(inf);
1090 #elif defined (HAVE__PCLOSE)
1091 int const pret = _pclose(inf);
1093 #error No pclose() function.
1097 perror("RunCommand:: could not terminate child process");
1099 return make_pair(pret, ret);
1103 FileName const findtexfile(string const & fil, string const & /*format*/)
1105 /* There is no problem to extend this function too use other
1106 methods to look for files. It could be setup to look
1107 in environment paths and also if wanted as a last resort
1108 to a recursive find. One of the easier extensions would
1109 perhaps be to use the LyX file lookup methods. But! I am
1110 going to implement this until I see some demand for it.
1114 // If the file can be found directly, we just return a
1115 // absolute path version of it.
1116 FileName const absfile(makeAbsPath(fil));
1117 if (fs::exists(absfile.toFilesystemEncoding()))
1120 // No we try to find it using kpsewhich.
1121 // It seems from the kpsewhich manual page that it is safe to use
1122 // kpsewhich without --format: "When the --format option is not
1123 // given, the search path used when looking for a file is inferred
1124 // from the name given, by looking for a known extension. If no
1125 // known extension is found, the search path for TeX source files
1127 // However, we want to take advantage of the format sine almost all
1128 // the different formats has environment variables that can be used
1129 // to controll which paths to search. f.ex. bib looks in
1130 // BIBINPUTS and TEXBIB. Small list follows:
1131 // bib - BIBINPUTS, TEXBIB
1133 // graphic/figure - TEXPICTS, TEXINPUTS
1134 // ist - TEXINDEXSTYLE, INDEXSTYLE
1135 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1137 // tfm - TFMFONTS, TEXFONTS
1138 // This means that to use kpsewhich in the best possible way we
1139 // should help it by setting additional path in the approp. envir.var.
1140 string const kpsecmd = "kpsewhich " + fil;
1142 cmd_ret const c = runCommand(kpsecmd);
1144 lyxerr[Debug::LATEX] << "kpse status = " << c.first << '\n'
1145 << "kpse result = `" << rtrim(c.second, "\n\r")
1148 return FileName(os::internal_path(rtrim(to_utf8(from_filesystem8bit(c.second)),
1155 void removeAutosaveFile(string const & filename)
1157 string a = onlyPath(filename);
1159 a += onlyFilename(filename);
1161 FileName const autosave(a);
1162 if (fs::exists(autosave.toFilesystemEncoding()))
1167 void readBB_lyxerrMessage(FileName const & file, bool & zipped,
1168 string const & message)
1170 lyxerr[Debug::GRAPHICS] << "[readBB_from_PSFile] "
1171 << message << std::endl;
1172 #ifdef WITH_WARNINGS
1173 #warning Why is this func deleting a file? (Lgb)
1180 string const readBB_from_PSFile(FileName const & file)
1182 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1183 // It seems that every command in the header has an own line,
1184 // getline() should work for all files.
1185 // On the other hand some plot programs write the bb at the
1186 // end of the file. Than we have in the header:
1187 // %%BoundingBox: (atend)
1188 // In this case we must check the end.
1189 bool zipped = zippedFile(file);
1190 FileName const file_ = zipped ? unzipFile(file) : file;
1191 string const format = getFormatFromContents(file_);
1193 if (format != "eps" && format != "ps") {
1194 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1198 static boost::regex bbox_re(
1199 "^%%BoundingBox:\\s*([[:digit:]]+)\\s+([[:digit:]]+)\\s+([[:digit:]]+)\\s+([[:digit:]]+)");
1200 std::ifstream is(file_.toFilesystemEncoding().c_str());
1205 if (regex_match(s, what, bbox_re)) {
1206 // Our callers expect the tokens in the string
1207 // separated by single spaces.
1208 // FIXME: change return type from string to something
1211 os << what.str(1) << ' ' << what.str(2) << ' '
1212 << what.str(3) << ' ' << what.str(4);
1213 string const bb = os.str();
1214 readBB_lyxerrMessage(file_, zipped, bb);
1218 readBB_lyxerrMessage(file_, zipped, "no bb found");
1223 int compare_timestamps(FileName const & filename1, FileName const & filename2)
1225 // If the original is newer than the copy, then copy the original
1226 // to the new directory.
1228 string const file1 = filename1.toFilesystemEncoding();
1229 string const file2 = filename2.toFilesystemEncoding();
1231 if (fs::exists(file1) && fs::exists(file2)) {
1232 double const tmp = difftime(fs::last_write_time(file1),
1233 fs::last_write_time(file2));
1235 cmp = tmp > 0 ? 1 : -1;
1237 } else if (fs::exists(file1)) {
1239 } else if (fs::exists(file2)) {
1246 } //namespace support