3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * parts Copyright 1985, 1990, 1993 Free Software Foundation, Inc.
8 * \author Ivan Schreter
9 * \author Dirk Niggemann
10 * \author Asger Alstrup
11 * \author Lars Gullik Bjønnes
12 * \author Jean-Marc Lasgouttes
13 * \author Angus Leeming
17 * Full author contact details are available in file CREDITS.
19 * General path-mangling functions
24 #include "support/convert.h"
25 #include "support/environment.h"
26 #include "support/filetools.h"
27 #include "support/fs_extras.h"
28 #include "support/lstrings.h"
29 #include "support/lyxlib.h"
30 #include "support/os.h"
31 #include "support/package.h"
32 #include "support/path.h"
33 #include "support/systemcall.h"
35 // FIXME Interface violation
39 #include <boost/assert.hpp>
40 #include <boost/filesystem/operations.hpp>
41 #include <boost/regex.hpp>
54 #ifndef CXX_GLOBAL_CSTD
65 using std::ostringstream;
68 namespace fs = boost::filesystem;
73 bool IsLyXFilename(string const & filename)
75 return suffixIs(ascii_lowercase(filename), ".lyx");
79 bool IsSGMLFilename(string const & filename)
81 return suffixIs(ascii_lowercase(filename), ".sgml");
85 string const latex_path(string const & original_path,
86 latex_path_extension extension,
89 string path = subst(original_path, "\\", "/");
90 path = subst(path, "~", "\\string~");
91 if (path.find(' ') != string::npos)
92 // We can't use '"' because " is sometimes active (e.g. if
93 // babel is loaded with the "german" option)
94 if (extension == EXCLUDE_EXTENSION) {
95 string const base = ChangeExtension(path, string());
96 string const ext = GetExtension(path);
97 // ChangeExtension calls os::internal_path internally
98 // so don't use it to re-add the extension.
99 path = "\\string\"" + base + "\\string\"." + ext;
101 path = "\\string\"" + path + "\\string\"";
102 if (dots == ESCAPE_DOTS)
103 return subst(path, ".", "\\lyxdot ");
108 // Substitutes spaces with underscores in filename (and path)
109 string const MakeLatexName(string const & file)
111 string name = OnlyFilename(file);
112 string const path = OnlyPath(file);
114 for (string::size_type i = 0; i < name.length(); ++i) {
115 name[i] &= 0x7f; // set 8th bit to 0
118 // ok so we scan through the string twice, but who cares.
119 string const keep("abcdefghijklmnopqrstuvwxyz"
120 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
121 "@!\"'()*+,-./0123456789:;<=>?[]`|");
123 string::size_type pos = 0;
124 while ((pos = name.find_first_not_of(keep, pos)) != string::npos) {
127 return AddName(path, name);
131 string const QuoteName(string const & name)
133 return (os::shell() == os::UNIX) ?
139 // Is a file readable ?
140 bool IsFileReadable(string const & path)
142 return fs::exists(path) && !fs::is_directory(path) && fs::is_readable(path);
146 //returns true: dir writeable
147 // false: not writeable
148 bool IsDirWriteable(string const & path)
150 lyxerr[Debug::FILES] << "IsDirWriteable: " << path << endl;
152 string const tmpfl(tempName(path, "lyxwritetest"));
162 // Uses a string of paths separated by ";"s to find a file to open.
163 // Can't cope with pathnames with a ';' in them. Returns full path to file.
164 // If path entry begins with $$LyX/, use system_lyxdir
165 // If path entry begins with $$User/, use user_lyxdir
166 // Example: "$$User/doc;$$LyX/doc"
167 string const FileOpenSearch(string const & path, string const & name,
172 bool notfound = true;
173 string tmppath = split(path, path_element, ';');
175 while (notfound && !path_element.empty()) {
176 path_element = os::internal_path(path_element);
177 if (!suffixIs(path_element, '/'))
179 path_element = subst(path_element, "$$LyX",
180 package().system_support());
181 path_element = subst(path_element, "$$User",
182 package().user_support());
184 real_file = FileSearch(path_element, name, ext);
186 if (real_file.empty()) {
188 tmppath = split(tmppath, path_element, ';');
189 } while (!tmppath.empty() && path_element.empty());
195 if (ext.empty() && notfound) {
196 real_file = FileOpenSearch(path, name, "exe");
197 if (notfound) real_file = FileOpenSearch(path, name, "cmd");
204 /// Returns a vector of all files in directory dir having extension ext.
205 vector<string> const DirList(string const & dir, string const & ext)
207 // EXCEPTIONS FIXME. Rewrite needed when we turn on exceptions. (Lgb)
208 vector<string> dirlist;
210 if (!(fs::exists(dir) && fs::is_directory(dir))) {
212 << "Directory \"" << dir
213 << "\" does not exist to DirList." << endl;
218 if (!ext.empty() && ext[0] != '.')
222 fs::directory_iterator dit(dir);
223 fs::directory_iterator end;
224 for (; dit != end; ++dit) {
225 string const & fil = dit->leaf();
226 if (suffixIs(fil, extension)) {
227 dirlist.push_back(fil);
234 // Returns the real name of file name in directory path, with optional
236 string const FileSearch(string const & path, string const & name,
239 // if `name' is an absolute path, we ignore the setting of `path'
240 // Expand Environmentvariables in 'name'
241 string const tmpname = ReplaceEnvironmentPath(name);
242 string fullname = MakeAbsPath(tmpname, path);
243 // search first without extension, then with it.
244 if (IsFileReadable(fullname))
246 else if (ext.empty())
248 else { // Is it not more reasonable to use ChangeExtension()? (SMiyata)
251 if (IsFileReadable(fullname))
259 // Search the file name.ext in the subdirectory dir of
261 // 2) build_lyxdir (if not empty)
263 string const LibFileSearch(string const & dir, string const & name,
266 string fullname = FileSearch(AddPath(package().user_support(), dir),
268 if (!fullname.empty())
271 if (!package().build_support().empty())
272 fullname = FileSearch(AddPath(package().build_support(), dir),
274 if (!fullname.empty())
277 return FileSearch(AddPath(package().system_support(), dir), name, ext);
282 i18nLibFileSearch(string const & dir, string const & name,
285 // the following comments are from intl/dcigettext.c. We try
286 // to mimick this behaviour here.
287 /* The highest priority value is the `LANGUAGE' environment
288 variable. But we don't use the value if the currently
289 selected locale is the C locale. This is a GNU extension. */
290 /* [Otherwise] We have to proceed with the POSIX methods of
291 looking to `LC_ALL', `LC_xxx', and `LANG'. */
293 string lang = getEnv("LC_ALL");
295 lang = getEnv("LC_MESSAGES");
297 lang = getEnv("LANG");
303 string const language = getEnv("LANGUAGE");
304 if (lang != "C" && lang != "POSIX" && !language.empty())
308 lang = split(lang, l, ':');
309 while (!l.empty() && l != "C" && l != "POSIX") {
310 string const tmp = LibFileSearch(dir,
311 token(l, '_', 0) + '_' + name,
315 lang = split(lang, l, ':');
318 return LibFileSearch(dir, name, ext);
322 string const LibScriptSearch(string const & command_in)
324 string const token_scriptpath("$$s/");
326 string command = command_in;
327 // Find the starting position of "$$s/"
328 string::size_type const pos1 = command.find(token_scriptpath);
329 if (pos1 == string::npos)
331 // Find the end of the "$$s/some_subdir/some_script" word within
332 // command. Assumes that the script name does not contain spaces.
333 string::size_type const start_script = pos1 + 4;
334 string::size_type const pos2 = command.find(' ', start_script);
335 string::size_type const size_script = pos2 == string::npos?
336 (command.size() - start_script) : pos2 - start_script;
338 // Does this script file exist?
339 string const script =
340 LibFileSearch(".", command.substr(start_script, size_script));
342 if (script.empty()) {
343 // Replace "$$s/" with ""
344 command.erase(pos1, 4);
346 // Replace "$$s/foo/some_script" with "<path to>/some_script".
347 string::size_type const size_replace = size_script + 4;
348 command.replace(pos1, size_replace, QuoteName(script));
357 string const createTmpDir(string const & tempdir, string const & mask)
360 << "createTmpDir: tempdir=`" << tempdir << "'\n"
361 << "createTmpDir: mask=`" << mask << '\'' << endl;
363 string const tmpfl(tempName(tempdir, mask));
364 // lyx::tempName actually creates a file to make sure that it
365 // stays unique. So we have to delete it before we can create
366 // a dir with the same name. Note also that we are not thread
367 // safe because of the gap between unlink and mkdir. (Lgb)
370 if (tmpfl.empty() || mkdir(tmpfl, 0700)) {
371 lyxerr << "LyX could not create the temporary directory '"
372 << tmpfl << "'" << endl;
376 return MakeAbsPath(tmpfl);
382 bool destroyDir(string const & tmpdir)
386 Path p(user_lyxdir());
388 return fs::remove_all(tmpdir) > 0;
392 string const createBufferTmpDir()
395 // We are in our own directory. Why bother to mangle name?
396 // In fact I wrote this code to circumvent a problematic behaviour
397 // (bug?) of EMX mkstemp().
399 package().temp_dir() + "/lyx_tmpbuf" +
400 convert<string>(count++);
402 if (mkdir(tmpfl, 0777)) {
403 lyxerr << "LyX could not create the temporary directory '"
404 << tmpfl << "'" << endl;
411 string const createLyXTmpDir(string const & deflt)
413 if (!deflt.empty() && deflt != "/tmp") {
414 if (mkdir(deflt, 0777)) {
416 Path p(package().user_support());
418 if (IsDirWriteable(deflt)) {
419 // deflt could not be created because it
420 // did exist already, so let's create our own
422 return createTmpDir(deflt, "lyx_tmpdir");
424 // some other error occured.
425 return createTmpDir("/tmp", "lyx_tmpdir");
431 Path p(package().user_support());
433 return createTmpDir("/tmp", "lyx_tmpdir");
438 bool createDirectory(string const & path, int permission)
440 string temp(rtrim(os::internal_path(path), "/"));
442 BOOST_ASSERT(!temp.empty());
444 if (mkdir(temp, permission))
451 // Strip filename from path name
452 string const OnlyPath(string const & Filename)
454 // If empty filename, return empty
455 if (Filename.empty()) return Filename;
457 // Find last / or start of filename
458 string::size_type j = Filename.rfind('/');
459 if (j == string::npos)
461 return Filename.substr(0, j + 1);
465 // Convert relative path into absolute path based on a basepath.
466 // If relpath is absolute, just use that.
467 // If basepath is empty, use CWD as base.
468 string const MakeAbsPath(string const & RelPath, string const & BasePath)
470 // checks for already absolute path
471 if (os::is_absolute_path(RelPath))
474 // Copies given paths
475 string TempRel(os::internal_path(RelPath));
476 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
477 TempRel = subst(TempRel, "//", "/");
481 if (os::is_absolute_path(BasePath))
484 TempBase = AddPath(getcwd(), BasePath);
486 // Handle /./ at the end of the path
487 while (suffixIs(TempBase, "/./"))
488 TempBase.erase(TempBase.length() - 2);
490 // processes relative path
491 string RTemp(TempRel);
494 while (!RTemp.empty()) {
496 RTemp = split(RTemp, Temp, '/');
498 if (Temp == ".") continue;
500 // Remove one level of TempBase
501 string::difference_type i = TempBase.length() - 2;
504 while (i > 0 && TempBase[i] != '/') --i;
508 while (i > 2 && TempBase[i] != '/') --i;
511 TempBase.erase(i, string::npos);
514 } else if (Temp.empty() && !RTemp.empty()) {
515 TempBase = os::current_root() + RTemp;
518 // Add this piece to TempBase
519 if (!suffixIs(TempBase, '/'))
525 // returns absolute path
526 return os::internal_path(TempBase);
530 // Correctly append filename to the pathname.
531 // If pathname is '.', then don't use pathname.
532 // Chops any path of filename.
533 string const AddName(string const & path, string const & fname)
536 string const basename(OnlyFilename(fname));
540 if (path != "." && path != "./" && !path.empty()) {
541 buf = os::internal_path(path);
542 if (!suffixIs(path, '/'))
546 return buf + basename;
550 // Strips path from filename
551 string const OnlyFilename(string const & fname)
556 string::size_type j = fname.rfind('/');
557 if (j == string::npos) // no '/' in fname
561 return fname.substr(j + 1);
565 /// Returns true is path is absolute
566 bool AbsolutePath(string const & path)
568 return os::is_absolute_path(path);
573 // Create absolute path. If impossible, don't do anything
574 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
575 string const ExpandPath(string const & path)
577 // checks for already absolute path
578 string RTemp(ReplaceEnvironmentPath(path));
579 if (os::is_absolute_path(RTemp))
583 string const copy(RTemp);
586 RTemp = split(RTemp, Temp, '/');
589 return getcwd() + '/' + RTemp;
592 return package().home_dir() + '/' + RTemp;
595 return MakeAbsPath(copy);
597 // Don't know how to handle this
603 // Constracts path/../path
604 // Can't handle "../../" or "/../" (Asger)
605 // Also converts paths like /foo//bar ==> /foo/bar
606 string const NormalizePath(string const & path)
612 if (os::is_absolute_path(path))
615 // Make implicit current directory explicit
618 // Normalise paths like /foo//bar ==> /foo/bar
619 boost::RegEx regex("/{2,}");
620 RTemp = regex.Merge(RTemp, "/");
622 while (!RTemp.empty()) {
624 RTemp = split(RTemp, Temp, '/');
628 } else if (Temp == "..") {
629 // Remove one level of TempBase
630 string::difference_type i = TempBase.length() - 2;
631 while (i > 0 && TempBase[i] != '/')
633 if (i >= 0 && TempBase[i] == '/')
634 TempBase.erase(i + 1, string::npos);
638 TempBase += Temp + '/';
642 // returns absolute path
647 string const GetFileContents(string const & fname)
649 if (fs::exists(fname)) {
650 ifstream ifs(fname.c_str());
658 lyxerr << "LyX was not able to read file '" << fname << '\'' << endl;
663 // Search the string for ${VAR} and $VAR and replace VAR using getenv.
664 string const ReplaceEnvironmentPath(string const & path)
666 // ${VAR} is defined as
667 // $\{[A-Za-z_][A-Za-z_0-9]*\}
668 static string const envvar_br = "[$]\\{([A-Za-z_][A-Za-z_0-9]*)\\}";
670 // $VAR is defined as:
671 // $\{[A-Za-z_][A-Za-z_0-9]*\}
672 static string const envvar = "[$]([A-Za-z_][A-Za-z_0-9]*)";
674 static boost::regex envvar_br_re("(.*)" + envvar_br + "(.*)");
675 static boost::regex envvar_re("(.*)" + envvar + "(.*)");
678 string result = path;
680 regex_match(result, what, envvar_br_re);
681 if (!what[0].matched) {
682 regex_match(result, what, envvar_re);
683 if (!what[0].matched)
686 result = what.str(1) + getEnv(what.str(2)) + what.str(3);
692 // Make relative path out of two absolute paths
693 string const MakeRelPath(string const & abspath, string const & basepath)
694 // Makes relative path out of absolute path. If it is deeper than basepath,
695 // it's easy. If basepath and abspath share something (they are all deeper
696 // than some directory), it'll be rendered using ..'s. If they are completely
697 // different, then the absolute path will be used as relative path.
699 string::size_type const abslen = abspath.length();
700 string::size_type const baselen = basepath.length();
702 string::size_type i = os::common_path(abspath, basepath);
705 // actually no match - cannot make it relative
709 // Count how many dirs there are in basepath above match
710 // and append as many '..''s into relpath
712 string::size_type j = i;
713 while (j < baselen) {
714 if (basepath[j] == '/') {
715 if (j + 1 == baselen)
722 // Append relative stuff from common directory to abspath
723 if (abspath[i] == '/')
725 for (; i < abslen; ++i)
728 if (suffixIs(buf, '/'))
729 buf.erase(buf.length() - 1);
730 // Substitute empty with .
737 // Append sub-directory(ies) to a path in an intelligent way
738 string const AddPath(string const & path, string const & path_2)
741 string const path2 = os::internal_path(path_2);
743 if (!path.empty() && path != "." && path != "./") {
744 buf = os::internal_path(path);
745 if (path[path.length() - 1] != '/')
749 if (!path2.empty()) {
750 string::size_type const p2start = path2.find_first_not_of('/');
751 string::size_type const p2end = path2.find_last_not_of('/');
752 string const tmp = path2.substr(p2start, p2end - p2start + 1);
760 Change extension of oldname to extension.
761 Strips path off if no_path == true.
762 If no extension on oldname, just appends.
764 string const ChangeExtension(string const & oldname, string const & extension)
766 string::size_type const last_slash = oldname.rfind('/');
767 string::size_type last_dot = oldname.rfind('.');
768 if (last_dot < last_slash && last_slash != string::npos)
769 last_dot = string::npos;
772 // Make sure the extension starts with a dot
773 if (!extension.empty() && extension[0] != '.')
774 ext= '.' + extension;
778 return os::internal_path(oldname.substr(0, last_dot) + ext);
782 /// Return the extension of the file (not including the .)
783 string const GetExtension(string const & name)
785 string::size_type const last_slash = name.rfind('/');
786 string::size_type const last_dot = name.rfind('.');
787 if (last_dot != string::npos &&
788 (last_slash == string::npos || last_dot > last_slash))
789 return name.substr(last_dot + 1,
790 name.length() - (last_dot + 1));
796 // the different filetypes and what they contain in one of the first lines
797 // (dots are any characters). (Herbert 20020131)
800 // EPS %!PS-Adobe-3.0 EPSF...
807 // PBM P1... or P4 (B/W)
808 // PGM P2... or P5 (Grayscale)
809 // PPM P3... or P6 (color)
810 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
811 // SGI \001\332... (decimal 474)
813 // TIFF II... or MM...
815 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
816 // ...static char *...
817 // XWD \000\000\000\151 (0x00006900) decimal 105
819 // GZIP \037\213 http://www.ietf.org/rfc/rfc1952.txt
820 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
821 // Z \037\235 UNIX compress
823 string const getFormatFromContents(string const & filename)
826 if (filename.empty() || !IsFileReadable(filename))
829 ifstream ifs(filename.c_str());
831 // Couldn't open file...
835 string const gzipStamp = "\037\213";
838 string const zipStamp = "PK";
841 string const compressStamp = "\037\235";
843 // Maximum strings to read
844 int const max_count = 50;
849 bool firstLine = true;
850 while ((count++ < max_count) && format.empty()) {
852 lyxerr[Debug::GRAPHICS]
853 << "filetools(getFormatFromContents)\n"
854 << "\tFile type not recognised before EOF!"
860 string const stamp = str.substr(0,2);
861 if (firstLine && str.size() >= 2) {
862 // at first we check for a zipped file, because this
863 // information is saved in the first bytes of the file!
864 // also some graphic formats which save the information
865 // in the first line, too.
866 if (prefixIs(str, gzipStamp)) {
869 } else if (stamp == zipStamp) {
872 } else if (stamp == compressStamp) {
876 } else if (stamp == "BM") {
879 } else if (stamp == "\001\332") {
883 // Don't need to use str.at(0), str.at(1) because
884 // we already know that str.size() >= 2
885 } else if (str[0] == 'P') {
901 } else if ((stamp == "II") || (stamp == "MM")) {
904 } else if (prefixIs(str,"%TGIF")) {
907 } else if (prefixIs(str,"#FIG")) {
910 } else if (prefixIs(str,"GIF")) {
913 } else if (str.size() > 3) {
914 int const c = ((str[0] << 24) & (str[1] << 16) &
915 (str[2] << 8) & str[3]);
926 else if (contains(str,"EPSF"))
927 // dummy, if we have wrong file description like
928 // %!PS-Adobe-2.0EPSF"
931 else if (contains(str,"Grace"))
934 else if (contains(str,"JFIF"))
937 else if (contains(str,"%PDF"))
940 else if (contains(str,"PNG"))
943 else if (contains(str,"%!PS-Adobe")) {
946 if (contains(str,"EPSF"))
952 else if (contains(str,"_bits[]"))
955 else if (contains(str,"XPM") || contains(str, "static char *"))
958 else if (contains(str,"BITPIX"))
962 if (!format.empty()) {
963 lyxerr[Debug::GRAPHICS]
964 << "Recognised Fileformat: " << format << endl;
968 lyxerr[Debug::GRAPHICS]
969 << "filetools(getFormatFromContents)\n"
970 << "\tCouldn't find a known format!\n";
975 /// check for zipped file
976 bool zippedFile(string const & name)
978 string const type = getFormatFromContents(name);
979 if (contains("gzip zip compress", type) && !type.empty())
985 string const unzippedFileName(string const & zipped_file)
987 string const ext = GetExtension(zipped_file);
988 if (ext == "gz" || ext == "z" || ext == "Z")
989 return ChangeExtension(zipped_file, string());
990 return "unzipped_" + zipped_file;
994 string const unzipFile(string const & zipped_file, string const & unzipped_file)
996 string const tempfile = unzipped_file.empty() ?
997 unzippedFileName(zipped_file) : unzipped_file;
999 string const command = "gunzip -c " + zipped_file + " > " + tempfile;
1001 one.startscript(Systemcall::Wait, command);
1002 // test that command was executed successfully (anon)
1003 // yes, please do. (Lgb)
1008 string const MakeDisplayPath(string const & path, unsigned int threshold)
1012 string const home(package().home_dir());
1014 // replace /home/blah with ~/
1015 if (prefixIs(str, home))
1016 str = subst(str, home, "~");
1018 if (str.length() <= threshold)
1019 return os::external_path(str);
1021 string const prefix = ".../";
1024 while (str.length() > threshold)
1025 str = split(str, temp, '/');
1027 // Did we shorten everything away?
1029 // Yes, filename itself is too long.
1030 // Pick the start and the end of the filename.
1031 str = OnlyFilename(path);
1032 string const head = str.substr(0, threshold / 2 - 3);
1034 string::size_type len = str.length();
1036 str.substr(len - threshold / 2 - 2, len - 1);
1037 str = head + "..." + tail;
1040 return os::external_path(prefix + str);
1044 bool LyXReadLink(string const & file, string & link, bool resolve)
1046 #ifdef HAVE_READLINK
1047 char linkbuffer[512];
1048 // Should be PATH_MAX but that needs autconf support
1049 int const nRead = ::readlink(file.c_str(),
1050 linkbuffer, sizeof(linkbuffer) - 1);
1053 linkbuffer[nRead] = '\0'; // terminator
1055 link = MakeAbsPath(linkbuffer, OnlyPath(file));
1065 cmd_ret const RunCommand(string const & cmd)
1067 // FIXME: replace all calls to RunCommand with ForkedCall
1068 // (if the output is not needed) or the code in ispell.C
1069 // (if the output is needed).
1071 // One question is if we should use popen or
1072 // create our own popen based on fork, exec, pipe
1073 // of course the best would be to have a
1074 // pstream (process stream), with the
1075 // variants ipstream, opstream
1077 #if defined (HAVE_POPEN)
1078 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1079 #elif defined (HAVE__POPEN)
1080 FILE * inf = ::_popen(cmd.c_str(), os::popen_read_mode());
1082 #error No popen() function.
1085 // (Claus Hentschel) Check if popen was succesful ;-)
1087 return make_pair(-1, string());
1088 lyxerr << "RunCommand:: could not start child process" << endl;
1094 ret += static_cast<char>(c);
1098 #if defined (HAVE_PCLOSE)
1099 int const pret = pclose(inf);
1100 #elif defined (HAVE__PCLOSE)
1101 int const pret = _pclose(inf);
1103 #error No pclose() function.
1107 perror("RunCommand:: could not terminate child process");
1109 return make_pair(pret, ret);
1113 string const findtexfile(string const & fil, string const & /*format*/)
1115 /* There is no problem to extend this function too use other
1116 methods to look for files. It could be setup to look
1117 in environment paths and also if wanted as a last resort
1118 to a recursive find. One of the easier extensions would
1119 perhaps be to use the LyX file lookup methods. But! I am
1120 going to implement this until I see some demand for it.
1124 // If the file can be found directly, we just return a
1125 // absolute path version of it.
1126 if (fs::exists(fil))
1127 return MakeAbsPath(fil);
1129 // No we try to find it using kpsewhich.
1130 // It seems from the kpsewhich manual page that it is safe to use
1131 // kpsewhich without --format: "When the --format option is not
1132 // given, the search path used when looking for a file is inferred
1133 // from the name given, by looking for a known extension. If no
1134 // known extension is found, the search path for TeX source files
1136 // However, we want to take advantage of the format sine almost all
1137 // the different formats has environment variables that can be used
1138 // to controll which paths to search. f.ex. bib looks in
1139 // BIBINPUTS and TEXBIB. Small list follows:
1140 // bib - BIBINPUTS, TEXBIB
1142 // graphic/figure - TEXPICTS, TEXINPUTS
1143 // ist - TEXINDEXSTYLE, INDEXSTYLE
1144 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1146 // tfm - TFMFONTS, TEXFONTS
1147 // This means that to use kpsewhich in the best possible way we
1148 // should help it by setting additional path in the approp. envir.var.
1149 string const kpsecmd = "kpsewhich " + fil;
1151 cmd_ret const c = RunCommand(kpsecmd);
1153 lyxerr[Debug::LATEX] << "kpse status = " << c.first << '\n'
1154 << "kpse result = `" << rtrim(c.second, "\n")
1157 return os::internal_path(rtrim(c.second, "\n\r"));
1163 void removeAutosaveFile(string const & filename)
1165 string a = OnlyPath(filename);
1167 a += OnlyFilename(filename);
1174 void readBB_lyxerrMessage(string const & file, bool & zipped,
1175 string const & message)
1177 lyxerr[Debug::GRAPHICS] << "[readBB_from_PSFile] "
1178 << message << std::endl;
1179 #ifdef WITH_WARNINGS
1180 #warning Why is this func deleting a file? (Lgb)
1187 string const readBB_from_PSFile(string const & file)
1189 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1190 // It seems that every command in the header has an own line,
1191 // getline() should work for all files.
1192 // On the other hand some plot programs write the bb at the
1193 // end of the file. Than we have in the header:
1194 // %%BoundingBox: (atend)
1195 // In this case we must check the end.
1196 bool zipped = zippedFile(file);
1197 string const file_ = zipped ?
1198 string(unzipFile(file)) : string(file);
1199 string const format = getFormatFromContents(file_);
1201 if (format != "eps" && format != "ps") {
1202 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1206 std::ifstream is(file_.c_str());
1210 if (contains(s,"%%BoundingBox:") && !contains(s,"atend")) {
1211 string const bb = ltrim(s.substr(14));
1212 readBB_lyxerrMessage(file_, zipped, bb);
1216 readBB_lyxerrMessage(file_, zipped, "no bb found");
1221 int compare_timestamps(string const & file1, string const & file2)
1223 BOOST_ASSERT(AbsolutePath(file1) && AbsolutePath(file2));
1225 // If the original is newer than the copy, then copy the original
1226 // to the new directory.
1229 if (fs::exists(file1) && fs::exists(file2)) {
1230 double const tmp = difftime(fs::last_write_time(file1),
1231 fs::last_write_time(file2));
1233 cmp = tmp > 0 ? 1 : -1;
1235 } else if (fs::exists(file1)) {
1237 } else if (fs::exists(file2)) {
1244 } //namespace support