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
25 #include "support/tostr.h"
26 #include "support/systemcall.h"
28 #include "filetools.h"
32 #include "path_defines.h"
37 #include "support/std_sstream.h"
39 #include <boost/assert.hpp>
40 #include <boost/regex.hpp>
52 // Which part of this is still necessary? (JMarc).
55 # define NAMLEN(dirent) strlen((dirent)->d_name)
57 # define dirent direct
58 # define NAMLEN(dirent) (dirent)->d_namlen
60 # include <sys/ndir.h>
70 #ifndef CXX_GLOBAL_CSTD
81 using std::ostringstream;
88 bool IsLyXFilename(string const & filename)
90 return suffixIs(ascii_lowercase(filename), ".lyx");
94 bool IsSGMLFilename(string const & filename)
96 return suffixIs(ascii_lowercase(filename), ".sgml");
100 // Substitutes spaces with underscores in filename (and path)
101 string const MakeLatexName(string const & file)
103 string name = OnlyFilename(file);
104 string const path = OnlyPath(file);
106 for (string::size_type i = 0; i < name.length(); ++i) {
107 name[i] &= 0x7f; // set 8th bit to 0
110 // ok so we scan through the string twice, but who cares.
111 string const keep("abcdefghijklmnopqrstuvwxyz"
112 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
113 "@!\"'()*+,-./0123456789:;<=>?[]`|");
115 string::size_type pos = 0;
116 while ((pos = name.find_first_not_of(keep, pos)) != string::npos) {
119 return AddName(path, name);
123 // Substitutes spaces with underscores in filename (and path)
124 string const QuoteName(string const & name)
126 return (os::shell() == os::UNIX) ?
132 // Is a file readable ?
133 bool IsFileReadable(string const & path)
136 return file.isOK() && file.isRegular() && file.readable();
140 // Is a file read_only?
141 // return 1 read-write
143 // -1 error (doesn't exist, no access, anything else)
144 int IsFileWriteable(string const & path)
148 if (fi.access(FileInfo::wperm|FileInfo::rperm)) // read-write
150 if (fi.readable()) // read-only
152 return -1; // everything else.
156 //returns true: dir writeable
157 // false: not writeable
158 bool IsDirWriteable(string const & path)
160 lyxerr[Debug::FILES] << "IsDirWriteable: " << path << endl;
162 string const tmpfl(tempName(path, "lyxwritetest"));
172 // Uses a string of paths separated by ";"s to find a file to open.
173 // Can't cope with pathnames with a ';' in them. Returns full path to file.
174 // If path entry begins with $$LyX/, use system_lyxdir
175 // If path entry begins with $$User/, use user_lyxdir
176 // Example: "$$User/doc;$$LyX/doc"
177 string const FileOpenSearch(string const & path, string const & name,
182 bool notfound = true;
183 string tmppath = split(path, path_element, ';');
185 while (notfound && !path_element.empty()) {
186 path_element = os::slashify_path(path_element);
187 if (!suffixIs(path_element, '/'))
189 path_element = subst(path_element, "$$LyX", system_lyxdir());
190 path_element = subst(path_element, "$$User", user_lyxdir());
192 real_file = FileSearch(path_element, name, ext);
194 if (real_file.empty()) {
196 tmppath = split(tmppath, path_element, ';');
197 } while (!tmppath.empty() && path_element.empty());
203 if (ext.empty() && notfound) {
204 real_file = FileOpenSearch(path, name, "exe");
205 if (notfound) real_file = FileOpenSearch(path, name, "cmd");
212 /// Returns a vector of all files in directory dir having extension ext.
213 vector<string> const DirList(string const & dir, string const & ext)
215 // This is a non-error checking C/system implementation
217 if (!ext.empty() && ext[0] != '.')
221 vector<string> dirlist;
222 DIR * dirp = ::opendir(dir.c_str());
225 << "Directory \"" << dir
226 << "\" does not exist to DirList." << endl;
231 while ((dire = ::readdir(dirp))) {
232 string const fil = dire->d_name;
233 if (suffixIs(fil, extension)) {
234 dirlist.push_back(fil);
239 /* I would have prefered to take a vector<string>& as parameter so
240 that we could avoid the copy of the vector when returning.
242 dirlist.swap(argvec);
243 to avoid the copy. (Lgb)
245 /* A C++ implementaion will look like this:
246 string extension(ext);
247 if (extension[0] != '.') extension.insert(0, 1, '.');
248 vector<string> dirlist;
249 directory_iterator dit("dir");
250 while (dit != directory_iterator()) {
251 string fil = dit->filename;
252 if (prefixIs(fil, extension)) {
253 dirlist.push_back(fil);
257 dirlist.swap(argvec);
263 // Returns the real name of file name in directory path, with optional
265 string const FileSearch(string const & path, string const & name,
268 // if `name' is an absolute path, we ignore the setting of `path'
269 // Expand Environmentvariables in 'name'
270 string const tmpname = ReplaceEnvironmentPath(name);
271 string fullname = MakeAbsPath(tmpname, path);
272 // search first without extension, then with it.
273 if (IsFileReadable(fullname))
275 else if (ext.empty())
277 else { // Is it not more reasonable to use ChangeExtension()? (SMiyata)
280 if (IsFileReadable(fullname))
288 // Search the file name.ext in the subdirectory dir of
290 // 2) build_lyxdir (if not empty)
292 string const LibFileSearch(string const & dir, string const & name,
295 string fullname = FileSearch(AddPath(user_lyxdir(), dir), name, ext);
296 if (!fullname.empty())
299 if (!build_lyxdir().empty())
300 fullname = FileSearch(AddPath(build_lyxdir(), dir), name, ext);
301 if (!fullname.empty())
304 return FileSearch(AddPath(system_lyxdir(), dir), name, ext);
309 i18nLibFileSearch(string const & dir, string const & name,
312 // this comment is from intl/dcigettext.c. We try to mimick this
314 /* The highest priority value is the `LANGUAGE' environment
315 variable. But we don't use the value if the currently
316 selected locale is the C locale. This is a GNU extension. */
318 string const lc_all = GetEnv("LC_ALL");
319 string lang = GetEnv("LANGUAGE");
320 if (lang.empty() || lc_all == "C") {
323 lang = GetEnv("LANG");
327 lang = token(lang, '_', 0);
329 if (lang.empty() || lang == "C")
330 return LibFileSearch(dir, name, ext);
332 string const tmp = LibFileSearch(dir, lang + '_' + name,
337 return LibFileSearch(dir, name, ext);
342 string const LibScriptSearch(string const & command_in)
344 string const token_scriptpath("$$s/");
346 string command = command_in;
347 // Find the starting position of "$$s/"
348 string::size_type const pos1 = command.find(token_scriptpath);
349 if (pos1 == string::npos)
351 // Find the end of the "$$s/some_script" word within command
352 string::size_type const start_script = pos1 + 4;
353 string::size_type const pos2 = command.find(' ', start_script);
354 string::size_type const size_script = pos2 == string::npos?
355 (command.size() - start_script) : pos2 - start_script;
357 // Does this script file exist?
358 string const script =
359 LibFileSearch("scripts", command.substr(start_script, size_script));
361 if (script.empty()) {
362 // Replace "$$s/" with ""
363 command.erase(pos1, 4);
365 // Replace "$$s/some_script" with "$LYX_SCRIPT_PATH/some_script"
366 string::size_type const size_replace = size_script + 4;
367 command.replace(pos1, size_replace, script);
374 string const GetEnv(string const & envname)
376 // f.ex. what about error checking?
377 char const * const ch = getenv(envname.c_str());
378 string const envstr = !ch ? "" : ch;
383 string const GetEnvPath(string const & name)
386 string const pathlist = subst(GetEnv(name), ':', ';');
388 string const pathlist = os::slashify_path(GetEnv(name));
390 return rtrim(pathlist, ";");
396 int DeleteAllFilesInDir(string const & path)
398 // I have decided that we will be using parts from the boost
399 // library. Check out http://www.boost.org/
400 // For directory access we will then use the directory_iterator.
401 // Then the code will be something like:
402 // directory_iterator dit(path);
403 // directory_iterator dend;
404 // if (dit == dend) {
407 // for (; dit != dend; ++dit) {
408 // string filename(*dit);
409 // if (filename == "." || filename == "..")
411 // string unlinkpath(AddName(path, filename));
412 // lyx::unlink(unlinkpath);
415 DIR * dir = ::opendir(path.c_str());
420 int return_value = 0;
421 while ((de = readdir(dir))) {
422 string const temp = de->d_name;
423 if (temp == "." || temp == "..")
425 string const unlinkpath = AddName (path, temp);
427 lyxerr[Debug::FILES] << "Deleting file: " << unlinkpath
431 FileInfo fi(unlinkpath);
432 if (fi.isOK() && fi.isDir())
433 deleted = (DeleteAllFilesInDir(unlinkpath) == 0);
434 deleted &= (unlink(unlinkpath) == 0);
443 string const CreateTmpDir(string const & tempdir, string const & mask)
446 << "CreateTmpDir: tempdir=`" << tempdir << "'\n"
447 << "CreateTmpDir: mask=`" << mask << '\'' << endl;
449 string const tmpfl(tempName(tempdir, mask));
450 // lyx::tempName actually creates a file to make sure that it
451 // stays unique. So we have to delete it before we can create
452 // a dir with the same name. Note also that we are not thread
453 // safe because of the gap between unlink and mkdir. (Lgb)
456 if (tmpfl.empty() || mkdir(tmpfl, 0700))
459 return MakeAbsPath(tmpfl);
465 int destroyDir(string const & tmpdir)
468 Path p(user_lyxdir());
470 if (DeleteAllFilesInDir(tmpdir))
480 string const CreateBufferTmpDir(string const & pathfor)
483 static string const tmpdir(pathfor.empty() ? os::getTmpDir() : pathfor);
484 // We are in our own directory. Why bother to mangle name?
485 // In fact I wrote this code to circumvent a problematic behaviour (bug?)
487 string const tmpfl = tmpdir + "/lyx_tmpbuf" + tostr(count++);
488 if (mkdir(tmpfl, 0777)) {
495 string const CreateLyXTmpDir(string const & deflt)
497 if ((!deflt.empty()) && (deflt != "/tmp")) {
498 if (mkdir(deflt, 0777)) {
500 Path p(user_lyxdir());
502 return CreateTmpDir(deflt, "lyx_tmpdir");
507 Path p(user_lyxdir());
509 return CreateTmpDir("/tmp", "lyx_tmpdir");
514 bool createDirectory(string const & path, int permission)
516 string temp(rtrim(os::slashify_path(path), "/"));
518 BOOST_ASSERT(!temp.empty());
520 if (mkdir(temp, permission))
527 // Strip filename from path name
528 string const OnlyPath(string const & Filename)
530 // If empty filename, return empty
531 if (Filename.empty()) return Filename;
533 // Find last / or start of filename
534 string::size_type j = Filename.rfind('/');
535 if (j == string::npos)
537 return Filename.substr(0, j + 1);
541 // Convert relative path into absolute path based on a basepath.
542 // If relpath is absolute, just use that.
543 // If basepath is empty, use CWD as base.
544 string const MakeAbsPath(string const & RelPath, string const & BasePath)
546 // checks for already absolute path
547 if (os::is_absolute_path(RelPath))
550 // Copies given paths
551 string TempRel(os::slashify_path(RelPath));
552 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
553 TempRel = subst(TempRel, "//", "/");
557 if (os::is_absolute_path(BasePath))
560 TempBase = AddPath(getcwd(), BasePath);
562 // Handle /./ at the end of the path
563 while (suffixIs(TempBase, "/./"))
564 TempBase.erase(TempBase.length() - 2);
566 // processes relative path
567 string RTemp(TempRel);
570 while (!RTemp.empty()) {
572 RTemp = split(RTemp, Temp, '/');
574 if (Temp == ".") continue;
576 // Remove one level of TempBase
577 string::difference_type i = TempBase.length() - 2;
580 while (i > 0 && TempBase[i] != '/') --i;
584 while (i > 2 && TempBase[i] != '/') --i;
587 TempBase.erase(i, string::npos);
590 } else if (Temp.empty() && !RTemp.empty()) {
591 TempBase = os::current_root() + RTemp;
594 // Add this piece to TempBase
595 if (!suffixIs(TempBase, '/'))
601 // returns absolute path
602 return os::slashify_path(TempBase);
606 // Correctly append filename to the pathname.
607 // If pathname is '.', then don't use pathname.
608 // Chops any path of filename.
609 string const AddName(string const & path, string const & fname)
612 string const basename(OnlyFilename(fname));
616 if (path != "." && path != "./" && !path.empty()) {
617 buf = os::slashify_path(path);
618 if (!suffixIs(path, '/'))
622 return buf + basename;
626 // Strips path from filename
627 string const OnlyFilename(string const & fname)
632 string::size_type j = fname.rfind('/');
633 if (j == string::npos) // no '/' in fname
637 return fname.substr(j + 1);
641 /// Returns true is path is absolute
642 bool AbsolutePath(string const & path)
644 return os::is_absolute_path(path);
649 // Create absolute path. If impossible, don't do anything
650 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
651 string const ExpandPath(string const & path)
653 // checks for already absolute path
654 string RTemp(ReplaceEnvironmentPath(path));
655 if (os::is_absolute_path(RTemp))
659 string const copy(RTemp);
662 RTemp = split(RTemp, Temp, '/');
665 return getcwd() + '/' + RTemp;
668 return GetEnvPath("HOME") + '/' + RTemp;
671 return MakeAbsPath(copy);
673 // Don't know how to handle this
679 // Constracts path/../path
680 // Can't handle "../../" or "/../" (Asger)
681 // Also converts paths like /foo//bar ==> /foo/bar
682 string const NormalizePath(string const & path)
688 if (os::is_absolute_path(path))
691 // Make implicit current directory explicit
694 // Normalise paths like /foo//bar ==> /foo/bar
695 boost::RegEx regex("/{2,}");
696 RTemp = regex.Merge(RTemp, "/");
698 while (!RTemp.empty()) {
700 RTemp = split(RTemp, Temp, '/');
704 } else if (Temp == "..") {
705 // Remove one level of TempBase
706 string::difference_type i = TempBase.length() - 2;
707 while (i > 0 && TempBase[i] != '/')
709 if (i >= 0 && TempBase[i] == '/')
710 TempBase.erase(i + 1, string::npos);
714 TempBase += Temp + '/';
718 // returns absolute path
723 string const GetFileContents(string const & fname)
725 FileInfo finfo(fname);
727 ifstream ifs(fname.c_str());
735 lyxerr << "LyX was not able to read file '" << fname << '\'' << endl;
740 // Search the string for ${VAR} and $VAR and replace VAR using getenv.
741 string const ReplaceEnvironmentPath(string const & path)
743 // ${VAR} is defined as
744 // $\{[A-Za-z_][A-Za-z_0-9]*\}
745 string const envvar_br = "[$]\\{([A-Za-z_][A-Za-z_0-9]*)\\}";
747 // $VAR is defined as:
748 // $\{[A-Za-z_][A-Za-z_0-9]*\}
749 string const envvar = "[$]([A-Za-z_][A-Za-z_0-9]*)";
751 boost::regex envvar_br_re("(.*)" + envvar_br + "(.*)");
752 boost::regex envvar_re("(.*)" + envvar + "(.*)");
755 string result = path;
757 regex_match(result, what, envvar_br_re);
758 if (!what[0].matched) {
759 regex_match(result, what, envvar_re);
760 if (!what[0].matched)
763 result = what.str(1) + GetEnv(what.str(2)) + what.str(3);
769 // Make relative path out of two absolute paths
770 string const MakeRelPath(string const & abspath, string const & basepath)
771 // Makes relative path out of absolute path. If it is deeper than basepath,
772 // it's easy. If basepath and abspath share something (they are all deeper
773 // than some directory), it'll be rendered using ..'s. If they are completely
774 // different, then the absolute path will be used as relative path.
776 string::size_type const abslen = abspath.length();
777 string::size_type const baselen = basepath.length();
779 string::size_type i = os::common_path(abspath, basepath);
782 // actually no match - cannot make it relative
786 // Count how many dirs there are in basepath above match
787 // and append as many '..''s into relpath
789 string::size_type j = i;
790 while (j < baselen) {
791 if (basepath[j] == '/') {
792 if (j + 1 == baselen)
799 // Append relative stuff from common directory to abspath
800 if (abspath[i] == '/')
802 for (; i < abslen; ++i)
805 if (suffixIs(buf, '/'))
806 buf.erase(buf.length() - 1);
807 // Substitute empty with .
814 // Append sub-directory(ies) to a path in an intelligent way
815 string const AddPath(string const & path, string const & path_2)
818 string const path2 = os::slashify_path(path_2);
820 if (!path.empty() && path != "." && path != "./") {
821 buf = os::slashify_path(path);
822 if (path[path.length() - 1] != '/')
826 if (!path2.empty()) {
827 string::size_type const p2start = path2.find_first_not_of('/');
828 string::size_type const p2end = path2.find_last_not_of('/');
829 string const tmp = path2.substr(p2start, p2end - p2start + 1);
837 Change extension of oldname to extension.
838 Strips path off if no_path == true.
839 If no extension on oldname, just appends.
841 string const ChangeExtension(string const & oldname, string const & extension)
843 string::size_type const last_slash = oldname.rfind('/');
844 string::size_type last_dot = oldname.rfind('.');
845 if (last_dot < last_slash && last_slash != string::npos)
846 last_dot = string::npos;
849 // Make sure the extension starts with a dot
850 if (!extension.empty() && extension[0] != '.')
851 ext= '.' + extension;
855 return os::slashify_path(oldname.substr(0, last_dot) + ext);
859 /// Return the extension of the file (not including the .)
860 string const GetExtension(string const & name)
862 string::size_type const last_slash = name.rfind('/');
863 string::size_type const last_dot = name.rfind('.');
864 if (last_dot != string::npos &&
865 (last_slash == string::npos || last_dot > last_slash))
866 return name.substr(last_dot + 1,
867 name.length() - (last_dot + 1));
872 // the different filetypes and what they contain in one of the first lines
873 // (dots are any characters). (Herbert 20020131)
876 // EPS %!PS-Adobe-3.0 EPSF...
883 // PBM P1... or P4 (B/W)
884 // PGM P2... or P5 (Grayscale)
885 // PPM P3... or P6 (color)
886 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
887 // SGI \001\332... (decimal 474)
889 // TIFF II... or MM...
891 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
892 // ...static char *...
893 // XWD \000\000\000\151 (0x00006900) decimal 105
895 // GZIP \037\213 http://www.ietf.org/rfc/rfc1952.txt
896 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
897 // Z \037\235 UNIX compress
899 /// return the "extension" which belongs to the contents.
900 /// for no knowing contents return the extension. Without
901 /// an extension and unknown contents we return "user"
902 string const getExtFromContents(string const & filename)
905 if (filename.empty() || !IsFileReadable(filename))
909 ifstream ifs(filename.c_str());
911 // Couldn't open file...
915 string const gzipStamp = "\037\213";
918 string const zipStamp = "PK";
921 string const compressStamp = "\037\235";
923 // Maximum strings to read
924 int const max_count = 50;
928 bool firstLine = true;
929 while ((count++ < max_count) && format.empty()) {
931 lyxerr[Debug::GRAPHICS]
932 << "filetools(getExtFromContents)\n"
933 << "\tFile type not recognised before EOF!"
939 string const stamp = str.substr(0,2);
940 if (firstLine && str.size() >= 2) {
941 // at first we check for a zipped file, because this
942 // information is saved in the first bytes of the file!
943 // also some graphic formats which save the information
944 // in the first line, too.
945 if (prefixIs(str, gzipStamp)) {
948 } else if (stamp == zipStamp) {
951 } else if (stamp == compressStamp) {
955 } else if (stamp == "BM") {
958 } else if (stamp == "\001\332") {
962 // Don't need to use str.at(0), str.at(1) because
963 // we already know that str.size() >= 2
964 } else if (str[0] == 'P') {
980 } else if ((stamp == "II") || (stamp == "MM")) {
983 } else if (prefixIs(str,"%TGIF")) {
986 } else if (prefixIs(str,"#FIG")) {
989 } else if (prefixIs(str,"GIF")) {
992 } else if (str.size() > 3) {
993 int const c = ((str[0] << 24) & (str[1] << 16) &
994 (str[2] << 8) & str[3]);
1003 if (!format.empty())
1005 else if (contains(str,"EPSF"))
1006 // dummy, if we have wrong file description like
1007 // %!PS-Adobe-2.0EPSF"
1010 else if (contains(str,"Grace"))
1013 else if (contains(str,"JFIF"))
1016 else if (contains(str,"%PDF"))
1019 else if (contains(str,"PNG"))
1022 else if (contains(str,"%!PS-Adobe")) {
1025 if (contains(str,"EPSF"))
1031 else if (contains(str,"_bits[]"))
1034 else if (contains(str,"XPM") || contains(str, "static char *"))
1037 else if (contains(str,"BITPIX"))
1041 if (!format.empty()) {
1042 lyxerr[Debug::GRAPHICS]
1043 << "Recognised Fileformat: " << format << endl;
1047 string const ext(GetExtension(filename));
1048 lyxerr[Debug::GRAPHICS]
1049 << "filetools(getExtFromContents)\n"
1050 << "\tCouldn't find a known Type!\n";
1052 lyxerr[Debug::GRAPHICS]
1053 << "\twill take the file extension -> "
1057 lyxerr[Debug::GRAPHICS]
1058 << "\twill use ext or a \"user\" defined format" << endl;
1064 /// check for zipped file
1065 bool zippedFile(string const & name)
1067 string const type = getExtFromContents(name);
1068 if (contains("gzip zip compress", type) && !type.empty())
1074 string const unzippedFileName(string const & zipped_file)
1076 string const ext = GetExtension(zipped_file);
1077 if (ext == "gz" || ext == "z" || ext == "Z")
1078 return ChangeExtension(zipped_file, string());
1079 return "unzipped_" + zipped_file;
1083 string const unzipFile(string const & zipped_file)
1085 string const tempfile = unzippedFileName(zipped_file);
1087 string const command = "gunzip -c " + zipped_file + " > " + tempfile;
1089 one.startscript(Systemcall::Wait, command);
1090 // test that command was executed successfully (anon)
1091 // yes, please do. (Lgb)
1096 string const MakeDisplayPath(string const & path, unsigned int threshold)
1100 string const home(GetEnvPath("HOME"));
1102 // replace /home/blah with ~/
1103 if (prefixIs(str, home))
1104 str = subst(str, home, "~");
1106 if (str.length() <= threshold)
1109 string const prefix = ".../";
1112 while (str.length() > threshold)
1113 str = split(str, temp, '/');
1115 // Did we shorten everything away?
1117 // Yes, filename itself is too long.
1118 // Pick the start and the end of the filename.
1119 str = OnlyFilename(path);
1120 string const head = str.substr(0, threshold / 2 - 3);
1122 string::size_type len = str.length();
1124 str.substr(len - threshold / 2 - 2, len - 1);
1125 str = head + "..." + tail;
1128 return prefix + str;
1132 bool LyXReadLink(string const & file, string & link, bool resolve)
1134 char linkbuffer[512];
1135 // Should be PATH_MAX but that needs autconf support
1136 int const nRead = ::readlink(file.c_str(),
1137 linkbuffer, sizeof(linkbuffer) - 1);
1140 linkbuffer[nRead] = '\0'; // terminator
1142 link = MakeAbsPath(linkbuffer, OnlyPath(file));
1149 cmd_ret const RunCommand(string const & cmd)
1151 // One question is if we should use popen or
1152 // create our own popen based on fork, exec, pipe
1153 // of course the best would be to have a
1154 // pstream (process stream), with the
1155 // variants ipstream, opstream
1157 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1159 // (Claus Hentschel) Check if popen was succesful ;-)
1161 return make_pair(-1, string());
1166 ret += static_cast<char>(c);
1169 int const pret = pclose(inf);
1170 return make_pair(pret, ret);
1174 string const findtexfile(string const & fil, string const & /*format*/)
1176 /* There is no problem to extend this function too use other
1177 methods to look for files. It could be setup to look
1178 in environment paths and also if wanted as a last resort
1179 to a recursive find. One of the easier extensions would
1180 perhaps be to use the LyX file lookup methods. But! I am
1181 going to implement this until I see some demand for it.
1185 // If the file can be found directly, we just return a
1186 // absolute path version of it.
1187 if (FileInfo(fil).exist())
1188 return MakeAbsPath(fil);
1190 // No we try to find it using kpsewhich.
1191 // It seems from the kpsewhich manual page that it is safe to use
1192 // kpsewhich without --format: "When the --format option is not
1193 // given, the search path used when looking for a file is inferred
1194 // from the name given, by looking for a known extension. If no
1195 // known extension is found, the search path for TeX source files
1197 // However, we want to take advantage of the format sine almost all
1198 // the different formats has environment variables that can be used
1199 // to controll which paths to search. f.ex. bib looks in
1200 // BIBINPUTS and TEXBIB. Small list follows:
1201 // bib - BIBINPUTS, TEXBIB
1203 // graphic/figure - TEXPICTS, TEXINPUTS
1204 // ist - TEXINDEXSTYLE, INDEXSTYLE
1205 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1207 // tfm - TFMFONTS, TEXFONTS
1208 // This means that to use kpsewhich in the best possible way we
1209 // should help it by setting additional path in the approp. envir.var.
1210 string const kpsecmd = "kpsewhich " + fil;
1212 cmd_ret const c = RunCommand(kpsecmd);
1214 lyxerr[Debug::LATEX] << "kpse status = " << c.first << '\n'
1215 << "kpse result = `" << rtrim(c.second, "\n")
1218 return os::internal_path(rtrim(c.second, "\n\r"));
1224 void removeAutosaveFile(string const & filename)
1226 string a = OnlyPath(filename);
1228 a += OnlyFilename(filename);
1230 FileInfo const fileinfo(a);
1231 if (fileinfo.exist())
1236 void readBB_lyxerrMessage(string const & file, bool & zipped,
1237 string const & message)
1239 lyxerr[Debug::GRAPHICS] << "[readBB_from_PSFile] "
1240 << message << std::endl;
1241 #warning Why is this func deleting a file? (Lgb)
1247 string const readBB_from_PSFile(string const & file)
1249 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1250 // It seems that every command in the header has an own line,
1251 // getline() should work for all files.
1252 // On the other hand some plot programs write the bb at the
1253 // end of the file. Than we have in the header:
1254 // %%BoundingBox: (atend)
1255 // In this case we must check the end.
1256 bool zipped = zippedFile(file);
1257 string const file_ = zipped ?
1258 string(unzipFile(file)) : string(file);
1259 string const format = getExtFromContents(file_);
1261 if (format != "eps" && format != "ps") {
1262 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1266 std::ifstream is(file_.c_str());
1270 if (contains(s,"%%BoundingBox:") && !contains(s,"atend")) {
1271 string const bb = ltrim(s.substr(14));
1272 readBB_lyxerrMessage(file_, zipped, bb);
1276 readBB_lyxerrMessage(file_, zipped, "no bb found");
1281 int compare_timestamps(string const & file1, string const & file2)
1283 BOOST_ASSERT(AbsolutePath(file1) && AbsolutePath(file2));
1285 // If the original is newer than the copy, then copy the original
1286 // to the new directory.
1291 if (f1.exist() && f2.exist()) {
1292 double const tmp = difftime(f1.getModificationTime(),
1293 f2.getModificationTime());
1295 cmp = tmp > 0 ? 1 : -1;
1297 } else if (f1.exist()) {
1299 } else if (f2.exist()) {
1306 } //namespace support