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
25 #include "support/tostr.h"
26 #include "support/systemcall.h"
28 #include "filetools.h"
32 #include "path_defines.h"
37 #include "support/std_sstream.h"
39 #include <boost/assert.hpp>
40 #include <boost/regex.hpp>
52 // Which part of this is still necessary? (JMarc).
55 # define NAMLEN(dirent) strlen((dirent)->d_name)
57 # define dirent direct
58 # define NAMLEN(dirent) (dirent)->d_namlen
60 # include <sys/ndir.h>
70 #ifndef CXX_GLOBAL_CSTD
81 using std::ostringstream;
88 bool IsLyXFilename(string const & filename)
90 return suffixIs(ascii_lowercase(filename), ".lyx");
94 bool IsSGMLFilename(string const & filename)
96 return suffixIs(ascii_lowercase(filename), ".sgml");
100 // Substitutes spaces with underscores in filename (and path)
101 string const MakeLatexName(string const & file)
103 string name = OnlyFilename(file);
104 string const path = OnlyPath(file);
106 for (string::size_type i = 0; i < name.length(); ++i) {
107 name[i] &= 0x7f; // set 8th bit to 0
110 // ok so we scan through the string twice, but who cares.
111 string const keep("abcdefghijklmnopqrstuvwxyz"
112 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
113 "@!\"'()*+,-./0123456789:;<=>?[]`|");
115 string::size_type pos = 0;
116 while ((pos = name.find_first_not_of(keep, pos)) != string::npos) {
119 return AddName(path, name);
123 // Substitutes spaces with underscores in filename (and path)
124 string const QuoteName(string const & name)
126 return (os::shell() == os::UNIX) ?
132 // Is a file readable ?
133 bool IsFileReadable(string const & path)
136 return file.isOK() && file.isRegular() && file.readable();
140 // Is a file read_only?
141 // return 1 read-write
143 // -1 error (doesn't exist, no access, anything else)
144 int IsFileWriteable(string const & path)
148 if (fi.access(FileInfo::wperm|FileInfo::rperm)) // read-write
150 if (fi.readable()) // read-only
152 return -1; // everything else.
156 //returns true: dir writeable
157 // false: not writeable
158 bool IsDirWriteable(string const & path)
160 lyxerr[Debug::FILES] << "IsDirWriteable: " << path << endl;
162 string const tmpfl(tempName(path, "lyxwritetest"));
172 // Uses a string of paths separated by ";"s to find a file to open.
173 // Can't cope with pathnames with a ';' in them. Returns full path to file.
174 // If path entry begins with $$LyX/, use system_lyxdir
175 // If path entry begins with $$User/, use user_lyxdir
176 // Example: "$$User/doc;$$LyX/doc"
177 string const FileOpenSearch(string const & path, string const & name,
182 bool notfound = true;
183 string tmppath = split(path, path_element, ';');
185 while (notfound && !path_element.empty()) {
186 path_element = os::slashify_path(path_element);
187 if (!suffixIs(path_element, '/'))
189 path_element = subst(path_element, "$$LyX", system_lyxdir());
190 path_element = subst(path_element, "$$User", user_lyxdir());
192 real_file = FileSearch(path_element, name, ext);
194 if (real_file.empty()) {
196 tmppath = split(tmppath, path_element, ';');
197 } while (!tmppath.empty() && path_element.empty());
203 if (ext.empty() && notfound) {
204 real_file = FileOpenSearch(path, name, "exe");
205 if (notfound) real_file = FileOpenSearch(path, name, "cmd");
212 /// Returns a vector of all files in directory dir having extension ext.
213 vector<string> const DirList(string const & dir, string const & ext)
215 // This is a non-error checking C/system implementation
217 if (!ext.empty() && ext[0] != '.')
221 vector<string> dirlist;
222 DIR * dirp = ::opendir(dir.c_str());
225 << "Directory \"" << dir
226 << "\" does not exist to DirList." << endl;
231 while ((dire = ::readdir(dirp))) {
232 string const fil = dire->d_name;
233 if (suffixIs(fil, extension)) {
234 dirlist.push_back(fil);
239 /* I would have prefered to take a vector<string>& as parameter so
240 that we could avoid the copy of the vector when returning.
242 dirlist.swap(argvec);
243 to avoid the copy. (Lgb)
245 /* A C++ implementaion will look like this:
246 string extension(ext);
247 if (extension[0] != '.') extension.insert(0, 1, '.');
248 vector<string> dirlist;
249 directory_iterator dit("dir");
250 while (dit != directory_iterator()) {
251 string fil = dit->filename;
252 if (prefixIs(fil, extension)) {
253 dirlist.push_back(fil);
257 dirlist.swap(argvec);
263 // Returns the real name of file name in directory path, with optional
265 string const FileSearch(string const & path, string const & name,
268 // if `name' is an absolute path, we ignore the setting of `path'
269 // Expand Environmentvariables in 'name'
270 string const tmpname = ReplaceEnvironmentPath(name);
271 string fullname = MakeAbsPath(tmpname, path);
272 // search first without extension, then with it.
273 if (IsFileReadable(fullname))
275 else if (ext.empty())
277 else { // Is it not more reasonable to use ChangeExtension()? (SMiyata)
280 if (IsFileReadable(fullname))
288 // Search the file name.ext in the subdirectory dir of
290 // 2) build_lyxdir (if not empty)
292 string const LibFileSearch(string const & dir, string const & name,
295 string fullname = FileSearch(AddPath(user_lyxdir(), dir), name, ext);
296 if (!fullname.empty())
299 if (!build_lyxdir().empty())
300 fullname = FileSearch(AddPath(build_lyxdir(), dir), name, ext);
301 if (!fullname.empty())
304 return FileSearch(AddPath(system_lyxdir(), dir), name, ext);
309 i18nLibFileSearch(string const & dir, string const & name,
312 // this comment is from intl/dcigettext.c. We try to mimick this
314 /* The highest priority value is the `LANGUAGE' environment
315 variable. But we don't use the value if the currently
316 selected locale is the C locale. This is a GNU extension. */
318 string const lc_all = GetEnv("LC_ALL");
319 string lang = GetEnv("LANGUAGE");
320 if (lang.empty() || lc_all == "C") {
323 lang = GetEnv("LANG");
327 lang = token(lang, '_', 0);
329 if (lang.empty() || lang == "C")
330 return LibFileSearch(dir, name, ext);
332 string const tmp = LibFileSearch(dir, lang + '_' + name,
337 return LibFileSearch(dir, name, ext);
342 string const LibScriptSearch(string const & command_in)
344 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_script" word within command
352 string::size_type const start_script = pos1 + 4;
353 string::size_type const pos2 = command.find(' ', start_script);
354 string::size_type const size_script = pos2 == string::npos?
355 (command.size() - start_script) : pos2 - start_script;
357 // Does this script file exist?
358 string const script =
359 LibFileSearch("scripts", command.substr(start_script, size_script));
361 if (script.empty()) {
362 // Replace "$$s/" with ""
363 command.erase(pos1, 4);
365 // Replace "$$s/some_script" with "$LYX_SCRIPT_PATH/some_script"
366 string::size_type const size_replace = size_script + 4;
367 command.replace(pos1, size_replace, script);
374 string const GetEnv(string const & envname)
376 // f.ex. what about error checking?
377 char const * const ch = getenv(envname.c_str());
378 string const envstr = !ch ? "" : ch;
383 string const GetEnvPath(string const & name)
386 string const pathlist = subst(GetEnv(name), ':', ';');
388 string const pathlist = os::slashify_path(GetEnv(name));
390 return rtrim(pathlist, ";");
396 int DeleteAllFilesInDir(string const & path)
398 // I have decided that we will be using parts from the boost
399 // library. Check out http://www.boost.org/
400 // For directory access we will then use the directory_iterator.
401 // Then the code will be something like:
402 // directory_iterator dit(path);
403 // directory_iterator dend;
404 // if (dit == dend) {
407 // for (; dit != dend; ++dit) {
408 // string filename(*dit);
409 // if (filename == "." || filename == "..")
411 // string unlinkpath(AddName(path, filename));
412 // lyx::unlink(unlinkpath);
415 DIR * dir = ::opendir(path.c_str());
420 int return_value = 0;
421 while ((de = readdir(dir))) {
422 string const temp = de->d_name;
423 if (temp == "." || temp == "..")
425 string const unlinkpath = AddName (path, temp);
427 lyxerr[Debug::FILES] << "Deleting file: " << unlinkpath
431 FileInfo fi(unlinkpath);
432 if (fi.isOK() && fi.isDir())
433 deleted = (DeleteAllFilesInDir(unlinkpath) == 0);
434 deleted &= (unlink(unlinkpath) == 0);
443 string const createTmpDir(string const & tempdir, string const & mask)
446 << "createTmpDir: tempdir=`" << tempdir << "'\n"
447 << "createTmpDir: mask=`" << mask << '\'' << endl;
449 string const tmpfl(tempName(tempdir, mask));
450 // lyx::tempName actually creates a file to make sure that it
451 // stays unique. So we have to delete it before we can create
452 // a dir with the same name. Note also that we are not thread
453 // safe because of the gap between unlink and mkdir. (Lgb)
456 if (tmpfl.empty() || mkdir(tmpfl, 0700)) {
457 lyxerr << "LyX could not create the temporary directory '"
458 << tmpfl << "'" << endl;
462 return MakeAbsPath(tmpfl);
468 int destroyDir(string const & tmpdir)
471 Path p(user_lyxdir());
473 if (DeleteAllFilesInDir(tmpdir))
483 string const createBufferTmpDir()
486 // We are in our own directory. Why bother to mangle name?
487 // In fact I wrote this code to circumvent a problematic behaviour (bug?)
489 string const tmpfl = os::getTmpDir() + "/lyx_tmpbuf" + tostr(count++);
490 if (mkdir(tmpfl, 0777)) {
491 lyxerr << "LyX could not create the temporary directory '"
492 << tmpfl << "'" << endl;
499 string const createLyXTmpDir(string const & deflt)
501 if (!deflt.empty() && deflt != "/tmp") {
502 if (mkdir(deflt, 0777)) {
503 if (IsDirWriteable(deflt))
504 // deflt could not be created because it
505 // did exist already, so let's create our own
508 Path p(user_lyxdir());
510 return createTmpDir(deflt, "lyx_tmpdir");
512 // some other error occured.
514 Path p(user_lyxdir());
516 return createTmpDir("/tmp", "lyx_tmpdir");
521 Path p(user_lyxdir());
523 return createTmpDir("/tmp", "lyx_tmpdir");
528 bool createDirectory(string const & path, int permission)
530 string temp(rtrim(os::slashify_path(path), "/"));
532 BOOST_ASSERT(!temp.empty());
534 if (mkdir(temp, permission))
541 // Strip filename from path name
542 string const OnlyPath(string const & Filename)
544 // If empty filename, return empty
545 if (Filename.empty()) return Filename;
547 // Find last / or start of filename
548 string::size_type j = Filename.rfind('/');
549 if (j == string::npos)
551 return Filename.substr(0, j + 1);
555 // Convert relative path into absolute path based on a basepath.
556 // If relpath is absolute, just use that.
557 // If basepath is empty, use CWD as base.
558 string const MakeAbsPath(string const & RelPath, string const & BasePath)
560 // checks for already absolute path
561 if (os::is_absolute_path(RelPath))
564 // Copies given paths
565 string TempRel(os::slashify_path(RelPath));
566 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
567 TempRel = subst(TempRel, "//", "/");
571 if (os::is_absolute_path(BasePath))
574 TempBase = AddPath(getcwd(), BasePath);
576 // Handle /./ at the end of the path
577 while (suffixIs(TempBase, "/./"))
578 TempBase.erase(TempBase.length() - 2);
580 // processes relative path
581 string RTemp(TempRel);
584 while (!RTemp.empty()) {
586 RTemp = split(RTemp, Temp, '/');
588 if (Temp == ".") continue;
590 // Remove one level of TempBase
591 string::difference_type i = TempBase.length() - 2;
594 while (i > 0 && TempBase[i] != '/') --i;
598 while (i > 2 && TempBase[i] != '/') --i;
601 TempBase.erase(i, string::npos);
604 } else if (Temp.empty() && !RTemp.empty()) {
605 TempBase = os::current_root() + RTemp;
608 // Add this piece to TempBase
609 if (!suffixIs(TempBase, '/'))
615 // returns absolute path
616 return os::slashify_path(TempBase);
620 // Correctly append filename to the pathname.
621 // If pathname is '.', then don't use pathname.
622 // Chops any path of filename.
623 string const AddName(string const & path, string const & fname)
626 string const basename(OnlyFilename(fname));
630 if (path != "." && path != "./" && !path.empty()) {
631 buf = os::slashify_path(path);
632 if (!suffixIs(path, '/'))
636 return buf + basename;
640 // Strips path from filename
641 string const OnlyFilename(string const & fname)
646 string::size_type j = fname.rfind('/');
647 if (j == string::npos) // no '/' in fname
651 return fname.substr(j + 1);
655 /// Returns true is path is absolute
656 bool AbsolutePath(string const & path)
658 return os::is_absolute_path(path);
663 // Create absolute path. If impossible, don't do anything
664 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
665 string const ExpandPath(string const & path)
667 // checks for already absolute path
668 string RTemp(ReplaceEnvironmentPath(path));
669 if (os::is_absolute_path(RTemp))
673 string const copy(RTemp);
676 RTemp = split(RTemp, Temp, '/');
679 return getcwd() + '/' + RTemp;
682 return GetEnvPath("HOME") + '/' + RTemp;
685 return MakeAbsPath(copy);
687 // Don't know how to handle this
693 // Constracts path/../path
694 // Can't handle "../../" or "/../" (Asger)
695 // Also converts paths like /foo//bar ==> /foo/bar
696 string const NormalizePath(string const & path)
702 if (os::is_absolute_path(path))
705 // Make implicit current directory explicit
708 // Normalise paths like /foo//bar ==> /foo/bar
709 boost::RegEx regex("/{2,}");
710 RTemp = regex.Merge(RTemp, "/");
712 while (!RTemp.empty()) {
714 RTemp = split(RTemp, Temp, '/');
718 } else if (Temp == "..") {
719 // Remove one level of TempBase
720 string::difference_type i = TempBase.length() - 2;
721 while (i > 0 && TempBase[i] != '/')
723 if (i >= 0 && TempBase[i] == '/')
724 TempBase.erase(i + 1, string::npos);
728 TempBase += Temp + '/';
732 // returns absolute path
737 string const GetFileContents(string const & fname)
739 FileInfo finfo(fname);
741 ifstream ifs(fname.c_str());
749 lyxerr << "LyX was not able to read file '" << fname << '\'' << endl;
754 // Search the string for ${VAR} and $VAR and replace VAR using getenv.
755 string const ReplaceEnvironmentPath(string const & path)
757 // ${VAR} is defined as
758 // $\{[A-Za-z_][A-Za-z_0-9]*\}
759 static string const envvar_br = "[$]\\{([A-Za-z_][A-Za-z_0-9]*)\\}";
761 // $VAR is defined as:
762 // $\{[A-Za-z_][A-Za-z_0-9]*\}
763 static string const envvar = "[$]([A-Za-z_][A-Za-z_0-9]*)";
765 static boost::regex envvar_br_re("(.*)" + envvar_br + "(.*)");
766 static boost::regex envvar_re("(.*)" + envvar + "(.*)");
769 string result = path;
771 regex_match(result, what, envvar_br_re);
772 if (!what[0].matched) {
773 regex_match(result, what, envvar_re);
774 if (!what[0].matched)
777 result = what.str(1) + GetEnv(what.str(2)) + what.str(3);
783 // Make relative path out of two absolute paths
784 string const MakeRelPath(string const & abspath, string const & basepath)
785 // Makes relative path out of absolute path. If it is deeper than basepath,
786 // it's easy. If basepath and abspath share something (they are all deeper
787 // than some directory), it'll be rendered using ..'s. If they are completely
788 // different, then the absolute path will be used as relative path.
790 string::size_type const abslen = abspath.length();
791 string::size_type const baselen = basepath.length();
793 string::size_type i = os::common_path(abspath, basepath);
796 // actually no match - cannot make it relative
800 // Count how many dirs there are in basepath above match
801 // and append as many '..''s into relpath
803 string::size_type j = i;
804 while (j < baselen) {
805 if (basepath[j] == '/') {
806 if (j + 1 == baselen)
813 // Append relative stuff from common directory to abspath
814 if (abspath[i] == '/')
816 for (; i < abslen; ++i)
819 if (suffixIs(buf, '/'))
820 buf.erase(buf.length() - 1);
821 // Substitute empty with .
828 // Append sub-directory(ies) to a path in an intelligent way
829 string const AddPath(string const & path, string const & path_2)
832 string const path2 = os::slashify_path(path_2);
834 if (!path.empty() && path != "." && path != "./") {
835 buf = os::slashify_path(path);
836 if (path[path.length() - 1] != '/')
840 if (!path2.empty()) {
841 string::size_type const p2start = path2.find_first_not_of('/');
842 string::size_type const p2end = path2.find_last_not_of('/');
843 string const tmp = path2.substr(p2start, p2end - p2start + 1);
851 Change extension of oldname to extension.
852 Strips path off if no_path == true.
853 If no extension on oldname, just appends.
855 string const ChangeExtension(string const & oldname, string const & extension)
857 string::size_type const last_slash = oldname.rfind('/');
858 string::size_type last_dot = oldname.rfind('.');
859 if (last_dot < last_slash && last_slash != string::npos)
860 last_dot = string::npos;
863 // Make sure the extension starts with a dot
864 if (!extension.empty() && extension[0] != '.')
865 ext= '.' + extension;
869 return os::slashify_path(oldname.substr(0, last_dot) + ext);
873 /// Return the extension of the file (not including the .)
874 string const GetExtension(string const & name)
876 string::size_type const last_slash = name.rfind('/');
877 string::size_type const last_dot = name.rfind('.');
878 if (last_dot != string::npos &&
879 (last_slash == string::npos || last_dot > last_slash))
880 return name.substr(last_dot + 1,
881 name.length() - (last_dot + 1));
886 // the different filetypes and what they contain in one of the first lines
887 // (dots are any characters). (Herbert 20020131)
890 // EPS %!PS-Adobe-3.0 EPSF...
897 // PBM P1... or P4 (B/W)
898 // PGM P2... or P5 (Grayscale)
899 // PPM P3... or P6 (color)
900 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
901 // SGI \001\332... (decimal 474)
903 // TIFF II... or MM...
905 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
906 // ...static char *...
907 // XWD \000\000\000\151 (0x00006900) decimal 105
909 // GZIP \037\213 http://www.ietf.org/rfc/rfc1952.txt
910 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
911 // Z \037\235 UNIX compress
913 /// return the "extension" which belongs to the contents.
914 /// for no knowing contents return the extension. Without
915 /// an extension and unknown contents we return "user"
916 string const getExtFromContents(string const & filename)
919 if (filename.empty() || !IsFileReadable(filename))
923 ifstream ifs(filename.c_str());
925 // Couldn't open file...
929 string const gzipStamp = "\037\213";
932 string const zipStamp = "PK";
935 string const compressStamp = "\037\235";
937 // Maximum strings to read
938 int const max_count = 50;
942 bool firstLine = true;
943 while ((count++ < max_count) && format.empty()) {
945 lyxerr[Debug::GRAPHICS]
946 << "filetools(getExtFromContents)\n"
947 << "\tFile type not recognised before EOF!"
953 string const stamp = str.substr(0,2);
954 if (firstLine && str.size() >= 2) {
955 // at first we check for a zipped file, because this
956 // information is saved in the first bytes of the file!
957 // also some graphic formats which save the information
958 // in the first line, too.
959 if (prefixIs(str, gzipStamp)) {
962 } else if (stamp == zipStamp) {
965 } else if (stamp == compressStamp) {
969 } else if (stamp == "BM") {
972 } else if (stamp == "\001\332") {
976 // Don't need to use str.at(0), str.at(1) because
977 // we already know that str.size() >= 2
978 } else if (str[0] == 'P') {
994 } else if ((stamp == "II") || (stamp == "MM")) {
997 } else if (prefixIs(str,"%TGIF")) {
1000 } else if (prefixIs(str,"#FIG")) {
1003 } else if (prefixIs(str,"GIF")) {
1006 } else if (str.size() > 3) {
1007 int const c = ((str[0] << 24) & (str[1] << 16) &
1008 (str[2] << 8) & str[3]);
1017 if (!format.empty())
1019 else if (contains(str,"EPSF"))
1020 // dummy, if we have wrong file description like
1021 // %!PS-Adobe-2.0EPSF"
1024 else if (contains(str,"Grace"))
1027 else if (contains(str,"JFIF"))
1030 else if (contains(str,"%PDF"))
1033 else if (contains(str,"PNG"))
1036 else if (contains(str,"%!PS-Adobe")) {
1039 if (contains(str,"EPSF"))
1045 else if (contains(str,"_bits[]"))
1048 else if (contains(str,"XPM") || contains(str, "static char *"))
1051 else if (contains(str,"BITPIX"))
1055 if (!format.empty()) {
1056 lyxerr[Debug::GRAPHICS]
1057 << "Recognised Fileformat: " << format << endl;
1061 string const ext(GetExtension(filename));
1062 lyxerr[Debug::GRAPHICS]
1063 << "filetools(getExtFromContents)\n"
1064 << "\tCouldn't find a known Type!\n";
1066 lyxerr[Debug::GRAPHICS]
1067 << "\twill take the file extension -> "
1071 lyxerr[Debug::GRAPHICS]
1072 << "\twill use ext or a \"user\" defined format" << endl;
1078 /// check for zipped file
1079 bool zippedFile(string const & name)
1081 string const type = getExtFromContents(name);
1082 if (contains("gzip zip compress", type) && !type.empty())
1088 string const unzippedFileName(string const & zipped_file)
1090 string const ext = GetExtension(zipped_file);
1091 if (ext == "gz" || ext == "z" || ext == "Z")
1092 return ChangeExtension(zipped_file, string());
1093 return "unzipped_" + zipped_file;
1097 string const unzipFile(string const & zipped_file, string const & unzipped_file)
1099 string const tempfile = unzipped_file.empty() ?
1100 unzippedFileName(zipped_file) : unzipped_file;
1102 string const command = "gunzip -c " + zipped_file + " > " + tempfile;
1104 one.startscript(Systemcall::Wait, command);
1105 // test that command was executed successfully (anon)
1106 // yes, please do. (Lgb)
1111 string const MakeDisplayPath(string const & path, unsigned int threshold)
1115 string const home(GetEnvPath("HOME"));
1117 // replace /home/blah with ~/
1118 if (prefixIs(str, home))
1119 str = subst(str, home, "~");
1121 if (str.length() <= threshold)
1124 string const prefix = ".../";
1127 while (str.length() > threshold)
1128 str = split(str, temp, '/');
1130 // Did we shorten everything away?
1132 // Yes, filename itself is too long.
1133 // Pick the start and the end of the filename.
1134 str = OnlyFilename(path);
1135 string const head = str.substr(0, threshold / 2 - 3);
1137 string::size_type len = str.length();
1139 str.substr(len - threshold / 2 - 2, len - 1);
1140 str = head + "..." + tail;
1143 return prefix + str;
1147 bool LyXReadLink(string const & file, string & link, bool resolve)
1149 char linkbuffer[512];
1150 // Should be PATH_MAX but that needs autconf support
1151 int const nRead = ::readlink(file.c_str(),
1152 linkbuffer, sizeof(linkbuffer) - 1);
1155 linkbuffer[nRead] = '\0'; // terminator
1157 link = MakeAbsPath(linkbuffer, OnlyPath(file));
1164 cmd_ret const RunCommand(string const & cmd)
1166 // One question is if we should use popen or
1167 // create our own popen based on fork, exec, pipe
1168 // of course the best would be to have a
1169 // pstream (process stream), with the
1170 // variants ipstream, opstream
1172 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1174 // (Claus Hentschel) Check if popen was succesful ;-)
1176 return make_pair(-1, string());
1181 ret += static_cast<char>(c);
1184 int const pret = pclose(inf);
1185 return make_pair(pret, ret);
1189 string const findtexfile(string const & fil, string const & /*format*/)
1191 /* There is no problem to extend this function too use other
1192 methods to look for files. It could be setup to look
1193 in environment paths and also if wanted as a last resort
1194 to a recursive find. One of the easier extensions would
1195 perhaps be to use the LyX file lookup methods. But! I am
1196 going to implement this until I see some demand for it.
1200 // If the file can be found directly, we just return a
1201 // absolute path version of it.
1202 if (FileInfo(fil).exist())
1203 return MakeAbsPath(fil);
1205 // No we try to find it using kpsewhich.
1206 // It seems from the kpsewhich manual page that it is safe to use
1207 // kpsewhich without --format: "When the --format option is not
1208 // given, the search path used when looking for a file is inferred
1209 // from the name given, by looking for a known extension. If no
1210 // known extension is found, the search path for TeX source files
1212 // However, we want to take advantage of the format sine almost all
1213 // the different formats has environment variables that can be used
1214 // to controll which paths to search. f.ex. bib looks in
1215 // BIBINPUTS and TEXBIB. Small list follows:
1216 // bib - BIBINPUTS, TEXBIB
1218 // graphic/figure - TEXPICTS, TEXINPUTS
1219 // ist - TEXINDEXSTYLE, INDEXSTYLE
1220 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1222 // tfm - TFMFONTS, TEXFONTS
1223 // This means that to use kpsewhich in the best possible way we
1224 // should help it by setting additional path in the approp. envir.var.
1225 string const kpsecmd = "kpsewhich " + fil;
1227 cmd_ret const c = RunCommand(kpsecmd);
1229 lyxerr[Debug::LATEX] << "kpse status = " << c.first << '\n'
1230 << "kpse result = `" << rtrim(c.second, "\n")
1233 return os::internal_path(rtrim(c.second, "\n\r"));
1239 void removeAutosaveFile(string const & filename)
1241 string a = OnlyPath(filename);
1243 a += OnlyFilename(filename);
1245 FileInfo const fileinfo(a);
1246 if (fileinfo.exist())
1251 void readBB_lyxerrMessage(string const & file, bool & zipped,
1252 string const & message)
1254 lyxerr[Debug::GRAPHICS] << "[readBB_from_PSFile] "
1255 << message << std::endl;
1256 #warning Why is this func deleting a file? (Lgb)
1262 string const readBB_from_PSFile(string const & file)
1264 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1265 // It seems that every command in the header has an own line,
1266 // getline() should work for all files.
1267 // On the other hand some plot programs write the bb at the
1268 // end of the file. Than we have in the header:
1269 // %%BoundingBox: (atend)
1270 // In this case we must check the end.
1271 bool zipped = zippedFile(file);
1272 string const file_ = zipped ?
1273 string(unzipFile(file)) : string(file);
1274 string const format = getExtFromContents(file_);
1276 if (format != "eps" && format != "ps") {
1277 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1281 std::ifstream is(file_.c_str());
1285 if (contains(s,"%%BoundingBox:") && !contains(s,"atend")) {
1286 string const bb = ltrim(s.substr(14));
1287 readBB_lyxerrMessage(file_, zipped, bb);
1291 readBB_lyxerrMessage(file_, zipped, "no bb found");
1296 int compare_timestamps(string const & file1, string const & file2)
1298 BOOST_ASSERT(AbsolutePath(file1) && AbsolutePath(file2));
1300 // If the original is newer than the copy, then copy the original
1301 // to the new directory.
1306 if (f1.exist() && f2.exist()) {
1307 double const tmp = difftime(f1.getModificationTime(),
1308 f2.getModificationTime());
1310 cmp = tmp > 0 ? 1 : -1;
1312 } else if (f1.exist()) {
1314 } else if (f2.exist()) {
1321 } //namespace support