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(FileName const & path)
170 lyxerr[Debug::FILES] << "isDirWriteable: " << path << endl;
172 FileName const tmpfl(tempName(path, "lyxwritetest"));
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<FileName> const dirList(FileName const & dir, string const & ext)
223 // EXCEPTIONS FIXME. Rewrite needed when we turn on exceptions. (Lgb)
224 vector<FileName> 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 dirlist.push_back(FileName::fromFilesystemEncoding(
245 encoded_dir + '/' + fil));
251 // Returns the real name of file name in directory path, with optional
253 FileName const fileSearch(string const & path, string const & name,
254 string const & ext, search_mode mode)
256 // if `name' is an absolute path, we ignore the setting of `path'
257 // Expand Environmentvariables in 'name'
258 string const tmpname = replaceEnvironmentPath(name);
259 FileName fullname(makeAbsPath(tmpname, path));
260 // search first without extension, then with it.
261 if (isFileReadable(fullname))
265 return mode == allow_unreadable ? fullname : FileName();
266 // Only add the extension if it is not already the extension of
268 if (getExtension(fullname.absFilename()) != ext)
269 fullname = FileName(addExtension(fullname.absFilename(), ext));
270 if (isFileReadable(fullname) || mode == allow_unreadable)
276 // Search the file name.ext in the subdirectory dir of
278 // 2) build_lyxdir (if not empty)
280 FileName const libFileSearch(string const & dir, string const & name,
283 FileName fullname = fileSearch(addPath(package().user_support(), dir),
285 if (!fullname.empty())
288 if (!package().build_support().empty())
289 fullname = fileSearch(addPath(package().build_support(), dir),
291 if (!fullname.empty())
294 return fileSearch(addPath(package().system_support(), dir), name, ext);
298 FileName const i18nLibFileSearch(string const & dir, string const & name,
301 // the following comments are from intl/dcigettext.c. We try
302 // to mimick this behaviour here.
303 /* The highest priority value is the `LANGUAGE' environment
304 variable. But we don't use the value if the currently
305 selected locale is the C locale. This is a GNU extension. */
306 /* [Otherwise] We have to proceed with the POSIX methods of
307 looking to `LC_ALL', `LC_xxx', and `LANG'. */
309 string lang = getEnv("LC_ALL");
311 lang = getEnv("LC_MESSAGES");
313 lang = getEnv("LANG");
319 string const language = getEnv("LANGUAGE");
320 if (lang != "C" && lang != "POSIX" && !language.empty())
324 lang = split(lang, l, ':');
325 while (!l.empty() && l != "C" && l != "POSIX") {
326 FileName const tmp = libFileSearch(dir,
327 token(l, '_', 0) + '_' + name,
331 lang = split(lang, l, ':');
334 return libFileSearch(dir, name, ext);
338 string const libScriptSearch(string const & command_in, quote_style style)
340 static string const token_scriptpath = "$$s/";
342 string command = command_in;
343 // Find the starting position of "$$s/"
344 string::size_type const pos1 = command.find(token_scriptpath);
345 if (pos1 == string::npos)
347 // Find the end of the "$$s/some_subdir/some_script" word within
348 // command. Assumes that the script name does not contain spaces.
349 string::size_type const start_script = pos1 + 4;
350 string::size_type const pos2 = command.find(' ', start_script);
351 string::size_type const size_script = pos2 == string::npos?
352 (command.size() - start_script) : pos2 - start_script;
354 // Does this script file exist?
355 string const script =
356 libFileSearch(".", command.substr(start_script, size_script)).absFilename();
358 if (script.empty()) {
359 // Replace "$$s/" with ""
360 command.erase(pos1, 4);
362 // Replace "$$s/foo/some_script" with "<path to>/some_script".
363 string::size_type const size_replace = size_script + 4;
364 command.replace(pos1, size_replace, quoteName(script, style));
373 FileName const createTmpDir(FileName const & tempdir, string const & mask)
376 << "createTmpDir: tempdir=`" << tempdir << "'\n"
377 << "createTmpDir: mask=`" << mask << '\'' << endl;
379 FileName const tmpfl(tempName(tempdir, mask));
380 // lyx::tempName actually creates a file to make sure that it
381 // stays unique. So we have to delete it before we can create
382 // a dir with the same name. Note also that we are not thread
383 // safe because of the gap between unlink and mkdir. (Lgb)
386 if (tmpfl.empty() || mkdir(tmpfl, 0700)) {
387 lyxerr << "LyX could not create the temporary directory '"
388 << tmpfl << "'" << endl;
398 bool destroyDir(FileName const & tmpdir)
401 return fs::remove_all(tmpdir.toFilesystemEncoding()) > 0;
402 } catch (fs::filesystem_error const & fe){
403 lyxerr << "Could not delete " << tmpdir << ". (" << fe.what() << ")" << std::endl;
409 string const createBufferTmpDir()
412 // We are in our own directory. Why bother to mangle name?
413 // In fact I wrote this code to circumvent a problematic behaviour
414 // (bug?) of EMX mkstemp().
416 package().temp_dir() + "/lyx_tmpbuf" +
417 convert<string>(count++);
419 if (mkdir(FileName(tmpfl), 0777)) {
420 lyxerr << "LyX could not create the temporary directory '"
421 << tmpfl << "'" << endl;
428 FileName const createLyXTmpDir(FileName const & deflt)
430 if (!deflt.empty() && deflt.absFilename() != "/tmp") {
431 if (mkdir(deflt, 0777)) {
432 if (isDirWriteable(deflt)) {
433 // deflt could not be created because it
434 // did exist already, so let's create our own
436 return createTmpDir(deflt, "lyx_tmpdir");
438 // some other error occured.
439 return createTmpDir(FileName("/tmp"), "lyx_tmpdir");
444 return createTmpDir(FileName("/tmp"), "lyx_tmpdir");
449 bool createDirectory(string const & path, int permission)
451 string temp = rtrim(os::internal_path(path), "/");
452 BOOST_ASSERT(!temp.empty());
453 return mkdir(FileName(temp), permission) == 0;
457 // Strip filename from path name
458 string const onlyPath(string const & filename)
460 // If empty filename, return empty
461 if (filename.empty())
464 // Find last / or start of filename
465 string::size_type j = filename.rfind('/');
466 return j == string::npos ? "./" : filename.substr(0, j + 1);
470 // Convert relative path into absolute path based on a basepath.
471 // If relpath is absolute, just use that.
472 // If basepath is empty, use CWD as base.
473 FileName const makeAbsPath(string const & relPath, string const & basePath)
475 // checks for already absolute path
476 if (os::is_absolute_path(relPath))
477 return FileName(relPath);
479 // Copies given paths
480 string tempRel = os::internal_path(relPath);
481 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
482 tempRel = subst(tempRel, "//", "/");
486 if (os::is_absolute_path(basePath))
489 tempBase = addPath(getcwd().absFilename(), basePath);
491 // Handle /./ at the end of the path
492 while (suffixIs(tempBase, "/./"))
493 tempBase.erase(tempBase.length() - 2);
495 // processes relative path
496 string rTemp = tempRel;
499 while (!rTemp.empty()) {
501 rTemp = split(rTemp, temp, '/');
503 if (temp == ".") continue;
505 // Remove one level of TempBase
506 string::difference_type i = tempBase.length() - 2;
509 while (i > 0 && tempBase[i] != '/')
512 tempBase.erase(i, string::npos);
515 } else if (temp.empty() && !rTemp.empty()) {
516 tempBase = os::current_root() + rTemp;
519 // Add this piece to TempBase
520 if (!suffixIs(tempBase, '/'))
526 // returns absolute path
527 return FileName(os::internal_path(tempBase));
531 // Correctly append filename to the pathname.
532 // If pathname is '.', then don't use pathname.
533 // Chops any path of filename.
534 string const addName(string const & path, string const & fname)
536 string const basename = onlyFilename(fname);
539 if (path != "." && path != "./" && !path.empty()) {
540 buf = os::internal_path(path);
541 if (!suffixIs(path, '/'))
545 return buf + basename;
549 // Strips path from filename
550 string const onlyFilename(string const & fname)
555 string::size_type j = fname.rfind('/');
556 if (j == string::npos) // no '/' in fname
560 return fname.substr(j + 1);
564 /// Returns true is path is absolute
565 bool absolutePath(string const & path)
567 return os::is_absolute_path(path);
571 // Create absolute path. If impossible, don't do anything
572 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
573 string const expandPath(string const & path)
575 // checks for already absolute path
576 string rTemp = replaceEnvironmentPath(path);
577 if (os::is_absolute_path(rTemp))
581 string const copy = rTemp;
584 rTemp = split(rTemp, temp, '/');
587 return getcwd().absFilename() + '/' + rTemp;
590 return package().home_dir() + '/' + rTemp;
593 return makeAbsPath(copy).absFilename();
595 // Don't know how to handle this
600 // Normalize a path. Constracts path/../path
601 // Can't handle "../../" or "/../" (Asger)
602 // Also converts paths like /foo//bar ==> /foo/bar
603 string const normalizePath(string const & path)
605 // Normalize paths like /foo//bar ==> /foo/bar
606 static boost::regex regex("/{2,}");
607 string const tmppath = boost::regex_merge(path, regex, "/");
609 fs::path const npath = fs::path(tmppath, fs::no_check).normalize();
611 if (!npath.is_complete())
612 return "./" + npath.string() + '/';
614 return npath.string() + '/';
618 string const getFileContents(FileName const & fname)
620 string const encodedname = fname.toFilesystemEncoding();
621 if (fs::exists(encodedname)) {
622 ifstream ifs(encodedname.c_str());
630 lyxerr << "LyX was not able to read file '" << fname << '\'' << endl;
635 // Search the string for ${VAR} and $VAR and replace VAR using getenv.
636 string const replaceEnvironmentPath(string const & path)
638 // ${VAR} is defined as
639 // $\{[A-Za-z_][A-Za-z_0-9]*\}
640 static string const envvar_br = "[$]\\{([A-Za-z_][A-Za-z_0-9]*)\\}";
642 // $VAR is defined as:
643 // $\{[A-Za-z_][A-Za-z_0-9]*\}
644 static string const envvar = "[$]([A-Za-z_][A-Za-z_0-9]*)";
646 static boost::regex envvar_br_re("(.*)" + envvar_br + "(.*)");
647 static boost::regex envvar_re("(.*)" + envvar + "(.*)");
650 string result = path;
652 regex_match(result, what, envvar_br_re);
653 if (!what[0].matched) {
654 regex_match(result, what, envvar_re);
655 if (!what[0].matched)
658 result = what.str(1) + getEnv(what.str(2)) + what.str(3);
664 // Make relative path out of two absolute paths
665 string const makeRelPath(string const & abspath, string const & basepath)
666 // Makes relative path out of absolute path. If it is deeper than basepath,
667 // it's easy. If basepath and abspath share something (they are all deeper
668 // than some directory), it'll be rendered using ..'s. If they are completely
669 // different, then the absolute path will be used as relative path.
671 string::size_type const abslen = abspath.length();
672 string::size_type const baselen = basepath.length();
674 string::size_type i = os::common_path(abspath, basepath);
677 // actually no match - cannot make it relative
681 // Count how many dirs there are in basepath above match
682 // and append as many '..''s into relpath
684 string::size_type j = i;
685 while (j < baselen) {
686 if (basepath[j] == '/') {
687 if (j + 1 == baselen)
694 // Append relative stuff from common directory to abspath
695 if (abspath[i] == '/')
697 for (; i < abslen; ++i)
700 if (suffixIs(buf, '/'))
701 buf.erase(buf.length() - 1);
702 // Substitute empty with .
709 // Append sub-directory(ies) to a path in an intelligent way
710 string const addPath(string const & path, string const & path_2)
713 string const path2 = os::internal_path(path_2);
715 if (!path.empty() && path != "." && path != "./") {
716 buf = os::internal_path(path);
717 if (path[path.length() - 1] != '/')
721 if (!path2.empty()) {
722 string::size_type const p2start = path2.find_first_not_of('/');
723 string::size_type const p2end = path2.find_last_not_of('/');
724 string const tmp = path2.substr(p2start, p2end - p2start + 1);
731 string const changeExtension(string const & oldname, string const & extension)
733 string::size_type const last_slash = oldname.rfind('/');
734 string::size_type last_dot = oldname.rfind('.');
735 if (last_dot < last_slash && last_slash != string::npos)
736 last_dot = string::npos;
739 // Make sure the extension starts with a dot
740 if (!extension.empty() && extension[0] != '.')
741 ext= '.' + extension;
745 return os::internal_path(oldname.substr(0, last_dot) + ext);
749 string const removeExtension(string const & name)
751 return changeExtension(name, string());
755 string const addExtension(string const & name, string const & extension)
757 if (!extension.empty() && extension[0] != '.')
758 return name + '.' + extension;
759 return name + extension;
763 /// Return the extension of the file (not including the .)
764 string const getExtension(string const & name)
766 string::size_type const last_slash = name.rfind('/');
767 string::size_type const last_dot = name.rfind('.');
768 if (last_dot != string::npos &&
769 (last_slash == string::npos || last_dot > last_slash))
770 return name.substr(last_dot + 1,
771 name.length() - (last_dot + 1));
777 // the different filetypes and what they contain in one of the first lines
778 // (dots are any characters). (Herbert 20020131)
781 // EPS %!PS-Adobe-3.0 EPSF...
788 // PBM P1... or P4 (B/W)
789 // PGM P2... or P5 (Grayscale)
790 // PPM P3... or P6 (color)
791 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
792 // SGI \001\332... (decimal 474)
794 // TIFF II... or MM...
796 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
797 // ...static char *...
798 // XWD \000\000\000\151 (0x00006900) decimal 105
800 // GZIP \037\213 http://www.ietf.org/rfc/rfc1952.txt
801 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
802 // Z \037\235 UNIX compress
804 string const getFormatFromContents(FileName const & filename)
807 if (filename.empty() || !isFileReadable(filename))
810 ifstream ifs(filename.toFilesystemEncoding().c_str());
812 // Couldn't open file...
816 static string const gzipStamp = "\037\213";
819 static string const zipStamp = "PK";
822 static string const compressStamp = "\037\235";
824 // Maximum strings to read
825 int const max_count = 50;
830 bool firstLine = true;
831 while ((count++ < max_count) && format.empty()) {
833 lyxerr[Debug::GRAPHICS]
834 << "filetools(getFormatFromContents)\n"
835 << "\tFile type not recognised before EOF!"
841 string const stamp = str.substr(0, 2);
842 if (firstLine && str.size() >= 2) {
843 // at first we check for a zipped file, because this
844 // information is saved in the first bytes of the file!
845 // also some graphic formats which save the information
846 // in the first line, too.
847 if (prefixIs(str, gzipStamp)) {
850 } else if (stamp == zipStamp) {
853 } else if (stamp == compressStamp) {
857 } else if (stamp == "BM") {
860 } else if (stamp == "\001\332") {
864 // Don't need to use str.at(0), str.at(1) because
865 // we already know that str.size() >= 2
866 } else if (str[0] == 'P') {
882 } else if ((stamp == "II") || (stamp == "MM")) {
885 } else if (prefixIs(str,"%TGIF")) {
888 } else if (prefixIs(str,"#FIG")) {
891 } else if (prefixIs(str,"GIF")) {
894 } else if (str.size() > 3) {
895 int const c = ((str[0] << 24) & (str[1] << 16) &
896 (str[2] << 8) & str[3]);
907 else if (contains(str,"EPSF"))
908 // dummy, if we have wrong file description like
909 // %!PS-Adobe-2.0EPSF"
912 else if (contains(str,"Grace"))
915 else if (contains(str,"JFIF"))
918 else if (contains(str,"%PDF"))
921 else if (contains(str,"PNG"))
924 else if (contains(str,"%!PS-Adobe")) {
927 if (contains(str,"EPSF"))
933 else if (contains(str,"_bits[]"))
936 else if (contains(str,"XPM") || contains(str, "static char *"))
939 else if (contains(str,"BITPIX"))
943 if (!format.empty()) {
944 lyxerr[Debug::GRAPHICS]
945 << "Recognised Fileformat: " << format << endl;
949 lyxerr[Debug::GRAPHICS]
950 << "filetools(getFormatFromContents)\n"
951 << "\tCouldn't find a known format!\n";
956 /// check for zipped file
957 bool zippedFile(FileName const & name)
959 string const type = getFormatFromContents(name);
960 if (contains("gzip zip compress", type) && !type.empty())
966 string const unzippedFileName(string const & zipped_file)
968 string const ext = getExtension(zipped_file);
969 if (ext == "gz" || ext == "z" || ext == "Z")
970 return changeExtension(zipped_file, string());
971 return "unzipped_" + zipped_file;
975 FileName const unzipFile(FileName const & zipped_file, string const & unzipped_file)
977 FileName const tempfile = FileName(unzipped_file.empty() ?
978 unzippedFileName(zipped_file.toFilesystemEncoding()) :
981 string const command = "gunzip -c " +
982 zipped_file.toFilesystemEncoding() + " > " +
983 tempfile.toFilesystemEncoding();
985 one.startscript(Systemcall::Wait, command);
986 // test that command was executed successfully (anon)
987 // yes, please do. (Lgb)
992 docstring const makeDisplayPath(string const & path, unsigned int threshold)
995 string const home = package().home_dir();
997 // replace /home/blah with ~/
998 if (!home.empty() && prefixIs(str, home))
999 str = subst(str, home, "~");
1001 if (str.length() <= threshold)
1002 return from_utf8(os::external_path(str));
1004 string const prefix = ".../";
1007 while (str.length() > threshold)
1008 str = split(str, temp, '/');
1010 // Did we shorten everything away?
1012 // Yes, filename itself is too long.
1013 // Pick the start and the end of the filename.
1014 str = onlyFilename(path);
1015 string const head = str.substr(0, threshold / 2 - 3);
1017 string::size_type len = str.length();
1019 str.substr(len - threshold / 2 - 2, len - 1);
1020 str = head + "..." + tail;
1023 return from_utf8(os::external_path(prefix + str));
1027 bool readLink(string const & file, string & link, bool resolve)
1029 #ifdef HAVE_READLINK
1030 char linkbuffer[512];
1031 // Should be PATH_MAX but that needs autconf support
1032 int const nRead = ::readlink(file.c_str(),
1033 linkbuffer, sizeof(linkbuffer) - 1);
1036 linkbuffer[nRead] = '\0'; // terminator
1038 link = makeAbsPath(linkbuffer, onlyPath(file)).absFilename();
1048 cmd_ret const runCommand(string const & cmd)
1050 // FIXME: replace all calls to RunCommand with ForkedCall
1051 // (if the output is not needed) or the code in ispell.C
1052 // (if the output is needed).
1054 // One question is if we should use popen or
1055 // create our own popen based on fork, exec, pipe
1056 // of course the best would be to have a
1057 // pstream (process stream), with the
1058 // variants ipstream, opstream
1060 #if defined (HAVE_POPEN)
1061 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1062 #elif defined (HAVE__POPEN)
1063 FILE * inf = ::_popen(cmd.c_str(), os::popen_read_mode());
1065 #error No popen() function.
1068 // (Claus Hentschel) Check if popen was succesful ;-)
1070 lyxerr << "RunCommand:: could not start child process" << endl;
1071 return make_pair(-1, string());
1077 ret += static_cast<char>(c);
1081 #if defined (HAVE_PCLOSE)
1082 int const pret = pclose(inf);
1083 #elif defined (HAVE__PCLOSE)
1084 int const pret = _pclose(inf);
1086 #error No pclose() function.
1090 perror("RunCommand:: could not terminate child process");
1092 return make_pair(pret, ret);
1096 FileName const findtexfile(string const & fil, string const & /*format*/)
1098 /* There is no problem to extend this function too use other
1099 methods to look for files. It could be setup to look
1100 in environment paths and also if wanted as a last resort
1101 to a recursive find. One of the easier extensions would
1102 perhaps be to use the LyX file lookup methods. But! I am
1103 going to implement this until I see some demand for it.
1107 // If the file can be found directly, we just return a
1108 // absolute path version of it.
1109 FileName const absfile(makeAbsPath(fil));
1110 if (fs::exists(absfile.toFilesystemEncoding()))
1113 // No we try to find it using kpsewhich.
1114 // It seems from the kpsewhich manual page that it is safe to use
1115 // kpsewhich without --format: "When the --format option is not
1116 // given, the search path used when looking for a file is inferred
1117 // from the name given, by looking for a known extension. If no
1118 // known extension is found, the search path for TeX source files
1120 // However, we want to take advantage of the format sine almost all
1121 // the different formats has environment variables that can be used
1122 // to controll which paths to search. f.ex. bib looks in
1123 // BIBINPUTS and TEXBIB. Small list follows:
1124 // bib - BIBINPUTS, TEXBIB
1126 // graphic/figure - TEXPICTS, TEXINPUTS
1127 // ist - TEXINDEXSTYLE, INDEXSTYLE
1128 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1130 // tfm - TFMFONTS, TEXFONTS
1131 // This means that to use kpsewhich in the best possible way we
1132 // should help it by setting additional path in the approp. envir.var.
1133 string const kpsecmd = "kpsewhich " + fil;
1135 cmd_ret const c = runCommand(kpsecmd);
1137 lyxerr[Debug::LATEX] << "kpse status = " << c.first << '\n'
1138 << "kpse result = `" << rtrim(c.second, "\n\r")
1141 return FileName(os::internal_path(rtrim(to_utf8(from_filesystem8bit(c.second)),
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