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 // ok so we scan through the string twice, but who cares.
121 string const keep = "abcdefghijklmnopqrstuvwxyz"
122 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
123 "@!'()*+,-./0123456789:;<=>?[]`|";
125 string::size_type pos = 0;
126 while ((pos = name.find_first_not_of(keep, pos)) != string::npos)
129 return addName(path, name);
133 string const quoteName(string const & name, quote_style style)
137 // This does not work for filenames containing " (windows)
138 // or ' (all other OSes). This can't be changed easily, since
139 // we would need to adapt the command line parser in
140 // Forkedcall::generateChild. Therefore we don't pass user
141 // filenames to child processes if possible. We store them in
142 // a python script instead, where we don't have these
144 return (os::shell() == os::UNIX) ?
148 return "\"" + subst(subst(name, "\\", "\\\\"), "\"", "\\\"")
151 // shut up stupid compiler
156 bool isFileReadable(FileName const & filename)
158 std::string const path = filename.toFilesystemEncoding();
159 return fs::exists(path) && !fs::is_directory(path) && fs::is_readable(path);
163 //returns true: dir writeable
164 // false: not writeable
165 bool isDirWriteable(FileName const & path)
167 lyxerr[Debug::FILES] << "isDirWriteable: " << path << endl;
169 FileName const tmpfl(tempName(path, "lyxwritetest"));
180 // Uses a string of paths separated by ";"s to find a file to open.
181 // Can't cope with pathnames with a ';' in them. Returns full path to file.
182 // If path entry begins with $$LyX/, use system_lyxdir
183 // If path entry begins with $$User/, use user_lyxdir
184 // Example: "$$User/doc;$$LyX/doc"
185 FileName const fileOpenSearch(string const & path, string const & name,
190 bool notfound = true;
191 string tmppath = split(path, path_element, ';');
193 while (notfound && !path_element.empty()) {
194 path_element = os::internal_path(path_element);
195 if (!suffixIs(path_element, '/'))
197 path_element = subst(path_element, "$$LyX",
198 package().system_support());
199 path_element = subst(path_element, "$$User",
200 package().user_support());
202 real_file = fileSearch(path_element, name, ext);
204 if (real_file.empty()) {
206 tmppath = split(tmppath, path_element, ';');
207 } while (!tmppath.empty() && path_element.empty());
217 /// Returns a vector of all files in directory dir having extension ext.
218 vector<FileName> const dirList(FileName const & dir, string const & ext)
220 // EXCEPTIONS FIXME. Rewrite needed when we turn on exceptions. (Lgb)
221 vector<FileName> dirlist;
223 string const encoded_dir = dir.toFilesystemEncoding();
224 if (!(fs::exists(encoded_dir) && fs::is_directory(encoded_dir))) {
226 << "Directory \"" << dir
227 << "\" does not exist to DirList." << endl;
232 if (!ext.empty() && ext[0] != '.')
236 fs::directory_iterator dit(encoded_dir);
237 fs::directory_iterator end;
238 for (; dit != end; ++dit) {
239 string const & fil = dit->leaf();
240 if (suffixIs(fil, extension))
241 dirlist.push_back(FileName::fromFilesystemEncoding(
242 encoded_dir + '/' + fil));
248 // Returns the real name of file name in directory path, with optional
250 FileName const fileSearch(string const & path, string const & name,
251 string const & ext, search_mode mode)
253 // if `name' is an absolute path, we ignore the setting of `path'
254 // Expand Environmentvariables in 'name'
255 string const tmpname = replaceEnvironmentPath(name);
256 FileName fullname(makeAbsPath(tmpname, path));
257 // search first without extension, then with it.
258 if (isFileReadable(fullname))
262 return mode == allow_unreadable ? fullname : FileName();
263 // Only add the extension if it is not already the extension of
265 if (getExtension(fullname.absFilename()) != ext)
266 fullname = FileName(addExtension(fullname.absFilename(), ext));
267 if (isFileReadable(fullname) || mode == allow_unreadable)
273 // Search the file name.ext in the subdirectory dir of
275 // 2) build_lyxdir (if not empty)
277 FileName const libFileSearch(string const & dir, string const & name,
280 FileName fullname = fileSearch(addPath(package().user_support(), dir),
282 if (!fullname.empty())
285 if (!package().build_support().empty())
286 fullname = fileSearch(addPath(package().build_support(), dir),
288 if (!fullname.empty())
291 return fileSearch(addPath(package().system_support(), dir), name, ext);
295 FileName const i18nLibFileSearch(string const & dir, string const & name,
298 // the following comments are from intl/dcigettext.c. We try
299 // to mimick this behaviour here.
300 /* The highest priority value is the `LANGUAGE' environment
301 variable. But we don't use the value if the currently
302 selected locale is the C locale. This is a GNU extension. */
303 /* [Otherwise] We have to proceed with the POSIX methods of
304 looking to `LC_ALL', `LC_xxx', and `LANG'. */
306 string lang = getEnv("LC_ALL");
308 lang = getEnv("LC_MESSAGES");
310 lang = getEnv("LANG");
316 string const language = getEnv("LANGUAGE");
317 if (lang != "C" && lang != "POSIX" && !language.empty())
321 lang = split(lang, l, ':');
322 while (!l.empty() && l != "C" && l != "POSIX") {
323 FileName const tmp = libFileSearch(addPath(dir, token(l, '_', 0)),
328 // to be removed later (JMarc)
329 FileName const tmpold = libFileSearch(dir,
330 token(l, '_', 0) + '_' + name,
332 if (!tmpold.empty()) {
333 lyxerr << "i18nLibFileSearch: File `" << tmpold
334 << "' has been found by the old method" <<endl;
338 lang = split(lang, l, ':');
341 return libFileSearch(dir, name, ext);
345 string const libScriptSearch(string const & command_in, quote_style style)
347 static string const token_scriptpath = "$$s/";
349 string command = command_in;
350 // Find the starting position of "$$s/"
351 string::size_type const pos1 = command.find(token_scriptpath);
352 if (pos1 == string::npos)
354 // Find the end of the "$$s/some_subdir/some_script" word within
355 // command. Assumes that the script name does not contain spaces.
356 string::size_type const start_script = pos1 + 4;
357 string::size_type const pos2 = command.find(' ', start_script);
358 string::size_type const size_script = pos2 == string::npos?
359 (command.size() - start_script) : pos2 - start_script;
361 // Does this script file exist?
362 string const script =
363 libFileSearch(".", command.substr(start_script, size_script)).absFilename();
365 if (script.empty()) {
366 // Replace "$$s/" with ""
367 command.erase(pos1, 4);
369 // Replace "$$s/foo/some_script" with "<path to>/some_script".
370 string::size_type const size_replace = size_script + 4;
371 command.replace(pos1, size_replace, quoteName(script, style));
380 FileName const createTmpDir(FileName const & tempdir, string const & mask)
383 << "createTmpDir: tempdir=`" << tempdir << "'\n"
384 << "createTmpDir: mask=`" << mask << '\'' << endl;
386 FileName const tmpfl(tempName(tempdir, mask));
387 // lyx::tempName actually creates a file to make sure that it
388 // stays unique. So we have to delete it before we can create
389 // a dir with the same name. Note also that we are not thread
390 // safe because of the gap between unlink and mkdir. (Lgb)
393 if (tmpfl.empty() || mkdir(tmpfl, 0700)) {
394 lyxerr << "LyX could not create the temporary directory '"
395 << tmpfl << "'" << endl;
405 bool destroyDir(FileName const & tmpdir)
408 return fs::remove_all(tmpdir.toFilesystemEncoding()) > 0;
409 } catch (fs::filesystem_error const & fe){
410 lyxerr << "Could not delete " << tmpdir << ". (" << fe.what() << ")" << std::endl;
416 string const createBufferTmpDir()
419 // We are in our own directory. Why bother to mangle name?
420 // In fact I wrote this code to circumvent a problematic behaviour
421 // (bug?) of EMX mkstemp().
423 package().temp_dir() + "/lyx_tmpbuf" +
424 convert<string>(count++);
426 if (mkdir(FileName(tmpfl), 0777)) {
427 lyxerr << "LyX could not create the temporary directory '"
428 << tmpfl << "'" << endl;
435 FileName const createLyXTmpDir(FileName const & deflt)
437 if (!deflt.empty() && deflt.absFilename() != "/tmp") {
438 if (mkdir(deflt, 0777)) {
439 if (isDirWriteable(deflt)) {
440 // deflt could not be created because it
441 // did exist already, so let's create our own
443 return createTmpDir(deflt, "lyx_tmpdir");
445 // some other error occured.
446 return createTmpDir(FileName("/tmp"), "lyx_tmpdir");
451 return createTmpDir(FileName("/tmp"), "lyx_tmpdir");
456 bool createDirectory(string const & path, int permission)
458 string temp = rtrim(os::internal_path(path), "/");
459 BOOST_ASSERT(!temp.empty());
460 return mkdir(FileName(temp), permission) == 0;
464 // Strip filename from path name
465 string const onlyPath(string const & filename)
467 // If empty filename, return empty
468 if (filename.empty())
471 // Find last / or start of filename
472 string::size_type j = filename.rfind('/');
473 return j == string::npos ? "./" : filename.substr(0, j + 1);
477 // Convert relative path into absolute path based on a basepath.
478 // If relpath is absolute, just use that.
479 // If basepath is empty, use CWD as base.
480 FileName const makeAbsPath(string const & relPath, string const & basePath)
482 // checks for already absolute path
483 if (os::is_absolute_path(relPath))
484 return FileName(relPath);
486 // Copies given paths
487 string tempRel = os::internal_path(relPath);
488 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
489 tempRel = subst(tempRel, "//", "/");
493 if (os::is_absolute_path(basePath))
496 tempBase = addPath(getcwd().absFilename(), basePath);
498 // Handle /./ at the end of the path
499 while (suffixIs(tempBase, "/./"))
500 tempBase.erase(tempBase.length() - 2);
502 // processes relative path
503 string rTemp = tempRel;
506 while (!rTemp.empty()) {
508 rTemp = split(rTemp, temp, '/');
510 if (temp == ".") continue;
512 // Remove one level of TempBase
513 string::difference_type i = tempBase.length() - 2;
516 while (i > 0 && tempBase[i] != '/')
519 tempBase.erase(i, string::npos);
522 } else if (temp.empty() && !rTemp.empty()) {
523 tempBase = os::current_root() + rTemp;
526 // Add this piece to TempBase
527 if (!suffixIs(tempBase, '/'))
533 // returns absolute path
534 return FileName(os::internal_path(tempBase));
538 // Correctly append filename to the pathname.
539 // If pathname is '.', then don't use pathname.
540 // Chops any path of filename.
541 string const addName(string const & path, string const & fname)
543 string const basename = onlyFilename(fname);
546 if (path != "." && path != "./" && !path.empty()) {
547 buf = os::internal_path(path);
548 if (!suffixIs(path, '/'))
552 return buf + basename;
556 // Strips path from filename
557 string const onlyFilename(string const & fname)
562 string::size_type j = fname.rfind('/');
563 if (j == string::npos) // no '/' in fname
567 return fname.substr(j + 1);
571 /// Returns true is path is absolute
572 bool absolutePath(string const & path)
574 return os::is_absolute_path(path);
578 // Create absolute path. If impossible, don't do anything
579 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
580 string const expandPath(string const & path)
582 // checks for already absolute path
583 string rTemp = replaceEnvironmentPath(path);
584 if (os::is_absolute_path(rTemp))
588 string const copy = rTemp;
591 rTemp = split(rTemp, temp, '/');
594 return getcwd().absFilename() + '/' + rTemp;
597 return package().home_dir() + '/' + rTemp;
600 return makeAbsPath(copy).absFilename();
602 // Don't know how to handle this
607 // Normalize a path. Constracts path/../path
608 // Can't handle "../../" or "/../" (Asger)
609 // Also converts paths like /foo//bar ==> /foo/bar
610 string const normalizePath(string const & path)
612 // Normalize paths like /foo//bar ==> /foo/bar
613 static boost::regex regex("/{2,}");
614 string const tmppath = boost::regex_merge(path, regex, "/");
616 fs::path const npath = fs::path(tmppath, fs::no_check).normalize();
618 if (!npath.is_complete())
619 return "./" + npath.string() + '/';
621 return npath.string() + '/';
625 string const getFileContents(FileName const & fname)
627 string const encodedname = fname.toFilesystemEncoding();
628 if (fs::exists(encodedname)) {
629 ifstream ifs(encodedname.c_str());
637 lyxerr << "LyX was not able to read file '" << fname << '\'' << endl;
642 // Search the string for ${VAR} and $VAR and replace VAR using getenv.
643 string const replaceEnvironmentPath(string const & path)
645 // ${VAR} is defined as
646 // $\{[A-Za-z_][A-Za-z_0-9]*\}
647 static string const envvar_br = "[$]\\{([A-Za-z_][A-Za-z_0-9]*)\\}";
649 // $VAR is defined as:
650 // $\{[A-Za-z_][A-Za-z_0-9]*\}
651 static string const envvar = "[$]([A-Za-z_][A-Za-z_0-9]*)";
653 static boost::regex envvar_br_re("(.*)" + envvar_br + "(.*)");
654 static boost::regex envvar_re("(.*)" + envvar + "(.*)");
657 string result = path;
659 regex_match(result, what, envvar_br_re);
660 if (!what[0].matched) {
661 regex_match(result, what, envvar_re);
662 if (!what[0].matched)
665 result = what.str(1) + getEnv(what.str(2)) + what.str(3);
671 // Make relative path out of two absolute paths
672 string const makeRelPath(string const & abspath, string const & basepath)
673 // Makes relative path out of absolute path. If it is deeper than basepath,
674 // it's easy. If basepath and abspath share something (they are all deeper
675 // than some directory), it'll be rendered using ..'s. If they are completely
676 // different, then the absolute path will be used as relative path.
678 string::size_type const abslen = abspath.length();
679 string::size_type const baselen = basepath.length();
682 docstring::size_type i =
683 os::common_path(from_utf8(abspath), from_utf8(basepath));
686 // actually no match - cannot make it relative
690 // Count how many dirs there are in basepath above match
691 // and append as many '..''s into relpath
693 string::size_type j = i;
694 while (j < baselen) {
695 if (basepath[j] == '/') {
696 if (j + 1 == baselen)
703 // Append relative stuff from common directory to abspath
704 if (abspath[i] == '/')
706 for (; i < abslen; ++i)
709 if (suffixIs(buf, '/'))
710 buf.erase(buf.length() - 1);
711 // Substitute empty with .
718 // Append sub-directory(ies) to a path in an intelligent way
719 string const addPath(string const & path, string const & path_2)
722 string const path2 = os::internal_path(path_2);
724 if (!path.empty() && path != "." && path != "./") {
725 buf = os::internal_path(path);
726 if (path[path.length() - 1] != '/')
730 if (!path2.empty()) {
731 string::size_type const p2start = path2.find_first_not_of('/');
732 string::size_type const p2end = path2.find_last_not_of('/');
733 string const tmp = path2.substr(p2start, p2end - p2start + 1);
740 string const changeExtension(string const & oldname, string const & extension)
742 string::size_type const last_slash = oldname.rfind('/');
743 string::size_type last_dot = oldname.rfind('.');
744 if (last_dot < last_slash && last_slash != string::npos)
745 last_dot = string::npos;
748 // Make sure the extension starts with a dot
749 if (!extension.empty() && extension[0] != '.')
750 ext= '.' + extension;
754 return os::internal_path(oldname.substr(0, last_dot) + ext);
758 string const removeExtension(string const & name)
760 return changeExtension(name, string());
764 string const addExtension(string const & name, string const & extension)
766 if (!extension.empty() && extension[0] != '.')
767 return name + '.' + extension;
768 return name + extension;
772 /// Return the extension of the file (not including the .)
773 string const getExtension(string const & name)
775 string::size_type const last_slash = name.rfind('/');
776 string::size_type const last_dot = name.rfind('.');
777 if (last_dot != string::npos &&
778 (last_slash == string::npos || last_dot > last_slash))
779 return name.substr(last_dot + 1,
780 name.length() - (last_dot + 1));
786 // the different filetypes and what they contain in one of the first lines
787 // (dots are any characters). (Herbert 20020131)
790 // EPS %!PS-Adobe-3.0 EPSF...
797 // PBM P1... or P4 (B/W)
798 // PGM P2... or P5 (Grayscale)
799 // PPM P3... or P6 (color)
800 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
801 // SGI \001\332... (decimal 474)
803 // TIFF II... or MM...
805 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
806 // ...static char *...
807 // XWD \000\000\000\151 (0x00006900) decimal 105
809 // GZIP \037\213 http://www.ietf.org/rfc/rfc1952.txt
810 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
811 // Z \037\235 UNIX compress
813 string const getFormatFromContents(FileName const & filename)
816 if (filename.empty() || !isFileReadable(filename))
819 ifstream ifs(filename.toFilesystemEncoding().c_str());
821 // Couldn't open file...
825 static string const gzipStamp = "\037\213";
828 static string const zipStamp = "PK";
831 static string const compressStamp = "\037\235";
833 // Maximum strings to read
834 int const max_count = 50;
839 bool firstLine = true;
840 while ((count++ < max_count) && format.empty()) {
842 lyxerr[Debug::GRAPHICS]
843 << "filetools(getFormatFromContents)\n"
844 << "\tFile type not recognised before EOF!"
850 string const stamp = str.substr(0, 2);
851 if (firstLine && str.size() >= 2) {
852 // at first we check for a zipped file, because this
853 // information is saved in the first bytes of the file!
854 // also some graphic formats which save the information
855 // in the first line, too.
856 if (prefixIs(str, gzipStamp)) {
859 } else if (stamp == zipStamp) {
862 } else if (stamp == compressStamp) {
866 } else if (stamp == "BM") {
869 } else if (stamp == "\001\332") {
873 // Don't need to use str.at(0), str.at(1) because
874 // we already know that str.size() >= 2
875 } else if (str[0] == 'P') {
891 } else if ((stamp == "II") || (stamp == "MM")) {
894 } else if (prefixIs(str,"%TGIF")) {
897 } else if (prefixIs(str,"#FIG")) {
900 } else if (prefixIs(str,"GIF")) {
903 } else if (str.size() > 3) {
904 int const c = ((str[0] << 24) & (str[1] << 16) &
905 (str[2] << 8) & str[3]);
916 else if (contains(str,"EPSF"))
917 // dummy, if we have wrong file description like
918 // %!PS-Adobe-2.0EPSF"
921 else if (contains(str,"Grace"))
924 else if (contains(str,"JFIF"))
927 else if (contains(str,"%PDF"))
930 else if (contains(str,"PNG"))
933 else if (contains(str,"%!PS-Adobe")) {
936 if (contains(str,"EPSF"))
942 else if (contains(str,"_bits[]"))
945 else if (contains(str,"XPM") || contains(str, "static char *"))
948 else if (contains(str,"BITPIX"))
952 if (!format.empty()) {
953 lyxerr[Debug::GRAPHICS]
954 << "Recognised Fileformat: " << format << endl;
958 lyxerr[Debug::GRAPHICS]
959 << "filetools(getFormatFromContents)\n"
960 << "\tCouldn't find a known format!\n";
965 /// check for zipped file
966 bool zippedFile(FileName const & name)
968 string const type = getFormatFromContents(name);
969 if (contains("gzip zip compress", type) && !type.empty())
975 string const unzippedFileName(string const & zipped_file)
977 string const ext = getExtension(zipped_file);
978 if (ext == "gz" || ext == "z" || ext == "Z")
979 return changeExtension(zipped_file, string());
980 return "unzipped_" + zipped_file;
984 FileName const unzipFile(FileName const & zipped_file, string const & unzipped_file)
986 FileName const tempfile = FileName(unzipped_file.empty() ?
987 unzippedFileName(zipped_file.toFilesystemEncoding()) :
990 string const command = "gunzip -c " +
991 zipped_file.toFilesystemEncoding() + " > " +
992 tempfile.toFilesystemEncoding();
994 one.startscript(Systemcall::Wait, command);
995 // test that command was executed successfully (anon)
996 // yes, please do. (Lgb)
1001 docstring const makeDisplayPath(string const & path, unsigned int threshold)
1005 // If file is from LyXDir, display it as if it were relative.
1006 string const system = package().system_support();
1007 if (prefixIs(str, system) && str != system)
1008 return from_utf8("[" + str.erase(0, system.length()) + "]");
1010 // replace /home/blah with ~/
1011 string const home = package().home_dir();
1012 if (!home.empty() && prefixIs(str, home))
1013 str = subst(str, home, "~");
1015 if (str.length() <= threshold)
1016 return from_utf8(os::external_path(str));
1018 string const prefix = ".../";
1021 while (str.length() > threshold)
1022 str = split(str, temp, '/');
1024 // Did we shorten everything away?
1026 // Yes, filename itself is too long.
1027 // Pick the start and the end of the filename.
1028 str = onlyFilename(path);
1029 string const head = str.substr(0, threshold / 2 - 3);
1031 string::size_type len = str.length();
1033 str.substr(len - threshold / 2 - 2, len - 1);
1034 str = head + "..." + tail;
1037 return from_utf8(os::external_path(prefix + str));
1041 bool readLink(string const & file, string & link, bool resolve)
1043 #ifdef HAVE_READLINK
1044 char linkbuffer[512];
1045 // Should be PATH_MAX but that needs autconf support
1046 int const nRead = ::readlink(file.c_str(),
1047 linkbuffer, sizeof(linkbuffer) - 1);
1050 linkbuffer[nRead] = '\0'; // terminator
1052 link = makeAbsPath(linkbuffer, onlyPath(file)).absFilename();
1062 cmd_ret const runCommand(string const & cmd)
1064 // FIXME: replace all calls to RunCommand with ForkedCall
1065 // (if the output is not needed) or the code in ispell.C
1066 // (if the output is needed).
1068 // One question is if we should use popen or
1069 // create our own popen based on fork, exec, pipe
1070 // of course the best would be to have a
1071 // pstream (process stream), with the
1072 // variants ipstream, opstream
1074 #if defined (HAVE_POPEN)
1075 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1076 #elif defined (HAVE__POPEN)
1077 FILE * inf = ::_popen(cmd.c_str(), os::popen_read_mode());
1079 #error No popen() function.
1082 // (Claus Hentschel) Check if popen was succesful ;-)
1084 lyxerr << "RunCommand:: could not start child process" << endl;
1085 return make_pair(-1, string());
1091 ret += static_cast<char>(c);
1095 #if defined (HAVE_PCLOSE)
1096 int const pret = pclose(inf);
1097 #elif defined (HAVE__PCLOSE)
1098 int const pret = _pclose(inf);
1100 #error No pclose() function.
1104 perror("RunCommand:: could not terminate child process");
1106 return make_pair(pret, ret);
1110 FileName const findtexfile(string const & fil, string const & /*format*/)
1112 /* There is no problem to extend this function too use other
1113 methods to look for files. It could be setup to look
1114 in environment paths and also if wanted as a last resort
1115 to a recursive find. One of the easier extensions would
1116 perhaps be to use the LyX file lookup methods. But! I am
1117 going to implement this until I see some demand for it.
1121 // If the file can be found directly, we just return a
1122 // absolute path version of it.
1123 FileName const absfile(makeAbsPath(fil));
1124 if (fs::exists(absfile.toFilesystemEncoding()))
1127 // No we try to find it using kpsewhich.
1128 // It seems from the kpsewhich manual page that it is safe to use
1129 // kpsewhich without --format: "When the --format option is not
1130 // given, the search path used when looking for a file is inferred
1131 // from the name given, by looking for a known extension. If no
1132 // known extension is found, the search path for TeX source files
1134 // However, we want to take advantage of the format sine almost all
1135 // the different formats has environment variables that can be used
1136 // to controll which paths to search. f.ex. bib looks in
1137 // BIBINPUTS and TEXBIB. Small list follows:
1138 // bib - BIBINPUTS, TEXBIB
1140 // graphic/figure - TEXPICTS, TEXINPUTS
1141 // ist - TEXINDEXSTYLE, INDEXSTYLE
1142 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1144 // tfm - TFMFONTS, TEXFONTS
1145 // This means that to use kpsewhich in the best possible way we
1146 // should help it by setting additional path in the approp. envir.var.
1147 string const kpsecmd = "kpsewhich " + fil;
1149 cmd_ret const c = runCommand(kpsecmd);
1151 lyxerr[Debug::LATEX] << "kpse status = " << c.first << '\n'
1152 << "kpse result = `" << rtrim(c.second, "\n\r")
1155 return FileName(os::internal_path(rtrim(to_utf8(from_filesystem8bit(c.second)),
1162 void removeAutosaveFile(string const & filename)
1164 string a = onlyPath(filename);
1166 a += onlyFilename(filename);
1168 FileName const autosave(a);
1169 if (fs::exists(autosave.toFilesystemEncoding()))
1174 void readBB_lyxerrMessage(FileName const & file, bool & zipped,
1175 string const & message)
1177 lyxerr[Debug::GRAPHICS] << "[readBB_from_PSFile] "
1178 << message << std::endl;
1179 #ifdef WITH_WARNINGS
1180 #warning Why is this func deleting a file? (Lgb)
1187 string const readBB_from_PSFile(FileName const & file)
1189 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1190 // It seems that every command in the header has an own line,
1191 // getline() should work for all files.
1192 // On the other hand some plot programs write the bb at the
1193 // end of the file. Than we have in the header:
1194 // %%BoundingBox: (atend)
1195 // In this case we must check the end.
1196 bool zipped = zippedFile(file);
1197 FileName const file_ = zipped ? unzipFile(file) : file;
1198 string const format = getFormatFromContents(file_);
1200 if (format != "eps" && format != "ps") {
1201 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1205 static boost::regex bbox_re(
1206 "^%%BoundingBox:\\s*([[:digit:]]+)\\s+([[:digit:]]+)\\s+([[:digit:]]+)\\s+([[:digit:]]+)");
1207 std::ifstream is(file_.toFilesystemEncoding().c_str());
1212 if (regex_match(s, what, bbox_re)) {
1213 // Our callers expect the tokens in the string
1214 // separated by single spaces.
1215 // FIXME: change return type from string to something
1218 os << what.str(1) << ' ' << what.str(2) << ' '
1219 << what.str(3) << ' ' << what.str(4);
1220 string const bb = os.str();
1221 readBB_lyxerrMessage(file_, zipped, bb);
1225 readBB_lyxerrMessage(file_, zipped, "no bb found");
1230 int compare_timestamps(FileName const & filename1, FileName const & filename2)
1232 // If the original is newer than the copy, then copy the original
1233 // to the new directory.
1235 string const file1 = filename1.toFilesystemEncoding();
1236 string const file2 = filename2.toFilesystemEncoding();
1238 if (fs::exists(file1) && fs::exists(file2)) {
1239 double const tmp = difftime(fs::last_write_time(file1),
1240 fs::last_write_time(file2));
1242 cmp = tmp > 0 ? 1 : -1;
1244 } else if (fs::exists(file1)) {
1246 } else if (fs::exists(file2)) {
1253 } //namespace support