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 docstring const makeRelPath(docstring const & abspath, docstring 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 docstring::size_type const abslen = abspath.length();
679 docstring::size_type const baselen = basepath.length();
681 docstring::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 docstring::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)
1003 // If file is from LyXDir, display it as if it were relative.
1004 string const system = package().system_support();
1005 if (prefixIs(str, system) && str != system)
1006 return from_utf8("[" + str.erase(0, system.length()) + "]");
1008 // replace /home/blah with ~/
1009 string const home = package().home_dir();
1010 if (!home.empty() && prefixIs(str, home))
1011 str = subst(str, home, "~");
1013 if (str.length() <= threshold)
1014 return from_utf8(os::external_path(str));
1016 string const prefix = ".../";
1019 while (str.length() > threshold)
1020 str = split(str, temp, '/');
1022 // Did we shorten everything away?
1024 // Yes, filename itself is too long.
1025 // Pick the start and the end of the filename.
1026 str = onlyFilename(path);
1027 string const head = str.substr(0, threshold / 2 - 3);
1029 string::size_type len = str.length();
1031 str.substr(len - threshold / 2 - 2, len - 1);
1032 str = head + "..." + tail;
1035 return from_utf8(os::external_path(prefix + str));
1039 bool readLink(string const & file, string & link, bool resolve)
1041 #ifdef HAVE_READLINK
1042 char linkbuffer[512];
1043 // Should be PATH_MAX but that needs autconf support
1044 int const nRead = ::readlink(file.c_str(),
1045 linkbuffer, sizeof(linkbuffer) - 1);
1048 linkbuffer[nRead] = '\0'; // terminator
1050 link = makeAbsPath(linkbuffer, onlyPath(file)).absFilename();
1060 cmd_ret const runCommand(string const & cmd)
1062 // FIXME: replace all calls to RunCommand with ForkedCall
1063 // (if the output is not needed) or the code in ispell.C
1064 // (if the output is needed).
1066 // One question is if we should use popen or
1067 // create our own popen based on fork, exec, pipe
1068 // of course the best would be to have a
1069 // pstream (process stream), with the
1070 // variants ipstream, opstream
1072 #if defined (HAVE_POPEN)
1073 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1074 #elif defined (HAVE__POPEN)
1075 FILE * inf = ::_popen(cmd.c_str(), os::popen_read_mode());
1077 #error No popen() function.
1080 // (Claus Hentschel) Check if popen was succesful ;-)
1082 lyxerr << "RunCommand:: could not start child process" << endl;
1083 return make_pair(-1, string());
1089 ret += static_cast<char>(c);
1093 #if defined (HAVE_PCLOSE)
1094 int const pret = pclose(inf);
1095 #elif defined (HAVE__PCLOSE)
1096 int const pret = _pclose(inf);
1098 #error No pclose() function.
1102 perror("RunCommand:: could not terminate child process");
1104 return make_pair(pret, ret);
1108 FileName const findtexfile(string const & fil, string const & /*format*/)
1110 /* There is no problem to extend this function too use other
1111 methods to look for files. It could be setup to look
1112 in environment paths and also if wanted as a last resort
1113 to a recursive find. One of the easier extensions would
1114 perhaps be to use the LyX file lookup methods. But! I am
1115 going to implement this until I see some demand for it.
1119 // If the file can be found directly, we just return a
1120 // absolute path version of it.
1121 FileName const absfile(makeAbsPath(fil));
1122 if (fs::exists(absfile.toFilesystemEncoding()))
1125 // No we try to find it using kpsewhich.
1126 // It seems from the kpsewhich manual page that it is safe to use
1127 // kpsewhich without --format: "When the --format option is not
1128 // given, the search path used when looking for a file is inferred
1129 // from the name given, by looking for a known extension. If no
1130 // known extension is found, the search path for TeX source files
1132 // However, we want to take advantage of the format sine almost all
1133 // the different formats has environment variables that can be used
1134 // to controll which paths to search. f.ex. bib looks in
1135 // BIBINPUTS and TEXBIB. Small list follows:
1136 // bib - BIBINPUTS, TEXBIB
1138 // graphic/figure - TEXPICTS, TEXINPUTS
1139 // ist - TEXINDEXSTYLE, INDEXSTYLE
1140 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1142 // tfm - TFMFONTS, TEXFONTS
1143 // This means that to use kpsewhich in the best possible way we
1144 // should help it by setting additional path in the approp. envir.var.
1145 string const kpsecmd = "kpsewhich " + fil;
1147 cmd_ret const c = runCommand(kpsecmd);
1149 LYXERR(Debug::LATEX) << "kpse status = " << c.first << '\n'
1150 << "kpse result = `" << rtrim(c.second, "\n\r")
1153 return FileName(os::internal_path(rtrim(to_utf8(from_filesystem8bit(c.second)),
1160 void removeAutosaveFile(string const & filename)
1162 string a = onlyPath(filename);
1164 a += onlyFilename(filename);
1166 FileName const autosave(a);
1167 if (fs::exists(autosave.toFilesystemEncoding()))
1172 void readBB_lyxerrMessage(FileName const & file, bool & zipped,
1173 string const & message)
1175 LYXERR(Debug::GRAPHICS) << "[readBB_from_PSFile] "
1176 << message << std::endl;
1177 #ifdef WITH_WARNINGS
1178 #warning Why is this func deleting a file? (Lgb)
1185 string const readBB_from_PSFile(FileName const & file)
1187 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1188 // It seems that every command in the header has an own line,
1189 // getline() should work for all files.
1190 // On the other hand some plot programs write the bb at the
1191 // end of the file. Than we have in the header:
1192 // %%BoundingBox: (atend)
1193 // In this case we must check the end.
1194 bool zipped = zippedFile(file);
1195 FileName const file_ = zipped ? unzipFile(file) : file;
1196 string const format = getFormatFromContents(file_);
1198 if (format != "eps" && format != "ps") {
1199 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1203 static boost::regex bbox_re(
1204 "^%%BoundingBox:\\s*([[:digit:]]+)\\s+([[:digit:]]+)\\s+([[:digit:]]+)\\s+([[:digit:]]+)");
1205 std::ifstream is(file_.toFilesystemEncoding().c_str());
1210 if (regex_match(s, what, bbox_re)) {
1211 // Our callers expect the tokens in the string
1212 // separated by single spaces.
1213 // FIXME: change return type from string to something
1216 os << what.str(1) << ' ' << what.str(2) << ' '
1217 << what.str(3) << ' ' << what.str(4);
1218 string const bb = os.str();
1219 readBB_lyxerrMessage(file_, zipped, bb);
1223 readBB_lyxerrMessage(file_, zipped, "no bb found");
1228 int compare_timestamps(FileName const & filename1, FileName const & filename2)
1230 // If the original is newer than the copy, then copy the original
1231 // to the new directory.
1233 string const file1 = filename1.toFilesystemEncoding();
1234 string const file2 = filename2.toFilesystemEncoding();
1236 if (fs::exists(file1) && fs::exists(file2)) {
1237 double const tmp = difftime(fs::last_write_time(file1),
1238 fs::last_write_time(file2));
1240 cmp = tmp > 0 ? 1 : -1;
1242 } else if (fs::exists(file1)) {
1244 } else if (fs::exists(file2)) {
1251 } //namespace support