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 string const latex_path(string const & original_path)
88 string path = subst(original_path, "\\", "/");
89 path = subst(path, "~", "\\string~");
90 if (path.find(' ') != string::npos)
91 path = '"' + path + '"';
96 // Substitutes spaces with underscores in filename (and path)
97 string const MakeLatexName(string const & file)
99 string name = OnlyFilename(file);
100 string const path = OnlyPath(file);
102 for (string::size_type i = 0; i < name.length(); ++i) {
103 name[i] &= 0x7f; // set 8th bit to 0
106 // ok so we scan through the string twice, but who cares.
107 string const keep("abcdefghijklmnopqrstuvwxyz"
108 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
109 "@!\"'()*+,-./0123456789:;<=>?[]`|");
111 string::size_type pos = 0;
112 while ((pos = name.find_first_not_of(keep, pos)) != string::npos) {
115 return AddName(path, name);
119 string const QuoteName(string const & name)
121 return (os::shell() == os::UNIX) ?
127 // Is a file readable ?
128 bool IsFileReadable(string const & path)
130 return fs::exists(path) && !fs::is_directory(path) && fs::is_readable(path);
134 //returns true: dir writeable
135 // false: not writeable
136 bool IsDirWriteable(string const & path)
138 lyxerr[Debug::FILES] << "IsDirWriteable: " << path << endl;
140 string const tmpfl(tempName(path, "lyxwritetest"));
150 // Uses a string of paths separated by ";"s to find a file to open.
151 // Can't cope with pathnames with a ';' in them. Returns full path to file.
152 // If path entry begins with $$LyX/, use system_lyxdir
153 // If path entry begins with $$User/, use user_lyxdir
154 // Example: "$$User/doc;$$LyX/doc"
155 string const FileOpenSearch(string const & path, string const & name,
160 bool notfound = true;
161 string tmppath = split(path, path_element, ';');
163 while (notfound && !path_element.empty()) {
164 path_element = os::internal_path(path_element);
165 if (!suffixIs(path_element, '/'))
167 path_element = subst(path_element, "$$LyX",
168 package().system_support());
169 path_element = subst(path_element, "$$User",
170 package().user_support());
172 real_file = FileSearch(path_element, name, ext);
174 if (real_file.empty()) {
176 tmppath = split(tmppath, path_element, ';');
177 } while (!tmppath.empty() && path_element.empty());
183 if (ext.empty() && notfound) {
184 real_file = FileOpenSearch(path, name, "exe");
185 if (notfound) real_file = FileOpenSearch(path, name, "cmd");
192 /// Returns a vector of all files in directory dir having extension ext.
193 vector<string> const DirList(string const & dir, string const & ext)
195 // EXCEPTIONS FIXME. Rewrite needed when we turn on exceptions. (Lgb)
196 vector<string> dirlist;
198 if (!(fs::exists(dir) && fs::is_directory(dir))) {
200 << "Directory \"" << dir
201 << "\" does not exist to DirList." << endl;
206 if (!ext.empty() && ext[0] != '.')
210 fs::directory_iterator dit(dir);
211 fs::directory_iterator end;
212 for (; dit != end; ++dit) {
213 string const & fil = dit->leaf();
214 if (suffixIs(fil, extension)) {
215 dirlist.push_back(fil);
222 // Returns the real name of file name in directory path, with optional
224 string const FileSearch(string const & path, string const & name,
227 // if `name' is an absolute path, we ignore the setting of `path'
228 // Expand Environmentvariables in 'name'
229 string const tmpname = ReplaceEnvironmentPath(name);
230 string fullname = MakeAbsPath(tmpname, path);
231 // search first without extension, then with it.
232 if (IsFileReadable(fullname))
234 else if (ext.empty())
236 else { // Is it not more reasonable to use ChangeExtension()? (SMiyata)
239 if (IsFileReadable(fullname))
247 // Search the file name.ext in the subdirectory dir of
249 // 2) build_lyxdir (if not empty)
251 string const LibFileSearch(string const & dir, string const & name,
254 string fullname = FileSearch(AddPath(package().user_support(), dir),
256 if (!fullname.empty())
259 if (!package().build_support().empty())
260 fullname = FileSearch(AddPath(package().build_support(), dir),
262 if (!fullname.empty())
265 return FileSearch(AddPath(package().system_support(), dir), name, ext);
270 i18nLibFileSearch(string const & dir, string const & name,
273 // the following comments are from intl/dcigettext.c. We try
274 // to mimick this behaviour here.
275 /* The highest priority value is the `LANGUAGE' environment
276 variable. But we don't use the value if the currently
277 selected locale is the C locale. This is a GNU extension. */
278 /* [Otherwise] We have to proceed with the POSIX methods of
279 looking to `LC_ALL', `LC_xxx', and `LANG'. */
281 string lang = getEnv("LC_ALL");
283 lang = getEnv("LC_MESSAGES");
285 lang = getEnv("LANG");
291 string const language = getEnv("LANGUAGE");
292 if (lang != "C" && lang != "POSIX" && !language.empty())
296 lang = split(lang, l, ':');
297 while (!l.empty() && l != "C" && l != "POSIX") {
298 string const tmp = LibFileSearch(dir,
299 token(l, '_', 0) + '_' + name,
303 lang = split(lang, l, ':');
306 return LibFileSearch(dir, name, ext);
310 string const LibScriptSearch(string const & command_in)
312 string const token_scriptpath("$$s/");
314 string command = command_in;
315 // Find the starting position of "$$s/"
316 string::size_type const pos1 = command.find(token_scriptpath);
317 if (pos1 == string::npos)
319 // Find the end of the "$$s/some_subdir/some_script" word within
320 // command. Assumes that the script name does not contain spaces.
321 string::size_type const start_script = pos1 + 4;
322 string::size_type const pos2 = command.find(' ', start_script);
323 string::size_type const size_script = pos2 == string::npos?
324 (command.size() - start_script) : pos2 - start_script;
326 // Does this script file exist?
327 string const script =
328 LibFileSearch(".", command.substr(start_script, size_script));
330 if (script.empty()) {
331 // Replace "$$s/" with ""
332 command.erase(pos1, 4);
334 // Replace "$$s/foo/some_script" with "<path to>/some_script".
335 string::size_type const size_replace = size_script + 4;
336 command.replace(pos1, size_replace, QuoteName(script));
345 string const createTmpDir(string const & tempdir, string const & mask)
348 << "createTmpDir: tempdir=`" << tempdir << "'\n"
349 << "createTmpDir: mask=`" << mask << '\'' << endl;
351 string const tmpfl(tempName(tempdir, mask));
352 // lyx::tempName actually creates a file to make sure that it
353 // stays unique. So we have to delete it before we can create
354 // a dir with the same name. Note also that we are not thread
355 // safe because of the gap between unlink and mkdir. (Lgb)
358 if (tmpfl.empty() || mkdir(tmpfl, 0700)) {
359 lyxerr << "LyX could not create the temporary directory '"
360 << tmpfl << "'" << endl;
364 return MakeAbsPath(tmpfl);
370 bool destroyDir(string const & tmpdir)
374 Path p(user_lyxdir());
376 return fs::remove_all(tmpdir) > 0;
380 string const createBufferTmpDir()
383 // We are in our own directory. Why bother to mangle name?
384 // In fact I wrote this code to circumvent a problematic behaviour
385 // (bug?) of EMX mkstemp().
387 package().temp_dir() + "/lyx_tmpbuf" +
388 convert<string>(count++);
390 if (mkdir(tmpfl, 0777)) {
391 lyxerr << "LyX could not create the temporary directory '"
392 << tmpfl << "'" << endl;
399 string const createLyXTmpDir(string const & deflt)
401 if (!deflt.empty() && deflt != "/tmp") {
402 if (mkdir(deflt, 0777)) {
404 Path p(package().user_support());
406 if (IsDirWriteable(deflt)) {
407 // deflt could not be created because it
408 // did exist already, so let's create our own
410 return createTmpDir(deflt, "lyx_tmpdir");
412 // some other error occured.
413 return createTmpDir("/tmp", "lyx_tmpdir");
419 Path p(package().user_support());
421 return createTmpDir("/tmp", "lyx_tmpdir");
426 bool createDirectory(string const & path, int permission)
428 string temp(rtrim(os::internal_path(path), "/"));
430 BOOST_ASSERT(!temp.empty());
432 if (mkdir(temp, permission))
439 // Strip filename from path name
440 string const OnlyPath(string const & Filename)
442 // If empty filename, return empty
443 if (Filename.empty()) return Filename;
445 // Find last / or start of filename
446 string::size_type j = Filename.rfind('/');
447 if (j == string::npos)
449 return Filename.substr(0, j + 1);
453 // Convert relative path into absolute path based on a basepath.
454 // If relpath is absolute, just use that.
455 // If basepath is empty, use CWD as base.
456 string const MakeAbsPath(string const & RelPath, string const & BasePath)
458 // checks for already absolute path
459 if (os::is_absolute_path(RelPath))
462 // Copies given paths
463 string TempRel(os::internal_path(RelPath));
464 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
465 TempRel = subst(TempRel, "//", "/");
469 if (os::is_absolute_path(BasePath))
472 TempBase = AddPath(getcwd(), BasePath);
474 // Handle /./ at the end of the path
475 while (suffixIs(TempBase, "/./"))
476 TempBase.erase(TempBase.length() - 2);
478 // processes relative path
479 string RTemp(TempRel);
482 while (!RTemp.empty()) {
484 RTemp = split(RTemp, Temp, '/');
486 if (Temp == ".") continue;
488 // Remove one level of TempBase
489 string::difference_type i = TempBase.length() - 2;
492 while (i > 0 && TempBase[i] != '/') --i;
496 while (i > 2 && TempBase[i] != '/') --i;
499 TempBase.erase(i, string::npos);
502 } else if (Temp.empty() && !RTemp.empty()) {
503 TempBase = os::current_root() + RTemp;
506 // Add this piece to TempBase
507 if (!suffixIs(TempBase, '/'))
513 // returns absolute path
514 return os::internal_path(TempBase);
518 // Correctly append filename to the pathname.
519 // If pathname is '.', then don't use pathname.
520 // Chops any path of filename.
521 string const AddName(string const & path, string const & fname)
524 string const basename(OnlyFilename(fname));
528 if (path != "." && path != "./" && !path.empty()) {
529 buf = os::internal_path(path);
530 if (!suffixIs(path, '/'))
534 return buf + basename;
538 // Strips path from filename
539 string const OnlyFilename(string const & fname)
544 string::size_type j = fname.rfind('/');
545 if (j == string::npos) // no '/' in fname
549 return fname.substr(j + 1);
553 /// Returns true is path is absolute
554 bool AbsolutePath(string const & path)
556 return os::is_absolute_path(path);
561 // Create absolute path. If impossible, don't do anything
562 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
563 string const ExpandPath(string const & path)
565 // checks for already absolute path
566 string RTemp(ReplaceEnvironmentPath(path));
567 if (os::is_absolute_path(RTemp))
571 string const copy(RTemp);
574 RTemp = split(RTemp, Temp, '/');
577 return getcwd() + '/' + RTemp;
580 return package().home_dir() + '/' + RTemp;
583 return MakeAbsPath(copy);
585 // Don't know how to handle this
591 // Constracts path/../path
592 // Can't handle "../../" or "/../" (Asger)
593 // Also converts paths like /foo//bar ==> /foo/bar
594 string const NormalizePath(string const & path)
600 if (os::is_absolute_path(path))
603 // Make implicit current directory explicit
606 // Normalise paths like /foo//bar ==> /foo/bar
607 boost::RegEx regex("/{2,}");
608 RTemp = regex.Merge(RTemp, "/");
610 while (!RTemp.empty()) {
612 RTemp = split(RTemp, Temp, '/');
616 } else if (Temp == "..") {
617 // Remove one level of TempBase
618 string::difference_type i = TempBase.length() - 2;
619 while (i > 0 && TempBase[i] != '/')
621 if (i >= 0 && TempBase[i] == '/')
622 TempBase.erase(i + 1, string::npos);
626 TempBase += Temp + '/';
630 // returns absolute path
635 string const GetFileContents(string const & fname)
637 if (fs::exists(fname)) {
638 ifstream ifs(fname.c_str());
646 lyxerr << "LyX was not able to read file '" << fname << '\'' << endl;
651 // Search the string for ${VAR} and $VAR and replace VAR using getenv.
652 string const ReplaceEnvironmentPath(string const & path)
654 // ${VAR} is defined as
655 // $\{[A-Za-z_][A-Za-z_0-9]*\}
656 static string const envvar_br = "[$]\\{([A-Za-z_][A-Za-z_0-9]*)\\}";
658 // $VAR is defined as:
659 // $\{[A-Za-z_][A-Za-z_0-9]*\}
660 static string const envvar = "[$]([A-Za-z_][A-Za-z_0-9]*)";
662 static boost::regex envvar_br_re("(.*)" + envvar_br + "(.*)");
663 static boost::regex envvar_re("(.*)" + envvar + "(.*)");
666 string result = path;
668 regex_match(result, what, envvar_br_re);
669 if (!what[0].matched) {
670 regex_match(result, what, envvar_re);
671 if (!what[0].matched)
674 result = what.str(1) + getEnv(what.str(2)) + what.str(3);
680 // Make relative path out of two absolute paths
681 string const MakeRelPath(string const & abspath, string const & basepath)
682 // Makes relative path out of absolute path. If it is deeper than basepath,
683 // it's easy. If basepath and abspath share something (they are all deeper
684 // than some directory), it'll be rendered using ..'s. If they are completely
685 // different, then the absolute path will be used as relative path.
687 string::size_type const abslen = abspath.length();
688 string::size_type const baselen = basepath.length();
690 string::size_type i = os::common_path(abspath, basepath);
693 // actually no match - cannot make it relative
697 // Count how many dirs there are in basepath above match
698 // and append as many '..''s into relpath
700 string::size_type j = i;
701 while (j < baselen) {
702 if (basepath[j] == '/') {
703 if (j + 1 == baselen)
710 // Append relative stuff from common directory to abspath
711 if (abspath[i] == '/')
713 for (; i < abslen; ++i)
716 if (suffixIs(buf, '/'))
717 buf.erase(buf.length() - 1);
718 // Substitute empty with .
725 // Append sub-directory(ies) to a path in an intelligent way
726 string const AddPath(string const & path, string const & path_2)
729 string const path2 = os::internal_path(path_2);
731 if (!path.empty() && path != "." && path != "./") {
732 buf = os::internal_path(path);
733 if (path[path.length() - 1] != '/')
737 if (!path2.empty()) {
738 string::size_type const p2start = path2.find_first_not_of('/');
739 string::size_type const p2end = path2.find_last_not_of('/');
740 string const tmp = path2.substr(p2start, p2end - p2start + 1);
748 Change extension of oldname to extension.
749 Strips path off if no_path == true.
750 If no extension on oldname, just appends.
752 string const ChangeExtension(string const & oldname, string const & extension)
754 string::size_type const last_slash = oldname.rfind('/');
755 string::size_type last_dot = oldname.rfind('.');
756 if (last_dot < last_slash && last_slash != string::npos)
757 last_dot = string::npos;
760 // Make sure the extension starts with a dot
761 if (!extension.empty() && extension[0] != '.')
762 ext= '.' + extension;
766 return os::internal_path(oldname.substr(0, last_dot) + ext);
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(string const & filename)
814 if (filename.empty() || !IsFileReadable(filename))
817 ifstream ifs(filename.c_str());
819 // Couldn't open file...
823 string const gzipStamp = "\037\213";
826 string const zipStamp = "PK";
829 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(string 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 string const unzipFile(string const & zipped_file, string const & unzipped_file)
984 string const tempfile = unzipped_file.empty() ?
985 unzippedFileName(zipped_file) : unzipped_file;
987 string const command = "gunzip -c " + zipped_file + " > " + tempfile;
989 one.startscript(Systemcall::Wait, command);
990 // test that command was executed successfully (anon)
991 // yes, please do. (Lgb)
996 string const MakeDisplayPath(string const & path, unsigned int threshold)
1000 string const home(package().home_dir());
1002 // replace /home/blah with ~/
1003 if (prefixIs(str, home))
1004 str = subst(str, home, "~");
1006 if (str.length() <= threshold)
1007 return os::external_path(str);
1009 string const prefix = ".../";
1012 while (str.length() > threshold)
1013 str = split(str, temp, '/');
1015 // Did we shorten everything away?
1017 // Yes, filename itself is too long.
1018 // Pick the start and the end of the filename.
1019 str = OnlyFilename(path);
1020 string const head = str.substr(0, threshold / 2 - 3);
1022 string::size_type len = str.length();
1024 str.substr(len - threshold / 2 - 2, len - 1);
1025 str = head + "..." + tail;
1028 return os::external_path(prefix + str);
1032 bool LyXReadLink(string const & file, string & link, bool resolve)
1034 #ifdef HAVE_READLINK
1035 char linkbuffer[512];
1036 // Should be PATH_MAX but that needs autconf support
1037 int const nRead = ::readlink(file.c_str(),
1038 linkbuffer, sizeof(linkbuffer) - 1);
1041 linkbuffer[nRead] = '\0'; // terminator
1043 link = MakeAbsPath(linkbuffer, OnlyPath(file));
1053 cmd_ret const RunCommand(string const & cmd)
1055 // FIXME: replace all calls to RunCommand with ForkedCall
1056 // (if the output is not needed) or the code in ispell.C
1057 // (if the output is needed).
1059 // One question is if we should use popen or
1060 // create our own popen based on fork, exec, pipe
1061 // of course the best would be to have a
1062 // pstream (process stream), with the
1063 // variants ipstream, opstream
1065 sigset_t newMask, oldMask;
1066 sigemptyset(&oldMask);
1067 sigemptyset(&newMask);
1068 sigaddset(&newMask, SIGCHLD);
1070 // Block the SIGCHLD signal.
1071 sigprocmask(SIG_BLOCK, &newMask, &oldMask);
1073 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1075 // (Claus Hentschel) Check if popen was succesful ;-)
1077 return make_pair(-1, string());
1078 lyxerr << "RunCommand:: could not start child process" << endl;
1084 ret += static_cast<char>(c);
1087 int const pret = pclose(inf);
1089 perror("RunCommand:: could not terminate child process");
1091 // Unblock the SIGCHLD signal and restore the old mask.
1092 sigprocmask(SIG_SETMASK, &oldMask, 0);
1094 return make_pair(pret, ret);
1098 string const findtexfile(string const & fil, string const & /*format*/)
1100 /* There is no problem to extend this function too use other
1101 methods to look for files. It could be setup to look
1102 in environment paths and also if wanted as a last resort
1103 to a recursive find. One of the easier extensions would
1104 perhaps be to use the LyX file lookup methods. But! I am
1105 going to implement this until I see some demand for it.
1109 // If the file can be found directly, we just return a
1110 // absolute path version of it.
1111 if (fs::exists(fil))
1112 return MakeAbsPath(fil);
1114 // No we try to find it using kpsewhich.
1115 // It seems from the kpsewhich manual page that it is safe to use
1116 // kpsewhich without --format: "When the --format option is not
1117 // given, the search path used when looking for a file is inferred
1118 // from the name given, by looking for a known extension. If no
1119 // known extension is found, the search path for TeX source files
1121 // However, we want to take advantage of the format sine almost all
1122 // the different formats has environment variables that can be used
1123 // to controll which paths to search. f.ex. bib looks in
1124 // BIBINPUTS and TEXBIB. Small list follows:
1125 // bib - BIBINPUTS, TEXBIB
1127 // graphic/figure - TEXPICTS, TEXINPUTS
1128 // ist - TEXINDEXSTYLE, INDEXSTYLE
1129 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1131 // tfm - TFMFONTS, TEXFONTS
1132 // This means that to use kpsewhich in the best possible way we
1133 // should help it by setting additional path in the approp. envir.var.
1134 string const kpsecmd = "kpsewhich " + fil;
1136 cmd_ret const c = RunCommand(kpsecmd);
1138 lyxerr[Debug::LATEX] << "kpse status = " << c.first << '\n'
1139 << "kpse result = `" << rtrim(c.second, "\n")
1142 return os::internal_path(rtrim(c.second, "\n\r"));
1148 void removeAutosaveFile(string const & filename)
1150 string a = OnlyPath(filename);
1152 a += OnlyFilename(filename);
1159 void readBB_lyxerrMessage(string const & file, bool & zipped,
1160 string const & message)
1162 lyxerr[Debug::GRAPHICS] << "[readBB_from_PSFile] "
1163 << message << std::endl;
1164 #ifdef WITH_WARNINGS
1165 #warning Why is this func deleting a file? (Lgb)
1172 string const readBB_from_PSFile(string const & file)
1174 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1175 // It seems that every command in the header has an own line,
1176 // getline() should work for all files.
1177 // On the other hand some plot programs write the bb at the
1178 // end of the file. Than we have in the header:
1179 // %%BoundingBox: (atend)
1180 // In this case we must check the end.
1181 bool zipped = zippedFile(file);
1182 string const file_ = zipped ?
1183 string(unzipFile(file)) : string(file);
1184 string const format = getFormatFromContents(file_);
1186 if (format != "eps" && format != "ps") {
1187 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1191 std::ifstream is(file_.c_str());
1195 if (contains(s,"%%BoundingBox:") && !contains(s,"atend")) {
1196 string const bb = ltrim(s.substr(14));
1197 readBB_lyxerrMessage(file_, zipped, bb);
1201 readBB_lyxerrMessage(file_, zipped, "no bb found");
1206 int compare_timestamps(string const & file1, string const & file2)
1208 BOOST_ASSERT(AbsolutePath(file1) && AbsolutePath(file2));
1210 // If the original is newer than the copy, then copy the original
1211 // to the new directory.
1214 if (fs::exists(file1) && fs::exists(file2)) {
1215 double const tmp = difftime(fs::last_write_time(file1),
1216 fs::last_write_time(file2));
1218 cmp = tmp > 0 ? 1 : -1;
1220 } else if (fs::exists(file1)) {
1222 } else if (fs::exists(file2)) {
1229 } //namespace support