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());
203 /// Returns a vector of all files in directory dir having extension ext.
204 vector<string> const dirList(string const & dir, string const & ext)
206 // EXCEPTIONS FIXME. Rewrite needed when we turn on exceptions. (Lgb)
207 vector<string> dirlist;
209 if (!(fs::exists(dir) && fs::is_directory(dir))) {
211 << "Directory \"" << dir
212 << "\" does not exist to DirList." << endl;
217 if (!ext.empty() && ext[0] != '.')
221 fs::directory_iterator dit(dir);
222 fs::directory_iterator end;
223 for (; dit != end; ++dit) {
224 string const & fil = dit->leaf();
225 if (suffixIs(fil, extension)) {
226 dirlist.push_back(fil);
233 // Returns the real name of file name in directory path, with optional
235 string const fileSearch(string const & path, string const & name,
238 // if `name' is an absolute path, we ignore the setting of `path'
239 // Expand Environmentvariables in 'name'
240 string const tmpname = replaceEnvironmentPath(name);
241 string fullname = makeAbsPath(tmpname, path);
242 // search first without extension, then with it.
243 if (isFileReadable(fullname))
247 // Is it not more reasonable to use ChangeExtension()? (SMiyata)
250 return isFileReadable(fullname) ? fullname : string();
254 // Search the file name.ext in the subdirectory dir of
256 // 2) build_lyxdir (if not empty)
258 string const libFileSearch(string const & dir, string const & name,
261 string fullname = fileSearch(addPath(package().user_support(), dir),
263 if (!fullname.empty())
266 if (!package().build_support().empty())
267 fullname = fileSearch(addPath(package().build_support(), dir),
269 if (!fullname.empty())
272 return fileSearch(addPath(package().system_support(), dir), name, ext);
276 string const i18nLibFileSearch(string const & dir, string const & name,
279 // the following comments are from intl/dcigettext.c. We try
280 // to mimick this behaviour here.
281 /* The highest priority value is the `LANGUAGE' environment
282 variable. But we don't use the value if the currently
283 selected locale is the C locale. This is a GNU extension. */
284 /* [Otherwise] We have to proceed with the POSIX methods of
285 looking to `LC_ALL', `LC_xxx', and `LANG'. */
287 string lang = getEnv("LC_ALL");
289 lang = getEnv("LC_MESSAGES");
291 lang = getEnv("LANG");
297 string const language = getEnv("LANGUAGE");
298 if (lang != "C" && lang != "POSIX" && !language.empty())
302 lang = split(lang, l, ':');
303 while (!l.empty() && l != "C" && l != "POSIX") {
304 string const tmp = libFileSearch(dir,
305 token(l, '_', 0) + '_' + name,
309 lang = split(lang, l, ':');
312 return libFileSearch(dir, name, ext);
316 string const libScriptSearch(string const & command_in)
318 static string const token_scriptpath = "$$s/";
320 string command = command_in;
321 // Find the starting position of "$$s/"
322 string::size_type const pos1 = command.find(token_scriptpath);
323 if (pos1 == string::npos)
325 // Find the end of the "$$s/some_subdir/some_script" word within
326 // command. Assumes that the script name does not contain spaces.
327 string::size_type const start_script = pos1 + 4;
328 string::size_type const pos2 = command.find(' ', start_script);
329 string::size_type const size_script = pos2 == string::npos?
330 (command.size() - start_script) : pos2 - start_script;
332 // Does this script file exist?
333 string const script =
334 libFileSearch(".", command.substr(start_script, size_script));
336 if (script.empty()) {
337 // Replace "$$s/" with ""
338 command.erase(pos1, 4);
340 // Replace "$$s/foo/some_script" with "<path to>/some_script".
341 string::size_type const size_replace = size_script + 4;
342 command.replace(pos1, size_replace, quoteName(script));
351 string const createTmpDir(string const & tempdir, string const & mask)
354 << "createTmpDir: tempdir=`" << tempdir << "'\n"
355 << "createTmpDir: mask=`" << mask << '\'' << endl;
357 string const tmpfl = tempName(tempdir, mask);
358 // lyx::tempName actually creates a file to make sure that it
359 // stays unique. So we have to delete it before we can create
360 // a dir with the same name. Note also that we are not thread
361 // safe because of the gap between unlink and mkdir. (Lgb)
364 if (tmpfl.empty() || mkdir(tmpfl, 0700)) {
365 lyxerr << "LyX could not create the temporary directory '"
366 << tmpfl << "'" << endl;
370 return makeAbsPath(tmpfl);
376 bool destroyDir(string const & tmpdir)
378 return fs::remove_all(tmpdir) > 0;
382 string const createBufferTmpDir()
385 // We are in our own directory. Why bother to mangle name?
386 // In fact I wrote this code to circumvent a problematic behaviour
387 // (bug?) of EMX mkstemp().
389 package().temp_dir() + "/lyx_tmpbuf" +
390 convert<string>(count++);
392 if (mkdir(tmpfl, 0777)) {
393 lyxerr << "LyX could not create the temporary directory '"
394 << tmpfl << "'" << endl;
401 string const createLyXTmpDir(string const & deflt)
403 if (!deflt.empty() && deflt != "/tmp") {
404 if (mkdir(deflt, 0777)) {
405 if (isDirWriteable(deflt)) {
406 // deflt could not be created because it
407 // did exist already, so let's create our own
409 return createTmpDir(deflt, "lyx_tmpdir");
411 // some other error occured.
412 return createTmpDir("/tmp", "lyx_tmpdir");
417 return createTmpDir("/tmp", "lyx_tmpdir");
422 bool createDirectory(string const & path, int permission)
424 string temp = rtrim(os::internal_path(path), "/");
425 BOOST_ASSERT(!temp.empty());
426 return mkdir(temp, permission) == 0;
430 // Strip filename from path name
431 string const onlyPath(string const & filename)
433 // If empty filename, return empty
434 if (filename.empty())
437 // Find last / or start of filename
438 string::size_type j = filename.rfind('/');
439 return j == string::npos ? "./" : filename.substr(0, j + 1);
443 // Convert relative path into absolute path based on a basepath.
444 // If relpath is absolute, just use that.
445 // If basepath is empty, use CWD as base.
446 string const makeAbsPath(string const & relPath, string const & basePath)
448 // checks for already absolute path
449 if (os::is_absolute_path(relPath))
452 // Copies given paths
453 string tempRel = os::internal_path(relPath);
454 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
455 tempRel = subst(tempRel, "//", "/");
459 if (os::is_absolute_path(basePath))
462 tempBase = addPath(getcwd(), basePath);
464 // Handle /./ at the end of the path
465 while (suffixIs(tempBase, "/./"))
466 tempBase.erase(tempBase.length() - 2);
468 // processes relative path
469 string rTemp = tempRel;
472 while (!rTemp.empty()) {
474 rTemp = split(rTemp, temp, '/');
476 if (temp == ".") continue;
478 // Remove one level of TempBase
479 string::difference_type i = tempBase.length() - 2;
482 while (i > 0 && tempBase[i] != '/')
485 tempBase.erase(i, string::npos);
488 } else if (temp.empty() && !rTemp.empty()) {
489 tempBase = os::current_root() + rTemp;
492 // Add this piece to TempBase
493 if (!suffixIs(tempBase, '/'))
499 // returns absolute path
500 return os::internal_path(tempBase);
504 // Correctly append filename to the pathname.
505 // If pathname is '.', then don't use pathname.
506 // Chops any path of filename.
507 string const addName(string const & path, string const & fname)
509 string const basename = onlyFilename(fname);
512 if (path != "." && path != "./" && !path.empty()) {
513 buf = os::internal_path(path);
514 if (!suffixIs(path, '/'))
518 return buf + basename;
522 // Strips path from filename
523 string const onlyFilename(string const & fname)
528 string::size_type j = fname.rfind('/');
529 if (j == string::npos) // no '/' in fname
533 return fname.substr(j + 1);
537 /// Returns true is path is absolute
538 bool absolutePath(string const & path)
540 return os::is_absolute_path(path);
544 // Create absolute path. If impossible, don't do anything
545 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
546 string const expandPath(string const & path)
548 // checks for already absolute path
549 string rTemp = replaceEnvironmentPath(path);
550 if (os::is_absolute_path(rTemp))
554 string const copy = rTemp;
557 rTemp = split(rTemp, temp, '/');
560 return getcwd() + '/' + rTemp;
563 return package().home_dir() + '/' + rTemp;
566 return makeAbsPath(copy);
568 // Don't know how to handle this
573 // Normalize a path. Constracts path/../path
574 // Can't handle "../../" or "/../" (Asger)
575 // Also converts paths like /foo//bar ==> /foo/bar
576 string const normalizePath(string const & path)
578 // Normalize paths like /foo//bar ==> /foo/bar
579 static boost::regex regex("/{2,}");
580 string const tmppath = boost::regex_merge(path, regex, "/");
582 fs::path const npath = fs::path(tmppath, fs::no_check).normalize();
584 if (!npath.is_complete())
585 return "./" + npath.string() + '/';
587 return npath.string() + '/';
591 string const getFileContents(string const & fname)
593 if (fs::exists(fname)) {
594 ifstream ifs(fname.c_str());
602 lyxerr << "LyX was not able to read file '" << fname << '\'' << endl;
607 // Search the string for ${VAR} and $VAR and replace VAR using getenv.
608 string const replaceEnvironmentPath(string const & path)
610 // ${VAR} is defined as
611 // $\{[A-Za-z_][A-Za-z_0-9]*\}
612 static string const envvar_br = "[$]\\{([A-Za-z_][A-Za-z_0-9]*)\\}";
614 // $VAR is defined as:
615 // $\{[A-Za-z_][A-Za-z_0-9]*\}
616 static string const envvar = "[$]([A-Za-z_][A-Za-z_0-9]*)";
618 static boost::regex envvar_br_re("(.*)" + envvar_br + "(.*)");
619 static boost::regex envvar_re("(.*)" + envvar + "(.*)");
622 string result = path;
624 regex_match(result, what, envvar_br_re);
625 if (!what[0].matched) {
626 regex_match(result, what, envvar_re);
627 if (!what[0].matched)
630 result = what.str(1) + getEnv(what.str(2)) + what.str(3);
636 // Make relative path out of two absolute paths
637 string const makeRelPath(string const & abspath, string const & basepath)
638 // Makes relative path out of absolute path. If it is deeper than basepath,
639 // it's easy. If basepath and abspath share something (they are all deeper
640 // than some directory), it'll be rendered using ..'s. If they are completely
641 // different, then the absolute path will be used as relative path.
643 string::size_type const abslen = abspath.length();
644 string::size_type const baselen = basepath.length();
646 string::size_type i = os::common_path(abspath, basepath);
649 // actually no match - cannot make it relative
653 // Count how many dirs there are in basepath above match
654 // and append as many '..''s into relpath
656 string::size_type j = i;
657 while (j < baselen) {
658 if (basepath[j] == '/') {
659 if (j + 1 == baselen)
666 // Append relative stuff from common directory to abspath
667 if (abspath[i] == '/')
669 for (; i < abslen; ++i)
672 if (suffixIs(buf, '/'))
673 buf.erase(buf.length() - 1);
674 // Substitute empty with .
681 // Append sub-directory(ies) to a path in an intelligent way
682 string const addPath(string const & path, string const & path_2)
685 string const path2 = os::internal_path(path_2);
687 if (!path.empty() && path != "." && path != "./") {
688 buf = os::internal_path(path);
689 if (path[path.length() - 1] != '/')
693 if (!path2.empty()) {
694 string::size_type const p2start = path2.find_first_not_of('/');
695 string::size_type const p2end = path2.find_last_not_of('/');
696 string const tmp = path2.substr(p2start, p2end - p2start + 1);
704 Change extension of oldname to extension.
705 Strips path off if no_path == true.
706 If no extension on oldname, just appends.
708 string const changeExtension(string const & oldname, string const & extension)
710 string::size_type const last_slash = oldname.rfind('/');
711 string::size_type last_dot = oldname.rfind('.');
712 if (last_dot < last_slash && last_slash != string::npos)
713 last_dot = string::npos;
716 // Make sure the extension starts with a dot
717 if (!extension.empty() && extension[0] != '.')
718 ext= '.' + extension;
722 return os::internal_path(oldname.substr(0, last_dot) + ext);
726 string const removeExtension(string const & name)
728 return changeExtension(name, string());
732 /// Return the extension of the file (not including the .)
733 string const getExtension(string const & name)
735 string::size_type const last_slash = name.rfind('/');
736 string::size_type const last_dot = name.rfind('.');
737 if (last_dot != string::npos &&
738 (last_slash == string::npos || last_dot > last_slash))
739 return name.substr(last_dot + 1,
740 name.length() - (last_dot + 1));
746 // the different filetypes and what they contain in one of the first lines
747 // (dots are any characters). (Herbert 20020131)
750 // EPS %!PS-Adobe-3.0 EPSF...
757 // PBM P1... or P4 (B/W)
758 // PGM P2... or P5 (Grayscale)
759 // PPM P3... or P6 (color)
760 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
761 // SGI \001\332... (decimal 474)
763 // TIFF II... or MM...
765 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
766 // ...static char *...
767 // XWD \000\000\000\151 (0x00006900) decimal 105
769 // GZIP \037\213 http://www.ietf.org/rfc/rfc1952.txt
770 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
771 // Z \037\235 UNIX compress
773 string const getFormatFromContents(string const & filename)
776 if (filename.empty() || !isFileReadable(filename))
779 ifstream ifs(filename.c_str());
781 // Couldn't open file...
785 static string const gzipStamp = "\037\213";
788 static string const zipStamp = "PK";
791 static string const compressStamp = "\037\235";
793 // Maximum strings to read
794 int const max_count = 50;
799 bool firstLine = true;
800 while ((count++ < max_count) && format.empty()) {
802 lyxerr[Debug::GRAPHICS]
803 << "filetools(getFormatFromContents)\n"
804 << "\tFile type not recognised before EOF!"
810 string const stamp = str.substr(0, 2);
811 if (firstLine && str.size() >= 2) {
812 // at first we check for a zipped file, because this
813 // information is saved in the first bytes of the file!
814 // also some graphic formats which save the information
815 // in the first line, too.
816 if (prefixIs(str, gzipStamp)) {
819 } else if (stamp == zipStamp) {
822 } else if (stamp == compressStamp) {
826 } else if (stamp == "BM") {
829 } else if (stamp == "\001\332") {
833 // Don't need to use str.at(0), str.at(1) because
834 // we already know that str.size() >= 2
835 } else if (str[0] == 'P') {
851 } else if ((stamp == "II") || (stamp == "MM")) {
854 } else if (prefixIs(str,"%TGIF")) {
857 } else if (prefixIs(str,"#FIG")) {
860 } else if (prefixIs(str,"GIF")) {
863 } else if (str.size() > 3) {
864 int const c = ((str[0] << 24) & (str[1] << 16) &
865 (str[2] << 8) & str[3]);
876 else if (contains(str,"EPSF"))
877 // dummy, if we have wrong file description like
878 // %!PS-Adobe-2.0EPSF"
881 else if (contains(str,"Grace"))
884 else if (contains(str,"JFIF"))
887 else if (contains(str,"%PDF"))
890 else if (contains(str,"PNG"))
893 else if (contains(str,"%!PS-Adobe")) {
896 if (contains(str,"EPSF"))
902 else if (contains(str,"_bits[]"))
905 else if (contains(str,"XPM") || contains(str, "static char *"))
908 else if (contains(str,"BITPIX"))
912 if (!format.empty()) {
913 lyxerr[Debug::GRAPHICS]
914 << "Recognised Fileformat: " << format << endl;
918 lyxerr[Debug::GRAPHICS]
919 << "filetools(getFormatFromContents)\n"
920 << "\tCouldn't find a known format!\n";
925 /// check for zipped file
926 bool zippedFile(string const & name)
928 string const type = getFormatFromContents(name);
929 if (contains("gzip zip compress", type) && !type.empty())
935 string const unzippedFileName(string const & zipped_file)
937 string const ext = getExtension(zipped_file);
938 if (ext == "gz" || ext == "z" || ext == "Z")
939 return changeExtension(zipped_file, string());
940 return "unzipped_" + zipped_file;
944 string const unzipFile(string const & zipped_file, string const & unzipped_file)
946 string const tempfile = unzipped_file.empty() ?
947 unzippedFileName(zipped_file) : unzipped_file;
949 string const command = "gunzip -c " + zipped_file + " > " + tempfile;
951 one.startscript(Systemcall::Wait, command);
952 // test that command was executed successfully (anon)
953 // yes, please do. (Lgb)
958 string const makeDisplayPath(string const & path, unsigned int threshold)
961 string const home = package().home_dir();
963 // replace /home/blah with ~/
964 if (!home.empty() && prefixIs(str, home))
965 str = subst(str, home, "~");
967 if (str.length() <= threshold)
968 return os::external_path(str);
970 string const prefix = ".../";
973 while (str.length() > threshold)
974 str = split(str, temp, '/');
976 // Did we shorten everything away?
978 // Yes, filename itself is too long.
979 // Pick the start and the end of the filename.
980 str = onlyFilename(path);
981 string const head = str.substr(0, threshold / 2 - 3);
983 string::size_type len = str.length();
985 str.substr(len - threshold / 2 - 2, len - 1);
986 str = head + "..." + tail;
989 return os::external_path(prefix + str);
993 bool readLink(string const & file, string & link, bool resolve)
996 char linkbuffer[512];
997 // Should be PATH_MAX but that needs autconf support
998 int const nRead = ::readlink(file.c_str(),
999 linkbuffer, sizeof(linkbuffer) - 1);
1002 linkbuffer[nRead] = '\0'; // terminator
1004 link = makeAbsPath(linkbuffer, onlyPath(file));
1014 cmd_ret const runCommand(string const & cmd)
1016 // FIXME: replace all calls to RunCommand with ForkedCall
1017 // (if the output is not needed) or the code in ispell.C
1018 // (if the output is needed).
1020 // One question is if we should use popen or
1021 // create our own popen based on fork, exec, pipe
1022 // of course the best would be to have a
1023 // pstream (process stream), with the
1024 // variants ipstream, opstream
1026 #if defined (HAVE_POPEN)
1027 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1028 #elif defined (HAVE__POPEN)
1029 FILE * inf = ::_popen(cmd.c_str(), os::popen_read_mode());
1031 #error No popen() function.
1034 // (Claus Hentschel) Check if popen was succesful ;-)
1036 lyxerr << "RunCommand:: could not start child process" << endl;
1037 return make_pair(-1, string());
1043 ret += static_cast<char>(c);
1047 #if defined (HAVE_PCLOSE)
1048 int const pret = pclose(inf);
1049 #elif defined (HAVE__PCLOSE)
1050 int const pret = _pclose(inf);
1052 #error No pclose() function.
1056 perror("RunCommand:: could not terminate child process");
1058 return make_pair(pret, ret);
1062 string const findtexfile(string const & fil, string const & /*format*/)
1064 /* There is no problem to extend this function too use other
1065 methods to look for files. It could be setup to look
1066 in environment paths and also if wanted as a last resort
1067 to a recursive find. One of the easier extensions would
1068 perhaps be to use the LyX file lookup methods. But! I am
1069 going to implement this until I see some demand for it.
1073 // If the file can be found directly, we just return a
1074 // absolute path version of it.
1075 if (fs::exists(fil))
1076 return makeAbsPath(fil);
1078 // No we try to find it using kpsewhich.
1079 // It seems from the kpsewhich manual page that it is safe to use
1080 // kpsewhich without --format: "When the --format option is not
1081 // given, the search path used when looking for a file is inferred
1082 // from the name given, by looking for a known extension. If no
1083 // known extension is found, the search path for TeX source files
1085 // However, we want to take advantage of the format sine almost all
1086 // the different formats has environment variables that can be used
1087 // to controll which paths to search. f.ex. bib looks in
1088 // BIBINPUTS and TEXBIB. Small list follows:
1089 // bib - BIBINPUTS, TEXBIB
1091 // graphic/figure - TEXPICTS, TEXINPUTS
1092 // ist - TEXINDEXSTYLE, INDEXSTYLE
1093 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1095 // tfm - TFMFONTS, TEXFONTS
1096 // This means that to use kpsewhich in the best possible way we
1097 // should help it by setting additional path in the approp. envir.var.
1098 string const kpsecmd = "kpsewhich " + fil;
1100 cmd_ret const c = runCommand(kpsecmd);
1102 lyxerr[Debug::LATEX] << "kpse status = " << c.first << '\n'
1103 << "kpse result = `" << rtrim(c.second, "\n")
1106 return os::internal_path(rtrim(c.second, "\n\r"));
1112 void removeAutosaveFile(string const & filename)
1114 string a = onlyPath(filename);
1116 a += onlyFilename(filename);
1123 void readBB_lyxerrMessage(string const & file, bool & zipped,
1124 string const & message)
1126 lyxerr[Debug::GRAPHICS] << "[readBB_from_PSFile] "
1127 << message << std::endl;
1128 #ifdef WITH_WARNINGS
1129 #warning Why is this func deleting a file? (Lgb)
1136 string const readBB_from_PSFile(string const & file)
1138 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1139 // It seems that every command in the header has an own line,
1140 // getline() should work for all files.
1141 // On the other hand some plot programs write the bb at the
1142 // end of the file. Than we have in the header:
1143 // %%BoundingBox: (atend)
1144 // In this case we must check the end.
1145 bool zipped = zippedFile(file);
1146 string const file_ = zipped ? unzipFile(file) : file;
1147 string const format = getFormatFromContents(file_);
1149 if (format != "eps" && format != "ps") {
1150 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1154 static boost::regex bbox_re(
1155 "^%%BoundingBox:\\s*([[:digit:]]+)\\s+([[:digit:]]+)\\s+([[:digit:]]+)\\s+([[:digit:]]+)");
1156 std::ifstream is(file_.c_str());
1161 if (regex_match(s, what, bbox_re)) {
1162 // Our callers expect the tokens in the string
1163 // separated by single spaces.
1164 // FIXME: change return type from string to something
1167 os << what.str(1) << ' ' << what.str(2) << ' '
1168 << what.str(3) << ' ' << what.str(4);
1169 string const bb = os.str();
1170 readBB_lyxerrMessage(file_, zipped, bb);
1174 readBB_lyxerrMessage(file_, zipped, "no bb found");
1179 int compare_timestamps(string const & file1, string const & file2)
1181 BOOST_ASSERT(absolutePath(file1));
1182 BOOST_ASSERT(absolutePath(file2));
1184 // If the original is newer than the copy, then copy the original
1185 // to the new directory.
1188 if (fs::exists(file1) && fs::exists(file2)) {
1189 double const tmp = difftime(fs::last_write_time(file1),
1190 fs::last_write_time(file2));
1192 cmp = tmp > 0 ? 1 : -1;
1194 } else if (fs::exists(file1)) {
1196 } else if (fs::exists(file2)) {
1203 } //namespace support