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/forkedcontr.h"
28 #include "support/fs_extras.h"
29 #include "support/lstrings.h"
30 #include "support/lyxlib.h"
31 #include "support/os.h"
32 #include "support/package.h"
33 #include "support/path.h"
34 #include "support/systemcall.h"
36 // FIXME Interface violation
40 #include <boost/assert.hpp>
41 #include <boost/filesystem/operations.hpp>
42 #include <boost/regex.hpp>
55 #ifndef CXX_GLOBAL_CSTD
66 using std::ostringstream;
69 namespace fs = boost::filesystem;
74 bool IsLyXFilename(string const & filename)
76 return suffixIs(ascii_lowercase(filename), ".lyx");
80 bool IsSGMLFilename(string const & filename)
82 return suffixIs(ascii_lowercase(filename), ".sgml");
86 // Substitutes spaces with underscores in filename (and path)
87 string const MakeLatexName(string const & file)
89 string name = OnlyFilename(file);
90 string const path = OnlyPath(file);
92 for (string::size_type i = 0; i < name.length(); ++i) {
93 name[i] &= 0x7f; // set 8th bit to 0
96 // ok so we scan through the string twice, but who cares.
97 string const keep("abcdefghijklmnopqrstuvwxyz"
98 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
99 "@!\"'()*+,-./0123456789:;<=>?[]`|");
101 string::size_type pos = 0;
102 while ((pos = name.find_first_not_of(keep, pos)) != string::npos) {
105 return AddName(path, name);
109 string const QuoteName(string const & name)
111 return (os::shell() == os::UNIX) ?
117 // Is a file readable ?
118 bool IsFileReadable(string const & path)
120 return fs::exists(path) && !fs::is_directory(path) && fs::is_readable(path);
124 //returns true: dir writeable
125 // false: not writeable
126 bool IsDirWriteable(string const & path)
128 lyxerr[Debug::FILES] << "IsDirWriteable: " << path << endl;
130 string const tmpfl(tempName(path, "lyxwritetest"));
140 // Uses a string of paths separated by ";"s to find a file to open.
141 // Can't cope with pathnames with a ';' in them. Returns full path to file.
142 // If path entry begins with $$LyX/, use system_lyxdir
143 // If path entry begins with $$User/, use user_lyxdir
144 // Example: "$$User/doc;$$LyX/doc"
145 string const FileOpenSearch(string const & path, string const & name,
150 bool notfound = true;
151 string tmppath = split(path, path_element, ';');
153 while (notfound && !path_element.empty()) {
154 path_element = os::internal_path(path_element);
155 if (!suffixIs(path_element, '/'))
157 path_element = subst(path_element, "$$LyX",
158 package().system_support());
159 path_element = subst(path_element, "$$User",
160 package().user_support());
162 real_file = FileSearch(path_element, name, ext);
164 if (real_file.empty()) {
166 tmppath = split(tmppath, path_element, ';');
167 } while (!tmppath.empty() && path_element.empty());
173 if (ext.empty() && notfound) {
174 real_file = FileOpenSearch(path, name, "exe");
175 if (notfound) real_file = FileOpenSearch(path, name, "cmd");
182 /// Returns a vector of all files in directory dir having extension ext.
183 vector<string> const DirList(string const & dir, string const & ext)
185 // EXCEPTIONS FIXME. Rewrite needed when we turn on exceptions. (Lgb)
186 vector<string> dirlist;
188 if (!(fs::exists(dir) && fs::is_directory(dir))) {
190 << "Directory \"" << dir
191 << "\" does not exist to DirList." << endl;
196 if (!ext.empty() && ext[0] != '.')
200 fs::directory_iterator dit(dir);
201 fs::directory_iterator end;
202 for (; dit != end; ++dit) {
203 string const & fil = dit->leaf();
204 if (suffixIs(fil, extension)) {
205 dirlist.push_back(fil);
212 // Returns the real name of file name in directory path, with optional
214 string const FileSearch(string const & path, string const & name,
217 // if `name' is an absolute path, we ignore the setting of `path'
218 // Expand Environmentvariables in 'name'
219 string const tmpname = ReplaceEnvironmentPath(name);
220 string fullname = MakeAbsPath(tmpname, path);
221 // search first without extension, then with it.
222 if (IsFileReadable(fullname))
224 else if (ext.empty())
226 else { // Is it not more reasonable to use ChangeExtension()? (SMiyata)
229 if (IsFileReadable(fullname))
237 // Search the file name.ext in the subdirectory dir of
239 // 2) build_lyxdir (if not empty)
241 string const LibFileSearch(string const & dir, string const & name,
244 string fullname = FileSearch(AddPath(package().user_support(), dir),
246 if (!fullname.empty())
249 if (!package().build_support().empty())
250 fullname = FileSearch(AddPath(package().build_support(), dir),
252 if (!fullname.empty())
255 return FileSearch(AddPath(package().system_support(), dir), name, ext);
260 i18nLibFileSearch(string const & dir, string const & name,
263 // the following comments are from intl/dcigettext.c. We try
264 // to mimick this behaviour here.
265 /* The highest priority value is the `LANGUAGE' environment
266 variable. But we don't use the value if the currently
267 selected locale is the C locale. This is a GNU extension. */
268 /* [Otherwise] We have to proceed with the POSIX methods of
269 looking to `LC_ALL', `LC_xxx', and `LANG'. */
271 string lang = getEnv("LC_ALL");
273 lang = getEnv("LC_MESSAGES");
275 lang = getEnv("LANG");
281 string const language = getEnv("LANGUAGE");
282 if (lang != "C" && lang != "POSIX" && !language.empty())
286 lang = split(lang, l, ':');
287 while (!l.empty() && l != "C" && l != "POSIX") {
288 string const tmp = LibFileSearch(dir,
289 token(l, '_', 0) + '_' + name,
293 lang = split(lang, l, ':');
296 return LibFileSearch(dir, name, ext);
300 string const LibScriptSearch(string const & command_in)
302 string const token_scriptpath("$$s/");
304 string command = command_in;
305 // Find the starting position of "$$s/"
306 string::size_type const pos1 = command.find(token_scriptpath);
307 if (pos1 == string::npos)
309 // Find the end of the "$$s/some_subdir/some_script" word within
310 // command. Assumes that the script name does not contain spaces.
311 string::size_type const start_script = pos1 + 4;
312 string::size_type const pos2 = command.find(' ', start_script);
313 string::size_type const size_script = pos2 == string::npos?
314 (command.size() - start_script) : pos2 - start_script;
316 // Does this script file exist?
317 string const script =
318 LibFileSearch(".", command.substr(start_script, size_script));
320 if (script.empty()) {
321 // Replace "$$s/" with ""
322 command.erase(pos1, 4);
324 // Replace "$$s/foo/some_script" with "<path to>/some_script".
325 string::size_type const size_replace = size_script + 4;
326 command.replace(pos1, size_replace, QuoteName(script));
335 string const createTmpDir(string const & tempdir, string const & mask)
338 << "createTmpDir: tempdir=`" << tempdir << "'\n"
339 << "createTmpDir: mask=`" << mask << '\'' << endl;
341 string const tmpfl(tempName(tempdir, mask));
342 // lyx::tempName actually creates a file to make sure that it
343 // stays unique. So we have to delete it before we can create
344 // a dir with the same name. Note also that we are not thread
345 // safe because of the gap between unlink and mkdir. (Lgb)
348 if (tmpfl.empty() || mkdir(tmpfl, 0700)) {
349 lyxerr << "LyX could not create the temporary directory '"
350 << tmpfl << "'" << endl;
354 return MakeAbsPath(tmpfl);
360 bool destroyDir(string const & tmpdir)
364 Path p(user_lyxdir());
366 return fs::remove_all(tmpdir) > 0;
370 string const createBufferTmpDir()
373 // We are in our own directory. Why bother to mangle name?
374 // In fact I wrote this code to circumvent a problematic behaviour
375 // (bug?) of EMX mkstemp().
377 package().temp_dir() + "/lyx_tmpbuf" +
378 convert<string>(count++);
380 if (mkdir(tmpfl, 0777)) {
381 lyxerr << "LyX could not create the temporary directory '"
382 << tmpfl << "'" << endl;
389 string const createLyXTmpDir(string const & deflt)
391 if (!deflt.empty() && deflt != "/tmp") {
392 if (mkdir(deflt, 0777)) {
394 Path p(package().user_support());
396 if (IsDirWriteable(deflt)) {
397 // deflt could not be created because it
398 // did exist already, so let's create our own
400 return createTmpDir(deflt, "lyx_tmpdir");
402 // some other error occured.
403 return createTmpDir("/tmp", "lyx_tmpdir");
409 Path p(package().user_support());
411 return createTmpDir("/tmp", "lyx_tmpdir");
416 bool createDirectory(string const & path, int permission)
418 string temp(rtrim(os::internal_path(path), "/"));
420 BOOST_ASSERT(!temp.empty());
422 if (mkdir(temp, permission))
429 // Strip filename from path name
430 string const OnlyPath(string const & Filename)
432 // If empty filename, return empty
433 if (Filename.empty()) return Filename;
435 // Find last / or start of filename
436 string::size_type j = Filename.rfind('/');
437 if (j == string::npos)
439 return Filename.substr(0, j + 1);
443 // Convert relative path into absolute path based on a basepath.
444 // If relpath is absolute, just use that.
445 // If basepath is empty, use CWD as base.
446 string const MakeAbsPath(string const & RelPath, string const & BasePath)
448 // checks for already absolute path
449 if (os::is_absolute_path(RelPath))
452 // Copies given paths
453 string TempRel(os::internal_path(RelPath));
454 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
455 TempRel = subst(TempRel, "//", "/");
459 if (os::is_absolute_path(BasePath))
462 TempBase = AddPath(getcwd(), BasePath);
464 // Handle /./ at the end of the path
465 while (suffixIs(TempBase, "/./"))
466 TempBase.erase(TempBase.length() - 2);
468 // processes relative path
469 string RTemp(TempRel);
472 while (!RTemp.empty()) {
474 RTemp = split(RTemp, Temp, '/');
476 if (Temp == ".") continue;
478 // Remove one level of TempBase
479 string::difference_type i = TempBase.length() - 2;
482 while (i > 0 && TempBase[i] != '/') --i;
486 while (i > 2 && TempBase[i] != '/') --i;
489 TempBase.erase(i, string::npos);
492 } else if (Temp.empty() && !RTemp.empty()) {
493 TempBase = os::current_root() + RTemp;
496 // Add this piece to TempBase
497 if (!suffixIs(TempBase, '/'))
503 // returns absolute path
504 return os::internal_path(TempBase);
508 // Correctly append filename to the pathname.
509 // If pathname is '.', then don't use pathname.
510 // Chops any path of filename.
511 string const AddName(string const & path, string const & fname)
514 string const basename(OnlyFilename(fname));
518 if (path != "." && path != "./" && !path.empty()) {
519 buf = os::internal_path(path);
520 if (!suffixIs(path, '/'))
524 return buf + basename;
528 // Strips path from filename
529 string const OnlyFilename(string const & fname)
534 string::size_type j = fname.rfind('/');
535 if (j == string::npos) // no '/' in fname
539 return fname.substr(j + 1);
543 /// Returns true is path is absolute
544 bool AbsolutePath(string const & path)
546 return os::is_absolute_path(path);
551 // Create absolute path. If impossible, don't do anything
552 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
553 string const ExpandPath(string const & path)
555 // checks for already absolute path
556 string RTemp(ReplaceEnvironmentPath(path));
557 if (os::is_absolute_path(RTemp))
561 string const copy(RTemp);
564 RTemp = split(RTemp, Temp, '/');
567 return getcwd() + '/' + RTemp;
570 return package().home_dir() + '/' + RTemp;
573 return MakeAbsPath(copy);
575 // Don't know how to handle this
581 // Constracts path/../path
582 // Can't handle "../../" or "/../" (Asger)
583 // Also converts paths like /foo//bar ==> /foo/bar
584 string const NormalizePath(string const & path)
590 if (os::is_absolute_path(path))
593 // Make implicit current directory explicit
596 // Normalise paths like /foo//bar ==> /foo/bar
597 boost::RegEx regex("/{2,}");
598 RTemp = regex.Merge(RTemp, "/");
600 while (!RTemp.empty()) {
602 RTemp = split(RTemp, Temp, '/');
606 } else if (Temp == "..") {
607 // Remove one level of TempBase
608 string::difference_type i = TempBase.length() - 2;
609 while (i > 0 && TempBase[i] != '/')
611 if (i >= 0 && TempBase[i] == '/')
612 TempBase.erase(i + 1, string::npos);
616 TempBase += Temp + '/';
620 // returns absolute path
625 string const GetFileContents(string const & fname)
627 if (fs::exists(fname)) {
628 ifstream ifs(fname.c_str());
636 lyxerr << "LyX was not able to read file '" << fname << '\'' << endl;
641 // Search the string for ${VAR} and $VAR and replace VAR using getenv.
642 string const ReplaceEnvironmentPath(string const & path)
644 // ${VAR} is defined as
645 // $\{[A-Za-z_][A-Za-z_0-9]*\}
646 static string const envvar_br = "[$]\\{([A-Za-z_][A-Za-z_0-9]*)\\}";
648 // $VAR is defined as:
649 // $\{[A-Za-z_][A-Za-z_0-9]*\}
650 static string const envvar = "[$]([A-Za-z_][A-Za-z_0-9]*)";
652 static boost::regex envvar_br_re("(.*)" + envvar_br + "(.*)");
653 static boost::regex envvar_re("(.*)" + envvar + "(.*)");
656 string result = path;
658 regex_match(result, what, envvar_br_re);
659 if (!what[0].matched) {
660 regex_match(result, what, envvar_re);
661 if (!what[0].matched)
664 result = what.str(1) + getEnv(what.str(2)) + what.str(3);
670 // Make relative path out of two absolute paths
671 string const MakeRelPath(string const & abspath, string const & basepath)
672 // Makes relative path out of absolute path. If it is deeper than basepath,
673 // it's easy. If basepath and abspath share something (they are all deeper
674 // than some directory), it'll be rendered using ..'s. If they are completely
675 // different, then the absolute path will be used as relative path.
677 string::size_type const abslen = abspath.length();
678 string::size_type const baselen = basepath.length();
680 string::size_type i = os::common_path(abspath, basepath);
683 // actually no match - cannot make it relative
687 // Count how many dirs there are in basepath above match
688 // and append as many '..''s into relpath
690 string::size_type j = i;
691 while (j < baselen) {
692 if (basepath[j] == '/') {
693 if (j + 1 == baselen)
700 // Append relative stuff from common directory to abspath
701 if (abspath[i] == '/')
703 for (; i < abslen; ++i)
706 if (suffixIs(buf, '/'))
707 buf.erase(buf.length() - 1);
708 // Substitute empty with .
715 // Append sub-directory(ies) to a path in an intelligent way
716 string const AddPath(string const & path, string const & path_2)
719 string const path2 = os::internal_path(path_2);
721 if (!path.empty() && path != "." && path != "./") {
722 buf = os::internal_path(path);
723 if (path[path.length() - 1] != '/')
727 if (!path2.empty()) {
728 string::size_type const p2start = path2.find_first_not_of('/');
729 string::size_type const p2end = path2.find_last_not_of('/');
730 string const tmp = path2.substr(p2start, p2end - p2start + 1);
738 Change extension of oldname to extension.
739 Strips path off if no_path == true.
740 If no extension on oldname, just appends.
742 string const ChangeExtension(string const & oldname, string const & extension)
744 string::size_type const last_slash = oldname.rfind('/');
745 string::size_type last_dot = oldname.rfind('.');
746 if (last_dot < last_slash && last_slash != string::npos)
747 last_dot = string::npos;
750 // Make sure the extension starts with a dot
751 if (!extension.empty() && extension[0] != '.')
752 ext= '.' + extension;
756 return os::internal_path(oldname.substr(0, last_dot) + ext);
760 /// Return the extension of the file (not including the .)
761 string const GetExtension(string const & name)
763 string::size_type const last_slash = name.rfind('/');
764 string::size_type const last_dot = name.rfind('.');
765 if (last_dot != string::npos &&
766 (last_slash == string::npos || last_dot > last_slash))
767 return name.substr(last_dot + 1,
768 name.length() - (last_dot + 1));
774 // the different filetypes and what they contain in one of the first lines
775 // (dots are any characters). (Herbert 20020131)
778 // EPS %!PS-Adobe-3.0 EPSF...
785 // PBM P1... or P4 (B/W)
786 // PGM P2... or P5 (Grayscale)
787 // PPM P3... or P6 (color)
788 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
789 // SGI \001\332... (decimal 474)
791 // TIFF II... or MM...
793 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
794 // ...static char *...
795 // XWD \000\000\000\151 (0x00006900) decimal 105
797 // GZIP \037\213 http://www.ietf.org/rfc/rfc1952.txt
798 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
799 // Z \037\235 UNIX compress
801 string const getFormatFromContents(string const & filename)
804 if (filename.empty() || !IsFileReadable(filename))
807 ifstream ifs(filename.c_str());
809 // Couldn't open file...
813 string const gzipStamp = "\037\213";
816 string const zipStamp = "PK";
819 string const compressStamp = "\037\235";
821 // Maximum strings to read
822 int const max_count = 50;
827 bool firstLine = true;
828 while ((count++ < max_count) && format.empty()) {
830 lyxerr[Debug::GRAPHICS]
831 << "filetools(getFormatFromContents)\n"
832 << "\tFile type not recognised before EOF!"
838 string const stamp = str.substr(0,2);
839 if (firstLine && str.size() >= 2) {
840 // at first we check for a zipped file, because this
841 // information is saved in the first bytes of the file!
842 // also some graphic formats which save the information
843 // in the first line, too.
844 if (prefixIs(str, gzipStamp)) {
847 } else if (stamp == zipStamp) {
850 } else if (stamp == compressStamp) {
854 } else if (stamp == "BM") {
857 } else if (stamp == "\001\332") {
861 // Don't need to use str.at(0), str.at(1) because
862 // we already know that str.size() >= 2
863 } else if (str[0] == 'P') {
879 } else if ((stamp == "II") || (stamp == "MM")) {
882 } else if (prefixIs(str,"%TGIF")) {
885 } else if (prefixIs(str,"#FIG")) {
888 } else if (prefixIs(str,"GIF")) {
891 } else if (str.size() > 3) {
892 int const c = ((str[0] << 24) & (str[1] << 16) &
893 (str[2] << 8) & str[3]);
904 else if (contains(str,"EPSF"))
905 // dummy, if we have wrong file description like
906 // %!PS-Adobe-2.0EPSF"
909 else if (contains(str,"Grace"))
912 else if (contains(str,"JFIF"))
915 else if (contains(str,"%PDF"))
918 else if (contains(str,"PNG"))
921 else if (contains(str,"%!PS-Adobe")) {
924 if (contains(str,"EPSF"))
930 else if (contains(str,"_bits[]"))
933 else if (contains(str,"XPM") || contains(str, "static char *"))
936 else if (contains(str,"BITPIX"))
940 if (!format.empty()) {
941 lyxerr[Debug::GRAPHICS]
942 << "Recognised Fileformat: " << format << endl;
946 lyxerr[Debug::GRAPHICS]
947 << "filetools(getFormatFromContents)\n"
948 << "\tCouldn't find a known format!\n";
953 /// check for zipped file
954 bool zippedFile(string const & name)
956 string const type = getFormatFromContents(name);
957 if (contains("gzip zip compress", type) && !type.empty())
963 string const unzippedFileName(string const & zipped_file)
965 string const ext = GetExtension(zipped_file);
966 if (ext == "gz" || ext == "z" || ext == "Z")
967 return ChangeExtension(zipped_file, string());
968 return "unzipped_" + zipped_file;
972 string const unzipFile(string const & zipped_file, string const & unzipped_file)
974 string const tempfile = unzipped_file.empty() ?
975 unzippedFileName(zipped_file) : unzipped_file;
977 string const command = "gunzip -c " + zipped_file + " > " + tempfile;
979 one.startscript(Systemcall::Wait, command);
980 // test that command was executed successfully (anon)
981 // yes, please do. (Lgb)
986 string const MakeDisplayPath(string const & path, unsigned int threshold)
990 string const home(package().home_dir());
992 // replace /home/blah with ~/
993 if (prefixIs(str, home))
994 str = subst(str, home, "~");
996 if (str.length() <= threshold)
999 string const prefix = ".../";
1002 while (str.length() > threshold)
1003 str = split(str, temp, '/');
1005 // Did we shorten everything away?
1007 // Yes, filename itself is too long.
1008 // Pick the start and the end of the filename.
1009 str = OnlyFilename(path);
1010 string const head = str.substr(0, threshold / 2 - 3);
1012 string::size_type len = str.length();
1014 str.substr(len - threshold / 2 - 2, len - 1);
1015 str = head + "..." + tail;
1018 return prefix + str;
1022 bool LyXReadLink(string const & file, string & link, bool resolve)
1024 #ifdef HAVE_READLINK
1025 char linkbuffer[512];
1026 // Should be PATH_MAX but that needs autconf support
1027 int const nRead = ::readlink(file.c_str(),
1028 linkbuffer, sizeof(linkbuffer) - 1);
1031 linkbuffer[nRead] = '\0'; // terminator
1033 link = MakeAbsPath(linkbuffer, OnlyPath(file));
1043 cmd_ret const RunCommand(string const & cmd)
1045 // FIXME: replace all calls to RunCommand with ForkedCall
1046 // (if the output is not needed) or the code in ispell.C
1047 // (if the output is needed).
1049 // One question is if we should use popen or
1050 // create our own popen based on fork, exec, pipe
1051 // of course the best would be to have a
1052 // pstream (process stream), with the
1053 // variants ipstream, opstream
1055 sigset_t newMask, oldMask;
1056 sigemptyset(&oldMask);
1057 sigemptyset(&newMask);
1058 sigaddset(&newMask, SIGCHLD);
1060 // Block the SIGCHLD signal.
1061 sigprocmask(SIG_BLOCK, &newMask, &oldMask);
1063 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1065 // (Claus Hentschel) Check if popen was succesful ;-)
1067 return make_pair(-1, string());
1068 lyxerr << "RunCommand:: could not start child process" << endl;
1074 ret += static_cast<char>(c);
1077 int const pret = pclose(inf);
1079 perror("RunCommand:: could not terminate child process");
1081 // Unblock the SIGCHLD signal and restore the old mask.
1082 sigprocmask(SIG_SETMASK, &oldMask, 0);
1084 return make_pair(pret, ret);
1088 string const findtexfile(string const & fil, string const & /*format*/)
1090 /* There is no problem to extend this function too use other
1091 methods to look for files. It could be setup to look
1092 in environment paths and also if wanted as a last resort
1093 to a recursive find. One of the easier extensions would
1094 perhaps be to use the LyX file lookup methods. But! I am
1095 going to implement this until I see some demand for it.
1099 // If the file can be found directly, we just return a
1100 // absolute path version of it.
1101 if (fs::exists(fil))
1102 return MakeAbsPath(fil);
1104 // No we try to find it using kpsewhich.
1105 // It seems from the kpsewhich manual page that it is safe to use
1106 // kpsewhich without --format: "When the --format option is not
1107 // given, the search path used when looking for a file is inferred
1108 // from the name given, by looking for a known extension. If no
1109 // known extension is found, the search path for TeX source files
1111 // However, we want to take advantage of the format sine almost all
1112 // the different formats has environment variables that can be used
1113 // to controll which paths to search. f.ex. bib looks in
1114 // BIBINPUTS and TEXBIB. Small list follows:
1115 // bib - BIBINPUTS, TEXBIB
1117 // graphic/figure - TEXPICTS, TEXINPUTS
1118 // ist - TEXINDEXSTYLE, INDEXSTYLE
1119 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1121 // tfm - TFMFONTS, TEXFONTS
1122 // This means that to use kpsewhich in the best possible way we
1123 // should help it by setting additional path in the approp. envir.var.
1124 string const kpsecmd = "kpsewhich " + fil;
1126 cmd_ret const c = RunCommand(kpsecmd);
1128 lyxerr[Debug::LATEX] << "kpse status = " << c.first << '\n'
1129 << "kpse result = `" << rtrim(c.second, "\n")
1132 return os::internal_path(rtrim(c.second, "\n\r"));
1138 void removeAutosaveFile(string const & filename)
1140 string a = OnlyPath(filename);
1142 a += OnlyFilename(filename);
1149 void readBB_lyxerrMessage(string const & file, bool & zipped,
1150 string const & message)
1152 lyxerr[Debug::GRAPHICS] << "[readBB_from_PSFile] "
1153 << message << std::endl;
1154 #ifdef WITH_WARNINGS
1155 #warning Why is this func deleting a file? (Lgb)
1162 string const readBB_from_PSFile(string const & file)
1164 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1165 // It seems that every command in the header has an own line,
1166 // getline() should work for all files.
1167 // On the other hand some plot programs write the bb at the
1168 // end of the file. Than we have in the header:
1169 // %%BoundingBox: (atend)
1170 // In this case we must check the end.
1171 bool zipped = zippedFile(file);
1172 string const file_ = zipped ?
1173 string(unzipFile(file)) : string(file);
1174 string const format = getFormatFromContents(file_);
1176 if (format != "eps" && format != "ps") {
1177 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1181 std::ifstream is(file_.c_str());
1185 if (contains(s,"%%BoundingBox:") && !contains(s,"atend")) {
1186 string const bb = ltrim(s.substr(14));
1187 readBB_lyxerrMessage(file_, zipped, bb);
1191 readBB_lyxerrMessage(file_, zipped, "no bb found");
1196 int compare_timestamps(string const & file1, string const & file2)
1198 BOOST_ASSERT(AbsolutePath(file1) && AbsolutePath(file2));
1200 // If the original is newer than the copy, then copy the original
1201 // to the new directory.
1204 if (fs::exists(file1) && fs::exists(file2)) {
1205 double const tmp = difftime(fs::last_write_time(file1),
1206 fs::last_write_time(file2));
1208 cmp = tmp > 0 ? 1 : -1;
1210 } else if (fs::exists(file1)) {
1212 } else if (fs::exists(file2)) {
1219 } //namespace support