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>
53 #ifndef CXX_GLOBAL_CSTD
62 using std::ostringstream;
65 namespace fs = boost::filesystem;
70 bool isLyXFilename(string const & filename)
72 return suffixIs(ascii_lowercase(filename), ".lyx");
76 bool isSGMLFilename(string const & filename)
78 return suffixIs(ascii_lowercase(filename), ".sgml");
82 string const latex_path(string const & original_path,
83 latex_path_extension extension,
86 // On cygwin, we may need windows or posix style paths.
87 string path = os::latex_path(original_path);
88 path = subst(path, "~", "\\string~");
89 if (path.find(' ') != string::npos) {
90 // We can't use '"' because " is sometimes active (e.g. if
91 // babel is loaded with the "german" option)
92 if (extension == EXCLUDE_EXTENSION) {
93 // ChangeExtension calls os::internal_path internally
94 // so don't use it to remove the extension.
95 string const ext = getExtension(path);
96 string const base = ext.empty() ?
98 path.substr(0, path.length() - ext.length() - 1);
99 // ChangeExtension calls os::internal_path internally
100 // so don't use it to re-add the extension.
101 path = "\\string\"" + base + "\\string\"." + ext;
103 path = "\\string\"" + path + "\\string\"";
107 return dots == ESCAPE_DOTS ? subst(path, ".", "\\lyxdot ") : path;
111 // Substitutes spaces with underscores in filename (and path)
112 string const makeLatexName(string const & file)
114 string name = onlyFilename(file);
115 string const path = onlyPath(file);
117 // ok so we scan through the string twice, but who cares.
118 string const keep = "abcdefghijklmnopqrstuvwxyz"
119 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
120 "@!'()*+,-./0123456789:;<=>?[]`|";
122 string::size_type pos = 0;
123 while ((pos = name.find_first_not_of(keep, pos)) != string::npos)
126 return addName(path, name);
130 string const quoteName(string const & name, quote_style style)
134 // This does not work for filenames containing " (windows)
135 // or ' (all other OSes). This can't be changed easily, since
136 // we would need to adapt the command line parser in
137 // Forkedcall::generateChild. Therefore we don't pass user
138 // filenames to child processes if possible. We store them in
139 // a python script instead, where we don't have these
141 return (os::shell() == os::UNIX) ?
145 return "\"" + subst(subst(name, "\\", "\\\\"), "\"", "\\\"")
148 // shut up stupid compiler
153 bool isFileReadable(FileName const & filename)
155 std::string const path = filename.toFilesystemEncoding();
156 return fs::exists(path) && !fs::is_directory(path) && fs::is_readable(path);
160 //returns true: dir writeable
161 // false: not writeable
162 bool isDirWriteable(FileName const & path)
164 LYXERR(Debug::FILES) << "isDirWriteable: " << path << endl;
166 FileName const tmpfl(tempName(path, "lyxwritetest"));
177 // Uses a string of paths separated by ";"s to find a file to open.
178 // Can't cope with pathnames with a ';' in them. Returns full path to file.
179 // If path entry begins with $$LyX/, use system_lyxdir
180 // If path entry begins with $$User/, use user_lyxdir
181 // Example: "$$User/doc;$$LyX/doc"
182 FileName const fileOpenSearch(string const & path, string const & name,
187 bool notfound = true;
188 string tmppath = split(path, path_element, ';');
190 while (notfound && !path_element.empty()) {
191 path_element = os::internal_path(path_element);
192 if (!suffixIs(path_element, '/'))
194 path_element = subst(path_element, "$$LyX",
195 package().system_support().absFilename());
196 path_element = subst(path_element, "$$User",
197 package().user_support().absFilename());
199 real_file = fileSearch(path_element, name, ext);
201 if (real_file.empty()) {
203 tmppath = split(tmppath, path_element, ';');
204 } while (!tmppath.empty() && path_element.empty());
214 /// Returns a vector of all files in directory dir having extension ext.
215 vector<FileName> const dirList(FileName const & dir, string const & ext)
217 // EXCEPTIONS FIXME. Rewrite needed when we turn on exceptions. (Lgb)
218 vector<FileName> dirlist;
220 string const encoded_dir = dir.toFilesystemEncoding();
221 if (!(fs::exists(encoded_dir) && fs::is_directory(encoded_dir))) {
223 << "Directory \"" << dir
224 << "\" does not exist to DirList." << endl;
229 if (!ext.empty() && ext[0] != '.')
233 fs::directory_iterator dit(encoded_dir);
234 fs::directory_iterator end;
235 for (; dit != end; ++dit) {
236 string const & fil = dit->leaf();
237 if (suffixIs(fil, extension))
238 dirlist.push_back(FileName::fromFilesystemEncoding(
239 encoded_dir + '/' + fil));
245 // Returns the real name of file name in directory path, with optional
247 FileName const fileSearch(string const & path, string const & name,
248 string const & ext, search_mode mode)
250 // if `name' is an absolute path, we ignore the setting of `path'
251 // Expand Environmentvariables in 'name'
252 string const tmpname = replaceEnvironmentPath(name);
253 FileName fullname(makeAbsPath(tmpname, path));
254 // search first without extension, then with it.
255 if (isFileReadable(fullname))
259 return mode == allow_unreadable ? fullname : FileName();
260 // Only add the extension if it is not already the extension of
262 if (getExtension(fullname.absFilename()) != ext)
263 fullname = FileName(addExtension(fullname.absFilename(), ext));
264 if (isFileReadable(fullname) || mode == allow_unreadable)
270 // Search the file name.ext in the subdirectory dir of
272 // 2) build_lyxdir (if not empty)
274 FileName const libFileSearch(string const & dir, string const & name,
277 FileName fullname = fileSearch(addPath(package().user_support().absFilename(), dir),
279 if (!fullname.empty())
282 if (!package().build_support().empty())
283 fullname = fileSearch(addPath(package().build_support().absFilename(), dir),
285 if (!fullname.empty())
288 return fileSearch(addPath(package().system_support().absFilename(), dir), name, ext);
292 FileName const i18nLibFileSearch(string const & dir, string const & name,
295 // the following comments are from intl/dcigettext.c. We try
296 // to mimick this behaviour here.
297 /* The highest priority value is the `LANGUAGE' environment
298 variable. But we don't use the value if the currently
299 selected locale is the C locale. This is a GNU extension. */
300 /* [Otherwise] We have to proceed with the POSIX methods of
301 looking to `LC_ALL', `LC_xxx', and `LANG'. */
303 string lang = getEnv("LC_ALL");
305 lang = getEnv("LC_MESSAGES");
307 lang = getEnv("LANG");
313 string const language = getEnv("LANGUAGE");
314 if (lang != "C" && lang != "POSIX" && !language.empty())
318 lang = split(lang, l, ':');
319 while (!l.empty() && l != "C" && l != "POSIX") {
320 FileName const tmp = libFileSearch(addPath(dir, token(l, '_', 0)),
325 // to be removed later (JMarc)
326 FileName const tmpold = libFileSearch(dir,
327 token(l, '_', 0) + '_' + name,
329 if (!tmpold.empty()) {
330 lyxerr << "i18nLibFileSearch: File `" << tmpold
331 << "' has been found by the old method" <<endl;
335 lang = split(lang, l, ':');
338 return libFileSearch(dir, name, ext);
342 string const libScriptSearch(string const & command_in, quote_style style)
344 static string const token_scriptpath = "$$s/";
346 string command = command_in;
347 // Find the starting position of "$$s/"
348 string::size_type const pos1 = command.find(token_scriptpath);
349 if (pos1 == string::npos)
351 // Find the end of the "$$s/some_subdir/some_script" word within
352 // command. Assumes that the script name does not contain spaces.
353 string::size_type const start_script = pos1 + 4;
354 string::size_type const pos2 = command.find(' ', start_script);
355 string::size_type const size_script = pos2 == string::npos?
356 (command.size() - start_script) : pos2 - start_script;
358 // Does this script file exist?
359 string const script =
360 libFileSearch(".", command.substr(start_script, size_script)).absFilename();
362 if (script.empty()) {
363 // Replace "$$s/" with ""
364 command.erase(pos1, 4);
366 // Replace "$$s/foo/some_script" with "<path to>/some_script".
367 string::size_type const size_replace = size_script + 4;
368 command.replace(pos1, size_replace, quoteName(script, style));
377 FileName const createTmpDir(FileName const & tempdir, string const & mask)
380 << "createTmpDir: tempdir=`" << tempdir << "'\n"
381 << "createTmpDir: mask=`" << mask << '\'' << endl;
383 FileName const tmpfl(tempName(tempdir, mask));
384 // lyx::tempName actually creates a file to make sure that it
385 // stays unique. So we have to delete it before we can create
386 // a dir with the same name. Note also that we are not thread
387 // safe because of the gap between unlink and mkdir. (Lgb)
390 if (tmpfl.empty() || mkdir(tmpfl, 0700)) {
391 lyxerr << "LyX could not create the temporary directory '"
392 << tmpfl << "'" << endl;
402 bool destroyDir(FileName const & tmpdir)
405 return fs::remove_all(tmpdir.toFilesystemEncoding()) > 0;
406 } catch (fs::filesystem_error const & fe){
407 lyxerr << "Could not delete " << tmpdir << ". (" << fe.what() << ")" << std::endl;
413 string const createBufferTmpDir()
416 // We are in our own directory. Why bother to mangle name?
417 // In fact I wrote this code to circumvent a problematic behaviour
418 // (bug?) of EMX mkstemp().
420 package().temp_dir().absFilename() + "/lyx_tmpbuf" +
421 convert<string>(count++);
423 if (mkdir(FileName(tmpfl), 0777)) {
424 lyxerr << "LyX could not create the temporary directory '"
425 << tmpfl << "'" << endl;
432 FileName const createLyXTmpDir(FileName const & deflt)
434 if (!deflt.empty() && deflt.absFilename() != "/tmp") {
435 if (mkdir(deflt, 0777)) {
436 if (isDirWriteable(deflt)) {
437 // deflt could not be created because it
438 // did exist already, so let's create our own
440 return createTmpDir(deflt, "lyx_tmpdir");
442 // some other error occured.
443 return createTmpDir(FileName("/tmp"), "lyx_tmpdir");
448 return createTmpDir(FileName("/tmp"), "lyx_tmpdir");
453 bool createDirectory(FileName const & path, int permission)
455 BOOST_ASSERT(!path.empty());
456 return mkdir(path, permission) == 0;
460 // Strip filename from path name
461 string const onlyPath(string const & filename)
463 // If empty filename, return empty
464 if (filename.empty())
467 // Find last / or start of filename
468 string::size_type j = filename.rfind('/');
469 return j == string::npos ? "./" : filename.substr(0, j + 1);
473 // Convert relative path into absolute path based on a basepath.
474 // If relpath is absolute, just use that.
475 // If basepath is empty, use CWD as base.
476 FileName const makeAbsPath(string const & relPath, string const & basePath)
478 // checks for already absolute path
479 if (os::is_absolute_path(relPath))
480 return FileName(relPath);
482 // Copies given paths
483 string tempRel = os::internal_path(relPath);
484 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
485 tempRel = subst(tempRel, "//", "/");
489 if (os::is_absolute_path(basePath))
492 tempBase = addPath(getcwd().absFilename(), basePath);
494 // Handle /./ at the end of the path
495 while (suffixIs(tempBase, "/./"))
496 tempBase.erase(tempBase.length() - 2);
498 // processes relative path
499 string rTemp = tempRel;
502 while (!rTemp.empty()) {
504 rTemp = split(rTemp, temp, '/');
506 if (temp == ".") continue;
508 // Remove one level of TempBase
509 string::difference_type i = tempBase.length() - 2;
512 while (i > 0 && tempBase[i] != '/')
515 tempBase.erase(i, string::npos);
518 } else if (temp.empty() && !rTemp.empty()) {
519 tempBase = os::current_root() + rTemp;
522 // Add this piece to TempBase
523 if (!suffixIs(tempBase, '/'))
529 // returns absolute path
530 return FileName(os::internal_path(tempBase));
534 // Correctly append filename to the pathname.
535 // If pathname is '.', then don't use pathname.
536 // Chops any path of filename.
537 string const addName(string const & path, string const & fname)
539 string const basename = onlyFilename(fname);
542 if (path != "." && path != "./" && !path.empty()) {
543 buf = os::internal_path(path);
544 if (!suffixIs(path, '/'))
548 return buf + basename;
552 // Strips path from filename
553 string const onlyFilename(string const & fname)
558 string::size_type j = fname.rfind('/');
559 if (j == string::npos) // no '/' in fname
563 return fname.substr(j + 1);
567 /// Returns true is path is absolute
568 bool absolutePath(string const & path)
570 return os::is_absolute_path(path);
574 // Create absolute path. If impossible, don't do anything
575 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
576 string const expandPath(string const & path)
578 // checks for already absolute path
579 string rTemp = replaceEnvironmentPath(path);
580 if (os::is_absolute_path(rTemp))
584 string const copy = rTemp;
587 rTemp = split(rTemp, temp, '/');
590 return getcwd().absFilename() + '/' + rTemp;
593 return package().home_dir().absFilename() + '/' + rTemp;
596 return makeAbsPath(copy).absFilename();
598 // Don't know how to handle this
603 // Normalize a path. Constracts path/../path
604 // Can't handle "../../" or "/../" (Asger)
605 // Also converts paths like /foo//bar ==> /foo/bar
606 string const normalizePath(string const & path)
608 // Normalize paths like /foo//bar ==> /foo/bar
609 static boost::regex regex("/{2,}");
610 string const tmppath = boost::regex_merge(path, regex, "/");
612 fs::path const npath = fs::path(tmppath, fs::no_check).normalize();
614 if (!npath.is_complete())
615 return "./" + npath.string() + '/';
617 return npath.string() + '/';
621 string const getFileContents(FileName const & fname)
623 string const encodedname = fname.toFilesystemEncoding();
624 if (fs::exists(encodedname)) {
625 ifstream ifs(encodedname.c_str());
633 lyxerr << "LyX was not able to read file '" << fname << '\'' << endl;
638 // Search the string for ${VAR} and $VAR and replace VAR using getenv.
639 string const replaceEnvironmentPath(string const & path)
641 // ${VAR} is defined as
642 // $\{[A-Za-z_][A-Za-z_0-9]*\}
643 static string const envvar_br = "[$]\\{([A-Za-z_][A-Za-z_0-9]*)\\}";
645 // $VAR is defined as:
646 // $\{[A-Za-z_][A-Za-z_0-9]*\}
647 static string const envvar = "[$]([A-Za-z_][A-Za-z_0-9]*)";
649 static boost::regex envvar_br_re("(.*)" + envvar_br + "(.*)");
650 static boost::regex envvar_re("(.*)" + envvar + "(.*)");
653 string result = path;
655 regex_match(result, what, envvar_br_re);
656 if (!what[0].matched) {
657 regex_match(result, what, envvar_re);
658 if (!what[0].matched)
661 result = what.str(1) + getEnv(what.str(2)) + what.str(3);
667 // Make relative path out of two absolute paths
668 docstring const makeRelPath(docstring const & abspath, docstring const & basepath)
669 // Makes relative path out of absolute path. If it is deeper than basepath,
670 // it's easy. If basepath and abspath share something (they are all deeper
671 // than some directory), it'll be rendered using ..'s. If they are completely
672 // different, then the absolute path will be used as relative path.
674 docstring::size_type const abslen = abspath.length();
675 docstring::size_type const baselen = basepath.length();
677 docstring::size_type i = os::common_path(abspath, basepath);
680 // actually no match - cannot make it relative
684 // Count how many dirs there are in basepath above match
685 // and append as many '..''s into relpath
687 docstring::size_type j = i;
688 while (j < baselen) {
689 if (basepath[j] == '/') {
690 if (j + 1 == baselen)
697 // Append relative stuff from common directory to abspath
698 if (abspath[i] == '/')
700 for (; i < abslen; ++i)
703 if (suffixIs(buf, '/'))
704 buf.erase(buf.length() - 1);
705 // Substitute empty with .
712 // Append sub-directory(ies) to a path in an intelligent way
713 string const addPath(string const & path, string const & path_2)
716 string const path2 = os::internal_path(path_2);
718 if (!path.empty() && path != "." && path != "./") {
719 buf = os::internal_path(path);
720 if (path[path.length() - 1] != '/')
724 if (!path2.empty()) {
725 string::size_type const p2start = path2.find_first_not_of('/');
726 string::size_type const p2end = path2.find_last_not_of('/');
727 string const tmp = path2.substr(p2start, p2end - p2start + 1);
734 string const changeExtension(string const & oldname, string const & extension)
736 string::size_type const last_slash = oldname.rfind('/');
737 string::size_type last_dot = oldname.rfind('.');
738 if (last_dot < last_slash && last_slash != string::npos)
739 last_dot = string::npos;
742 // Make sure the extension starts with a dot
743 if (!extension.empty() && extension[0] != '.')
744 ext= '.' + extension;
748 return os::internal_path(oldname.substr(0, last_dot) + ext);
752 string const removeExtension(string const & name)
754 return changeExtension(name, string());
758 string const addExtension(string const & name, string const & extension)
760 if (!extension.empty() && extension[0] != '.')
761 return name + '.' + extension;
762 return name + extension;
766 /// Return the extension of the file (not including the .)
767 string const getExtension(string const & name)
769 string::size_type const last_slash = name.rfind('/');
770 string::size_type const last_dot = name.rfind('.');
771 if (last_dot != string::npos &&
772 (last_slash == string::npos || last_dot > last_slash))
773 return name.substr(last_dot + 1,
774 name.length() - (last_dot + 1));
780 // the different filetypes and what they contain in one of the first lines
781 // (dots are any characters). (Herbert 20020131)
784 // EPS %!PS-Adobe-3.0 EPSF...
791 // PBM P1... or P4 (B/W)
792 // PGM P2... or P5 (Grayscale)
793 // PPM P3... or P6 (color)
794 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
795 // SGI \001\332... (decimal 474)
797 // TIFF II... or MM...
799 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
800 // ...static char *...
801 // XWD \000\000\000\151 (0x00006900) decimal 105
803 // GZIP \037\213 http://www.ietf.org/rfc/rfc1952.txt
804 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
805 // Z \037\235 UNIX compress
807 string const getFormatFromContents(FileName const & filename)
810 if (filename.empty() || !isFileReadable(filename))
813 ifstream ifs(filename.toFilesystemEncoding().c_str());
815 // Couldn't open file...
819 static string const gzipStamp = "\037\213";
822 static string const zipStamp = "PK";
825 static string const compressStamp = "\037\235";
827 // Maximum strings to read
828 int const max_count = 50;
833 bool firstLine = true;
834 while ((count++ < max_count) && format.empty()) {
836 LYXERR(Debug::GRAPHICS)
837 << "filetools(getFormatFromContents)\n"
838 << "\tFile type not recognised before EOF!"
844 string const stamp = str.substr(0, 2);
845 if (firstLine && str.size() >= 2) {
846 // at first we check for a zipped file, because this
847 // information is saved in the first bytes of the file!
848 // also some graphic formats which save the information
849 // in the first line, too.
850 if (prefixIs(str, gzipStamp)) {
853 } else if (stamp == zipStamp) {
856 } else if (stamp == compressStamp) {
860 } else if (stamp == "BM") {
863 } else if (stamp == "\001\332") {
867 // Don't need to use str.at(0), str.at(1) because
868 // we already know that str.size() >= 2
869 } else if (str[0] == 'P') {
885 } else if ((stamp == "II") || (stamp == "MM")) {
888 } else if (prefixIs(str,"%TGIF")) {
891 } else if (prefixIs(str,"#FIG")) {
894 } else if (prefixIs(str,"GIF")) {
897 } else if (str.size() > 3) {
898 int const c = ((str[0] << 24) & (str[1] << 16) &
899 (str[2] << 8) & str[3]);
910 else if (contains(str,"EPSF"))
911 // dummy, if we have wrong file description like
912 // %!PS-Adobe-2.0EPSF"
915 else if (contains(str,"Grace"))
918 else if (contains(str,"JFIF"))
921 else if (contains(str,"%PDF"))
924 else if (contains(str,"PNG"))
927 else if (contains(str,"%!PS-Adobe")) {
930 if (contains(str,"EPSF"))
936 else if (contains(str,"_bits[]"))
939 else if (contains(str,"XPM") || contains(str, "static char *"))
942 else if (contains(str,"BITPIX"))
946 if (!format.empty()) {
947 LYXERR(Debug::GRAPHICS)
948 << "Recognised Fileformat: " << format << endl;
952 LYXERR(Debug::GRAPHICS)
953 << "filetools(getFormatFromContents)\n"
954 << "\tCouldn't find a known format!\n";
959 /// check for zipped file
960 bool zippedFile(FileName const & name)
962 string const type = getFormatFromContents(name);
963 if (contains("gzip zip compress", type) && !type.empty())
969 string const unzippedFileName(string const & zipped_file)
971 string const ext = getExtension(zipped_file);
972 if (ext == "gz" || ext == "z" || ext == "Z")
973 return changeExtension(zipped_file, string());
974 return "unzipped_" + zipped_file;
978 FileName const unzipFile(FileName const & zipped_file, string const & unzipped_file)
980 FileName const tempfile = FileName(unzipped_file.empty() ?
981 unzippedFileName(zipped_file.toFilesystemEncoding()) :
984 string const command = "gunzip -c " +
985 zipped_file.toFilesystemEncoding() + " > " +
986 tempfile.toFilesystemEncoding();
988 one.startscript(Systemcall::Wait, command);
989 // test that command was executed successfully (anon)
990 // yes, please do. (Lgb)
995 docstring const makeDisplayPath(string const & path, unsigned int threshold)
999 // If file is from LyXDir, display it as if it were relative.
1000 string const system = package().system_support().absFilename();
1001 if (prefixIs(str, system) && str != system)
1002 return from_utf8("[" + str.erase(0, system.length()) + "]");
1004 // replace /home/blah with ~/
1005 string const home = package().home_dir().absFilename();
1006 if (!home.empty() && prefixIs(str, home))
1007 str = subst(str, home, "~");
1009 if (str.length() <= threshold)
1010 return from_utf8(os::external_path(str));
1012 string const prefix = ".../";
1015 while (str.length() > threshold)
1016 str = split(str, temp, '/');
1018 // Did we shorten everything away?
1020 // Yes, filename itself is too long.
1021 // Pick the start and the end of the filename.
1022 str = onlyFilename(path);
1023 string const head = str.substr(0, threshold / 2 - 3);
1025 string::size_type len = str.length();
1027 str.substr(len - threshold / 2 - 2, len - 1);
1028 str = head + "..." + tail;
1031 return from_utf8(os::external_path(prefix + str));
1035 bool readLink(FileName const & file, FileName & link)
1037 #ifdef HAVE_READLINK
1038 char linkbuffer[512];
1039 // Should be PATH_MAX but that needs autconf support
1040 string const encoded = file.toFilesystemEncoding();
1041 int const nRead = ::readlink(encoded.c_str(),
1042 linkbuffer, sizeof(linkbuffer) - 1);
1045 linkbuffer[nRead] = '\0'; // terminator
1046 link = makeAbsPath(linkbuffer, onlyPath(file.absFilename()));
1054 cmd_ret const runCommand(string const & cmd)
1056 // FIXME: replace all calls to RunCommand with ForkedCall
1057 // (if the output is not needed) or the code in ISpell.cpp
1058 // (if the output is needed).
1060 // One question is if we should use popen or
1061 // create our own popen based on fork, exec, pipe
1062 // of course the best would be to have a
1063 // pstream (process stream), with the
1064 // variants ipstream, opstream
1066 #if defined (HAVE_POPEN)
1067 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1068 #elif defined (HAVE__POPEN)
1069 FILE * inf = ::_popen(cmd.c_str(), os::popen_read_mode());
1071 #error No popen() function.
1074 // (Claus Hentschel) Check if popen was succesful ;-)
1076 lyxerr << "RunCommand:: could not start child process" << endl;
1077 return make_pair(-1, string());
1083 ret += static_cast<char>(c);
1087 #if defined (HAVE_PCLOSE)
1088 int const pret = pclose(inf);
1089 #elif defined (HAVE__PCLOSE)
1090 int const pret = _pclose(inf);
1092 #error No pclose() function.
1096 perror("RunCommand:: could not terminate child process");
1098 return make_pair(pret, ret);
1102 FileName const findtexfile(string const & fil, string const & /*format*/)
1104 /* There is no problem to extend this function too use other
1105 methods to look for files. It could be setup to look
1106 in environment paths and also if wanted as a last resort
1107 to a recursive find. One of the easier extensions would
1108 perhaps be to use the LyX file lookup methods. But! I am
1109 going to implement this until I see some demand for it.
1113 // If the file can be found directly, we just return a
1114 // absolute path version of it.
1115 FileName const absfile(makeAbsPath(fil));
1116 if (fs::exists(absfile.toFilesystemEncoding()))
1119 // No we try to find it using kpsewhich.
1120 // It seems from the kpsewhich manual page that it is safe to use
1121 // kpsewhich without --format: "When the --format option is not
1122 // given, the search path used when looking for a file is inferred
1123 // from the name given, by looking for a known extension. If no
1124 // known extension is found, the search path for TeX source files
1126 // However, we want to take advantage of the format sine almost all
1127 // the different formats has environment variables that can be used
1128 // to controll which paths to search. f.ex. bib looks in
1129 // BIBINPUTS and TEXBIB. Small list follows:
1130 // bib - BIBINPUTS, TEXBIB
1132 // graphic/figure - TEXPICTS, TEXINPUTS
1133 // ist - TEXINDEXSTYLE, INDEXSTYLE
1134 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1136 // tfm - TFMFONTS, TEXFONTS
1137 // This means that to use kpsewhich in the best possible way we
1138 // should help it by setting additional path in the approp. envir.var.
1139 string const kpsecmd = "kpsewhich " + fil;
1141 cmd_ret const c = runCommand(kpsecmd);
1143 LYXERR(Debug::LATEX) << "kpse status = " << c.first << '\n'
1144 << "kpse result = `" << rtrim(c.second, "\n\r")
1147 return FileName(os::internal_path(rtrim(to_utf8(from_filesystem8bit(c.second)),
1154 void removeAutosaveFile(string const & filename)
1156 string a = onlyPath(filename);
1158 a += onlyFilename(filename);
1160 FileName const autosave(a);
1161 if (fs::exists(autosave.toFilesystemEncoding()))
1166 void readBB_lyxerrMessage(FileName const & file, bool & zipped,
1167 string const & message)
1169 LYXERR(Debug::GRAPHICS) << "[readBB_from_PSFile] "
1170 << message << std::endl;
1171 #ifdef WITH_WARNINGS
1172 #warning Why is this func deleting a file? (Lgb)
1179 string const readBB_from_PSFile(FileName const & file)
1181 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1182 // It seems that every command in the header has an own line,
1183 // getline() should work for all files.
1184 // On the other hand some plot programs write the bb at the
1185 // end of the file. Than we have in the header:
1186 // %%BoundingBox: (atend)
1187 // In this case we must check the end.
1188 bool zipped = zippedFile(file);
1189 FileName const file_ = zipped ? unzipFile(file) : file;
1190 string const format = getFormatFromContents(file_);
1192 if (format != "eps" && format != "ps") {
1193 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1197 static boost::regex bbox_re(
1198 "^%%BoundingBox:\\s*([[:digit:]]+)\\s+([[:digit:]]+)\\s+([[:digit:]]+)\\s+([[:digit:]]+)");
1199 std::ifstream is(file_.toFilesystemEncoding().c_str());
1204 if (regex_match(s, what, bbox_re)) {
1205 // Our callers expect the tokens in the string
1206 // separated by single spaces.
1207 // FIXME: change return type from string to something
1210 os << what.str(1) << ' ' << what.str(2) << ' '
1211 << what.str(3) << ' ' << what.str(4);
1212 string const bb = os.str();
1213 readBB_lyxerrMessage(file_, zipped, bb);
1217 readBB_lyxerrMessage(file_, zipped, "no bb found");
1222 int compare_timestamps(FileName const & filename1, FileName const & filename2)
1224 // If the original is newer than the copy, then copy the original
1225 // to the new directory.
1227 string const file1 = filename1.toFilesystemEncoding();
1228 string const file2 = filename2.toFilesystemEncoding();
1230 if (fs::exists(file1) && fs::exists(file2)) {
1231 double const tmp = difftime(fs::last_write_time(file1),
1232 fs::last_write_time(file2));
1234 cmp = tmp > 0 ? 1 : -1;
1236 } else if (fs::exists(file1)) {
1238 } else if (fs::exists(file2)) {
1245 } //namespace support