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)
87 string path = subst(original_path, "\\", "/");
88 path = subst(path, "~", "\\string~");
89 if (path.find(' ') != string::npos)
90 path = '"' + path + '"';
95 // Substitutes spaces with underscores in filename (and path)
96 string const MakeLatexName(string const & file)
98 string name = OnlyFilename(file);
99 string const path = OnlyPath(file);
101 for (string::size_type i = 0; i < name.length(); ++i) {
102 name[i] &= 0x7f; // set 8th bit to 0
105 // ok so we scan through the string twice, but who cares.
106 string const keep("abcdefghijklmnopqrstuvwxyz"
107 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
108 "@!\"'()*+,-./0123456789:;<=>?[]`|");
110 string::size_type pos = 0;
111 while ((pos = name.find_first_not_of(keep, pos)) != string::npos) {
114 return AddName(path, name);
118 string const QuoteName(string const & name)
120 return (os::shell() == os::UNIX) ?
126 // Is a file readable ?
127 bool IsFileReadable(string const & path)
129 return fs::exists(path) && !fs::is_directory(path) && fs::is_readable(path);
133 //returns true: dir writeable
134 // false: not writeable
135 bool IsDirWriteable(string const & path)
137 lyxerr[Debug::FILES] << "IsDirWriteable: " << path << endl;
139 string const tmpfl(tempName(path, "lyxwritetest"));
149 // Uses a string of paths separated by ";"s to find a file to open.
150 // Can't cope with pathnames with a ';' in them. Returns full path to file.
151 // If path entry begins with $$LyX/, use system_lyxdir
152 // If path entry begins with $$User/, use user_lyxdir
153 // Example: "$$User/doc;$$LyX/doc"
154 string const FileOpenSearch(string const & path, string const & name,
159 bool notfound = true;
160 string tmppath = split(path, path_element, ';');
162 while (notfound && !path_element.empty()) {
163 path_element = os::internal_path(path_element);
164 if (!suffixIs(path_element, '/'))
166 path_element = subst(path_element, "$$LyX",
167 package().system_support());
168 path_element = subst(path_element, "$$User",
169 package().user_support());
171 real_file = FileSearch(path_element, name, ext);
173 if (real_file.empty()) {
175 tmppath = split(tmppath, path_element, ';');
176 } while (!tmppath.empty() && path_element.empty());
182 if (ext.empty() && notfound) {
183 real_file = FileOpenSearch(path, name, "exe");
184 if (notfound) real_file = FileOpenSearch(path, name, "cmd");
191 /// Returns a vector of all files in directory dir having extension ext.
192 vector<string> const DirList(string const & dir, string const & ext)
194 // EXCEPTIONS FIXME. Rewrite needed when we turn on exceptions. (Lgb)
195 vector<string> dirlist;
197 if (!(fs::exists(dir) && fs::is_directory(dir))) {
199 << "Directory \"" << dir
200 << "\" does not exist to DirList." << endl;
205 if (!ext.empty() && ext[0] != '.')
209 fs::directory_iterator dit(dir);
210 fs::directory_iterator end;
211 for (; dit != end; ++dit) {
212 string const & fil = dit->leaf();
213 if (suffixIs(fil, extension)) {
214 dirlist.push_back(fil);
221 // Returns the real name of file name in directory path, with optional
223 string const FileSearch(string const & path, string const & name,
226 // if `name' is an absolute path, we ignore the setting of `path'
227 // Expand Environmentvariables in 'name'
228 string const tmpname = ReplaceEnvironmentPath(name);
229 string fullname = MakeAbsPath(tmpname, path);
230 // search first without extension, then with it.
231 if (IsFileReadable(fullname))
233 else if (ext.empty())
235 else { // Is it not more reasonable to use ChangeExtension()? (SMiyata)
238 if (IsFileReadable(fullname))
246 // Search the file name.ext in the subdirectory dir of
248 // 2) build_lyxdir (if not empty)
250 string const LibFileSearch(string const & dir, string const & name,
253 string fullname = FileSearch(AddPath(package().user_support(), dir),
255 if (!fullname.empty())
258 if (!package().build_support().empty())
259 fullname = FileSearch(AddPath(package().build_support(), dir),
261 if (!fullname.empty())
264 return FileSearch(AddPath(package().system_support(), dir), name, ext);
269 i18nLibFileSearch(string const & dir, string const & name,
272 // the following comments are from intl/dcigettext.c. We try
273 // to mimick this behaviour here.
274 /* The highest priority value is the `LANGUAGE' environment
275 variable. But we don't use the value if the currently
276 selected locale is the C locale. This is a GNU extension. */
277 /* [Otherwise] We have to proceed with the POSIX methods of
278 looking to `LC_ALL', `LC_xxx', and `LANG'. */
280 string lang = getEnv("LC_ALL");
282 lang = getEnv("LC_MESSAGES");
284 lang = getEnv("LANG");
290 string const language = getEnv("LANGUAGE");
291 if (lang != "C" && lang != "POSIX" && !language.empty())
295 lang = split(lang, l, ':');
296 while (!l.empty() && l != "C" && l != "POSIX") {
297 string const tmp = LibFileSearch(dir,
298 token(l, '_', 0) + '_' + name,
302 lang = split(lang, l, ':');
305 return LibFileSearch(dir, name, ext);
309 string const LibScriptSearch(string const & command_in)
311 string const token_scriptpath("$$s/");
313 string command = command_in;
314 // Find the starting position of "$$s/"
315 string::size_type const pos1 = command.find(token_scriptpath);
316 if (pos1 == string::npos)
318 // Find the end of the "$$s/some_subdir/some_script" word within
319 // command. Assumes that the script name does not contain spaces.
320 string::size_type const start_script = pos1 + 4;
321 string::size_type const pos2 = command.find(' ', start_script);
322 string::size_type const size_script = pos2 == string::npos?
323 (command.size() - start_script) : pos2 - start_script;
325 // Does this script file exist?
326 string const script =
327 LibFileSearch(".", command.substr(start_script, size_script));
329 if (script.empty()) {
330 // Replace "$$s/" with ""
331 command.erase(pos1, 4);
333 // Replace "$$s/foo/some_script" with "<path to>/some_script".
334 string::size_type const size_replace = size_script + 4;
335 command.replace(pos1, size_replace, QuoteName(script));
344 string const createTmpDir(string const & tempdir, string const & mask)
347 << "createTmpDir: tempdir=`" << tempdir << "'\n"
348 << "createTmpDir: mask=`" << mask << '\'' << endl;
350 string const tmpfl(tempName(tempdir, mask));
351 // lyx::tempName actually creates a file to make sure that it
352 // stays unique. So we have to delete it before we can create
353 // a dir with the same name. Note also that we are not thread
354 // safe because of the gap between unlink and mkdir. (Lgb)
357 if (tmpfl.empty() || mkdir(tmpfl, 0700)) {
358 lyxerr << "LyX could not create the temporary directory '"
359 << tmpfl << "'" << endl;
363 return MakeAbsPath(tmpfl);
369 bool destroyDir(string const & tmpdir)
373 Path p(user_lyxdir());
375 return fs::remove_all(tmpdir) > 0;
379 string const createBufferTmpDir()
382 // We are in our own directory. Why bother to mangle name?
383 // In fact I wrote this code to circumvent a problematic behaviour
384 // (bug?) of EMX mkstemp().
386 package().temp_dir() + "/lyx_tmpbuf" +
387 convert<string>(count++);
389 if (mkdir(tmpfl, 0777)) {
390 lyxerr << "LyX could not create the temporary directory '"
391 << tmpfl << "'" << endl;
398 string const createLyXTmpDir(string const & deflt)
400 if (!deflt.empty() && deflt != "/tmp") {
401 if (mkdir(deflt, 0777)) {
403 Path p(package().user_support());
405 if (IsDirWriteable(deflt)) {
406 // deflt could not be created because it
407 // did exist already, so let's create our own
409 return createTmpDir(deflt, "lyx_tmpdir");
411 // some other error occured.
412 return createTmpDir("/tmp", "lyx_tmpdir");
418 Path p(package().user_support());
420 return createTmpDir("/tmp", "lyx_tmpdir");
425 bool createDirectory(string const & path, int permission)
427 string temp(rtrim(os::internal_path(path), "/"));
429 BOOST_ASSERT(!temp.empty());
431 if (mkdir(temp, permission))
438 // Strip filename from path name
439 string const OnlyPath(string const & Filename)
441 // If empty filename, return empty
442 if (Filename.empty()) return Filename;
444 // Find last / or start of filename
445 string::size_type j = Filename.rfind('/');
446 if (j == string::npos)
448 return Filename.substr(0, j + 1);
452 // Convert relative path into absolute path based on a basepath.
453 // If relpath is absolute, just use that.
454 // If basepath is empty, use CWD as base.
455 string const MakeAbsPath(string const & RelPath, string const & BasePath)
457 // checks for already absolute path
458 if (os::is_absolute_path(RelPath))
461 // Copies given paths
462 string TempRel(os::internal_path(RelPath));
463 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
464 TempRel = subst(TempRel, "//", "/");
468 if (os::is_absolute_path(BasePath))
471 TempBase = AddPath(getcwd(), BasePath);
473 // Handle /./ at the end of the path
474 while (suffixIs(TempBase, "/./"))
475 TempBase.erase(TempBase.length() - 2);
477 // processes relative path
478 string RTemp(TempRel);
481 while (!RTemp.empty()) {
483 RTemp = split(RTemp, Temp, '/');
485 if (Temp == ".") continue;
487 // Remove one level of TempBase
488 string::difference_type i = TempBase.length() - 2;
491 while (i > 0 && TempBase[i] != '/') --i;
495 while (i > 2 && TempBase[i] != '/') --i;
498 TempBase.erase(i, string::npos);
501 } else if (Temp.empty() && !RTemp.empty()) {
502 TempBase = os::current_root() + RTemp;
505 // Add this piece to TempBase
506 if (!suffixIs(TempBase, '/'))
512 // returns absolute path
513 return os::internal_path(TempBase);
517 // Correctly append filename to the pathname.
518 // If pathname is '.', then don't use pathname.
519 // Chops any path of filename.
520 string const AddName(string const & path, string const & fname)
523 string const basename(OnlyFilename(fname));
527 if (path != "." && path != "./" && !path.empty()) {
528 buf = os::internal_path(path);
529 if (!suffixIs(path, '/'))
533 return buf + basename;
537 // Strips path from filename
538 string const OnlyFilename(string const & fname)
543 string::size_type j = fname.rfind('/');
544 if (j == string::npos) // no '/' in fname
548 return fname.substr(j + 1);
552 /// Returns true is path is absolute
553 bool AbsolutePath(string const & path)
555 return os::is_absolute_path(path);
560 // Create absolute path. If impossible, don't do anything
561 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
562 string const ExpandPath(string const & path)
564 // checks for already absolute path
565 string RTemp(ReplaceEnvironmentPath(path));
566 if (os::is_absolute_path(RTemp))
570 string const copy(RTemp);
573 RTemp = split(RTemp, Temp, '/');
576 return getcwd() + '/' + RTemp;
579 return package().home_dir() + '/' + RTemp;
582 return MakeAbsPath(copy);
584 // Don't know how to handle this
590 // Constracts path/../path
591 // Can't handle "../../" or "/../" (Asger)
592 // Also converts paths like /foo//bar ==> /foo/bar
593 string const NormalizePath(string const & path)
599 if (os::is_absolute_path(path))
602 // Make implicit current directory explicit
605 // Normalise paths like /foo//bar ==> /foo/bar
606 boost::RegEx regex("/{2,}");
607 RTemp = regex.Merge(RTemp, "/");
609 while (!RTemp.empty()) {
611 RTemp = split(RTemp, Temp, '/');
615 } else if (Temp == "..") {
616 // Remove one level of TempBase
617 string::difference_type i = TempBase.length() - 2;
618 while (i > 0 && TempBase[i] != '/')
620 if (i >= 0 && TempBase[i] == '/')
621 TempBase.erase(i + 1, string::npos);
625 TempBase += Temp + '/';
629 // returns absolute path
634 string const GetFileContents(string const & fname)
636 if (fs::exists(fname)) {
637 ifstream ifs(fname.c_str());
645 lyxerr << "LyX was not able to read file '" << fname << '\'' << endl;
650 // Search the string for ${VAR} and $VAR and replace VAR using getenv.
651 string const ReplaceEnvironmentPath(string const & path)
653 // ${VAR} is defined as
654 // $\{[A-Za-z_][A-Za-z_0-9]*\}
655 static string const envvar_br = "[$]\\{([A-Za-z_][A-Za-z_0-9]*)\\}";
657 // $VAR is defined as:
658 // $\{[A-Za-z_][A-Za-z_0-9]*\}
659 static string const envvar = "[$]([A-Za-z_][A-Za-z_0-9]*)";
661 static boost::regex envvar_br_re("(.*)" + envvar_br + "(.*)");
662 static boost::regex envvar_re("(.*)" + envvar + "(.*)");
665 string result = path;
667 regex_match(result, what, envvar_br_re);
668 if (!what[0].matched) {
669 regex_match(result, what, envvar_re);
670 if (!what[0].matched)
673 result = what.str(1) + getEnv(what.str(2)) + what.str(3);
679 // Make relative path out of two absolute paths
680 string const MakeRelPath(string const & abspath, string const & basepath)
681 // Makes relative path out of absolute path. If it is deeper than basepath,
682 // it's easy. If basepath and abspath share something (they are all deeper
683 // than some directory), it'll be rendered using ..'s. If they are completely
684 // different, then the absolute path will be used as relative path.
686 string::size_type const abslen = abspath.length();
687 string::size_type const baselen = basepath.length();
689 string::size_type i = os::common_path(abspath, basepath);
692 // actually no match - cannot make it relative
696 // Count how many dirs there are in basepath above match
697 // and append as many '..''s into relpath
699 string::size_type j = i;
700 while (j < baselen) {
701 if (basepath[j] == '/') {
702 if (j + 1 == baselen)
709 // Append relative stuff from common directory to abspath
710 if (abspath[i] == '/')
712 for (; i < abslen; ++i)
715 if (suffixIs(buf, '/'))
716 buf.erase(buf.length() - 1);
717 // Substitute empty with .
724 // Append sub-directory(ies) to a path in an intelligent way
725 string const AddPath(string const & path, string const & path_2)
728 string const path2 = os::internal_path(path_2);
730 if (!path.empty() && path != "." && path != "./") {
731 buf = os::internal_path(path);
732 if (path[path.length() - 1] != '/')
736 if (!path2.empty()) {
737 string::size_type const p2start = path2.find_first_not_of('/');
738 string::size_type const p2end = path2.find_last_not_of('/');
739 string const tmp = path2.substr(p2start, p2end - p2start + 1);
747 Change extension of oldname to extension.
748 Strips path off if no_path == true.
749 If no extension on oldname, just appends.
751 string const ChangeExtension(string const & oldname, string const & extension)
753 string::size_type const last_slash = oldname.rfind('/');
754 string::size_type last_dot = oldname.rfind('.');
755 if (last_dot < last_slash && last_slash != string::npos)
756 last_dot = string::npos;
759 // Make sure the extension starts with a dot
760 if (!extension.empty() && extension[0] != '.')
761 ext= '.' + extension;
765 return os::internal_path(oldname.substr(0, last_dot) + ext);
769 /// Return the extension of the file (not including the .)
770 string const GetExtension(string const & name)
772 string::size_type const last_slash = name.rfind('/');
773 string::size_type const last_dot = name.rfind('.');
774 if (last_dot != string::npos &&
775 (last_slash == string::npos || last_dot > last_slash))
776 return name.substr(last_dot + 1,
777 name.length() - (last_dot + 1));
783 // the different filetypes and what they contain in one of the first lines
784 // (dots are any characters). (Herbert 20020131)
787 // EPS %!PS-Adobe-3.0 EPSF...
794 // PBM P1... or P4 (B/W)
795 // PGM P2... or P5 (Grayscale)
796 // PPM P3... or P6 (color)
797 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
798 // SGI \001\332... (decimal 474)
800 // TIFF II... or MM...
802 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
803 // ...static char *...
804 // XWD \000\000\000\151 (0x00006900) decimal 105
806 // GZIP \037\213 http://www.ietf.org/rfc/rfc1952.txt
807 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
808 // Z \037\235 UNIX compress
810 string const getFormatFromContents(string const & filename)
813 if (filename.empty() || !IsFileReadable(filename))
816 ifstream ifs(filename.c_str());
818 // Couldn't open file...
822 string const gzipStamp = "\037\213";
825 string const zipStamp = "PK";
828 string const compressStamp = "\037\235";
830 // Maximum strings to read
831 int const max_count = 50;
836 bool firstLine = true;
837 while ((count++ < max_count) && format.empty()) {
839 lyxerr[Debug::GRAPHICS]
840 << "filetools(getFormatFromContents)\n"
841 << "\tFile type not recognised before EOF!"
847 string const stamp = str.substr(0,2);
848 if (firstLine && str.size() >= 2) {
849 // at first we check for a zipped file, because this
850 // information is saved in the first bytes of the file!
851 // also some graphic formats which save the information
852 // in the first line, too.
853 if (prefixIs(str, gzipStamp)) {
856 } else if (stamp == zipStamp) {
859 } else if (stamp == compressStamp) {
863 } else if (stamp == "BM") {
866 } else if (stamp == "\001\332") {
870 // Don't need to use str.at(0), str.at(1) because
871 // we already know that str.size() >= 2
872 } else if (str[0] == 'P') {
888 } else if ((stamp == "II") || (stamp == "MM")) {
891 } else if (prefixIs(str,"%TGIF")) {
894 } else if (prefixIs(str,"#FIG")) {
897 } else if (prefixIs(str,"GIF")) {
900 } else if (str.size() > 3) {
901 int const c = ((str[0] << 24) & (str[1] << 16) &
902 (str[2] << 8) & str[3]);
913 else if (contains(str,"EPSF"))
914 // dummy, if we have wrong file description like
915 // %!PS-Adobe-2.0EPSF"
918 else if (contains(str,"Grace"))
921 else if (contains(str,"JFIF"))
924 else if (contains(str,"%PDF"))
927 else if (contains(str,"PNG"))
930 else if (contains(str,"%!PS-Adobe")) {
933 if (contains(str,"EPSF"))
939 else if (contains(str,"_bits[]"))
942 else if (contains(str,"XPM") || contains(str, "static char *"))
945 else if (contains(str,"BITPIX"))
949 if (!format.empty()) {
950 lyxerr[Debug::GRAPHICS]
951 << "Recognised Fileformat: " << format << endl;
955 lyxerr[Debug::GRAPHICS]
956 << "filetools(getFormatFromContents)\n"
957 << "\tCouldn't find a known format!\n";
962 /// check for zipped file
963 bool zippedFile(string const & name)
965 string const type = getFormatFromContents(name);
966 if (contains("gzip zip compress", type) && !type.empty())
972 string const unzippedFileName(string const & zipped_file)
974 string const ext = GetExtension(zipped_file);
975 if (ext == "gz" || ext == "z" || ext == "Z")
976 return ChangeExtension(zipped_file, string());
977 return "unzipped_" + zipped_file;
981 string const unzipFile(string const & zipped_file, string const & unzipped_file)
983 string const tempfile = unzipped_file.empty() ?
984 unzippedFileName(zipped_file) : unzipped_file;
986 string const command = "gunzip -c " + zipped_file + " > " + tempfile;
988 one.startscript(Systemcall::Wait, command);
989 // test that command was executed successfully (anon)
990 // yes, please do. (Lgb)
995 string const MakeDisplayPath(string const & path, unsigned int threshold)
999 string const home(package().home_dir());
1001 // replace /home/blah with ~/
1002 if (prefixIs(str, home))
1003 str = subst(str, home, "~");
1005 if (str.length() <= threshold)
1006 return os::external_path(str);
1008 string const prefix = ".../";
1011 while (str.length() > threshold)
1012 str = split(str, temp, '/');
1014 // Did we shorten everything away?
1016 // Yes, filename itself is too long.
1017 // Pick the start and the end of the filename.
1018 str = OnlyFilename(path);
1019 string const head = str.substr(0, threshold / 2 - 3);
1021 string::size_type len = str.length();
1023 str.substr(len - threshold / 2 - 2, len - 1);
1024 str = head + "..." + tail;
1027 return os::external_path(prefix + str);
1031 bool LyXReadLink(string const & file, string & link, bool resolve)
1033 #ifdef HAVE_READLINK
1034 char linkbuffer[512];
1035 // Should be PATH_MAX but that needs autconf support
1036 int const nRead = ::readlink(file.c_str(),
1037 linkbuffer, sizeof(linkbuffer) - 1);
1040 linkbuffer[nRead] = '\0'; // terminator
1042 link = MakeAbsPath(linkbuffer, OnlyPath(file));
1052 cmd_ret const RunCommand(string const & cmd)
1054 // FIXME: replace all calls to RunCommand with ForkedCall
1055 // (if the output is not needed) or the code in ispell.C
1056 // (if the output is needed).
1058 // One question is if we should use popen or
1059 // create our own popen based on fork, exec, pipe
1060 // of course the best would be to have a
1061 // pstream (process stream), with the
1062 // variants ipstream, opstream
1064 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1066 // (Claus Hentschel) Check if popen was succesful ;-)
1068 return make_pair(-1, string());
1069 lyxerr << "RunCommand:: could not start child process" << endl;
1075 ret += static_cast<char>(c);
1078 int const pret = pclose(inf);
1080 perror("RunCommand:: could not terminate child process");
1082 return make_pair(pret, ret);
1086 string const findtexfile(string const & fil, string const & /*format*/)
1088 /* There is no problem to extend this function too use other
1089 methods to look for files. It could be setup to look
1090 in environment paths and also if wanted as a last resort
1091 to a recursive find. One of the easier extensions would
1092 perhaps be to use the LyX file lookup methods. But! I am
1093 going to implement this until I see some demand for it.
1097 // If the file can be found directly, we just return a
1098 // absolute path version of it.
1099 if (fs::exists(fil))
1100 return MakeAbsPath(fil);
1102 // No we try to find it using kpsewhich.
1103 // It seems from the kpsewhich manual page that it is safe to use
1104 // kpsewhich without --format: "When the --format option is not
1105 // given, the search path used when looking for a file is inferred
1106 // from the name given, by looking for a known extension. If no
1107 // known extension is found, the search path for TeX source files
1109 // However, we want to take advantage of the format sine almost all
1110 // the different formats has environment variables that can be used
1111 // to controll which paths to search. f.ex. bib looks in
1112 // BIBINPUTS and TEXBIB. Small list follows:
1113 // bib - BIBINPUTS, TEXBIB
1115 // graphic/figure - TEXPICTS, TEXINPUTS
1116 // ist - TEXINDEXSTYLE, INDEXSTYLE
1117 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1119 // tfm - TFMFONTS, TEXFONTS
1120 // This means that to use kpsewhich in the best possible way we
1121 // should help it by setting additional path in the approp. envir.var.
1122 string const kpsecmd = "kpsewhich " + fil;
1124 cmd_ret const c = RunCommand(kpsecmd);
1126 lyxerr[Debug::LATEX] << "kpse status = " << c.first << '\n'
1127 << "kpse result = `" << rtrim(c.second, "\n")
1130 return os::internal_path(rtrim(c.second, "\n\r"));
1136 void removeAutosaveFile(string const & filename)
1138 string a = OnlyPath(filename);
1140 a += OnlyFilename(filename);
1147 void readBB_lyxerrMessage(string const & file, bool & zipped,
1148 string const & message)
1150 lyxerr[Debug::GRAPHICS] << "[readBB_from_PSFile] "
1151 << message << std::endl;
1152 #ifdef WITH_WARNINGS
1153 #warning Why is this func deleting a file? (Lgb)
1160 string const readBB_from_PSFile(string const & file)
1162 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1163 // It seems that every command in the header has an own line,
1164 // getline() should work for all files.
1165 // On the other hand some plot programs write the bb at the
1166 // end of the file. Than we have in the header:
1167 // %%BoundingBox: (atend)
1168 // In this case we must check the end.
1169 bool zipped = zippedFile(file);
1170 string const file_ = zipped ?
1171 string(unzipFile(file)) : string(file);
1172 string const format = getFormatFromContents(file_);
1174 if (format != "eps" && format != "ps") {
1175 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1179 std::ifstream is(file_.c_str());
1183 if (contains(s,"%%BoundingBox:") && !contains(s,"atend")) {
1184 string const bb = ltrim(s.substr(14));
1185 readBB_lyxerrMessage(file_, zipped, bb);
1189 readBB_lyxerrMessage(file_, zipped, "no bb found");
1194 int compare_timestamps(string const & file1, string const & file2)
1196 BOOST_ASSERT(AbsolutePath(file1) && AbsolutePath(file2));
1198 // If the original is newer than the copy, then copy the original
1199 // to the new directory.
1202 if (fs::exists(file1) && fs::exists(file2)) {
1203 double const tmp = difftime(fs::last_write_time(file1),
1204 fs::last_write_time(file2));
1206 cmp = tmp > 0 ? 1 : -1;
1208 } else if (fs::exists(file1)) {
1210 } else if (fs::exists(file2)) {
1217 } //namespace support