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>
58 using std::ostringstream;
62 namespace fs = boost::filesystem;
67 bool isLyXFilename(string const & filename)
69 return suffixIs(ascii_lowercase(filename), ".lyx");
73 bool isSGMLFilename(string const & filename)
75 return suffixIs(ascii_lowercase(filename), ".sgml");
79 bool isValidLaTeXFilename(string const & filename)
81 string const invalid_chars("#$%{}()[]\"^");
82 if (filename.find_first_of(invalid_chars) != string::npos)
89 string const latex_path(string const & original_path,
90 latex_path_extension extension,
93 // On cygwin, we may need windows or posix style paths.
94 string path = os::latex_path(original_path);
95 path = subst(path, "~", "\\string~");
96 if (path.find(' ') != string::npos) {
97 // We can't use '"' because " is sometimes active (e.g. if
98 // babel is loaded with the "german" option)
99 if (extension == EXCLUDE_EXTENSION) {
100 // ChangeExtension calls os::internal_path internally
101 // so don't use it to remove the extension.
102 string const ext = getExtension(path);
103 string const base = ext.empty() ?
105 path.substr(0, path.length() - ext.length() - 1);
106 // ChangeExtension calls os::internal_path internally
107 // so don't use it to re-add the extension.
108 path = "\\string\"" + base + "\\string\"." + ext;
110 path = "\\string\"" + path + "\\string\"";
114 return dots == ESCAPE_DOTS ? subst(path, ".", "\\lyxdot ") : path;
118 // Substitutes spaces with underscores in filename (and path)
119 string const makeLatexName(string const & file)
121 string name = onlyFilename(file);
122 string const path = onlyPath(file);
124 // ok so we scan through the string twice, but who cares.
125 string const keep = "abcdefghijklmnopqrstuvwxyz"
126 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
127 "@!'()*+,-./0123456789:;<=>?[]`|";
129 string::size_type pos = 0;
130 while ((pos = name.find_first_not_of(keep, pos)) != string::npos)
133 return addName(path, name);
137 string const quoteName(string const & name, quote_style style)
141 // This does not work for filenames containing " (windows)
142 // or ' (all other OSes). This can't be changed easily, since
143 // we would need to adapt the command line parser in
144 // Forkedcall::generateChild. Therefore we don't pass user
145 // filenames to child processes if possible. We store them in
146 // a python script instead, where we don't have these
148 return (os::shell() == os::UNIX) ?
152 return "\"" + subst(subst(name, "\\", "\\\\"), "\"", "\\\"")
155 // shut up stupid compiler
160 bool isFileReadable(FileName const & filename)
162 std::string const path = filename.toFilesystemEncoding();
163 return fs::exists(path) && !fs::is_directory(path) && fs::is_readable(path);
167 //returns true: dir writeable
168 // false: not writeable
169 bool isDirWriteable(FileName const & path)
171 LYXERR(Debug::FILES) << "isDirWriteable: " << path << endl;
173 FileName const tmpfl(tempName(path, "lyxwritetest"));
184 // Uses a string of paths separated by ";"s to find a file to open.
185 // Can't cope with pathnames with a ';' in them. Returns full path to file.
186 // If path entry begins with $$LyX/, use system_lyxdir
187 // If path entry begins with $$User/, use user_lyxdir
188 // Example: "$$User/doc;$$LyX/doc"
189 FileName const fileOpenSearch(string const & path, string const & name,
194 bool notfound = true;
195 string tmppath = split(path, path_element, ';');
197 while (notfound && !path_element.empty()) {
198 path_element = os::internal_path(path_element);
199 if (!suffixIs(path_element, '/'))
201 path_element = subst(path_element, "$$LyX",
202 package().system_support().absFilename());
203 path_element = subst(path_element, "$$User",
204 package().user_support().absFilename());
206 real_file = fileSearch(path_element, name, ext);
208 if (real_file.empty()) {
210 tmppath = split(tmppath, path_element, ';');
211 } while (!tmppath.empty() && path_element.empty());
221 /// Returns a vector of all files in directory dir having extension ext.
222 vector<FileName> const dirList(FileName const & dir, string const & ext)
224 // EXCEPTIONS FIXME. Rewrite needed when we turn on exceptions. (Lgb)
225 vector<FileName> dirlist;
227 if (!(dir.exists() && dir.isDirectory())) {
229 << "Directory \"" << dir
230 << "\" does not exist to DirList." << endl;
235 if (!ext.empty() && ext[0] != '.')
239 string const encoded_dir = dir.toFilesystemEncoding();
240 fs::directory_iterator dit(encoded_dir);
241 fs::directory_iterator end;
242 for (; dit != end; ++dit) {
243 string const & fil = dit->leaf();
244 if (suffixIs(fil, extension))
245 dirlist.push_back(FileName::fromFilesystemEncoding(
246 encoded_dir + '/' + 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().absFilename(), dir),
286 if (!fullname.empty())
289 if (!package().build_support().empty())
290 fullname = fileSearch(addPath(package().build_support().absFilename(), dir),
292 if (!fullname.empty())
295 return fileSearch(addPath(package().system_support().absFilename(), dir), name, ext);
299 FileName const i18nLibFileSearch(string const & dir, string const & name,
302 /* The highest priority value is the `LANGUAGE' environment
303 variable. But we don't use the value if the currently
304 selected locale is the C locale. This is a GNU extension.
306 Otherwise, w use a trick to guess what gettext has done:
307 each po file is able to tell us its name. (JMarc)
310 string lang = to_ascii(_("[[Replace with the code of your language]]"));
311 string const language = getEnv("LANGUAGE");
312 if (!lang.empty() && !language.empty())
316 lang = split(lang, l, ':');
319 // First try with the full name
320 tmp = libFileSearch(addPath(dir, l), name, ext);
324 // Then the name without country code
325 string const shortl = token(l, '_', 0);
327 tmp = libFileSearch(addPath(dir, shortl), name, ext);
333 // For compatibility, to be removed later (JMarc)
334 tmp = libFileSearch(dir, token(l, '_', 0) + '_' + name,
337 lyxerr << "i18nLibFileSearch: File `" << tmp
338 << "' has been found by the old method" <<endl;
342 lang = split(lang, l, ':');
345 return libFileSearch(dir, name, ext);
349 string const libScriptSearch(string const & command_in, quote_style style)
351 static string const token_scriptpath = "$$s/";
353 string command = command_in;
354 // Find the starting position of "$$s/"
355 string::size_type const pos1 = command.find(token_scriptpath);
356 if (pos1 == string::npos)
358 // Find the end of the "$$s/some_subdir/some_script" word within
359 // command. Assumes that the script name does not contain spaces.
360 string::size_type const start_script = pos1 + 4;
361 string::size_type const pos2 = command.find(' ', start_script);
362 string::size_type const size_script = pos2 == string::npos?
363 (command.size() - start_script) : pos2 - start_script;
365 // Does this script file exist?
366 string const script =
367 libFileSearch(".", command.substr(start_script, size_script)).absFilename();
369 if (script.empty()) {
370 // Replace "$$s/" with ""
371 command.erase(pos1, 4);
373 // Replace "$$s/foo/some_script" with "<path to>/some_script".
374 string::size_type const size_replace = size_script + 4;
375 command.replace(pos1, size_replace, quoteName(script, style));
384 FileName const createTmpDir(FileName const & tempdir, string const & mask)
387 << "createTmpDir: tempdir=`" << tempdir << "'\n"
388 << "createTmpDir: mask=`" << mask << '\'' << endl;
390 FileName const tmpfl(tempName(tempdir, mask));
391 // lyx::tempName actually creates a file to make sure that it
392 // stays unique. So we have to delete it before we can create
393 // a dir with the same name. Note also that we are not thread
394 // safe because of the gap between unlink and mkdir. (Lgb)
397 if (tmpfl.empty() || mkdir(tmpfl, 0700)) {
398 lyxerr << "LyX could not create the temporary directory '"
399 << tmpfl << "'" << endl;
409 bool destroyDir(FileName const & tmpdir)
412 return fs::remove_all(tmpdir.toFilesystemEncoding()) > 0;
413 } catch (fs::filesystem_error const & fe){
414 lyxerr << "Could not delete " << tmpdir << ". (" << fe.what() << ")" << std::endl;
420 string const createBufferTmpDir()
423 // We are in our own directory. Why bother to mangle name?
424 // In fact I wrote this code to circumvent a problematic behaviour
425 // (bug?) of EMX mkstemp().
427 package().temp_dir().absFilename() + "/lyx_tmpbuf" +
428 convert<string>(count++);
430 if (mkdir(FileName(tmpfl), 0777)) {
431 lyxerr << "LyX could not create the temporary directory '"
432 << tmpfl << "'" << endl;
439 FileName const createLyXTmpDir(FileName const & deflt)
441 if (!deflt.empty() && deflt.absFilename() != "/tmp") {
442 if (mkdir(deflt, 0777)) {
443 if (isDirWriteable(deflt)) {
444 // deflt could not be created because it
445 // did exist already, so let's create our own
447 return createTmpDir(deflt, "lyx_tmpdir");
449 // some other error occured.
450 return createTmpDir(FileName("/tmp"), "lyx_tmpdir");
455 return createTmpDir(FileName("/tmp"), "lyx_tmpdir");
460 bool createDirectory(FileName const & path, int permission)
462 BOOST_ASSERT(!path.empty());
463 return mkdir(path, permission) == 0;
467 // Strip filename from path name
468 string const onlyPath(string const & filename)
470 // If empty filename, return empty
471 if (filename.empty())
474 // Find last / or start of filename
475 string::size_type j = filename.rfind('/');
476 return j == string::npos ? "./" : filename.substr(0, j + 1);
480 // Convert relative path into absolute path based on a basepath.
481 // If relpath is absolute, just use that.
482 // If basepath is empty, use CWD as base.
483 FileName const makeAbsPath(string const & relPath, string const & basePath)
485 // checks for already absolute path
486 if (os::is_absolute_path(relPath))
487 return FileName(relPath);
489 // Copies given paths
490 string tempRel = os::internal_path(relPath);
491 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
492 tempRel = subst(tempRel, "//", "/");
496 if (os::is_absolute_path(basePath))
499 tempBase = addPath(getcwd().absFilename(), basePath);
501 // Handle /./ at the end of the path
502 while (suffixIs(tempBase, "/./"))
503 tempBase.erase(tempBase.length() - 2);
505 // processes relative path
506 string rTemp = tempRel;
509 while (!rTemp.empty()) {
511 rTemp = split(rTemp, temp, '/');
513 if (temp == ".") continue;
515 // Remove one level of TempBase
516 string::difference_type i = tempBase.length() - 2;
519 while (i > 0 && tempBase[i] != '/')
522 tempBase.erase(i, string::npos);
525 } else if (temp.empty() && !rTemp.empty()) {
526 tempBase = os::current_root() + rTemp;
529 // Add this piece to TempBase
530 if (!suffixIs(tempBase, '/'))
536 // returns absolute path
537 return FileName(os::internal_path(tempBase));
541 // Correctly append filename to the pathname.
542 // If pathname is '.', then don't use pathname.
543 // Chops any path of filename.
544 string const addName(string const & path, string const & fname)
546 string const basename = onlyFilename(fname);
549 if (path != "." && path != "./" && !path.empty()) {
550 buf = os::internal_path(path);
551 if (!suffixIs(path, '/'))
555 return buf + basename;
559 // Strips path from filename
560 string const onlyFilename(string const & fname)
565 string::size_type j = fname.rfind('/');
566 if (j == string::npos) // no '/' in fname
570 return fname.substr(j + 1);
574 /// Returns true is path is absolute
575 bool absolutePath(string const & path)
577 return os::is_absolute_path(path);
581 // Create absolute path. If impossible, don't do anything
582 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
583 string const expandPath(string const & path)
585 // checks for already absolute path
586 string rTemp = replaceEnvironmentPath(path);
587 if (os::is_absolute_path(rTemp))
591 string const copy = rTemp;
594 rTemp = split(rTemp, temp, '/');
597 return getcwd().absFilename() + '/' + rTemp;
600 return package().home_dir().absFilename() + '/' + rTemp;
603 return makeAbsPath(copy).absFilename();
605 // Don't know how to handle this
610 // Normalize a path. Constracts path/../path
611 // Can't handle "../../" or "/../" (Asger)
612 // Also converts paths like /foo//bar ==> /foo/bar
613 string const normalizePath(string const & path)
615 // Normalize paths like /foo//bar ==> /foo/bar
616 static boost::regex regex("/{2,}");
617 string const tmppath = boost::regex_merge(path, regex, "/");
619 fs::path const npath = fs::path(tmppath, fs::no_check).normalize();
621 if (!npath.is_complete())
622 return "./" + npath.string() + '/';
624 return npath.string() + '/';
628 string const getFileContents(FileName const & fname)
630 string const encodedname = fname.toFilesystemEncoding();
631 if (fs::exists(encodedname)) {
632 ifstream ifs(encodedname.c_str());
640 lyxerr << "LyX was not able to read file '" << fname << '\'' << endl;
645 // Search the string for ${VAR} and $VAR and replace VAR using getenv.
646 string const replaceEnvironmentPath(string const & path)
648 // ${VAR} is defined as
649 // $\{[A-Za-z_][A-Za-z_0-9]*\}
650 static string const envvar_br = "[$]\\{([A-Za-z_][A-Za-z_0-9]*)\\}";
652 // $VAR is defined as:
653 // $\{[A-Za-z_][A-Za-z_0-9]*\}
654 static string const envvar = "[$]([A-Za-z_][A-Za-z_0-9]*)";
656 static boost::regex envvar_br_re("(.*)" + envvar_br + "(.*)");
657 static boost::regex envvar_re("(.*)" + envvar + "(.*)");
660 string result = path;
662 regex_match(result, what, envvar_br_re);
663 if (!what[0].matched) {
664 regex_match(result, what, envvar_re);
665 if (!what[0].matched)
668 result = what.str(1) + getEnv(what.str(2)) + what.str(3);
674 // Make relative path out of two absolute paths
675 docstring const makeRelPath(docstring const & abspath, docstring const & basepath)
676 // Makes relative path out of absolute path. If it is deeper than basepath,
677 // it's easy. If basepath and abspath share something (they are all deeper
678 // than some directory), it'll be rendered using ..'s. If they are completely
679 // different, then the absolute path will be used as relative path.
681 docstring::size_type const abslen = abspath.length();
682 docstring::size_type const baselen = basepath.length();
684 docstring::size_type i = os::common_path(abspath, basepath);
687 // actually no match - cannot make it relative
691 // Count how many dirs there are in basepath above match
692 // and append as many '..''s into relpath
694 docstring::size_type j = i;
695 while (j < baselen) {
696 if (basepath[j] == '/') {
697 if (j + 1 == baselen)
704 // Append relative stuff from common directory to abspath
705 if (abspath[i] == '/')
707 for (; i < abslen; ++i)
710 if (suffixIs(buf, '/'))
711 buf.erase(buf.length() - 1);
712 // Substitute empty with .
719 // Append sub-directory(ies) to a path in an intelligent way
720 string const addPath(string const & path, string const & path_2)
723 string const path2 = os::internal_path(path_2);
725 if (!path.empty() && path != "." && path != "./") {
726 buf = os::internal_path(path);
727 if (path[path.length() - 1] != '/')
731 if (!path2.empty()) {
732 string::size_type const p2start = path2.find_first_not_of('/');
733 string::size_type const p2end = path2.find_last_not_of('/');
734 string const tmp = path2.substr(p2start, p2end - p2start + 1);
741 string const changeExtension(string const & oldname, string const & extension)
743 string::size_type const last_slash = oldname.rfind('/');
744 string::size_type last_dot = oldname.rfind('.');
745 if (last_dot < last_slash && last_slash != string::npos)
746 last_dot = string::npos;
749 // Make sure the extension starts with a dot
750 if (!extension.empty() && extension[0] != '.')
751 ext= '.' + extension;
755 return os::internal_path(oldname.substr(0, last_dot) + ext);
759 string const removeExtension(string const & name)
761 return changeExtension(name, string());
765 string const addExtension(string const & name, string const & extension)
767 if (!extension.empty() && extension[0] != '.')
768 return name + '.' + extension;
769 return name + extension;
773 /// Return the extension of the file (not including the .)
774 string const getExtension(string const & name)
776 string::size_type const last_slash = name.rfind('/');
777 string::size_type const last_dot = name.rfind('.');
778 if (last_dot != string::npos &&
779 (last_slash == string::npos || last_dot > last_slash))
780 return name.substr(last_dot + 1,
781 name.length() - (last_dot + 1));
787 // the different filetypes and what they contain in one of the first lines
788 // (dots are any characters). (Herbert 20020131)
791 // EPS %!PS-Adobe-3.0 EPSF...
798 // PBM P1... or P4 (B/W)
799 // PGM P2... or P5 (Grayscale)
800 // PPM P3... or P6 (color)
801 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
802 // SGI \001\332... (decimal 474)
804 // TIFF II... or MM...
806 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
807 // ...static char *...
808 // XWD \000\000\000\151 (0x00006900) decimal 105
810 // GZIP \037\213 http://www.ietf.org/rfc/rfc1952.txt
811 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
812 // Z \037\235 UNIX compress
814 string const getFormatFromContents(FileName const & filename)
817 if (filename.empty() || !isFileReadable(filename))
820 ifstream ifs(filename.toFilesystemEncoding().c_str());
822 // Couldn't open file...
826 static string const gzipStamp = "\037\213";
829 static string const zipStamp = "PK";
832 static string const compressStamp = "\037\235";
834 // Maximum strings to read
835 int const max_count = 50;
840 bool firstLine = true;
841 while ((count++ < max_count) && format.empty()) {
843 LYXERR(Debug::GRAPHICS)
844 << "filetools(getFormatFromContents)\n"
845 << "\tFile type not recognised before EOF!"
851 string const stamp = str.substr(0, 2);
852 if (firstLine && str.size() >= 2) {
853 // at first we check for a zipped file, because this
854 // information is saved in the first bytes of the file!
855 // also some graphic formats which save the information
856 // in the first line, too.
857 if (prefixIs(str, gzipStamp)) {
860 } else if (stamp == zipStamp) {
863 } else if (stamp == compressStamp) {
867 } else if (stamp == "BM") {
870 } else if (stamp == "\001\332") {
874 // Don't need to use str.at(0), str.at(1) because
875 // we already know that str.size() >= 2
876 } else if (str[0] == 'P') {
892 } else if ((stamp == "II") || (stamp == "MM")) {
895 } else if (prefixIs(str,"%TGIF")) {
898 } else if (prefixIs(str,"#FIG")) {
901 } else if (prefixIs(str,"GIF")) {
904 } else if (str.size() > 3) {
905 int const c = ((str[0] << 24) & (str[1] << 16) &
906 (str[2] << 8) & str[3]);
917 else if (contains(str,"EPSF"))
918 // dummy, if we have wrong file description like
919 // %!PS-Adobe-2.0EPSF"
922 else if (contains(str, "Grace"))
925 else if (contains(str, "JFIF"))
928 else if (contains(str, "%PDF"))
931 else if (contains(str, "PNG"))
934 else if (contains(str, "%!PS-Adobe")) {
937 if (contains(str,"EPSF"))
943 else if (contains(str, "_bits[]"))
946 else if (contains(str, "XPM") || contains(str, "static char *"))
949 else if (contains(str, "BITPIX"))
953 if (!format.empty()) {
954 LYXERR(Debug::GRAPHICS)
955 << "Recognised Fileformat: " << format << endl;
959 LYXERR(Debug::GRAPHICS)
960 << "filetools(getFormatFromContents)\n"
961 << "\tCouldn't find a known format!\n";
966 /// check for zipped file
967 bool zippedFile(FileName const & name)
969 string const type = getFormatFromContents(name);
970 if (contains("gzip zip compress", type) && !type.empty())
976 string const unzippedFileName(string const & zipped_file)
978 string const ext = getExtension(zipped_file);
979 if (ext == "gz" || ext == "z" || ext == "Z")
980 return changeExtension(zipped_file, string());
981 return "unzipped_" + zipped_file;
985 FileName const unzipFile(FileName const & zipped_file, string const & unzipped_file)
987 FileName const tempfile = FileName(unzipped_file.empty() ?
988 unzippedFileName(zipped_file.toFilesystemEncoding()) :
991 string const command = "gunzip -c " +
992 zipped_file.toFilesystemEncoding() + " > " +
993 tempfile.toFilesystemEncoding();
995 one.startscript(Systemcall::Wait, command);
996 // test that command was executed successfully (anon)
997 // yes, please do. (Lgb)
1002 docstring const makeDisplayPath(string const & path, unsigned int threshold)
1006 // If file is from LyXDir, display it as if it were relative.
1007 string const system = package().system_support().absFilename();
1008 if (prefixIs(str, system) && str != system)
1009 return from_utf8("[" + str.erase(0, system.length()) + "]");
1011 // replace /home/blah with ~/
1012 string const home = package().home_dir().absFilename();
1013 if (!home.empty() && prefixIs(str, home))
1014 str = subst(str, home, "~");
1016 if (str.length() <= threshold)
1017 return from_utf8(os::external_path(str));
1019 string const prefix = ".../";
1022 while (str.length() > threshold)
1023 str = split(str, temp, '/');
1025 // Did we shorten everything away?
1027 // Yes, filename itself is too long.
1028 // Pick the start and the end of the filename.
1029 str = onlyFilename(path);
1030 string const head = str.substr(0, threshold / 2 - 3);
1032 string::size_type len = str.length();
1034 str.substr(len - threshold / 2 - 2, len - 1);
1035 str = head + "..." + tail;
1038 return from_utf8(os::external_path(prefix + str));
1042 bool readLink(FileName const & file, FileName & link)
1044 #ifdef HAVE_READLINK
1045 char linkbuffer[512];
1046 // Should be PATH_MAX but that needs autconf support
1047 string const encoded = file.toFilesystemEncoding();
1048 int const nRead = ::readlink(encoded.c_str(),
1049 linkbuffer, sizeof(linkbuffer) - 1);
1052 linkbuffer[nRead] = '\0'; // terminator
1053 link = makeAbsPath(linkbuffer, onlyPath(file.absFilename()));
1061 cmd_ret const runCommand(string const & cmd)
1063 // FIXME: replace all calls to RunCommand with ForkedCall
1064 // (if the output is not needed) or the code in ISpell.cpp
1065 // (if the output is needed).
1067 // One question is if we should use popen or
1068 // create our own popen based on fork, exec, pipe
1069 // of course the best would be to have a
1070 // pstream (process stream), with the
1071 // variants ipstream, opstream
1073 #if defined (HAVE_POPEN)
1074 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1075 #elif defined (HAVE__POPEN)
1076 FILE * inf = ::_popen(cmd.c_str(), os::popen_read_mode());
1078 #error No popen() function.
1081 // (Claus Hentschel) Check if popen was succesful ;-)
1083 lyxerr << "RunCommand:: could not start child process" << endl;
1084 return make_pair(-1, string());
1090 ret += static_cast<char>(c);
1094 #if defined (HAVE_PCLOSE)
1095 int const pret = pclose(inf);
1096 #elif defined (HAVE__PCLOSE)
1097 int const pret = _pclose(inf);
1099 #error No pclose() function.
1103 perror("RunCommand:: could not terminate child process");
1105 return make_pair(pret, ret);
1109 FileName const findtexfile(string const & fil, string const & /*format*/)
1111 /* There is no problem to extend this function too use other
1112 methods to look for files. It could be setup to look
1113 in environment paths and also if wanted as a last resort
1114 to a recursive find. One of the easier extensions would
1115 perhaps be to use the LyX file lookup methods. But! I am
1116 going to implement this until I see some demand for it.
1120 // If the file can be found directly, we just return a
1121 // absolute path version of it.
1122 FileName const absfile(makeAbsPath(fil));
1123 if (fs::exists(absfile.toFilesystemEncoding()))
1126 // No we try to find it using kpsewhich.
1127 // It seems from the kpsewhich manual page that it is safe to use
1128 // kpsewhich without --format: "When the --format option is not
1129 // given, the search path used when looking for a file is inferred
1130 // from the name given, by looking for a known extension. If no
1131 // known extension is found, the search path for TeX source files
1133 // However, we want to take advantage of the format sine almost all
1134 // the different formats has environment variables that can be used
1135 // to controll which paths to search. f.ex. bib looks in
1136 // BIBINPUTS and TEXBIB. Small list follows:
1137 // bib - BIBINPUTS, TEXBIB
1139 // graphic/figure - TEXPICTS, TEXINPUTS
1140 // ist - TEXINDEXSTYLE, INDEXSTYLE
1141 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1143 // tfm - TFMFONTS, TEXFONTS
1144 // This means that to use kpsewhich in the best possible way we
1145 // should help it by setting additional path in the approp. envir.var.
1146 string const kpsecmd = "kpsewhich " + fil;
1148 cmd_ret const c = runCommand(kpsecmd);
1150 LYXERR(Debug::LATEX) << "kpse status = " << c.first << '\n'
1151 << "kpse result = `" << rtrim(c.second, "\n\r")
1154 return FileName(os::internal_path(rtrim(to_utf8(from_filesystem8bit(c.second)),
1161 void removeAutosaveFile(string const & filename)
1163 string a = onlyPath(filename);
1165 a += onlyFilename(filename);
1167 FileName const autosave(a);
1168 if (fs::exists(autosave.toFilesystemEncoding()))
1173 void readBB_lyxerrMessage(FileName const & file, bool & zipped,
1174 string const & message)
1176 LYXERR(Debug::GRAPHICS) << "[readBB_from_PSFile] "
1177 << message << std::endl;
1178 // FIXME: Why is this func deleting a file? (Lgb)
1184 string const readBB_from_PSFile(FileName const & file)
1186 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1187 // It seems that every command in the header has an own line,
1188 // getline() should work for all files.
1189 // On the other hand some plot programs write the bb at the
1190 // end of the file. Than we have in the header:
1191 // %%BoundingBox: (atend)
1192 // In this case we must check the end.
1193 bool zipped = zippedFile(file);
1194 FileName const file_ = zipped ? unzipFile(file) : file;
1195 string const format = getFormatFromContents(file_);
1197 if (format != "eps" && format != "ps") {
1198 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1202 static boost::regex bbox_re(
1203 "^%%BoundingBox:\\s*([[:digit:]]+)\\s+([[:digit:]]+)\\s+([[:digit:]]+)\\s+([[:digit:]]+)");
1204 std::ifstream is(file_.toFilesystemEncoding().c_str());
1209 if (regex_match(s, what, bbox_re)) {
1210 // Our callers expect the tokens in the string
1211 // separated by single spaces.
1212 // FIXME: change return type from string to something
1215 os << what.str(1) << ' ' << what.str(2) << ' '
1216 << what.str(3) << ' ' << what.str(4);
1217 string const bb = os.str();
1218 readBB_lyxerrMessage(file_, zipped, bb);
1222 readBB_lyxerrMessage(file_, zipped, "no bb found");
1227 int compare_timestamps(FileName const & file1, FileName const & file2)
1229 // If the original is newer than the copy, then copy the original
1230 // to the new directory.
1233 if (file1.exists() && file2.exists()) {
1234 double const tmp = difftime(file1.lastModified(), file2.lastModified());
1236 cmp = tmp > 0 ? 1 : -1;
1238 } else if (file1.exists()) {
1240 } else if (file2.exists()) {
1247 } //namespace support