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 // On cygwin, we may need windows or posix style paths.
90 string path = os::latex_path(original_path);
91 path = subst(path, "~", "\\string~");
92 if (path.find(' ') != string::npos) {
93 // We can't use '"' because " is sometimes active (e.g. if
94 // babel is loaded with the "german" option)
95 if (extension == EXCLUDE_EXTENSION) {
96 // ChangeExtension calls os::internal_path internally
97 // so don't use it to remove the extension.
98 string const ext = GetExtension(path);
99 string const base = ext.empty() ?
101 path.substr(0, path.length() - ext.length() - 1);
102 // ChangeExtension calls os::internal_path internally
103 // so don't use it to re-add the extension.
104 path = "\\string\"" + base + "\\string\"." + ext;
106 path = "\\string\"" + path + "\\string\"";
110 return dots == ESCAPE_DOTS ? subst(path, ".", "\\lyxdot ") : path;
114 // Substitutes spaces with underscores in filename (and path)
115 string const MakeLatexName(string const & file)
117 string name = OnlyFilename(file);
118 string const path = OnlyPath(file);
120 for (string::size_type i = 0; i < name.length(); ++i)
121 name[i] &= 0x7f; // set 8th bit to 0
123 // ok so we scan through the string twice, but who cares.
124 string const keep = "abcdefghijklmnopqrstuvwxyz"
125 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
126 "@!\"'()*+,-./0123456789:;<=>?[]`|";
128 string::size_type pos = 0;
129 while ((pos = name.find_first_not_of(keep, pos)) != string::npos)
132 return AddName(path, name);
136 string const QuoteName(string const & name)
138 return (os::shell() == os::UNIX) ?
144 // Is a file readable ?
145 bool IsFileReadable(string const & path)
147 return fs::exists(path) && !fs::is_directory(path) && fs::is_readable(path);
151 //returns true: dir writeable
152 // false: not writeable
153 bool IsDirWriteable(string const & path)
155 lyxerr[Debug::FILES] << "IsDirWriteable: " << path << endl;
157 string const tmpfl = tempName(path, "lyxwritetest");
167 // Uses a string of paths separated by ";"s to find a file to open.
168 // Can't cope with pathnames with a ';' in them. Returns full path to file.
169 // If path entry begins with $$LyX/, use system_lyxdir
170 // If path entry begins with $$User/, use user_lyxdir
171 // Example: "$$User/doc;$$LyX/doc"
172 string const FileOpenSearch(string const & path, string const & name,
177 bool notfound = true;
178 string tmppath = split(path, path_element, ';');
180 while (notfound && !path_element.empty()) {
181 path_element = os::internal_path(path_element);
182 if (!suffixIs(path_element, '/'))
184 path_element = subst(path_element, "$$LyX",
185 package().system_support());
186 path_element = subst(path_element, "$$User",
187 package().user_support());
189 real_file = FileSearch(path_element, name, ext);
191 if (real_file.empty()) {
193 tmppath = split(tmppath, path_element, ';');
194 } while (!tmppath.empty() && path_element.empty());
200 if (ext.empty() && notfound) {
201 real_file = FileOpenSearch(path, name, "exe");
202 if (notfound) real_file = FileOpenSearch(path, name, "cmd");
209 /// Returns a vector of all files in directory dir having extension ext.
210 vector<string> const DirList(string const & dir, string const & ext)
212 // EXCEPTIONS FIXME. Rewrite needed when we turn on exceptions. (Lgb)
213 vector<string> dirlist;
215 if (!(fs::exists(dir) && fs::is_directory(dir))) {
217 << "Directory \"" << dir
218 << "\" does not exist to DirList." << endl;
223 if (!ext.empty() && ext[0] != '.')
227 fs::directory_iterator dit(dir);
228 fs::directory_iterator end;
229 for (; dit != end; ++dit) {
230 string const & fil = dit->leaf();
231 if (suffixIs(fil, extension)) {
232 dirlist.push_back(fil);
239 // Returns the real name of file name in directory path, with optional
241 string const FileSearch(string const & path, string const & name,
244 // if `name' is an absolute path, we ignore the setting of `path'
245 // Expand Environmentvariables in 'name'
246 string const tmpname = ReplaceEnvironmentPath(name);
247 string fullname = MakeAbsPath(tmpname, path);
248 // search first without extension, then with it.
249 if (IsFileReadable(fullname))
253 // Is it not more reasonable to use ChangeExtension()? (SMiyata)
256 return IsFileReadable(fullname) ? fullname : string();
260 // Search the file name.ext in the subdirectory dir of
262 // 2) build_lyxdir (if not empty)
264 string const LibFileSearch(string const & dir, string const & name,
267 string fullname = FileSearch(AddPath(package().user_support(), dir),
269 if (!fullname.empty())
272 if (!package().build_support().empty())
273 fullname = FileSearch(AddPath(package().build_support(), dir),
275 if (!fullname.empty())
278 return FileSearch(AddPath(package().system_support(), dir), name, ext);
282 string const 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 static 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), "/");
441 BOOST_ASSERT(!temp.empty());
442 return mkdir(temp, permission) == 0;
446 // Strip filename from path name
447 string const OnlyPath(string const & filename)
449 // If empty filename, return empty
450 if (filename.empty())
453 // Find last / or start of filename
454 string::size_type j = filename.rfind('/');
455 return j == string::npos ? "./" : filename.substr(0, j + 1);
459 // Convert relative path into absolute path based on a basepath.
460 // If relpath is absolute, just use that.
461 // If basepath is empty, use CWD as base.
462 string const MakeAbsPath(string const & RelPath, string const & BasePath)
464 // checks for already absolute path
465 if (os::is_absolute_path(RelPath))
468 // Copies given paths
469 string TempRel = os::internal_path(RelPath);
470 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
471 TempRel = subst(TempRel, "//", "/");
475 if (os::is_absolute_path(BasePath))
478 TempBase = AddPath(getcwd(), BasePath);
480 // Handle /./ at the end of the path
481 while (suffixIs(TempBase, "/./"))
482 TempBase.erase(TempBase.length() - 2);
484 // processes relative path
485 string RTemp = TempRel;
488 while (!RTemp.empty()) {
490 RTemp = split(RTemp, Temp, '/');
492 if (Temp == ".") continue;
494 // Remove one level of TempBase
495 string::difference_type i = TempBase.length() - 2;
499 while (i > 0 && TempBase[i] != '/')
505 while (i > 2 && TempBase[i] != '/')
509 TempBase.erase(i, string::npos);
512 } else if (Temp.empty() && !RTemp.empty()) {
513 TempBase = os::current_root() + RTemp;
516 // Add this piece to TempBase
517 if (!suffixIs(TempBase, '/'))
523 // returns absolute path
524 return os::internal_path(TempBase);
528 // Correctly append filename to the pathname.
529 // If pathname is '.', then don't use pathname.
530 // Chops any path of filename.
531 string const AddName(string const & path, string const & fname)
533 string const basename = OnlyFilename(fname);
536 if (path != "." && path != "./" && !path.empty()) {
537 buf = os::internal_path(path);
538 if (!suffixIs(path, '/'))
542 return buf + basename;
546 // Strips path from filename
547 string const OnlyFilename(string const & fname)
552 string::size_type j = fname.rfind('/');
553 if (j == string::npos) // no '/' in fname
557 return fname.substr(j + 1);
561 /// Returns true is path is absolute
562 bool AbsolutePath(string const & path)
564 return os::is_absolute_path(path);
568 // Create absolute path. If impossible, don't do anything
569 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
570 string const ExpandPath(string const & path)
572 // checks for already absolute path
573 string RTemp = ReplaceEnvironmentPath(path);
574 if (os::is_absolute_path(RTemp))
578 string const copy = RTemp;
581 RTemp = split(RTemp, Temp, '/');
584 return getcwd() + '/' + RTemp;
587 return package().home_dir() + '/' + RTemp;
590 return MakeAbsPath(copy);
592 // Don't know how to handle this
597 // Normalize a path. Constracts path/../path
598 // Can't handle "../../" or "/../" (Asger)
599 // Also converts paths like /foo//bar ==> /foo/bar
600 string const NormalizePath(string const & path)
602 // Normalize paths like /foo//bar ==> /foo/bar
603 static boost::regex regex("/{2,}");
604 string const tmppath = boost::regex_merge(path, regex, "/");
606 fs::path const npath = fs::path(tmppath, fs::no_check).normalize();
608 if (!npath.is_complete())
609 return "./" + npath.string() + '/';
611 return npath.string() + '/';
615 string const GetFileContents(string const & fname)
617 if (fs::exists(fname)) {
618 ifstream ifs(fname.c_str());
626 lyxerr << "LyX was not able to read file '" << fname << '\'' << endl;
631 // Search the string for ${VAR} and $VAR and replace VAR using getenv.
632 string const ReplaceEnvironmentPath(string const & path)
634 // ${VAR} is defined as
635 // $\{[A-Za-z_][A-Za-z_0-9]*\}
636 static string const envvar_br = "[$]\\{([A-Za-z_][A-Za-z_0-9]*)\\}";
638 // $VAR is defined as:
639 // $\{[A-Za-z_][A-Za-z_0-9]*\}
640 static string const envvar = "[$]([A-Za-z_][A-Za-z_0-9]*)";
642 static boost::regex envvar_br_re("(.*)" + envvar_br + "(.*)");
643 static boost::regex envvar_re("(.*)" + envvar + "(.*)");
646 string result = path;
648 regex_match(result, what, envvar_br_re);
649 if (!what[0].matched) {
650 regex_match(result, what, envvar_re);
651 if (!what[0].matched)
654 result = what.str(1) + getEnv(what.str(2)) + what.str(3);
660 // Make relative path out of two absolute paths
661 string const MakeRelPath(string const & abspath, string const & basepath)
662 // Makes relative path out of absolute path. If it is deeper than basepath,
663 // it's easy. If basepath and abspath share something (they are all deeper
664 // than some directory), it'll be rendered using ..'s. If they are completely
665 // different, then the absolute path will be used as relative path.
667 string::size_type const abslen = abspath.length();
668 string::size_type const baselen = basepath.length();
670 string::size_type i = os::common_path(abspath, basepath);
673 // actually no match - cannot make it relative
677 // Count how many dirs there are in basepath above match
678 // and append as many '..''s into relpath
680 string::size_type j = i;
681 while (j < baselen) {
682 if (basepath[j] == '/') {
683 if (j + 1 == baselen)
690 // Append relative stuff from common directory to abspath
691 if (abspath[i] == '/')
693 for (; i < abslen; ++i)
696 if (suffixIs(buf, '/'))
697 buf.erase(buf.length() - 1);
698 // Substitute empty with .
705 // Append sub-directory(ies) to a path in an intelligent way
706 string const AddPath(string const & path, string const & path_2)
709 string const path2 = os::internal_path(path_2);
711 if (!path.empty() && path != "." && path != "./") {
712 buf = os::internal_path(path);
713 if (path[path.length() - 1] != '/')
717 if (!path2.empty()) {
718 string::size_type const p2start = path2.find_first_not_of('/');
719 string::size_type const p2end = path2.find_last_not_of('/');
720 string const tmp = path2.substr(p2start, p2end - p2start + 1);
728 Change extension of oldname to extension.
729 Strips path off if no_path == true.
730 If no extension on oldname, just appends.
732 string const ChangeExtension(string const & oldname, string const & extension)
734 string::size_type const last_slash = oldname.rfind('/');
735 string::size_type last_dot = oldname.rfind('.');
736 if (last_dot < last_slash && last_slash != string::npos)
737 last_dot = string::npos;
740 // Make sure the extension starts with a dot
741 if (!extension.empty() && extension[0] != '.')
742 ext= '.' + extension;
746 return os::internal_path(oldname.substr(0, last_dot) + ext);
750 string const removeExtension(string const & name)
752 return ChangeExtension(name, string());
756 /// Return the extension of the file (not including the .)
757 string const GetExtension(string const & name)
759 string::size_type const last_slash = name.rfind('/');
760 string::size_type const last_dot = name.rfind('.');
761 if (last_dot != string::npos &&
762 (last_slash == string::npos || last_dot > last_slash))
763 return name.substr(last_dot + 1,
764 name.length() - (last_dot + 1));
770 // the different filetypes and what they contain in one of the first lines
771 // (dots are any characters). (Herbert 20020131)
774 // EPS %!PS-Adobe-3.0 EPSF...
781 // PBM P1... or P4 (B/W)
782 // PGM P2... or P5 (Grayscale)
783 // PPM P3... or P6 (color)
784 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
785 // SGI \001\332... (decimal 474)
787 // TIFF II... or MM...
789 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
790 // ...static char *...
791 // XWD \000\000\000\151 (0x00006900) decimal 105
793 // GZIP \037\213 http://www.ietf.org/rfc/rfc1952.txt
794 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
795 // Z \037\235 UNIX compress
797 string const getFormatFromContents(string const & filename)
800 if (filename.empty() || !IsFileReadable(filename))
803 ifstream ifs(filename.c_str());
805 // Couldn't open file...
809 static string const gzipStamp = "\037\213";
812 static string const zipStamp = "PK";
815 static string const compressStamp = "\037\235";
817 // Maximum strings to read
818 int const max_count = 50;
823 bool firstLine = true;
824 while ((count++ < max_count) && format.empty()) {
826 lyxerr[Debug::GRAPHICS]
827 << "filetools(getFormatFromContents)\n"
828 << "\tFile type not recognised before EOF!"
834 string const stamp = str.substr(0, 2);
835 if (firstLine && str.size() >= 2) {
836 // at first we check for a zipped file, because this
837 // information is saved in the first bytes of the file!
838 // also some graphic formats which save the information
839 // in the first line, too.
840 if (prefixIs(str, gzipStamp)) {
843 } else if (stamp == zipStamp) {
846 } else if (stamp == compressStamp) {
850 } else if (stamp == "BM") {
853 } else if (stamp == "\001\332") {
857 // Don't need to use str.at(0), str.at(1) because
858 // we already know that str.size() >= 2
859 } else if (str[0] == 'P') {
875 } else if ((stamp == "II") || (stamp == "MM")) {
878 } else if (prefixIs(str,"%TGIF")) {
881 } else if (prefixIs(str,"#FIG")) {
884 } else if (prefixIs(str,"GIF")) {
887 } else if (str.size() > 3) {
888 int const c = ((str[0] << 24) & (str[1] << 16) &
889 (str[2] << 8) & str[3]);
900 else if (contains(str,"EPSF"))
901 // dummy, if we have wrong file description like
902 // %!PS-Adobe-2.0EPSF"
905 else if (contains(str,"Grace"))
908 else if (contains(str,"JFIF"))
911 else if (contains(str,"%PDF"))
914 else if (contains(str,"PNG"))
917 else if (contains(str,"%!PS-Adobe")) {
920 if (contains(str,"EPSF"))
926 else if (contains(str,"_bits[]"))
929 else if (contains(str,"XPM") || contains(str, "static char *"))
932 else if (contains(str,"BITPIX"))
936 if (!format.empty()) {
937 lyxerr[Debug::GRAPHICS]
938 << "Recognised Fileformat: " << format << endl;
942 lyxerr[Debug::GRAPHICS]
943 << "filetools(getFormatFromContents)\n"
944 << "\tCouldn't find a known format!\n";
949 /// check for zipped file
950 bool zippedFile(string const & name)
952 string const type = getFormatFromContents(name);
953 if (contains("gzip zip compress", type) && !type.empty())
959 string const unzippedFileName(string const & zipped_file)
961 string const ext = GetExtension(zipped_file);
962 if (ext == "gz" || ext == "z" || ext == "Z")
963 return ChangeExtension(zipped_file, string());
964 return "unzipped_" + zipped_file;
968 string const unzipFile(string const & zipped_file, string const & unzipped_file)
970 string const tempfile = unzipped_file.empty() ?
971 unzippedFileName(zipped_file) : unzipped_file;
973 string const command = "gunzip -c " + zipped_file + " > " + tempfile;
975 one.startscript(Systemcall::Wait, command);
976 // test that command was executed successfully (anon)
977 // yes, please do. (Lgb)
982 string const MakeDisplayPath(string const & path, unsigned int threshold)
985 string const home = package().home_dir();
987 // replace /home/blah with ~/
988 if (!home.empty() && prefixIs(str, home))
989 str = subst(str, home, "~");
991 if (str.length() <= threshold)
992 return os::external_path(str);
994 string const prefix = ".../";
997 while (str.length() > threshold)
998 str = split(str, temp, '/');
1000 // Did we shorten everything away?
1002 // Yes, filename itself is too long.
1003 // Pick the start and the end of the filename.
1004 str = OnlyFilename(path);
1005 string const head = str.substr(0, threshold / 2 - 3);
1007 string::size_type len = str.length();
1009 str.substr(len - threshold / 2 - 2, len - 1);
1010 str = head + "..." + tail;
1013 return os::external_path(prefix + str);
1017 bool LyXReadLink(string const & file, string & link, bool resolve)
1019 #ifdef HAVE_READLINK
1020 char linkbuffer[512];
1021 // Should be PATH_MAX but that needs autconf support
1022 int const nRead = ::readlink(file.c_str(),
1023 linkbuffer, sizeof(linkbuffer) - 1);
1026 linkbuffer[nRead] = '\0'; // terminator
1028 link = MakeAbsPath(linkbuffer, OnlyPath(file));
1038 cmd_ret const RunCommand(string const & cmd)
1040 // FIXME: replace all calls to RunCommand with ForkedCall
1041 // (if the output is not needed) or the code in ispell.C
1042 // (if the output is needed).
1044 // One question is if we should use popen or
1045 // create our own popen based on fork, exec, pipe
1046 // of course the best would be to have a
1047 // pstream (process stream), with the
1048 // variants ipstream, opstream
1050 #if defined (HAVE_POPEN)
1051 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1052 #elif defined (HAVE__POPEN)
1053 FILE * inf = ::_popen(cmd.c_str(), os::popen_read_mode());
1055 #error No popen() function.
1058 // (Claus Hentschel) Check if popen was succesful ;-)
1060 lyxerr << "RunCommand:: could not start child process" << endl;
1061 return make_pair(-1, string());
1067 ret += static_cast<char>(c);
1071 #if defined (HAVE_PCLOSE)
1072 int const pret = pclose(inf);
1073 #elif defined (HAVE__PCLOSE)
1074 int const pret = _pclose(inf);
1076 #error No pclose() function.
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 ? unzipFile(file) : file;
1171 string const format = getFormatFromContents(file_);
1173 if (format != "eps" && format != "ps") {
1174 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1178 static boost::regex bbox_re(
1179 "^%%BoundingBox:\\s*([[:digit:]]+)\\s+([[:digit:]]+)\\s+([[:digit:]]+)\\s+([[:digit:]]+)");
1180 std::ifstream is(file_.c_str());
1185 if (regex_match(s, what, bbox_re)) {
1186 // Our callers expect the tokens in the string
1187 // separated by single spaces.
1188 // FIXME: change return type from string to something
1191 os << what.str(1) << ' ' << what.str(2) << ' '
1192 << what.str(3) << ' ' << what.str(4);
1193 string const bb = os.str();
1194 readBB_lyxerrMessage(file_, zipped, bb);
1198 readBB_lyxerrMessage(file_, zipped, "no bb found");
1203 int compare_timestamps(string const & file1, string const & file2)
1205 BOOST_ASSERT(AbsolutePath(file1));
1206 BOOST_ASSERT(AbsolutePath(file2));
1208 // If the original is newer than the copy, then copy the original
1209 // to the new directory.
1212 if (fs::exists(file1) && fs::exists(file2)) {
1213 double const tmp = difftime(fs::last_write_time(file1),
1214 fs::last_write_time(file2));
1216 cmp = tmp > 0 ? 1 : -1;
1218 } else if (fs::exists(file1)) {
1220 } else if (fs::exists(file2)) {
1227 } //namespace support