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>
53 #ifndef CXX_GLOBAL_CSTD
62 using std::ostringstream;
65 namespace fs = boost::filesystem;
70 bool isLyXFilename(string const & filename)
72 return suffixIs(ascii_lowercase(filename), ".lyx");
76 bool isSGMLFilename(string const & filename)
78 return suffixIs(ascii_lowercase(filename), ".sgml");
82 string const latex_path(string const & original_path,
83 latex_path_extension extension,
86 // On cygwin, we may need windows or posix style paths.
87 string path = os::latex_path(original_path);
88 path = subst(path, "~", "\\string~");
89 if (path.find(' ') != string::npos) {
90 // We can't use '"' because " is sometimes active (e.g. if
91 // babel is loaded with the "german" option)
92 if (extension == EXCLUDE_EXTENSION) {
93 // ChangeExtension calls os::internal_path internally
94 // so don't use it to remove the extension.
95 string const ext = getExtension(path);
96 string const base = ext.empty() ?
98 path.substr(0, path.length() - ext.length() - 1);
99 // ChangeExtension calls os::internal_path internally
100 // so don't use it to re-add the extension.
101 path = "\\string\"" + base + "\\string\"." + ext;
103 path = "\\string\"" + path + "\\string\"";
107 return dots == ESCAPE_DOTS ? subst(path, ".", "\\lyxdot ") : path;
111 // Substitutes spaces with underscores in filename (and path)
112 string const makeLatexName(string const & file)
114 string name = onlyFilename(file);
115 string const path = onlyPath(file);
117 // ok so we scan through the string twice, but who cares.
118 string const keep = "abcdefghijklmnopqrstuvwxyz"
119 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
120 "@!'()*+,-./0123456789:;<=>?[]`|";
122 string::size_type pos = 0;
123 while ((pos = name.find_first_not_of(keep, pos)) != string::npos)
126 return addName(path, name);
130 string const quoteName(string const & name, quote_style style)
134 // This does not work for filenames containing " (windows)
135 // or ' (all other OSes). This can't be changed easily, since
136 // we would need to adapt the command line parser in
137 // Forkedcall::generateChild. Therefore we don't pass user
138 // filenames to child processes if possible. We store them in
139 // a python script instead, where we don't have these
141 return (os::shell() == os::UNIX) ?
145 return "\"" + subst(subst(name, "\\", "\\\\"), "\"", "\\\"")
148 // shut up stupid compiler
153 bool isFileReadable(FileName const & filename)
155 std::string const path = filename.toFilesystemEncoding();
156 return fs::exists(path) && !fs::is_directory(path) && fs::is_readable(path);
160 //returns true: dir writeable
161 // false: not writeable
162 bool isDirWriteable(FileName const & path)
164 LYXERR(Debug::FILES) << "isDirWriteable: " << path << endl;
166 FileName const tmpfl(tempName(path, "lyxwritetest"));
177 // Uses a string of paths separated by ";"s to find a file to open.
178 // Can't cope with pathnames with a ';' in them. Returns full path to file.
179 // If path entry begins with $$LyX/, use system_lyxdir
180 // If path entry begins with $$User/, use user_lyxdir
181 // Example: "$$User/doc;$$LyX/doc"
182 FileName const fileOpenSearch(string const & path, string const & name,
187 bool notfound = true;
188 string tmppath = split(path, path_element, ';');
190 while (notfound && !path_element.empty()) {
191 path_element = os::internal_path(path_element);
192 if (!suffixIs(path_element, '/'))
194 path_element = subst(path_element, "$$LyX",
195 package().system_support());
196 path_element = subst(path_element, "$$User",
197 package().user_support());
199 real_file = fileSearch(path_element, name, ext);
201 if (real_file.empty()) {
203 tmppath = split(tmppath, path_element, ';');
204 } while (!tmppath.empty() && path_element.empty());
214 /// Returns a vector of all files in directory dir having extension ext.
215 vector<FileName> const dirList(FileName const & dir, string const & ext)
217 // EXCEPTIONS FIXME. Rewrite needed when we turn on exceptions. (Lgb)
218 vector<FileName> dirlist;
220 string const encoded_dir = dir.toFilesystemEncoding();
221 if (!(fs::exists(encoded_dir) && fs::is_directory(encoded_dir))) {
223 << "Directory \"" << dir
224 << "\" does not exist to DirList." << endl;
229 if (!ext.empty() && ext[0] != '.')
233 fs::directory_iterator dit(encoded_dir);
234 fs::directory_iterator end;
235 for (; dit != end; ++dit) {
236 string const & fil = dit->leaf();
237 if (suffixIs(fil, extension))
238 dirlist.push_back(FileName::fromFilesystemEncoding(
239 encoded_dir + '/' + fil));
245 // Returns the real name of file name in directory path, with optional
247 FileName const fileSearch(string const & path, string const & name,
248 string const & ext, search_mode mode)
250 // if `name' is an absolute path, we ignore the setting of `path'
251 // Expand Environmentvariables in 'name'
252 string const tmpname = replaceEnvironmentPath(name);
253 FileName fullname(makeAbsPath(tmpname, path));
254 // search first without extension, then with it.
255 if (isFileReadable(fullname))
259 return mode == allow_unreadable ? fullname : FileName();
260 // Only add the extension if it is not already the extension of
262 if (getExtension(fullname.absFilename()) != ext)
263 fullname = FileName(addExtension(fullname.absFilename(), ext));
264 if (isFileReadable(fullname) || mode == allow_unreadable)
270 // Search the file name.ext in the subdirectory dir of
272 // 2) build_lyxdir (if not empty)
274 FileName const libFileSearch(string const & dir, string const & name,
277 FileName fullname = fileSearch(addPath(package().user_support(), dir),
279 if (!fullname.empty())
282 if (!package().build_support().empty())
283 fullname = fileSearch(addPath(package().build_support(), dir),
285 if (!fullname.empty())
288 return fileSearch(addPath(package().system_support(), dir), name, ext);
292 FileName const i18nLibFileSearch(string const & dir, string const & name,
295 // the following comments are from intl/dcigettext.c. We try
296 // to mimick this behaviour here.
297 /* The highest priority value is the `LANGUAGE' environment
298 variable. But we don't use the value if the currently
299 selected locale is the C locale. This is a GNU extension. */
300 /* [Otherwise] We have to proceed with the POSIX methods of
301 looking to `LC_ALL', `LC_xxx', and `LANG'. */
303 string lang = getEnv("LC_ALL");
305 lang = getEnv("LC_MESSAGES");
307 lang = getEnv("LANG");
313 string const language = getEnv("LANGUAGE");
314 if (lang != "C" && lang != "POSIX" && !language.empty())
318 lang = split(lang, l, ':');
319 while (!l.empty() && l != "C" && l != "POSIX") {
320 FileName const tmp = libFileSearch(addPath(dir, token(l, '_', 0)),
325 // to be removed later (JMarc)
326 FileName const tmpold = libFileSearch(dir,
327 token(l, '_', 0) + '_' + name,
329 if (!tmpold.empty()) {
330 lyxerr << "i18nLibFileSearch: File `" << tmpold
331 << "' has been found by the old method" <<endl;
335 lang = split(lang, l, ':');
338 return libFileSearch(dir, name, ext);
342 string const libScriptSearch(string const & command_in, quote_style style)
344 static string const token_scriptpath = "$$s/";
346 string command = command_in;
347 // Find the starting position of "$$s/"
348 string::size_type const pos1 = command.find(token_scriptpath);
349 if (pos1 == string::npos)
351 // Find the end of the "$$s/some_subdir/some_script" word within
352 // command. Assumes that the script name does not contain spaces.
353 string::size_type const start_script = pos1 + 4;
354 string::size_type const pos2 = command.find(' ', start_script);
355 string::size_type const size_script = pos2 == string::npos?
356 (command.size() - start_script) : pos2 - start_script;
358 // Does this script file exist?
359 string const script =
360 libFileSearch(".", command.substr(start_script, size_script)).absFilename();
362 if (script.empty()) {
363 // Replace "$$s/" with ""
364 command.erase(pos1, 4);
366 // Replace "$$s/foo/some_script" with "<path to>/some_script".
367 string::size_type const size_replace = size_script + 4;
368 command.replace(pos1, size_replace, quoteName(script, style));
377 FileName const createTmpDir(FileName const & tempdir, string const & mask)
380 << "createTmpDir: tempdir=`" << tempdir << "'\n"
381 << "createTmpDir: mask=`" << mask << '\'' << endl;
383 FileName const tmpfl(tempName(tempdir, mask));
384 // lyx::tempName actually creates a file to make sure that it
385 // stays unique. So we have to delete it before we can create
386 // a dir with the same name. Note also that we are not thread
387 // safe because of the gap between unlink and mkdir. (Lgb)
390 if (tmpfl.empty() || mkdir(tmpfl, 0700)) {
391 lyxerr << "LyX could not create the temporary directory '"
392 << tmpfl << "'" << endl;
402 bool destroyDir(FileName const & tmpdir)
405 return fs::remove_all(tmpdir.toFilesystemEncoding()) > 0;
406 } catch (fs::filesystem_error const & fe){
407 lyxerr << "Could not delete " << tmpdir << ". (" << fe.what() << ")" << std::endl;
413 string const createBufferTmpDir()
416 // We are in our own directory. Why bother to mangle name?
417 // In fact I wrote this code to circumvent a problematic behaviour
418 // (bug?) of EMX mkstemp().
420 package().temp_dir() + "/lyx_tmpbuf" +
421 convert<string>(count++);
423 if (mkdir(FileName(tmpfl), 0777)) {
424 lyxerr << "LyX could not create the temporary directory '"
425 << tmpfl << "'" << endl;
432 FileName const createLyXTmpDir(FileName const & deflt)
434 if (!deflt.empty() && deflt.absFilename() != "/tmp") {
435 if (mkdir(deflt, 0777)) {
436 if (isDirWriteable(deflt)) {
437 // deflt could not be created because it
438 // did exist already, so let's create our own
440 return createTmpDir(deflt, "lyx_tmpdir");
442 // some other error occured.
443 return createTmpDir(FileName("/tmp"), "lyx_tmpdir");
448 return createTmpDir(FileName("/tmp"), "lyx_tmpdir");
453 bool createDirectory(string const & path, int permission)
455 string temp = rtrim(os::internal_path(path), "/");
456 BOOST_ASSERT(!temp.empty());
457 return mkdir(FileName(temp), permission) == 0;
461 // Strip filename from path name
462 string const onlyPath(string const & filename)
464 // If empty filename, return empty
465 if (filename.empty())
468 // Find last / or start of filename
469 string::size_type j = filename.rfind('/');
470 return j == string::npos ? "./" : filename.substr(0, j + 1);
474 // Convert relative path into absolute path based on a basepath.
475 // If relpath is absolute, just use that.
476 // If basepath is empty, use CWD as base.
477 FileName const makeAbsPath(string const & relPath, string const & basePath)
479 // checks for already absolute path
480 if (os::is_absolute_path(relPath))
481 return FileName(relPath);
483 // Copies given paths
484 string tempRel = os::internal_path(relPath);
485 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
486 tempRel = subst(tempRel, "//", "/");
490 if (os::is_absolute_path(basePath))
493 tempBase = addPath(getcwd().absFilename(), basePath);
495 // Handle /./ at the end of the path
496 while (suffixIs(tempBase, "/./"))
497 tempBase.erase(tempBase.length() - 2);
499 // processes relative path
500 string rTemp = tempRel;
503 while (!rTemp.empty()) {
505 rTemp = split(rTemp, temp, '/');
507 if (temp == ".") continue;
509 // Remove one level of TempBase
510 string::difference_type i = tempBase.length() - 2;
513 while (i > 0 && tempBase[i] != '/')
516 tempBase.erase(i, string::npos);
519 } else if (temp.empty() && !rTemp.empty()) {
520 tempBase = os::current_root() + rTemp;
523 // Add this piece to TempBase
524 if (!suffixIs(tempBase, '/'))
530 // returns absolute path
531 return FileName(os::internal_path(tempBase));
535 // Correctly append filename to the pathname.
536 // If pathname is '.', then don't use pathname.
537 // Chops any path of filename.
538 string const addName(string const & path, string const & fname)
540 string const basename = onlyFilename(fname);
543 if (path != "." && path != "./" && !path.empty()) {
544 buf = os::internal_path(path);
545 if (!suffixIs(path, '/'))
549 return buf + basename;
553 // Strips path from filename
554 string const onlyFilename(string const & fname)
559 string::size_type j = fname.rfind('/');
560 if (j == string::npos) // no '/' in fname
564 return fname.substr(j + 1);
568 /// Returns true is path is absolute
569 bool absolutePath(string const & path)
571 return os::is_absolute_path(path);
575 // Create absolute path. If impossible, don't do anything
576 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
577 string const expandPath(string const & path)
579 // checks for already absolute path
580 string rTemp = replaceEnvironmentPath(path);
581 if (os::is_absolute_path(rTemp))
585 string const copy = rTemp;
588 rTemp = split(rTemp, temp, '/');
591 return getcwd().absFilename() + '/' + rTemp;
594 return package().home_dir() + '/' + rTemp;
597 return makeAbsPath(copy).absFilename();
599 // Don't know how to handle this
604 // Normalize a path. Constracts path/../path
605 // Can't handle "../../" or "/../" (Asger)
606 // Also converts paths like /foo//bar ==> /foo/bar
607 string const normalizePath(string const & path)
609 // Normalize paths like /foo//bar ==> /foo/bar
610 static boost::regex regex("/{2,}");
611 string const tmppath = boost::regex_merge(path, regex, "/");
613 fs::path const npath = fs::path(tmppath, fs::no_check).normalize();
615 if (!npath.is_complete())
616 return "./" + npath.string() + '/';
618 return npath.string() + '/';
622 string const getFileContents(FileName const & fname)
624 string const encodedname = fname.toFilesystemEncoding();
625 if (fs::exists(encodedname)) {
626 ifstream ifs(encodedname.c_str());
634 lyxerr << "LyX was not able to read file '" << fname << '\'' << endl;
639 // Search the string for ${VAR} and $VAR and replace VAR using getenv.
640 string const replaceEnvironmentPath(string const & path)
642 // ${VAR} is defined as
643 // $\{[A-Za-z_][A-Za-z_0-9]*\}
644 static string const envvar_br = "[$]\\{([A-Za-z_][A-Za-z_0-9]*)\\}";
646 // $VAR is defined as:
647 // $\{[A-Za-z_][A-Za-z_0-9]*\}
648 static string const envvar = "[$]([A-Za-z_][A-Za-z_0-9]*)";
650 static boost::regex envvar_br_re("(.*)" + envvar_br + "(.*)");
651 static boost::regex envvar_re("(.*)" + envvar + "(.*)");
654 string result = path;
656 regex_match(result, what, envvar_br_re);
657 if (!what[0].matched) {
658 regex_match(result, what, envvar_re);
659 if (!what[0].matched)
662 result = what.str(1) + getEnv(what.str(2)) + what.str(3);
668 // Make relative path out of two absolute paths
669 docstring const makeRelPath(docstring const & abspath, docstring const & basepath)
670 // Makes relative path out of absolute path. If it is deeper than basepath,
671 // it's easy. If basepath and abspath share something (they are all deeper
672 // than some directory), it'll be rendered using ..'s. If they are completely
673 // different, then the absolute path will be used as relative path.
675 docstring::size_type const abslen = abspath.length();
676 docstring::size_type const baselen = basepath.length();
678 docstring::size_type i = os::common_path(abspath, basepath);
681 // actually no match - cannot make it relative
685 // Count how many dirs there are in basepath above match
686 // and append as many '..''s into relpath
688 docstring::size_type j = i;
689 while (j < baselen) {
690 if (basepath[j] == '/') {
691 if (j + 1 == baselen)
698 // Append relative stuff from common directory to abspath
699 if (abspath[i] == '/')
701 for (; i < abslen; ++i)
704 if (suffixIs(buf, '/'))
705 buf.erase(buf.length() - 1);
706 // Substitute empty with .
713 // Append sub-directory(ies) to a path in an intelligent way
714 string const addPath(string const & path, string const & path_2)
717 string const path2 = os::internal_path(path_2);
719 if (!path.empty() && path != "." && path != "./") {
720 buf = os::internal_path(path);
721 if (path[path.length() - 1] != '/')
725 if (!path2.empty()) {
726 string::size_type const p2start = path2.find_first_not_of('/');
727 string::size_type const p2end = path2.find_last_not_of('/');
728 string const tmp = path2.substr(p2start, p2end - p2start + 1);
735 string const changeExtension(string const & oldname, string const & extension)
737 string::size_type const last_slash = oldname.rfind('/');
738 string::size_type last_dot = oldname.rfind('.');
739 if (last_dot < last_slash && last_slash != string::npos)
740 last_dot = string::npos;
743 // Make sure the extension starts with a dot
744 if (!extension.empty() && extension[0] != '.')
745 ext= '.' + extension;
749 return os::internal_path(oldname.substr(0, last_dot) + ext);
753 string const removeExtension(string const & name)
755 return changeExtension(name, string());
759 string const addExtension(string const & name, string const & extension)
761 if (!extension.empty() && extension[0] != '.')
762 return name + '.' + extension;
763 return name + extension;
767 /// Return the extension of the file (not including the .)
768 string const getExtension(string const & name)
770 string::size_type const last_slash = name.rfind('/');
771 string::size_type const last_dot = name.rfind('.');
772 if (last_dot != string::npos &&
773 (last_slash == string::npos || last_dot > last_slash))
774 return name.substr(last_dot + 1,
775 name.length() - (last_dot + 1));
781 // the different filetypes and what they contain in one of the first lines
782 // (dots are any characters). (Herbert 20020131)
785 // EPS %!PS-Adobe-3.0 EPSF...
792 // PBM P1... or P4 (B/W)
793 // PGM P2... or P5 (Grayscale)
794 // PPM P3... or P6 (color)
795 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
796 // SGI \001\332... (decimal 474)
798 // TIFF II... or MM...
800 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
801 // ...static char *...
802 // XWD \000\000\000\151 (0x00006900) decimal 105
804 // GZIP \037\213 http://www.ietf.org/rfc/rfc1952.txt
805 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
806 // Z \037\235 UNIX compress
808 string const getFormatFromContents(FileName const & filename)
811 if (filename.empty() || !isFileReadable(filename))
814 ifstream ifs(filename.toFilesystemEncoding().c_str());
816 // Couldn't open file...
820 static string const gzipStamp = "\037\213";
823 static string const zipStamp = "PK";
826 static string const compressStamp = "\037\235";
828 // Maximum strings to read
829 int const max_count = 50;
834 bool firstLine = true;
835 while ((count++ < max_count) && format.empty()) {
837 LYXERR(Debug::GRAPHICS)
838 << "filetools(getFormatFromContents)\n"
839 << "\tFile type not recognised before EOF!"
845 string const stamp = str.substr(0, 2);
846 if (firstLine && str.size() >= 2) {
847 // at first we check for a zipped file, because this
848 // information is saved in the first bytes of the file!
849 // also some graphic formats which save the information
850 // in the first line, too.
851 if (prefixIs(str, gzipStamp)) {
854 } else if (stamp == zipStamp) {
857 } else if (stamp == compressStamp) {
861 } else if (stamp == "BM") {
864 } else if (stamp == "\001\332") {
868 // Don't need to use str.at(0), str.at(1) because
869 // we already know that str.size() >= 2
870 } else if (str[0] == 'P') {
886 } else if ((stamp == "II") || (stamp == "MM")) {
889 } else if (prefixIs(str,"%TGIF")) {
892 } else if (prefixIs(str,"#FIG")) {
895 } else if (prefixIs(str,"GIF")) {
898 } else if (str.size() > 3) {
899 int const c = ((str[0] << 24) & (str[1] << 16) &
900 (str[2] << 8) & str[3]);
911 else if (contains(str,"EPSF"))
912 // dummy, if we have wrong file description like
913 // %!PS-Adobe-2.0EPSF"
916 else if (contains(str,"Grace"))
919 else if (contains(str,"JFIF"))
922 else if (contains(str,"%PDF"))
925 else if (contains(str,"PNG"))
928 else if (contains(str,"%!PS-Adobe")) {
931 if (contains(str,"EPSF"))
937 else if (contains(str,"_bits[]"))
940 else if (contains(str,"XPM") || contains(str, "static char *"))
943 else if (contains(str,"BITPIX"))
947 if (!format.empty()) {
948 LYXERR(Debug::GRAPHICS)
949 << "Recognised Fileformat: " << format << endl;
953 LYXERR(Debug::GRAPHICS)
954 << "filetools(getFormatFromContents)\n"
955 << "\tCouldn't find a known format!\n";
960 /// check for zipped file
961 bool zippedFile(FileName const & name)
963 string const type = getFormatFromContents(name);
964 if (contains("gzip zip compress", type) && !type.empty())
970 string const unzippedFileName(string const & zipped_file)
972 string const ext = getExtension(zipped_file);
973 if (ext == "gz" || ext == "z" || ext == "Z")
974 return changeExtension(zipped_file, string());
975 return "unzipped_" + zipped_file;
979 FileName const unzipFile(FileName const & zipped_file, string const & unzipped_file)
981 FileName const tempfile = FileName(unzipped_file.empty() ?
982 unzippedFileName(zipped_file.toFilesystemEncoding()) :
985 string const command = "gunzip -c " +
986 zipped_file.toFilesystemEncoding() + " > " +
987 tempfile.toFilesystemEncoding();
989 one.startscript(Systemcall::Wait, command);
990 // test that command was executed successfully (anon)
991 // yes, please do. (Lgb)
996 docstring const makeDisplayPath(string const & path, unsigned int threshold)
1000 // If file is from LyXDir, display it as if it were relative.
1001 string const system = package().system_support();
1002 if (prefixIs(str, system) && str != system)
1003 return from_utf8("[" + str.erase(0, system.length()) + "]");
1005 // replace /home/blah with ~/
1006 string const home = package().home_dir();
1007 if (!home.empty() && prefixIs(str, home))
1008 str = subst(str, home, "~");
1010 if (str.length() <= threshold)
1011 return from_utf8(os::external_path(str));
1013 string const prefix = ".../";
1016 while (str.length() > threshold)
1017 str = split(str, temp, '/');
1019 // Did we shorten everything away?
1021 // Yes, filename itself is too long.
1022 // Pick the start and the end of the filename.
1023 str = onlyFilename(path);
1024 string const head = str.substr(0, threshold / 2 - 3);
1026 string::size_type len = str.length();
1028 str.substr(len - threshold / 2 - 2, len - 1);
1029 str = head + "..." + tail;
1032 return from_utf8(os::external_path(prefix + str));
1036 bool readLink(string const & file, string & link, bool resolve)
1038 #ifdef HAVE_READLINK
1039 char linkbuffer[512];
1040 // Should be PATH_MAX but that needs autconf support
1041 int const nRead = ::readlink(file.c_str(),
1042 linkbuffer, sizeof(linkbuffer) - 1);
1045 linkbuffer[nRead] = '\0'; // terminator
1047 link = makeAbsPath(linkbuffer, onlyPath(file)).absFilename();
1057 cmd_ret const runCommand(string const & cmd)
1059 // FIXME: replace all calls to RunCommand with ForkedCall
1060 // (if the output is not needed) or the code in ispell.C
1061 // (if the output is needed).
1063 // One question is if we should use popen or
1064 // create our own popen based on fork, exec, pipe
1065 // of course the best would be to have a
1066 // pstream (process stream), with the
1067 // variants ipstream, opstream
1069 #if defined (HAVE_POPEN)
1070 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1071 #elif defined (HAVE__POPEN)
1072 FILE * inf = ::_popen(cmd.c_str(), os::popen_read_mode());
1074 #error No popen() function.
1077 // (Claus Hentschel) Check if popen was succesful ;-)
1079 lyxerr << "RunCommand:: could not start child process" << endl;
1080 return make_pair(-1, string());
1086 ret += static_cast<char>(c);
1090 #if defined (HAVE_PCLOSE)
1091 int const pret = pclose(inf);
1092 #elif defined (HAVE__PCLOSE)
1093 int const pret = _pclose(inf);
1095 #error No pclose() function.
1099 perror("RunCommand:: could not terminate child process");
1101 return make_pair(pret, ret);
1105 FileName const findtexfile(string const & fil, string const & /*format*/)
1107 /* There is no problem to extend this function too use other
1108 methods to look for files. It could be setup to look
1109 in environment paths and also if wanted as a last resort
1110 to a recursive find. One of the easier extensions would
1111 perhaps be to use the LyX file lookup methods. But! I am
1112 going to implement this until I see some demand for it.
1116 // If the file can be found directly, we just return a
1117 // absolute path version of it.
1118 FileName const absfile(makeAbsPath(fil));
1119 if (fs::exists(absfile.toFilesystemEncoding()))
1122 // No we try to find it using kpsewhich.
1123 // It seems from the kpsewhich manual page that it is safe to use
1124 // kpsewhich without --format: "When the --format option is not
1125 // given, the search path used when looking for a file is inferred
1126 // from the name given, by looking for a known extension. If no
1127 // known extension is found, the search path for TeX source files
1129 // However, we want to take advantage of the format sine almost all
1130 // the different formats has environment variables that can be used
1131 // to controll which paths to search. f.ex. bib looks in
1132 // BIBINPUTS and TEXBIB. Small list follows:
1133 // bib - BIBINPUTS, TEXBIB
1135 // graphic/figure - TEXPICTS, TEXINPUTS
1136 // ist - TEXINDEXSTYLE, INDEXSTYLE
1137 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1139 // tfm - TFMFONTS, TEXFONTS
1140 // This means that to use kpsewhich in the best possible way we
1141 // should help it by setting additional path in the approp. envir.var.
1142 string const kpsecmd = "kpsewhich " + fil;
1144 cmd_ret const c = runCommand(kpsecmd);
1146 LYXERR(Debug::LATEX) << "kpse status = " << c.first << '\n'
1147 << "kpse result = `" << rtrim(c.second, "\n\r")
1150 return FileName(os::internal_path(rtrim(to_utf8(from_filesystem8bit(c.second)),
1157 void removeAutosaveFile(string const & filename)
1159 string a = onlyPath(filename);
1161 a += onlyFilename(filename);
1163 FileName const autosave(a);
1164 if (fs::exists(autosave.toFilesystemEncoding()))
1169 void readBB_lyxerrMessage(FileName const & file, bool & zipped,
1170 string const & message)
1172 LYXERR(Debug::GRAPHICS) << "[readBB_from_PSFile] "
1173 << message << std::endl;
1174 #ifdef WITH_WARNINGS
1175 #warning Why is this func deleting a file? (Lgb)
1182 string const readBB_from_PSFile(FileName const & file)
1184 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1185 // It seems that every command in the header has an own line,
1186 // getline() should work for all files.
1187 // On the other hand some plot programs write the bb at the
1188 // end of the file. Than we have in the header:
1189 // %%BoundingBox: (atend)
1190 // In this case we must check the end.
1191 bool zipped = zippedFile(file);
1192 FileName const file_ = zipped ? unzipFile(file) : file;
1193 string const format = getFormatFromContents(file_);
1195 if (format != "eps" && format != "ps") {
1196 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1200 static boost::regex bbox_re(
1201 "^%%BoundingBox:\\s*([[:digit:]]+)\\s+([[:digit:]]+)\\s+([[:digit:]]+)\\s+([[:digit:]]+)");
1202 std::ifstream is(file_.toFilesystemEncoding().c_str());
1207 if (regex_match(s, what, bbox_re)) {
1208 // Our callers expect the tokens in the string
1209 // separated by single spaces.
1210 // FIXME: change return type from string to something
1213 os << what.str(1) << ' ' << what.str(2) << ' '
1214 << what.str(3) << ' ' << what.str(4);
1215 string const bb = os.str();
1216 readBB_lyxerrMessage(file_, zipped, bb);
1220 readBB_lyxerrMessage(file_, zipped, "no bb found");
1225 int compare_timestamps(FileName const & filename1, FileName const & filename2)
1227 // If the original is newer than the copy, then copy the original
1228 // to the new directory.
1230 string const file1 = filename1.toFilesystemEncoding();
1231 string const file2 = filename2.toFilesystemEncoding();
1233 if (fs::exists(file1) && fs::exists(file2)) {
1234 double const tmp = difftime(fs::last_write_time(file1),
1235 fs::last_write_time(file2));
1237 cmp = tmp > 0 ? 1 : -1;
1239 } else if (fs::exists(file1)) {
1241 } else if (fs::exists(file2)) {
1248 } //namespace support