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, quote_style style)
140 // This does not work for filenames containing " (windows)
141 // or ' (all other OSes). This can't be changed easily, since
142 // we would need to adapt the command line parser in
143 // Forkedcall::generateChild. Therefore we don't pass user
144 // filenames to child processes if possible. We store them in
145 // a python script instead, where we don't have these
147 return (os::shell() == os::UNIX) ?
151 return "\"" + subst(subst(name, "\\", "\\\\"), "\"", "\\\"")
154 // shut up stupid compiler
159 bool isFileReadable(FileName const & filename)
161 std::string const path = filename.toFilesystemEncoding();
162 return fs::exists(path) && !fs::is_directory(path) && fs::is_readable(path);
166 //returns true: dir writeable
167 // false: not writeable
168 bool isDirWriteable(string const & path)
170 lyxerr[Debug::FILES] << "isDirWriteable: " << path << endl;
172 string const tmpfl = tempName(path, "lyxwritetest");
177 unlink(FileName(tmpfl));
183 // Uses a string of paths separated by ";"s to find a file to open.
184 // Can't cope with pathnames with a ';' in them. Returns full path to file.
185 // If path entry begins with $$LyX/, use system_lyxdir
186 // If path entry begins with $$User/, use user_lyxdir
187 // Example: "$$User/doc;$$LyX/doc"
188 FileName const fileOpenSearch(string const & path, string const & name,
193 bool notfound = true;
194 string tmppath = split(path, path_element, ';');
196 while (notfound && !path_element.empty()) {
197 path_element = os::internal_path(path_element);
198 if (!suffixIs(path_element, '/'))
200 path_element = subst(path_element, "$$LyX",
201 package().system_support());
202 path_element = subst(path_element, "$$User",
203 package().user_support());
205 real_file = fileSearch(path_element, name, ext);
207 if (real_file.empty()) {
209 tmppath = split(tmppath, path_element, ';');
210 } while (!tmppath.empty() && path_element.empty());
220 /// Returns a vector of all files in directory dir having extension ext.
221 vector<string> const dirList(FileName const & dir, string const & ext)
223 // EXCEPTIONS FIXME. Rewrite needed when we turn on exceptions. (Lgb)
224 vector<string> dirlist;
226 string const encoded_dir = dir.toFilesystemEncoding();
227 if (!(fs::exists(encoded_dir) && fs::is_directory(encoded_dir))) {
229 << "Directory \"" << dir
230 << "\" does not exist to DirList." << endl;
235 if (!ext.empty() && ext[0] != '.')
239 fs::directory_iterator dit(encoded_dir);
240 fs::directory_iterator end;
241 for (; dit != end; ++dit) {
242 string const & fil = dit->leaf();
243 if (suffixIs(fil, extension)) {
244 // FIXME UNICODE: We need to convert from filesystem encoding to utf8
245 dirlist.push_back(fil);
252 // Returns the real name of file name in directory path, with optional
254 FileName const fileSearch(string const & path, string const & name,
255 string const & ext, search_mode mode)
257 // if `name' is an absolute path, we ignore the setting of `path'
258 // Expand Environmentvariables in 'name'
259 string const tmpname = replaceEnvironmentPath(name);
260 FileName fullname(makeAbsPath(tmpname, path));
261 // search first without extension, then with it.
262 if (isFileReadable(fullname))
266 return mode == allow_unreadable ? fullname : FileName();
267 // Only add the extension if it is not already the extension of
269 if (getExtension(fullname.absFilename()) != ext)
270 fullname = FileName(addExtension(fullname.absFilename(), ext));
271 if (isFileReadable(fullname) || mode == allow_unreadable)
277 // Search the file name.ext in the subdirectory dir of
279 // 2) build_lyxdir (if not empty)
281 FileName const libFileSearch(string const & dir, string const & name,
284 FileName fullname = fileSearch(addPath(package().user_support(), dir),
286 if (!fullname.empty())
289 if (!package().build_support().empty())
290 fullname = fileSearch(addPath(package().build_support(), dir),
292 if (!fullname.empty())
295 return fileSearch(addPath(package().system_support(), dir), name, ext);
299 FileName const i18nLibFileSearch(string const & dir, string const & name,
302 // the following comments are from intl/dcigettext.c. We try
303 // to mimick this behaviour here.
304 /* The highest priority value is the `LANGUAGE' environment
305 variable. But we don't use the value if the currently
306 selected locale is the C locale. This is a GNU extension. */
307 /* [Otherwise] We have to proceed with the POSIX methods of
308 looking to `LC_ALL', `LC_xxx', and `LANG'. */
310 string lang = getEnv("LC_ALL");
312 lang = getEnv("LC_MESSAGES");
314 lang = getEnv("LANG");
320 string const language = getEnv("LANGUAGE");
321 if (lang != "C" && lang != "POSIX" && !language.empty())
325 lang = split(lang, l, ':');
326 while (!l.empty() && l != "C" && l != "POSIX") {
327 FileName const tmp = libFileSearch(dir,
328 token(l, '_', 0) + '_' + name,
332 lang = split(lang, l, ':');
335 return libFileSearch(dir, name, ext);
339 string const libScriptSearch(string const & command_in, quote_style style)
341 static string const token_scriptpath = "$$s/";
343 string command = command_in;
344 // Find the starting position of "$$s/"
345 string::size_type const pos1 = command.find(token_scriptpath);
346 if (pos1 == string::npos)
348 // Find the end of the "$$s/some_subdir/some_script" word within
349 // command. Assumes that the script name does not contain spaces.
350 string::size_type const start_script = pos1 + 4;
351 string::size_type const pos2 = command.find(' ', start_script);
352 string::size_type const size_script = pos2 == string::npos?
353 (command.size() - start_script) : pos2 - start_script;
355 // Does this script file exist?
356 string const script =
357 libFileSearch(".", command.substr(start_script, size_script)).absFilename();
359 if (script.empty()) {
360 // Replace "$$s/" with ""
361 command.erase(pos1, 4);
363 // Replace "$$s/foo/some_script" with "<path to>/some_script".
364 string::size_type const size_replace = size_script + 4;
365 command.replace(pos1, size_replace, quoteName(script, style));
374 FileName const createTmpDir(FileName const & tempdir, string const & mask)
377 << "createTmpDir: tempdir=`" << tempdir << "'\n"
378 << "createTmpDir: mask=`" << mask << '\'' << endl;
380 string const tmpfl = tempName(tempdir.absFilename(), mask);
381 // lyx::tempName actually creates a file to make sure that it
382 // stays unique. So we have to delete it before we can create
383 // a dir with the same name. Note also that we are not thread
384 // safe because of the gap between unlink and mkdir. (Lgb)
385 unlink(FileName(tmpfl));
387 if (tmpfl.empty() || mkdir(FileName(tmpfl), 0700)) {
388 lyxerr << "LyX could not create the temporary directory '"
389 << tmpfl << "'" << endl;
393 return FileName(tmpfl);
399 bool destroyDir(FileName const & tmpdir)
402 return fs::remove_all(tmpdir.toFilesystemEncoding()) > 0;
403 } catch (fs::filesystem_error const & fe){
404 lyxerr << "Could not delete " << tmpdir << ". (" << fe.what() << ")" << std::endl;
410 string const createBufferTmpDir()
413 // We are in our own directory. Why bother to mangle name?
414 // In fact I wrote this code to circumvent a problematic behaviour
415 // (bug?) of EMX mkstemp().
417 package().temp_dir() + "/lyx_tmpbuf" +
418 convert<string>(count++);
420 if (mkdir(FileName(tmpfl), 0777)) {
421 lyxerr << "LyX could not create the temporary directory '"
422 << tmpfl << "'" << endl;
429 FileName const createLyXTmpDir(FileName const & deflt)
431 if (!deflt.empty() && deflt.absFilename() != "/tmp") {
432 if (mkdir(deflt, 0777)) {
433 if (isDirWriteable(deflt.absFilename())) {
434 // deflt could not be created because it
435 // did exist already, so let's create our own
437 return createTmpDir(deflt, "lyx_tmpdir");
439 // some other error occured.
440 return createTmpDir(FileName("/tmp"), "lyx_tmpdir");
445 return createTmpDir(FileName("/tmp"), "lyx_tmpdir");
450 bool createDirectory(string const & path, int permission)
452 string temp = rtrim(os::internal_path(path), "/");
453 BOOST_ASSERT(!temp.empty());
454 return mkdir(FileName(temp), permission) == 0;
458 // Strip filename from path name
459 string const onlyPath(string const & filename)
461 // If empty filename, return empty
462 if (filename.empty())
465 // Find last / or start of filename
466 string::size_type j = filename.rfind('/');
467 return j == string::npos ? "./" : filename.substr(0, j + 1);
471 // Convert relative path into absolute path based on a basepath.
472 // If relpath is absolute, just use that.
473 // If basepath is empty, use CWD as base.
474 string const makeAbsPath(string const & relPath, string const & basePath)
476 // checks for already absolute path
477 if (os::is_absolute_path(relPath))
480 // Copies given paths
481 string tempRel = os::internal_path(relPath);
482 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
483 tempRel = subst(tempRel, "//", "/");
487 if (os::is_absolute_path(basePath))
490 tempBase = addPath(getcwd(), basePath);
492 // Handle /./ at the end of the path
493 while (suffixIs(tempBase, "/./"))
494 tempBase.erase(tempBase.length() - 2);
496 // processes relative path
497 string rTemp = tempRel;
500 while (!rTemp.empty()) {
502 rTemp = split(rTemp, temp, '/');
504 if (temp == ".") continue;
506 // Remove one level of TempBase
507 string::difference_type i = tempBase.length() - 2;
510 while (i > 0 && tempBase[i] != '/')
513 tempBase.erase(i, string::npos);
516 } else if (temp.empty() && !rTemp.empty()) {
517 tempBase = os::current_root() + rTemp;
520 // Add this piece to TempBase
521 if (!suffixIs(tempBase, '/'))
527 // returns absolute path
528 return os::internal_path(tempBase);
532 // Correctly append filename to the pathname.
533 // If pathname is '.', then don't use pathname.
534 // Chops any path of filename.
535 string const addName(string const & path, string const & fname)
537 string const basename = onlyFilename(fname);
540 if (path != "." && path != "./" && !path.empty()) {
541 buf = os::internal_path(path);
542 if (!suffixIs(path, '/'))
546 return buf + basename;
550 // Strips path from filename
551 string const onlyFilename(string const & fname)
556 string::size_type j = fname.rfind('/');
557 if (j == string::npos) // no '/' in fname
561 return fname.substr(j + 1);
565 /// Returns true is path is absolute
566 bool absolutePath(string const & path)
568 return os::is_absolute_path(path);
572 // Create absolute path. If impossible, don't do anything
573 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
574 string const expandPath(string const & path)
576 // checks for already absolute path
577 string rTemp = replaceEnvironmentPath(path);
578 if (os::is_absolute_path(rTemp))
582 string const copy = rTemp;
585 rTemp = split(rTemp, temp, '/');
588 return getcwd() + '/' + rTemp;
591 return package().home_dir() + '/' + rTemp;
594 return makeAbsPath(copy);
596 // Don't know how to handle this
601 // Normalize a path. Constracts path/../path
602 // Can't handle "../../" or "/../" (Asger)
603 // Also converts paths like /foo//bar ==> /foo/bar
604 string const normalizePath(string const & path)
606 // Normalize paths like /foo//bar ==> /foo/bar
607 static boost::regex regex("/{2,}");
608 string const tmppath = boost::regex_merge(path, regex, "/");
610 fs::path const npath = fs::path(tmppath, fs::no_check).normalize();
612 if (!npath.is_complete())
613 return "./" + npath.string() + '/';
615 return npath.string() + '/';
619 string const getFileContents(FileName const & fname)
621 string const encodedname = fname.toFilesystemEncoding();
622 if (fs::exists(encodedname)) {
623 ifstream ifs(encodedname.c_str());
631 lyxerr << "LyX was not able to read file '" << fname << '\'' << endl;
636 // Search the string for ${VAR} and $VAR and replace VAR using getenv.
637 string const replaceEnvironmentPath(string const & path)
639 // ${VAR} is defined as
640 // $\{[A-Za-z_][A-Za-z_0-9]*\}
641 static string const envvar_br = "[$]\\{([A-Za-z_][A-Za-z_0-9]*)\\}";
643 // $VAR is defined as:
644 // $\{[A-Za-z_][A-Za-z_0-9]*\}
645 static string const envvar = "[$]([A-Za-z_][A-Za-z_0-9]*)";
647 static boost::regex envvar_br_re("(.*)" + envvar_br + "(.*)");
648 static boost::regex envvar_re("(.*)" + envvar + "(.*)");
651 string result = path;
653 regex_match(result, what, envvar_br_re);
654 if (!what[0].matched) {
655 regex_match(result, what, envvar_re);
656 if (!what[0].matched)
659 result = what.str(1) + getEnv(what.str(2)) + what.str(3);
665 // Make relative path out of two absolute paths
666 string const makeRelPath(string const & abspath, string const & basepath)
667 // Makes relative path out of absolute path. If it is deeper than basepath,
668 // it's easy. If basepath and abspath share something (they are all deeper
669 // than some directory), it'll be rendered using ..'s. If they are completely
670 // different, then the absolute path will be used as relative path.
672 string::size_type const abslen = abspath.length();
673 string::size_type const baselen = basepath.length();
675 string::size_type i = os::common_path(abspath, basepath);
678 // actually no match - cannot make it relative
682 // Count how many dirs there are in basepath above match
683 // and append as many '..''s into relpath
685 string::size_type j = i;
686 while (j < baselen) {
687 if (basepath[j] == '/') {
688 if (j + 1 == baselen)
695 // Append relative stuff from common directory to abspath
696 if (abspath[i] == '/')
698 for (; i < abslen; ++i)
701 if (suffixIs(buf, '/'))
702 buf.erase(buf.length() - 1);
703 // Substitute empty with .
710 // Append sub-directory(ies) to a path in an intelligent way
711 string const addPath(string const & path, string const & path_2)
714 string const path2 = os::internal_path(path_2);
716 if (!path.empty() && path != "." && path != "./") {
717 buf = os::internal_path(path);
718 if (path[path.length() - 1] != '/')
722 if (!path2.empty()) {
723 string::size_type const p2start = path2.find_first_not_of('/');
724 string::size_type const p2end = path2.find_last_not_of('/');
725 string const tmp = path2.substr(p2start, p2end - p2start + 1);
732 string const changeExtension(string const & oldname, string const & extension)
734 string::size_type const last_slash = oldname.rfind('/');
735 string::size_type last_dot = oldname.rfind('.');
736 if (last_dot < last_slash && last_slash != string::npos)
737 last_dot = string::npos;
740 // Make sure the extension starts with a dot
741 if (!extension.empty() && extension[0] != '.')
742 ext= '.' + extension;
746 return os::internal_path(oldname.substr(0, last_dot) + ext);
750 string const removeExtension(string const & name)
752 return changeExtension(name, string());
756 string const addExtension(string const & name, string const & extension)
758 if (!extension.empty() && extension[0] != '.')
759 return name + '.' + extension;
760 return name + extension;
764 /// Return the extension of the file (not including the .)
765 string const getExtension(string const & name)
767 string::size_type const last_slash = name.rfind('/');
768 string::size_type const last_dot = name.rfind('.');
769 if (last_dot != string::npos &&
770 (last_slash == string::npos || last_dot > last_slash))
771 return name.substr(last_dot + 1,
772 name.length() - (last_dot + 1));
778 // the different filetypes and what they contain in one of the first lines
779 // (dots are any characters). (Herbert 20020131)
782 // EPS %!PS-Adobe-3.0 EPSF...
789 // PBM P1... or P4 (B/W)
790 // PGM P2... or P5 (Grayscale)
791 // PPM P3... or P6 (color)
792 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
793 // SGI \001\332... (decimal 474)
795 // TIFF II... or MM...
797 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
798 // ...static char *...
799 // XWD \000\000\000\151 (0x00006900) decimal 105
801 // GZIP \037\213 http://www.ietf.org/rfc/rfc1952.txt
802 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
803 // Z \037\235 UNIX compress
805 string const getFormatFromContents(FileName const & filename)
808 if (filename.empty() || !isFileReadable(filename))
811 ifstream ifs(filename.toFilesystemEncoding().c_str());
813 // Couldn't open file...
817 static string const gzipStamp = "\037\213";
820 static string const zipStamp = "PK";
823 static string const compressStamp = "\037\235";
825 // Maximum strings to read
826 int const max_count = 50;
831 bool firstLine = true;
832 while ((count++ < max_count) && format.empty()) {
834 lyxerr[Debug::GRAPHICS]
835 << "filetools(getFormatFromContents)\n"
836 << "\tFile type not recognised before EOF!"
842 string const stamp = str.substr(0, 2);
843 if (firstLine && str.size() >= 2) {
844 // at first we check for a zipped file, because this
845 // information is saved in the first bytes of the file!
846 // also some graphic formats which save the information
847 // in the first line, too.
848 if (prefixIs(str, gzipStamp)) {
851 } else if (stamp == zipStamp) {
854 } else if (stamp == compressStamp) {
858 } else if (stamp == "BM") {
861 } else if (stamp == "\001\332") {
865 // Don't need to use str.at(0), str.at(1) because
866 // we already know that str.size() >= 2
867 } else if (str[0] == 'P') {
883 } else if ((stamp == "II") || (stamp == "MM")) {
886 } else if (prefixIs(str,"%TGIF")) {
889 } else if (prefixIs(str,"#FIG")) {
892 } else if (prefixIs(str,"GIF")) {
895 } else if (str.size() > 3) {
896 int const c = ((str[0] << 24) & (str[1] << 16) &
897 (str[2] << 8) & str[3]);
908 else if (contains(str,"EPSF"))
909 // dummy, if we have wrong file description like
910 // %!PS-Adobe-2.0EPSF"
913 else if (contains(str,"Grace"))
916 else if (contains(str,"JFIF"))
919 else if (contains(str,"%PDF"))
922 else if (contains(str,"PNG"))
925 else if (contains(str,"%!PS-Adobe")) {
928 if (contains(str,"EPSF"))
934 else if (contains(str,"_bits[]"))
937 else if (contains(str,"XPM") || contains(str, "static char *"))
940 else if (contains(str,"BITPIX"))
944 if (!format.empty()) {
945 lyxerr[Debug::GRAPHICS]
946 << "Recognised Fileformat: " << format << endl;
950 lyxerr[Debug::GRAPHICS]
951 << "filetools(getFormatFromContents)\n"
952 << "\tCouldn't find a known format!\n";
957 /// check for zipped file
958 bool zippedFile(FileName const & name)
960 string const type = getFormatFromContents(name);
961 if (contains("gzip zip compress", type) && !type.empty())
967 string const unzippedFileName(string const & zipped_file)
969 string const ext = getExtension(zipped_file);
970 if (ext == "gz" || ext == "z" || ext == "Z")
971 return changeExtension(zipped_file, string());
972 return "unzipped_" + zipped_file;
976 FileName const unzipFile(FileName const & zipped_file, string const & unzipped_file)
978 FileName const tempfile = FileName(unzipped_file.empty() ?
979 unzippedFileName(zipped_file.toFilesystemEncoding()) :
982 string const command = "gunzip -c " +
983 zipped_file.toFilesystemEncoding() + " > " +
984 tempfile.toFilesystemEncoding();
986 one.startscript(Systemcall::Wait, command);
987 // test that command was executed successfully (anon)
988 // yes, please do. (Lgb)
993 docstring const makeDisplayPath(string const & path, unsigned int threshold)
996 string const home = package().home_dir();
998 // replace /home/blah with ~/
999 if (!home.empty() && prefixIs(str, home))
1000 str = subst(str, home, "~");
1002 if (str.length() <= threshold)
1003 return lyx::from_utf8(os::external_path(str));
1005 string const prefix = ".../";
1008 while (str.length() > threshold)
1009 str = split(str, temp, '/');
1011 // Did we shorten everything away?
1013 // Yes, filename itself is too long.
1014 // Pick the start and the end of the filename.
1015 str = onlyFilename(path);
1016 string const head = str.substr(0, threshold / 2 - 3);
1018 string::size_type len = str.length();
1020 str.substr(len - threshold / 2 - 2, len - 1);
1021 str = head + "..." + tail;
1024 return lyx::from_utf8(os::external_path(prefix + str));
1028 bool readLink(string const & file, string & link, bool resolve)
1030 #ifdef HAVE_READLINK
1031 char linkbuffer[512];
1032 // Should be PATH_MAX but that needs autconf support
1033 int const nRead = ::readlink(file.c_str(),
1034 linkbuffer, sizeof(linkbuffer) - 1);
1037 linkbuffer[nRead] = '\0'; // terminator
1039 link = makeAbsPath(linkbuffer, onlyPath(file));
1049 cmd_ret const runCommand(string const & cmd)
1051 // FIXME: replace all calls to RunCommand with ForkedCall
1052 // (if the output is not needed) or the code in ispell.C
1053 // (if the output is needed).
1055 // One question is if we should use popen or
1056 // create our own popen based on fork, exec, pipe
1057 // of course the best would be to have a
1058 // pstream (process stream), with the
1059 // variants ipstream, opstream
1061 #if defined (HAVE_POPEN)
1062 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1063 #elif defined (HAVE__POPEN)
1064 FILE * inf = ::_popen(cmd.c_str(), os::popen_read_mode());
1066 #error No popen() function.
1069 // (Claus Hentschel) Check if popen was succesful ;-)
1071 lyxerr << "RunCommand:: could not start child process" << endl;
1072 return make_pair(-1, string());
1078 ret += static_cast<char>(c);
1082 #if defined (HAVE_PCLOSE)
1083 int const pret = pclose(inf);
1084 #elif defined (HAVE__PCLOSE)
1085 int const pret = _pclose(inf);
1087 #error No pclose() function.
1091 perror("RunCommand:: could not terminate child process");
1093 return make_pair(pret, ret);
1097 FileName const findtexfile(string const & fil, string const & /*format*/)
1099 /* There is no problem to extend this function too use other
1100 methods to look for files. It could be setup to look
1101 in environment paths and also if wanted as a last resort
1102 to a recursive find. One of the easier extensions would
1103 perhaps be to use the LyX file lookup methods. But! I am
1104 going to implement this until I see some demand for it.
1108 // If the file can be found directly, we just return a
1109 // absolute path version of it.
1110 FileName const absfile(makeAbsPath(fil));
1111 if (fs::exists(absfile.toFilesystemEncoding()))
1114 // No we try to find it using kpsewhich.
1115 // It seems from the kpsewhich manual page that it is safe to use
1116 // kpsewhich without --format: "When the --format option is not
1117 // given, the search path used when looking for a file is inferred
1118 // from the name given, by looking for a known extension. If no
1119 // known extension is found, the search path for TeX source files
1121 // However, we want to take advantage of the format sine almost all
1122 // the different formats has environment variables that can be used
1123 // to controll which paths to search. f.ex. bib looks in
1124 // BIBINPUTS and TEXBIB. Small list follows:
1125 // bib - BIBINPUTS, TEXBIB
1127 // graphic/figure - TEXPICTS, TEXINPUTS
1128 // ist - TEXINDEXSTYLE, INDEXSTYLE
1129 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1131 // tfm - TFMFONTS, TEXFONTS
1132 // This means that to use kpsewhich in the best possible way we
1133 // should help it by setting additional path in the approp. envir.var.
1134 string const kpsecmd = "kpsewhich " + fil;
1136 cmd_ret const c = runCommand(kpsecmd);
1138 lyxerr[Debug::LATEX] << "kpse status = " << c.first << '\n'
1139 << "kpse result = `" << rtrim(c.second, "\n")
1142 return FileName(os::internal_path(rtrim(c.second, "\n\r")));
1148 void removeAutosaveFile(string const & filename)
1150 string a = onlyPath(filename);
1152 a += onlyFilename(filename);
1154 FileName const autosave(a);
1155 if (fs::exists(autosave.toFilesystemEncoding()))
1160 void readBB_lyxerrMessage(FileName const & file, bool & zipped,
1161 string const & message)
1163 lyxerr[Debug::GRAPHICS] << "[readBB_from_PSFile] "
1164 << message << std::endl;
1165 #ifdef WITH_WARNINGS
1166 #warning Why is this func deleting a file? (Lgb)
1173 string const readBB_from_PSFile(FileName const & file)
1175 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1176 // It seems that every command in the header has an own line,
1177 // getline() should work for all files.
1178 // On the other hand some plot programs write the bb at the
1179 // end of the file. Than we have in the header:
1180 // %%BoundingBox: (atend)
1181 // In this case we must check the end.
1182 bool zipped = zippedFile(file);
1183 FileName const file_ = zipped ? unzipFile(file) : file;
1184 string const format = getFormatFromContents(file_);
1186 if (format != "eps" && format != "ps") {
1187 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1191 static boost::regex bbox_re(
1192 "^%%BoundingBox:\\s*([[:digit:]]+)\\s+([[:digit:]]+)\\s+([[:digit:]]+)\\s+([[:digit:]]+)");
1193 std::ifstream is(file_.toFilesystemEncoding().c_str());
1198 if (regex_match(s, what, bbox_re)) {
1199 // Our callers expect the tokens in the string
1200 // separated by single spaces.
1201 // FIXME: change return type from string to something
1204 os << what.str(1) << ' ' << what.str(2) << ' '
1205 << what.str(3) << ' ' << what.str(4);
1206 string const bb = os.str();
1207 readBB_lyxerrMessage(file_, zipped, bb);
1211 readBB_lyxerrMessage(file_, zipped, "no bb found");
1216 int compare_timestamps(FileName const & filename1, FileName const & filename2)
1218 // If the original is newer than the copy, then copy the original
1219 // to the new directory.
1221 string const file1 = filename1.toFilesystemEncoding();
1222 string const file2 = filename2.toFilesystemEncoding();
1224 if (fs::exists(file1) && fs::exists(file2)) {
1225 double const tmp = difftime(fs::last_write_time(file1),
1226 fs::last_write_time(file2));
1228 cmp = tmp > 0 ? 1 : -1;
1230 } else if (fs::exists(file1)) {
1232 } else if (fs::exists(file2)) {
1239 } //namespace support