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 ${...} and replace the ... with the value of the
741 // denoted environment variable
742 string const ReplaceEnvironmentPath(string const & path)
744 // A valid environment variable is defined as
745 // $\{[A-Za-z_][A-Za-z_0-9]*\}
746 string const valid_var = "[$]\\{([A-Za-z_][A-Za-z_0-9]*)\\}";
748 boost::regex re("(.*)" + valid_var + "(.*)");
751 string result = path;
753 regex_match(result, what, re, boost::match_partial);
754 if (!what[0].matched)
756 result = what.str(1) + GetEnv(what.str(2)) + what.str(3);
762 // Make relative path out of two absolute paths
763 string const MakeRelPath(string const & abspath, string const & basepath)
764 // Makes relative path out of absolute path. If it is deeper than basepath,
765 // it's easy. If basepath and abspath share something (they are all deeper
766 // than some directory), it'll be rendered using ..'s. If they are completely
767 // different, then the absolute path will be used as relative path.
769 string::size_type const abslen = abspath.length();
770 string::size_type const baselen = basepath.length();
772 string::size_type i = os::common_path(abspath, basepath);
775 // actually no match - cannot make it relative
779 // Count how many dirs there are in basepath above match
780 // and append as many '..''s into relpath
782 string::size_type j = i;
783 while (j < baselen) {
784 if (basepath[j] == '/') {
785 if (j + 1 == baselen)
792 // Append relative stuff from common directory to abspath
793 if (abspath[i] == '/')
795 for (; i < abslen; ++i)
798 if (suffixIs(buf, '/'))
799 buf.erase(buf.length() - 1);
800 // Substitute empty with .
807 // Append sub-directory(ies) to a path in an intelligent way
808 string const AddPath(string const & path, string const & path_2)
811 string const path2 = os::slashify_path(path_2);
813 if (!path.empty() && path != "." && path != "./") {
814 buf = os::slashify_path(path);
815 if (path[path.length() - 1] != '/')
819 if (!path2.empty()) {
820 string::size_type const p2start = path2.find_first_not_of('/');
821 string::size_type const p2end = path2.find_last_not_of('/');
822 string const tmp = path2.substr(p2start, p2end - p2start + 1);
830 Change extension of oldname to extension.
831 Strips path off if no_path == true.
832 If no extension on oldname, just appends.
834 string const ChangeExtension(string const & oldname, string const & extension)
836 string::size_type const last_slash = oldname.rfind('/');
837 string::size_type last_dot = oldname.rfind('.');
838 if (last_dot < last_slash && last_slash != string::npos)
839 last_dot = string::npos;
842 // Make sure the extension starts with a dot
843 if (!extension.empty() && extension[0] != '.')
844 ext= '.' + extension;
848 return os::slashify_path(oldname.substr(0, last_dot) + ext);
852 /// Return the extension of the file (not including the .)
853 string const GetExtension(string const & name)
855 string::size_type const last_slash = name.rfind('/');
856 string::size_type const last_dot = name.rfind('.');
857 if (last_dot != string::npos &&
858 (last_slash == string::npos || last_dot > last_slash))
859 return name.substr(last_dot + 1,
860 name.length() - (last_dot + 1));
865 // the different filetypes and what they contain in one of the first lines
866 // (dots are any characters). (Herbert 20020131)
869 // EPS %!PS-Adobe-3.0 EPSF...
876 // PBM P1... or P4 (B/W)
877 // PGM P2... or P5 (Grayscale)
878 // PPM P3... or P6 (color)
879 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
880 // SGI \001\332... (decimal 474)
882 // TIFF II... or MM...
884 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
885 // ...static char *...
886 // XWD \000\000\000\151 (0x00006900) decimal 105
888 // GZIP \037\213 http://www.ietf.org/rfc/rfc1952.txt
889 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
890 // Z \037\235 UNIX compress
892 /// return the "extension" which belongs to the contents.
893 /// for no knowing contents return the extension. Without
894 /// an extension and unknown contents we return "user"
895 string const getExtFromContents(string const & filename)
898 if (filename.empty() || !IsFileReadable(filename))
902 ifstream ifs(filename.c_str());
904 // Couldn't open file...
908 string const gzipStamp = "\037\213";
911 string const zipStamp = "PK";
914 string const compressStamp = "\037\235";
916 // Maximum strings to read
917 int const max_count = 50;
921 bool firstLine = true;
922 while ((count++ < max_count) && format.empty()) {
924 lyxerr[Debug::GRAPHICS]
925 << "filetools(getExtFromContents)\n"
926 << "\tFile type not recognised before EOF!"
932 string const stamp = str.substr(0,2);
933 if (firstLine && str.size() >= 2) {
934 // at first we check for a zipped file, because this
935 // information is saved in the first bytes of the file!
936 // also some graphic formats which save the information
937 // in the first line, too.
938 if (prefixIs(str, gzipStamp)) {
941 } else if (stamp == zipStamp) {
944 } else if (stamp == compressStamp) {
948 } else if (stamp == "BM") {
951 } else if (stamp == "\001\332") {
955 // Don't need to use str.at(0), str.at(1) because
956 // we already know that str.size() >= 2
957 } else if (str[0] == 'P') {
973 } else if ((stamp == "II") || (stamp == "MM")) {
976 } else if (prefixIs(str,"%TGIF")) {
979 } else if (prefixIs(str,"#FIG")) {
982 } else if (prefixIs(str,"GIF")) {
985 } else if (str.size() > 3) {
986 int const c = ((str[0] << 24) & (str[1] << 16) &
987 (str[2] << 8) & str[3]);
998 else if (contains(str,"EPSF"))
999 // dummy, if we have wrong file description like
1000 // %!PS-Adobe-2.0EPSF"
1003 else if (contains(str,"Grace"))
1006 else if (contains(str,"JFIF"))
1009 else if (contains(str,"%PDF"))
1012 else if (contains(str,"PNG"))
1015 else if (contains(str,"%!PS-Adobe")) {
1018 if (contains(str,"EPSF"))
1024 else if (contains(str,"_bits[]"))
1027 else if (contains(str,"XPM") || contains(str, "static char *"))
1030 else if (contains(str,"BITPIX"))
1034 if (!format.empty()) {
1035 lyxerr[Debug::GRAPHICS]
1036 << "Recognised Fileformat: " << format << endl;
1040 string const ext(GetExtension(filename));
1041 lyxerr[Debug::GRAPHICS]
1042 << "filetools(getExtFromContents)\n"
1043 << "\tCouldn't find a known Type!\n";
1045 lyxerr[Debug::GRAPHICS]
1046 << "\twill take the file extension -> "
1050 lyxerr[Debug::GRAPHICS]
1051 << "\twill use ext or a \"user\" defined format" << endl;
1057 /// check for zipped file
1058 bool zippedFile(string const & name)
1060 string const type = getExtFromContents(name);
1061 if (contains("gzip zip compress", type) && !type.empty())
1067 string const unzippedFileName(string const & zipped_file)
1069 string const ext = GetExtension(zipped_file);
1070 if (ext == "gz" || ext == "z" || ext == "Z")
1071 return ChangeExtension(zipped_file, string());
1072 return "unzipped_" + zipped_file;
1076 string const unzipFile(string const & zipped_file)
1078 string const tempfile = unzippedFileName(zipped_file);
1080 string const command = "gunzip -c " + zipped_file + " > " + tempfile;
1082 one.startscript(Systemcall::Wait, command);
1083 // test that command was executed successfully (anon)
1084 // yes, please do. (Lgb)
1089 string const MakeDisplayPath(string const & path, unsigned int threshold)
1093 string const home(GetEnvPath("HOME"));
1095 // replace /home/blah with ~/
1096 if (prefixIs(str, home))
1097 str = subst(str, home, "~");
1099 if (str.length() <= threshold)
1102 string const prefix = ".../";
1105 while (str.length() > threshold)
1106 str = split(str, temp, '/');
1108 // Did we shorten everything away?
1110 // Yes, filename itself is too long.
1111 // Pick the start and the end of the filename.
1112 str = OnlyFilename(path);
1113 string const head = str.substr(0, threshold / 2 - 3);
1115 string::size_type len = str.length();
1117 str.substr(len - threshold / 2 - 2, len - 1);
1118 str = head + "..." + tail;
1121 return prefix + str;
1125 bool LyXReadLink(string const & file, string & link, bool resolve)
1127 char linkbuffer[512];
1128 // Should be PATH_MAX but that needs autconf support
1129 int const nRead = ::readlink(file.c_str(),
1130 linkbuffer, sizeof(linkbuffer) - 1);
1133 linkbuffer[nRead] = '\0'; // terminator
1135 link = MakeAbsPath(linkbuffer, OnlyPath(file));
1142 cmd_ret const RunCommand(string const & cmd)
1144 // One question is if we should use popen or
1145 // create our own popen based on fork, exec, pipe
1146 // of course the best would be to have a
1147 // pstream (process stream), with the
1148 // variants ipstream, opstream
1150 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1152 // (Claus Hentschel) Check if popen was succesful ;-)
1154 return make_pair(-1, string());
1159 ret += static_cast<char>(c);
1162 int const pret = pclose(inf);
1163 return make_pair(pret, ret);
1167 string const findtexfile(string const & fil, string const & /*format*/)
1169 /* There is no problem to extend this function too use other
1170 methods to look for files. It could be setup to look
1171 in environment paths and also if wanted as a last resort
1172 to a recursive find. One of the easier extensions would
1173 perhaps be to use the LyX file lookup methods. But! I am
1174 going to implement this until I see some demand for it.
1178 // If the file can be found directly, we just return a
1179 // absolute path version of it.
1180 if (FileInfo(fil).exist())
1181 return MakeAbsPath(fil);
1183 // No we try to find it using kpsewhich.
1184 // It seems from the kpsewhich manual page that it is safe to use
1185 // kpsewhich without --format: "When the --format option is not
1186 // given, the search path used when looking for a file is inferred
1187 // from the name given, by looking for a known extension. If no
1188 // known extension is found, the search path for TeX source files
1190 // However, we want to take advantage of the format sine almost all
1191 // the different formats has environment variables that can be used
1192 // to controll which paths to search. f.ex. bib looks in
1193 // BIBINPUTS and TEXBIB. Small list follows:
1194 // bib - BIBINPUTS, TEXBIB
1196 // graphic/figure - TEXPICTS, TEXINPUTS
1197 // ist - TEXINDEXSTYLE, INDEXSTYLE
1198 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1200 // tfm - TFMFONTS, TEXFONTS
1201 // This means that to use kpsewhich in the best possible way we
1202 // should help it by setting additional path in the approp. envir.var.
1203 string const kpsecmd = "kpsewhich " + fil;
1205 cmd_ret const c = RunCommand(kpsecmd);
1207 lyxerr[Debug::LATEX] << "kpse status = " << c.first << '\n'
1208 << "kpse result = `" << rtrim(c.second, "\n")
1211 return os::internal_path(rtrim(c.second, "\n\r"));
1217 void removeAutosaveFile(string const & filename)
1219 string a = OnlyPath(filename);
1221 a += OnlyFilename(filename);
1223 FileInfo const fileinfo(a);
1224 if (fileinfo.exist())
1229 void readBB_lyxerrMessage(string const & file, bool & zipped,
1230 string const & message)
1232 lyxerr[Debug::GRAPHICS] << "[readBB_from_PSFile] "
1233 << message << std::endl;
1234 #warning Why is this func deleting a file? (Lgb)
1240 string const readBB_from_PSFile(string const & file)
1242 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1243 // It seems that every command in the header has an own line,
1244 // getline() should work for all files.
1245 // On the other hand some plot programs write the bb at the
1246 // end of the file. Than we have in the header:
1247 // %%BoundingBox: (atend)
1248 // In this case we must check the end.
1249 bool zipped = zippedFile(file);
1250 string const file_ = zipped ?
1251 string(unzipFile(file)) : string(file);
1252 string const format = getExtFromContents(file_);
1254 if (format != "eps" && format != "ps") {
1255 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1259 std::ifstream is(file_.c_str());
1263 if (contains(s,"%%BoundingBox:") && !contains(s,"atend")) {
1264 string const bb = ltrim(s.substr(14));
1265 readBB_lyxerrMessage(file_, zipped, bb);
1269 readBB_lyxerrMessage(file_, zipped, "no bb found");
1274 int compare_timestamps(string const & file1, string const & file2)
1276 BOOST_ASSERT(AbsolutePath(file1) && AbsolutePath(file2));
1278 // If the original is newer than the copy, then copy the original
1279 // to the new directory.
1284 if (f1.exist() && f2.exist()) {
1285 double const tmp = difftime(f1.getModificationTime(),
1286 f2.getModificationTime());
1288 cmp = tmp > 0 ? 1 : -1;
1290 } else if (f1.exist()) {
1292 } else if (f2.exist()) {
1299 } //namespace support