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");
203 real_file = fileOpenSearch(path, name, "cmd");
210 /// Returns a vector of all files in directory dir having extension ext.
211 vector<string> const dirList(string const & dir, string const & ext)
213 // EXCEPTIONS FIXME. Rewrite needed when we turn on exceptions. (Lgb)
214 vector<string> dirlist;
216 if (!(fs::exists(dir) && fs::is_directory(dir))) {
218 << "Directory \"" << dir
219 << "\" does not exist to DirList." << endl;
224 if (!ext.empty() && ext[0] != '.')
228 fs::directory_iterator dit(dir);
229 fs::directory_iterator end;
230 for (; dit != end; ++dit) {
231 string const & fil = dit->leaf();
232 if (suffixIs(fil, extension)) {
233 dirlist.push_back(fil);
240 // Returns the real name of file name in directory path, with optional
242 string const fileSearch(string const & path, string const & name,
245 // if `name' is an absolute path, we ignore the setting of `path'
246 // Expand Environmentvariables in 'name'
247 string const tmpname = replaceEnvironmentPath(name);
248 string fullname = makeAbsPath(tmpname, path);
249 // search first without extension, then with it.
250 if (isFileReadable(fullname))
254 // Is it not more reasonable to use ChangeExtension()? (SMiyata)
257 return isFileReadable(fullname) ? fullname : string();
261 // Search the file name.ext in the subdirectory dir of
263 // 2) build_lyxdir (if not empty)
265 string const libFileSearch(string const & dir, string const & name,
268 string fullname = fileSearch(addPath(package().user_support(), dir),
270 if (!fullname.empty())
273 if (!package().build_support().empty())
274 fullname = fileSearch(addPath(package().build_support(), dir),
276 if (!fullname.empty())
279 return fileSearch(addPath(package().system_support(), dir), name, ext);
283 string const i18nLibFileSearch(string const & dir, string const & name,
286 // the following comments are from intl/dcigettext.c. We try
287 // to mimick this behaviour here.
288 /* The highest priority value is the `LANGUAGE' environment
289 variable. But we don't use the value if the currently
290 selected locale is the C locale. This is a GNU extension. */
291 /* [Otherwise] We have to proceed with the POSIX methods of
292 looking to `LC_ALL', `LC_xxx', and `LANG'. */
294 string lang = getEnv("LC_ALL");
296 lang = getEnv("LC_MESSAGES");
298 lang = getEnv("LANG");
304 string const language = getEnv("LANGUAGE");
305 if (lang != "C" && lang != "POSIX" && !language.empty())
309 lang = split(lang, l, ':');
310 while (!l.empty() && l != "C" && l != "POSIX") {
311 string const tmp = libFileSearch(dir,
312 token(l, '_', 0) + '_' + name,
316 lang = split(lang, l, ':');
319 return libFileSearch(dir, name, ext);
323 string const libScriptSearch(string const & command_in)
325 static string const token_scriptpath = "$$s/";
327 string command = command_in;
328 // Find the starting position of "$$s/"
329 string::size_type const pos1 = command.find(token_scriptpath);
330 if (pos1 == string::npos)
332 // Find the end of the "$$s/some_subdir/some_script" word within
333 // command. Assumes that the script name does not contain spaces.
334 string::size_type const start_script = pos1 + 4;
335 string::size_type const pos2 = command.find(' ', start_script);
336 string::size_type const size_script = pos2 == string::npos?
337 (command.size() - start_script) : pos2 - start_script;
339 // Does this script file exist?
340 string const script =
341 libFileSearch(".", command.substr(start_script, size_script));
343 if (script.empty()) {
344 // Replace "$$s/" with ""
345 command.erase(pos1, 4);
347 // Replace "$$s/foo/some_script" with "<path to>/some_script".
348 string::size_type const size_replace = size_script + 4;
349 command.replace(pos1, size_replace, quoteName(script));
358 string const createTmpDir(string const & tempdir, string const & mask)
361 << "createTmpDir: tempdir=`" << tempdir << "'\n"
362 << "createTmpDir: mask=`" << mask << '\'' << endl;
364 string const tmpfl = tempName(tempdir, mask);
365 // lyx::tempName actually creates a file to make sure that it
366 // stays unique. So we have to delete it before we can create
367 // a dir with the same name. Note also that we are not thread
368 // safe because of the gap between unlink and mkdir. (Lgb)
371 if (tmpfl.empty() || mkdir(tmpfl, 0700)) {
372 lyxerr << "LyX could not create the temporary directory '"
373 << tmpfl << "'" << endl;
377 return makeAbsPath(tmpfl);
383 bool destroyDir(string const & tmpdir)
387 Path p(user_lyxdir());
389 return fs::remove_all(tmpdir) > 0;
393 string const createBufferTmpDir()
396 // We are in our own directory. Why bother to mangle name?
397 // In fact I wrote this code to circumvent a problematic behaviour
398 // (bug?) of EMX mkstemp().
400 package().temp_dir() + "/lyx_tmpbuf" +
401 convert<string>(count++);
403 if (mkdir(tmpfl, 0777)) {
404 lyxerr << "LyX could not create the temporary directory '"
405 << tmpfl << "'" << endl;
412 string const createLyXTmpDir(string const & deflt)
414 if (!deflt.empty() && deflt != "/tmp") {
415 if (mkdir(deflt, 0777)) {
417 Path p(package().user_support());
419 if (isDirWriteable(deflt)) {
420 // deflt could not be created because it
421 // did exist already, so let's create our own
423 return createTmpDir(deflt, "lyx_tmpdir");
425 // some other error occured.
426 return createTmpDir("/tmp", "lyx_tmpdir");
432 Path p(package().user_support());
434 return createTmpDir("/tmp", "lyx_tmpdir");
439 bool createDirectory(string const & path, int permission)
441 string temp = rtrim(os::internal_path(path), "/");
442 BOOST_ASSERT(!temp.empty());
443 return mkdir(temp, permission) == 0;
447 // Strip filename from path name
448 string const onlyPath(string const & filename)
450 // If empty filename, return empty
451 if (filename.empty())
454 // Find last / or start of filename
455 string::size_type j = filename.rfind('/');
456 return j == string::npos ? "./" : filename.substr(0, j + 1);
460 // Convert relative path into absolute path based on a basepath.
461 // If relpath is absolute, just use that.
462 // If basepath is empty, use CWD as base.
463 string const makeAbsPath(string const & relPath, string const & basePath)
465 // checks for already absolute path
466 if (os::is_absolute_path(relPath))
469 // Copies given paths
470 string tempRel = os::internal_path(relPath);
471 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
472 tempRel = subst(tempRel, "//", "/");
476 if (os::is_absolute_path(basePath))
479 tempBase = addPath(getcwd(), basePath);
481 // Handle /./ at the end of the path
482 while (suffixIs(tempBase, "/./"))
483 tempBase.erase(tempBase.length() - 2);
485 // processes relative path
486 string rTemp = tempRel;
489 while (!rTemp.empty()) {
491 rTemp = split(rTemp, temp, '/');
493 if (temp == ".") continue;
495 // Remove one level of TempBase
496 string::difference_type i = tempBase.length() - 2;
500 while (i > 0 && tempBase[i] != '/')
506 while (i > 2 && tempBase[i] != '/')
510 tempBase.erase(i, string::npos);
513 } else if (temp.empty() && !rTemp.empty()) {
514 tempBase = os::current_root() + rTemp;
517 // Add this piece to TempBase
518 if (!suffixIs(tempBase, '/'))
524 // returns absolute path
525 return os::internal_path(tempBase);
529 // Correctly append filename to the pathname.
530 // If pathname is '.', then don't use pathname.
531 // Chops any path of filename.
532 string const addName(string const & path, string const & fname)
534 string const basename = onlyFilename(fname);
537 if (path != "." && path != "./" && !path.empty()) {
538 buf = os::internal_path(path);
539 if (!suffixIs(path, '/'))
543 return buf + basename;
547 // Strips path from filename
548 string const onlyFilename(string const & fname)
553 string::size_type j = fname.rfind('/');
554 if (j == string::npos) // no '/' in fname
558 return fname.substr(j + 1);
562 /// Returns true is path is absolute
563 bool absolutePath(string const & path)
565 return os::is_absolute_path(path);
569 // Create absolute path. If impossible, don't do anything
570 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
571 string const expandPath(string const & path)
573 // checks for already absolute path
574 string rTemp = replaceEnvironmentPath(path);
575 if (os::is_absolute_path(rTemp))
579 string const copy = rTemp;
582 rTemp = split(rTemp, temp, '/');
585 return getcwd() + '/' + rTemp;
588 return package().home_dir() + '/' + rTemp;
591 return makeAbsPath(copy);
593 // Don't know how to handle this
598 // Normalize a path. Constracts path/../path
599 // Can't handle "../../" or "/../" (Asger)
600 // Also converts paths like /foo//bar ==> /foo/bar
601 string const normalizePath(string const & path)
603 // Normalize paths like /foo//bar ==> /foo/bar
604 static boost::regex regex("/{2,}");
605 string const tmppath = boost::regex_merge(path, regex, "/");
607 fs::path const npath = fs::path(tmppath, fs::no_check).normalize();
609 if (!npath.is_complete())
610 return "./" + npath.string() + '/';
612 return npath.string() + '/';
616 string const getFileContents(string const & fname)
618 if (fs::exists(fname)) {
619 ifstream ifs(fname.c_str());
627 lyxerr << "LyX was not able to read file '" << fname << '\'' << endl;
632 // Search the string for ${VAR} and $VAR and replace VAR using getenv.
633 string const replaceEnvironmentPath(string const & path)
635 // ${VAR} is defined as
636 // $\{[A-Za-z_][A-Za-z_0-9]*\}
637 static string const envvar_br = "[$]\\{([A-Za-z_][A-Za-z_0-9]*)\\}";
639 // $VAR is defined as:
640 // $\{[A-Za-z_][A-Za-z_0-9]*\}
641 static string const envvar = "[$]([A-Za-z_][A-Za-z_0-9]*)";
643 static boost::regex envvar_br_re("(.*)" + envvar_br + "(.*)");
644 static boost::regex envvar_re("(.*)" + envvar + "(.*)");
647 string result = path;
649 regex_match(result, what, envvar_br_re);
650 if (!what[0].matched) {
651 regex_match(result, what, envvar_re);
652 if (!what[0].matched)
655 result = what.str(1) + getEnv(what.str(2)) + what.str(3);
661 // Make relative path out of two absolute paths
662 string const makeRelPath(string const & abspath, string const & basepath)
663 // Makes relative path out of absolute path. If it is deeper than basepath,
664 // it's easy. If basepath and abspath share something (they are all deeper
665 // than some directory), it'll be rendered using ..'s. If they are completely
666 // different, then the absolute path will be used as relative path.
668 string::size_type const abslen = abspath.length();
669 string::size_type const baselen = basepath.length();
671 string::size_type i = os::common_path(abspath, basepath);
674 // actually no match - cannot make it relative
678 // Count how many dirs there are in basepath above match
679 // and append as many '..''s into relpath
681 string::size_type j = i;
682 while (j < baselen) {
683 if (basepath[j] == '/') {
684 if (j + 1 == baselen)
691 // Append relative stuff from common directory to abspath
692 if (abspath[i] == '/')
694 for (; i < abslen; ++i)
697 if (suffixIs(buf, '/'))
698 buf.erase(buf.length() - 1);
699 // Substitute empty with .
706 // Append sub-directory(ies) to a path in an intelligent way
707 string const addPath(string const & path, string const & path_2)
710 string const path2 = os::internal_path(path_2);
712 if (!path.empty() && path != "." && path != "./") {
713 buf = os::internal_path(path);
714 if (path[path.length() - 1] != '/')
718 if (!path2.empty()) {
719 string::size_type const p2start = path2.find_first_not_of('/');
720 string::size_type const p2end = path2.find_last_not_of('/');
721 string const tmp = path2.substr(p2start, p2end - p2start + 1);
729 Change extension of oldname to extension.
730 Strips path off if no_path == true.
731 If no extension on oldname, just appends.
733 string const changeExtension(string const & oldname, string const & extension)
735 string::size_type const last_slash = oldname.rfind('/');
736 string::size_type last_dot = oldname.rfind('.');
737 if (last_dot < last_slash && last_slash != string::npos)
738 last_dot = string::npos;
741 // Make sure the extension starts with a dot
742 if (!extension.empty() && extension[0] != '.')
743 ext= '.' + extension;
747 return os::internal_path(oldname.substr(0, last_dot) + ext);
751 string const removeExtension(string const & name)
753 return changeExtension(name, string());
757 /// Return the extension of the file (not including the .)
758 string const getExtension(string const & name)
760 string::size_type const last_slash = name.rfind('/');
761 string::size_type const last_dot = name.rfind('.');
762 if (last_dot != string::npos &&
763 (last_slash == string::npos || last_dot > last_slash))
764 return name.substr(last_dot + 1,
765 name.length() - (last_dot + 1));
771 // the different filetypes and what they contain in one of the first lines
772 // (dots are any characters). (Herbert 20020131)
775 // EPS %!PS-Adobe-3.0 EPSF...
782 // PBM P1... or P4 (B/W)
783 // PGM P2... or P5 (Grayscale)
784 // PPM P3... or P6 (color)
785 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
786 // SGI \001\332... (decimal 474)
788 // TIFF II... or MM...
790 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
791 // ...static char *...
792 // XWD \000\000\000\151 (0x00006900) decimal 105
794 // GZIP \037\213 http://www.ietf.org/rfc/rfc1952.txt
795 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
796 // Z \037\235 UNIX compress
798 string const getFormatFromContents(string const & filename)
801 if (filename.empty() || !isFileReadable(filename))
804 ifstream ifs(filename.c_str());
806 // Couldn't open file...
810 static string const gzipStamp = "\037\213";
813 static string const zipStamp = "PK";
816 static string const compressStamp = "\037\235";
818 // Maximum strings to read
819 int const max_count = 50;
824 bool firstLine = true;
825 while ((count++ < max_count) && format.empty()) {
827 lyxerr[Debug::GRAPHICS]
828 << "filetools(getFormatFromContents)\n"
829 << "\tFile type not recognised before EOF!"
835 string const stamp = str.substr(0, 2);
836 if (firstLine && str.size() >= 2) {
837 // at first we check for a zipped file, because this
838 // information is saved in the first bytes of the file!
839 // also some graphic formats which save the information
840 // in the first line, too.
841 if (prefixIs(str, gzipStamp)) {
844 } else if (stamp == zipStamp) {
847 } else if (stamp == compressStamp) {
851 } else if (stamp == "BM") {
854 } else if (stamp == "\001\332") {
858 // Don't need to use str.at(0), str.at(1) because
859 // we already know that str.size() >= 2
860 } else if (str[0] == 'P') {
876 } else if ((stamp == "II") || (stamp == "MM")) {
879 } else if (prefixIs(str,"%TGIF")) {
882 } else if (prefixIs(str,"#FIG")) {
885 } else if (prefixIs(str,"GIF")) {
888 } else if (str.size() > 3) {
889 int const c = ((str[0] << 24) & (str[1] << 16) &
890 (str[2] << 8) & str[3]);
901 else if (contains(str,"EPSF"))
902 // dummy, if we have wrong file description like
903 // %!PS-Adobe-2.0EPSF"
906 else if (contains(str,"Grace"))
909 else if (contains(str,"JFIF"))
912 else if (contains(str,"%PDF"))
915 else if (contains(str,"PNG"))
918 else if (contains(str,"%!PS-Adobe")) {
921 if (contains(str,"EPSF"))
927 else if (contains(str,"_bits[]"))
930 else if (contains(str,"XPM") || contains(str, "static char *"))
933 else if (contains(str,"BITPIX"))
937 if (!format.empty()) {
938 lyxerr[Debug::GRAPHICS]
939 << "Recognised Fileformat: " << format << endl;
943 lyxerr[Debug::GRAPHICS]
944 << "filetools(getFormatFromContents)\n"
945 << "\tCouldn't find a known format!\n";
950 /// check for zipped file
951 bool zippedFile(string const & name)
953 string const type = getFormatFromContents(name);
954 if (contains("gzip zip compress", type) && !type.empty())
960 string const unzippedFileName(string const & zipped_file)
962 string const ext = getExtension(zipped_file);
963 if (ext == "gz" || ext == "z" || ext == "Z")
964 return changeExtension(zipped_file, string());
965 return "unzipped_" + zipped_file;
969 string const unzipFile(string const & zipped_file, string const & unzipped_file)
971 string const tempfile = unzipped_file.empty() ?
972 unzippedFileName(zipped_file) : unzipped_file;
974 string const command = "gunzip -c " + zipped_file + " > " + tempfile;
976 one.startscript(Systemcall::Wait, command);
977 // test that command was executed successfully (anon)
978 // yes, please do. (Lgb)
983 string const makeDisplayPath(string const & path, unsigned int threshold)
986 string const home = package().home_dir();
988 // replace /home/blah with ~/
989 if (!home.empty() && prefixIs(str, home))
990 str = subst(str, home, "~");
992 if (str.length() <= threshold)
993 return os::external_path(str);
995 string const prefix = ".../";
998 while (str.length() > threshold)
999 str = split(str, temp, '/');
1001 // Did we shorten everything away?
1003 // Yes, filename itself is too long.
1004 // Pick the start and the end of the filename.
1005 str = onlyFilename(path);
1006 string const head = str.substr(0, threshold / 2 - 3);
1008 string::size_type len = str.length();
1010 str.substr(len - threshold / 2 - 2, len - 1);
1011 str = head + "..." + tail;
1014 return os::external_path(prefix + str);
1018 bool readLink(string const & file, string & link, bool resolve)
1020 #ifdef HAVE_READLINK
1021 char linkbuffer[512];
1022 // Should be PATH_MAX but that needs autconf support
1023 int const nRead = ::readlink(file.c_str(),
1024 linkbuffer, sizeof(linkbuffer) - 1);
1027 linkbuffer[nRead] = '\0'; // terminator
1029 link = makeAbsPath(linkbuffer, onlyPath(file));
1039 cmd_ret const runCommand(string const & cmd)
1041 // FIXME: replace all calls to RunCommand with ForkedCall
1042 // (if the output is not needed) or the code in ispell.C
1043 // (if the output is needed).
1045 // One question is if we should use popen or
1046 // create our own popen based on fork, exec, pipe
1047 // of course the best would be to have a
1048 // pstream (process stream), with the
1049 // variants ipstream, opstream
1051 #if defined (HAVE_POPEN)
1052 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1053 #elif defined (HAVE__POPEN)
1054 FILE * inf = ::_popen(cmd.c_str(), os::popen_read_mode());
1056 #error No popen() function.
1059 // (Claus Hentschel) Check if popen was succesful ;-)
1061 lyxerr << "RunCommand:: could not start child process" << endl;
1062 return make_pair(-1, string());
1068 ret += static_cast<char>(c);
1072 #if defined (HAVE_PCLOSE)
1073 int const pret = pclose(inf);
1074 #elif defined (HAVE__PCLOSE)
1075 int const pret = _pclose(inf);
1077 #error No pclose() function.
1081 perror("RunCommand:: could not terminate child process");
1083 return make_pair(pret, ret);
1087 string const findtexfile(string const & fil, string const & /*format*/)
1089 /* There is no problem to extend this function too use other
1090 methods to look for files. It could be setup to look
1091 in environment paths and also if wanted as a last resort
1092 to a recursive find. One of the easier extensions would
1093 perhaps be to use the LyX file lookup methods. But! I am
1094 going to implement this until I see some demand for it.
1098 // If the file can be found directly, we just return a
1099 // absolute path version of it.
1100 if (fs::exists(fil))
1101 return makeAbsPath(fil);
1103 // No we try to find it using kpsewhich.
1104 // It seems from the kpsewhich manual page that it is safe to use
1105 // kpsewhich without --format: "When the --format option is not
1106 // given, the search path used when looking for a file is inferred
1107 // from the name given, by looking for a known extension. If no
1108 // known extension is found, the search path for TeX source files
1110 // However, we want to take advantage of the format sine almost all
1111 // the different formats has environment variables that can be used
1112 // to controll which paths to search. f.ex. bib looks in
1113 // BIBINPUTS and TEXBIB. Small list follows:
1114 // bib - BIBINPUTS, TEXBIB
1116 // graphic/figure - TEXPICTS, TEXINPUTS
1117 // ist - TEXINDEXSTYLE, INDEXSTYLE
1118 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1120 // tfm - TFMFONTS, TEXFONTS
1121 // This means that to use kpsewhich in the best possible way we
1122 // should help it by setting additional path in the approp. envir.var.
1123 string const kpsecmd = "kpsewhich " + fil;
1125 cmd_ret const c = runCommand(kpsecmd);
1127 lyxerr[Debug::LATEX] << "kpse status = " << c.first << '\n'
1128 << "kpse result = `" << rtrim(c.second, "\n")
1131 return os::internal_path(rtrim(c.second, "\n\r"));
1137 void removeAutosaveFile(string const & filename)
1139 string a = onlyPath(filename);
1141 a += onlyFilename(filename);
1148 void readBB_lyxerrMessage(string const & file, bool & zipped,
1149 string const & message)
1151 lyxerr[Debug::GRAPHICS] << "[readBB_from_PSFile] "
1152 << message << std::endl;
1153 #ifdef WITH_WARNINGS
1154 #warning Why is this func deleting a file? (Lgb)
1161 string const readBB_from_PSFile(string const & file)
1163 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1164 // It seems that every command in the header has an own line,
1165 // getline() should work for all files.
1166 // On the other hand some plot programs write the bb at the
1167 // end of the file. Than we have in the header:
1168 // %%BoundingBox: (atend)
1169 // In this case we must check the end.
1170 bool zipped = zippedFile(file);
1171 string const file_ = zipped ? unzipFile(file) : file;
1172 string const format = getFormatFromContents(file_);
1174 if (format != "eps" && format != "ps") {
1175 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1179 static boost::regex bbox_re(
1180 "^%%BoundingBox:\\s*([[:digit:]]+)\\s+([[:digit:]]+)\\s+([[:digit:]]+)\\s+([[:digit:]]+)");
1181 std::ifstream is(file_.c_str());
1186 if (regex_match(s, what, bbox_re)) {
1187 // Our callers expect the tokens in the string
1188 // separated by single spaces.
1189 // FIXME: change return type from string to something
1192 os << what.str(1) << ' ' << what.str(2) << ' '
1193 << what.str(3) << ' ' << what.str(4);
1194 string const bb = os.str();
1195 readBB_lyxerrMessage(file_, zipped, bb);
1199 readBB_lyxerrMessage(file_, zipped, "no bb found");
1204 int compare_timestamps(string const & file1, string const & file2)
1206 BOOST_ASSERT(absolutePath(file1));
1207 BOOST_ASSERT(absolutePath(file2));
1209 // If the original is newer than the copy, then copy the original
1210 // to the new directory.
1213 if (fs::exists(file1) && fs::exists(file2)) {
1214 double const tmp = difftime(fs::last_write_time(file1),
1215 fs::last_write_time(file2));
1217 cmp = tmp > 0 ? 1 : -1;
1219 } else if (fs::exists(file1)) {
1221 } else if (fs::exists(file2)) {
1228 } //namespace support