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/tostr.h"
25 #include "support/systemcall.h"
26 #include "support/filetools.h"
27 #include "support/lstrings.h"
28 #include "support/FileInfo.h"
29 #include "support/forkedcontr.h"
30 #include "support/path.h"
31 #include "support/path_defines.h"
32 #include "support/lyxlib.h"
33 #include "support/os.h"
35 // FIXME Interface violation
39 #include <boost/assert.hpp>
40 #include <boost/regex.hpp>
54 // Which part of this is still necessary? (JMarc).
57 # define NAMLEN(dirent) strlen((dirent)->d_name)
59 # define dirent direct
60 # define NAMLEN(dirent) (dirent)->d_namlen
62 # include <sys/ndir.h>
72 #ifndef CXX_GLOBAL_CSTD
83 using std::ostringstream;
90 bool IsLyXFilename(string const & filename)
92 return suffixIs(ascii_lowercase(filename), ".lyx");
96 bool IsSGMLFilename(string const & filename)
98 return suffixIs(ascii_lowercase(filename), ".sgml");
102 // Substitutes spaces with underscores in filename (and path)
103 string const MakeLatexName(string const & file)
105 string name = OnlyFilename(file);
106 string const path = OnlyPath(file);
108 for (string::size_type i = 0; i < name.length(); ++i) {
109 name[i] &= 0x7f; // set 8th bit to 0
112 // ok so we scan through the string twice, but who cares.
113 string const keep("abcdefghijklmnopqrstuvwxyz"
114 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
115 "@!\"'()*+,-./0123456789:;<=>?[]`|");
117 string::size_type pos = 0;
118 while ((pos = name.find_first_not_of(keep, pos)) != string::npos) {
121 return AddName(path, name);
125 // Substitutes spaces with underscores in filename (and path)
126 string const QuoteName(string const & name)
128 return (os::shell() == os::UNIX) ?
134 // Is a file readable ?
135 bool IsFileReadable(string const & path)
138 return file.isOK() && file.isRegular() && file.readable();
142 // Is a file read_only?
143 // return 1 read-write
145 // -1 error (doesn't exist, no access, anything else)
146 int IsFileWriteable(string const & path)
150 if (fi.access(FileInfo::wperm|FileInfo::rperm)) // read-write
152 if (fi.readable()) // read-only
154 return -1; // everything else.
158 //returns true: dir writeable
159 // false: not writeable
160 bool IsDirWriteable(string const & path)
162 lyxerr[Debug::FILES] << "IsDirWriteable: " << path << endl;
164 string const tmpfl(tempName(path, "lyxwritetest"));
174 // Uses a string of paths separated by ";"s to find a file to open.
175 // Can't cope with pathnames with a ';' in them. Returns full path to file.
176 // If path entry begins with $$LyX/, use system_lyxdir
177 // If path entry begins with $$User/, use user_lyxdir
178 // Example: "$$User/doc;$$LyX/doc"
179 string const FileOpenSearch(string const & path, string const & name,
184 bool notfound = true;
185 string tmppath = split(path, path_element, ';');
187 while (notfound && !path_element.empty()) {
188 path_element = os::internal_path(path_element);
189 if (!suffixIs(path_element, '/'))
191 path_element = subst(path_element, "$$LyX", system_lyxdir());
192 path_element = subst(path_element, "$$User", user_lyxdir());
194 real_file = FileSearch(path_element, name, ext);
196 if (real_file.empty()) {
198 tmppath = split(tmppath, path_element, ';');
199 } while (!tmppath.empty() && path_element.empty());
205 if (ext.empty() && notfound) {
206 real_file = FileOpenSearch(path, name, "exe");
207 if (notfound) real_file = FileOpenSearch(path, name, "cmd");
214 /// Returns a vector of all files in directory dir having extension ext.
215 vector<string> const DirList(string const & dir, string const & ext)
217 // This is a non-error checking C/system implementation
219 if (!ext.empty() && ext[0] != '.')
223 vector<string> dirlist;
224 DIR * dirp = ::opendir(dir.c_str());
227 << "Directory \"" << dir
228 << "\" does not exist to DirList." << endl;
233 while ((dire = ::readdir(dirp))) {
234 string const fil = dire->d_name;
235 if (suffixIs(fil, extension)) {
236 dirlist.push_back(fil);
241 /* I would have prefered to take a vector<string>& as parameter so
242 that we could avoid the copy of the vector when returning.
244 dirlist.swap(argvec);
245 to avoid the copy. (Lgb)
247 /* A C++ implementaion will look like this:
248 string extension(ext);
249 if (extension[0] != '.') extension.insert(0, 1, '.');
250 vector<string> dirlist;
251 directory_iterator dit("dir");
252 while (dit != directory_iterator()) {
253 string fil = dit->filename;
254 if (prefixIs(fil, extension)) {
255 dirlist.push_back(fil);
259 dirlist.swap(argvec);
265 // Returns the real name of file name in directory path, with optional
267 string const FileSearch(string const & path, string const & name,
270 // if `name' is an absolute path, we ignore the setting of `path'
271 // Expand Environmentvariables in 'name'
272 string const tmpname = ReplaceEnvironmentPath(name);
273 string fullname = MakeAbsPath(tmpname, path);
274 // search first without extension, then with it.
275 if (IsFileReadable(fullname))
277 else if (ext.empty())
279 else { // Is it not more reasonable to use ChangeExtension()? (SMiyata)
282 if (IsFileReadable(fullname))
290 // Search the file name.ext in the subdirectory dir of
292 // 2) build_lyxdir (if not empty)
294 string const LibFileSearch(string const & dir, string const & name,
297 string fullname = FileSearch(AddPath(user_lyxdir(), dir), name, ext);
298 if (!fullname.empty())
301 if (!build_lyxdir().empty())
302 fullname = FileSearch(AddPath(build_lyxdir(), dir), name, ext);
303 if (!fullname.empty())
306 return FileSearch(AddPath(system_lyxdir(), dir), name, ext);
311 i18nLibFileSearch(string const & dir, string const & name,
314 // the following comments are from intl/dcigettext.c. We try
315 // to mimick this behaviour here.
316 /* The highest priority value is the `LANGUAGE' environment
317 variable. But we don't use the value if the currently
318 selected locale is the C locale. This is a GNU extension. */
319 /* [Otherwise] We have to proceed with the POSIX methods of
320 looking to `LC_ALL', `LC_xxx', and `LANG'. */
322 string lang = GetEnv("LC_ALL");
324 lang = GetEnv("LC_MESSAGES");
326 lang = GetEnv("LANG");
332 string const language = GetEnv("LANGUAGE");
333 if (lang != "C" && lang != "POSIX" && !language.empty())
337 lang = split(lang, l, ':');
338 while (!l.empty() && l != "C" && l != "POSIX") {
339 string const tmp = LibFileSearch(dir,
340 token(l, '_', 0) + '_' + name,
344 lang = split(lang, l, ':');
347 return LibFileSearch(dir, name, ext);
351 string const LibScriptSearch(string const & command_in)
353 string const token_scriptpath("$$s/");
355 string command = command_in;
356 // Find the starting position of "$$s/"
357 string::size_type const pos1 = command.find(token_scriptpath);
358 if (pos1 == string::npos)
360 // Find the end of the "$$s/some_script" word within command
361 string::size_type const start_script = pos1 + 4;
362 string::size_type const pos2 = command.find(' ', start_script);
363 string::size_type const size_script = pos2 == string::npos?
364 (command.size() - start_script) : pos2 - start_script;
366 // Does this script file exist?
367 string const script =
368 LibFileSearch("scripts", command.substr(start_script, size_script));
370 if (script.empty()) {
371 // Replace "$$s/" with ""
372 command.erase(pos1, 4);
374 // Replace "$$s/some_script" with "$LYX_SCRIPT_PATH/some_script"
375 string::size_type const size_replace = size_script + 4;
376 command.replace(pos1, size_replace, QuoteName(script));
383 string const GetEnv(string const & envname)
385 // f.ex. what about error checking?
386 char const * const ch = getenv(envname.c_str());
387 string const envstr = !ch ? "" : ch;
392 string const GetEnvPath(string const & name)
395 string const pathlist = subst(GetEnv(name), ':', ';');
397 string const pathlist = os::internal_path(GetEnv(name));
399 return rtrim(pathlist, ";");
405 int DeleteAllFilesInDir(string const & path)
407 // I have decided that we will be using parts from the boost
408 // library. Check out http://www.boost.org/
409 // For directory access we will then use the directory_iterator.
410 // Then the code will be something like:
411 // directory_iterator dit(path);
412 // directory_iterator dend;
413 // if (dit == dend) {
416 // for (; dit != dend; ++dit) {
417 // string filename(*dit);
418 // if (filename == "." || filename == "..")
420 // string unlinkpath(AddName(path, filename));
421 // lyx::unlink(unlinkpath);
424 DIR * dir = ::opendir(path.c_str());
429 int return_value = 0;
430 while ((de = readdir(dir))) {
431 string const temp = de->d_name;
432 if (temp == "." || temp == "..")
434 string const unlinkpath = AddName (path, temp);
436 lyxerr[Debug::FILES] << "Deleting file: " << unlinkpath
440 FileInfo fi(unlinkpath);
441 if (fi.isOK() && fi.isDir()) {
442 deleted = (DeleteAllFilesInDir(unlinkpath) == 0);
443 deleted &= (rmdir(unlinkpath) == 0);
445 deleted &= (unlink(unlinkpath) == 0);
454 string const createTmpDir(string const & tempdir, string const & mask)
457 << "createTmpDir: tempdir=`" << tempdir << "'\n"
458 << "createTmpDir: mask=`" << mask << '\'' << endl;
460 string const tmpfl(tempName(tempdir, mask));
461 // lyx::tempName actually creates a file to make sure that it
462 // stays unique. So we have to delete it before we can create
463 // a dir with the same name. Note also that we are not thread
464 // safe because of the gap between unlink and mkdir. (Lgb)
467 if (tmpfl.empty() || mkdir(tmpfl, 0700)) {
468 lyxerr << "LyX could not create the temporary directory '"
469 << tmpfl << "'" << endl;
473 return MakeAbsPath(tmpfl);
479 int destroyDir(string const & tmpdir)
482 Path p(user_lyxdir());
484 if (DeleteAllFilesInDir(tmpdir))
494 string const createBufferTmpDir()
497 // We are in our own directory. Why bother to mangle name?
498 // In fact I wrote this code to circumvent a problematic behaviour (bug?)
500 string const tmpfl = os::getTmpDir() + "/lyx_tmpbuf" + convert<string>(count++);
501 if (mkdir(tmpfl, 0777)) {
502 lyxerr << "LyX could not create the temporary directory '"
503 << tmpfl << "'" << endl;
510 string const createLyXTmpDir(string const & deflt)
512 if (!deflt.empty() && deflt != "/tmp") {
513 if (mkdir(deflt, 0777)) {
514 if (IsDirWriteable(deflt))
515 // deflt could not be created because it
516 // did exist already, so let's create our own
519 Path p(user_lyxdir());
521 return createTmpDir(deflt, "lyx_tmpdir");
523 // some other error occured.
525 Path p(user_lyxdir());
527 return createTmpDir("/tmp", "lyx_tmpdir");
532 Path p(user_lyxdir());
534 return createTmpDir("/tmp", "lyx_tmpdir");
539 bool createDirectory(string const & path, int permission)
541 string temp(rtrim(os::internal_path(path), "/"));
543 BOOST_ASSERT(!temp.empty());
545 if (mkdir(temp, permission))
552 // Strip filename from path name
553 string const OnlyPath(string const & Filename)
555 // If empty filename, return empty
556 if (Filename.empty()) return Filename;
558 // Find last / or start of filename
559 string::size_type j = Filename.rfind('/');
560 if (j == string::npos)
562 return Filename.substr(0, j + 1);
566 // Convert relative path into absolute path based on a basepath.
567 // If relpath is absolute, just use that.
568 // If basepath is empty, use CWD as base.
569 string const MakeAbsPath(string const & RelPath, string const & BasePath)
571 // checks for already absolute path
572 if (os::is_absolute_path(RelPath))
575 // Copies given paths
576 string TempRel(os::internal_path(RelPath));
577 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
578 TempRel = subst(TempRel, "//", "/");
582 if (os::is_absolute_path(BasePath))
585 TempBase = AddPath(getcwd(), BasePath);
587 // Handle /./ at the end of the path
588 while (suffixIs(TempBase, "/./"))
589 TempBase.erase(TempBase.length() - 2);
591 // processes relative path
592 string RTemp(TempRel);
595 while (!RTemp.empty()) {
597 RTemp = split(RTemp, Temp, '/');
599 if (Temp == ".") continue;
601 // Remove one level of TempBase
602 string::difference_type i = TempBase.length() - 2;
605 while (i > 0 && TempBase[i] != '/') --i;
609 while (i > 2 && TempBase[i] != '/') --i;
612 TempBase.erase(i, string::npos);
615 } else if (Temp.empty() && !RTemp.empty()) {
616 TempBase = os::current_root() + RTemp;
619 // Add this piece to TempBase
620 if (!suffixIs(TempBase, '/'))
626 // returns absolute path
627 return os::internal_path(TempBase);
631 // Correctly append filename to the pathname.
632 // If pathname is '.', then don't use pathname.
633 // Chops any path of filename.
634 string const AddName(string const & path, string const & fname)
637 string const basename(OnlyFilename(fname));
641 if (path != "." && path != "./" && !path.empty()) {
642 buf = os::internal_path(path);
643 if (!suffixIs(path, '/'))
647 return buf + basename;
651 // Strips path from filename
652 string const OnlyFilename(string const & fname)
657 string::size_type j = fname.rfind('/');
658 if (j == string::npos) // no '/' in fname
662 return fname.substr(j + 1);
666 /// Returns true is path is absolute
667 bool AbsolutePath(string const & path)
669 return os::is_absolute_path(path);
674 // Create absolute path. If impossible, don't do anything
675 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
676 string const ExpandPath(string const & path)
678 // checks for already absolute path
679 string RTemp(ReplaceEnvironmentPath(path));
680 if (os::is_absolute_path(RTemp))
684 string const copy(RTemp);
687 RTemp = split(RTemp, Temp, '/');
690 return getcwd() + '/' + RTemp;
693 return os::homepath() + '/' + RTemp;
696 return MakeAbsPath(copy);
698 // Don't know how to handle this
704 // Constracts path/../path
705 // Can't handle "../../" or "/../" (Asger)
706 // Also converts paths like /foo//bar ==> /foo/bar
707 string const NormalizePath(string const & path)
713 if (os::is_absolute_path(path))
716 // Make implicit current directory explicit
719 // Normalise paths like /foo//bar ==> /foo/bar
720 boost::RegEx regex("/{2,}");
721 RTemp = regex.Merge(RTemp, "/");
723 while (!RTemp.empty()) {
725 RTemp = split(RTemp, Temp, '/');
729 } else if (Temp == "..") {
730 // Remove one level of TempBase
731 string::difference_type i = TempBase.length() - 2;
732 while (i > 0 && TempBase[i] != '/')
734 if (i >= 0 && TempBase[i] == '/')
735 TempBase.erase(i + 1, string::npos);
739 TempBase += Temp + '/';
743 // returns absolute path
748 string const GetFileContents(string const & fname)
750 FileInfo finfo(fname);
752 ifstream ifs(fname.c_str());
760 lyxerr << "LyX was not able to read file '" << fname << '\'' << endl;
765 // Search the string for ${VAR} and $VAR and replace VAR using getenv.
766 string const ReplaceEnvironmentPath(string const & path)
768 // ${VAR} is defined as
769 // $\{[A-Za-z_][A-Za-z_0-9]*\}
770 static string const envvar_br = "[$]\\{([A-Za-z_][A-Za-z_0-9]*)\\}";
772 // $VAR is defined as:
773 // $\{[A-Za-z_][A-Za-z_0-9]*\}
774 static string const envvar = "[$]([A-Za-z_][A-Za-z_0-9]*)";
776 static boost::regex envvar_br_re("(.*)" + envvar_br + "(.*)");
777 static boost::regex envvar_re("(.*)" + envvar + "(.*)");
780 string result = path;
782 regex_match(result, what, envvar_br_re);
783 if (!what[0].matched) {
784 regex_match(result, what, envvar_re);
785 if (!what[0].matched)
788 result = what.str(1) + GetEnv(what.str(2)) + what.str(3);
794 // Make relative path out of two absolute paths
795 string const MakeRelPath(string const & abspath, string const & basepath)
796 // Makes relative path out of absolute path. If it is deeper than basepath,
797 // it's easy. If basepath and abspath share something (they are all deeper
798 // than some directory), it'll be rendered using ..'s. If they are completely
799 // different, then the absolute path will be used as relative path.
801 string::size_type const abslen = abspath.length();
802 string::size_type const baselen = basepath.length();
804 string::size_type i = os::common_path(abspath, basepath);
807 // actually no match - cannot make it relative
811 // Count how many dirs there are in basepath above match
812 // and append as many '..''s into relpath
814 string::size_type j = i;
815 while (j < baselen) {
816 if (basepath[j] == '/') {
817 if (j + 1 == baselen)
824 // Append relative stuff from common directory to abspath
825 if (abspath[i] == '/')
827 for (; i < abslen; ++i)
830 if (suffixIs(buf, '/'))
831 buf.erase(buf.length() - 1);
832 // Substitute empty with .
839 // Append sub-directory(ies) to a path in an intelligent way
840 string const AddPath(string const & path, string const & path_2)
843 string const path2 = os::internal_path(path_2);
845 if (!path.empty() && path != "." && path != "./") {
846 buf = os::internal_path(path);
847 if (path[path.length() - 1] != '/')
851 if (!path2.empty()) {
852 string::size_type const p2start = path2.find_first_not_of('/');
853 string::size_type const p2end = path2.find_last_not_of('/');
854 string const tmp = path2.substr(p2start, p2end - p2start + 1);
862 Change extension of oldname to extension.
863 Strips path off if no_path == true.
864 If no extension on oldname, just appends.
866 string const ChangeExtension(string const & oldname, string const & extension)
868 string::size_type const last_slash = oldname.rfind('/');
869 string::size_type last_dot = oldname.rfind('.');
870 if (last_dot < last_slash && last_slash != string::npos)
871 last_dot = string::npos;
874 // Make sure the extension starts with a dot
875 if (!extension.empty() && extension[0] != '.')
876 ext= '.' + extension;
880 return os::internal_path(oldname.substr(0, last_dot) + ext);
884 /// Return the extension of the file (not including the .)
885 string const GetExtension(string const & name)
887 string::size_type const last_slash = name.rfind('/');
888 string::size_type const last_dot = name.rfind('.');
889 if (last_dot != string::npos &&
890 (last_slash == string::npos || last_dot > last_slash))
891 return name.substr(last_dot + 1,
892 name.length() - (last_dot + 1));
898 // the different filetypes and what they contain in one of the first lines
899 // (dots are any characters). (Herbert 20020131)
902 // EPS %!PS-Adobe-3.0 EPSF...
909 // PBM P1... or P4 (B/W)
910 // PGM P2... or P5 (Grayscale)
911 // PPM P3... or P6 (color)
912 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
913 // SGI \001\332... (decimal 474)
915 // TIFF II... or MM...
917 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
918 // ...static char *...
919 // XWD \000\000\000\151 (0x00006900) decimal 105
921 // GZIP \037\213 http://www.ietf.org/rfc/rfc1952.txt
922 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
923 // Z \037\235 UNIX compress
925 string const getFormatFromContents(string const & filename)
928 if (filename.empty() || !IsFileReadable(filename))
931 ifstream ifs(filename.c_str());
933 // Couldn't open file...
937 string const gzipStamp = "\037\213";
940 string const zipStamp = "PK";
943 string const compressStamp = "\037\235";
945 // Maximum strings to read
946 int const max_count = 50;
951 bool firstLine = true;
952 while ((count++ < max_count) && format.empty()) {
954 lyxerr[Debug::GRAPHICS]
955 << "filetools(getFormatFromContents)\n"
956 << "\tFile type not recognised before EOF!"
962 string const stamp = str.substr(0,2);
963 if (firstLine && str.size() >= 2) {
964 // at first we check for a zipped file, because this
965 // information is saved in the first bytes of the file!
966 // also some graphic formats which save the information
967 // in the first line, too.
968 if (prefixIs(str, gzipStamp)) {
971 } else if (stamp == zipStamp) {
974 } else if (stamp == compressStamp) {
978 } else if (stamp == "BM") {
981 } else if (stamp == "\001\332") {
985 // Don't need to use str.at(0), str.at(1) because
986 // we already know that str.size() >= 2
987 } else if (str[0] == 'P') {
1003 } else if ((stamp == "II") || (stamp == "MM")) {
1006 } else if (prefixIs(str,"%TGIF")) {
1009 } else if (prefixIs(str,"#FIG")) {
1012 } else if (prefixIs(str,"GIF")) {
1015 } else if (str.size() > 3) {
1016 int const c = ((str[0] << 24) & (str[1] << 16) &
1017 (str[2] << 8) & str[3]);
1026 if (!format.empty())
1028 else if (contains(str,"EPSF"))
1029 // dummy, if we have wrong file description like
1030 // %!PS-Adobe-2.0EPSF"
1033 else if (contains(str,"Grace"))
1036 else if (contains(str,"JFIF"))
1039 else if (contains(str,"%PDF"))
1042 else if (contains(str,"PNG"))
1045 else if (contains(str,"%!PS-Adobe")) {
1048 if (contains(str,"EPSF"))
1054 else if (contains(str,"_bits[]"))
1057 else if (contains(str,"XPM") || contains(str, "static char *"))
1060 else if (contains(str,"BITPIX"))
1064 if (!format.empty()) {
1065 lyxerr[Debug::GRAPHICS]
1066 << "Recognised Fileformat: " << format << endl;
1070 lyxerr[Debug::GRAPHICS]
1071 << "filetools(getFormatFromContents)\n"
1072 << "\tCouldn't find a known format!\n";
1077 /// check for zipped file
1078 bool zippedFile(string const & name)
1080 string const type = getFormatFromContents(name);
1081 if (contains("gzip zip compress", type) && !type.empty())
1087 string const unzippedFileName(string const & zipped_file)
1089 string const ext = GetExtension(zipped_file);
1090 if (ext == "gz" || ext == "z" || ext == "Z")
1091 return ChangeExtension(zipped_file, string());
1092 return "unzipped_" + zipped_file;
1096 string const unzipFile(string const & zipped_file, string const & unzipped_file)
1098 string const tempfile = unzipped_file.empty() ?
1099 unzippedFileName(zipped_file) : unzipped_file;
1101 string const command = "gunzip -c " + zipped_file + " > " + tempfile;
1103 one.startscript(Systemcall::Wait, command);
1104 // test that command was executed successfully (anon)
1105 // yes, please do. (Lgb)
1110 string const MakeDisplayPath(string const & path, unsigned int threshold)
1114 string const home(os::homepath());
1116 // replace /home/blah with ~/
1117 if (prefixIs(str, home))
1118 str = subst(str, home, "~");
1120 if (str.length() <= threshold)
1123 string const prefix = ".../";
1126 while (str.length() > threshold)
1127 str = split(str, temp, '/');
1129 // Did we shorten everything away?
1131 // Yes, filename itself is too long.
1132 // Pick the start and the end of the filename.
1133 str = OnlyFilename(path);
1134 string const head = str.substr(0, threshold / 2 - 3);
1136 string::size_type len = str.length();
1138 str.substr(len - threshold / 2 - 2, len - 1);
1139 str = head + "..." + tail;
1142 return prefix + str;
1146 bool LyXReadLink(string const & file, string & link, bool resolve)
1148 #ifdef HAVE_READLINK
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));
1167 cmd_ret const RunCommand(string const & cmd)
1169 // FIXME: replace all calls to RunCommand with ForkedCall
1170 // (if the output is not needed) or the code in ispell.C
1171 // (if the output is needed).
1173 // One question is if we should use popen or
1174 // create our own popen based on fork, exec, pipe
1175 // of course the best would be to have a
1176 // pstream (process stream), with the
1177 // variants ipstream, opstream
1179 sigset_t newMask, oldMask;
1180 sigemptyset(&oldMask);
1181 sigemptyset(&newMask);
1182 sigaddset(&newMask, SIGCHLD);
1184 // Block the SIGCHLD signal.
1185 sigprocmask(SIG_BLOCK, &newMask, &oldMask);
1187 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1189 // (Claus Hentschel) Check if popen was succesful ;-)
1191 return make_pair(-1, string());
1192 lyxerr << "RunCommand:: could not start child process" << endl;
1198 ret += static_cast<char>(c);
1201 int const pret = pclose(inf);
1203 perror("RunCommand:: could not terminate child process");
1205 // Unblock the SIGCHLD signal and restore the old mask.
1206 sigprocmask(SIG_SETMASK, &oldMask, 0);
1208 return make_pair(pret, ret);
1212 string const findtexfile(string const & fil, string const & /*format*/)
1214 /* There is no problem to extend this function too use other
1215 methods to look for files. It could be setup to look
1216 in environment paths and also if wanted as a last resort
1217 to a recursive find. One of the easier extensions would
1218 perhaps be to use the LyX file lookup methods. But! I am
1219 going to implement this until I see some demand for it.
1223 // If the file can be found directly, we just return a
1224 // absolute path version of it.
1225 if (FileInfo(fil).exist())
1226 return MakeAbsPath(fil);
1228 // No we try to find it using kpsewhich.
1229 // It seems from the kpsewhich manual page that it is safe to use
1230 // kpsewhich without --format: "When the --format option is not
1231 // given, the search path used when looking for a file is inferred
1232 // from the name given, by looking for a known extension. If no
1233 // known extension is found, the search path for TeX source files
1235 // However, we want to take advantage of the format sine almost all
1236 // the different formats has environment variables that can be used
1237 // to controll which paths to search. f.ex. bib looks in
1238 // BIBINPUTS and TEXBIB. Small list follows:
1239 // bib - BIBINPUTS, TEXBIB
1241 // graphic/figure - TEXPICTS, TEXINPUTS
1242 // ist - TEXINDEXSTYLE, INDEXSTYLE
1243 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1245 // tfm - TFMFONTS, TEXFONTS
1246 // This means that to use kpsewhich in the best possible way we
1247 // should help it by setting additional path in the approp. envir.var.
1248 string const kpsecmd = "kpsewhich " + fil;
1250 cmd_ret const c = RunCommand(kpsecmd);
1252 lyxerr[Debug::LATEX] << "kpse status = " << c.first << '\n'
1253 << "kpse result = `" << rtrim(c.second, "\n")
1256 return os::internal_path(rtrim(c.second, "\n\r"));
1262 void removeAutosaveFile(string const & filename)
1264 string a = OnlyPath(filename);
1266 a += OnlyFilename(filename);
1268 FileInfo const fileinfo(a);
1269 if (fileinfo.exist())
1274 void readBB_lyxerrMessage(string const & file, bool & zipped,
1275 string const & message)
1277 lyxerr[Debug::GRAPHICS] << "[readBB_from_PSFile] "
1278 << message << std::endl;
1279 #ifdef WITH_WARNINGS
1280 #warning Why is this func deleting a file? (Lgb)
1287 string const readBB_from_PSFile(string const & file)
1289 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1290 // It seems that every command in the header has an own line,
1291 // getline() should work for all files.
1292 // On the other hand some plot programs write the bb at the
1293 // end of the file. Than we have in the header:
1294 // %%BoundingBox: (atend)
1295 // In this case we must check the end.
1296 bool zipped = zippedFile(file);
1297 string const file_ = zipped ?
1298 string(unzipFile(file)) : string(file);
1299 string const format = getFormatFromContents(file_);
1301 if (format != "eps" && format != "ps") {
1302 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1306 std::ifstream is(file_.c_str());
1310 if (contains(s,"%%BoundingBox:") && !contains(s,"atend")) {
1311 string const bb = ltrim(s.substr(14));
1312 readBB_lyxerrMessage(file_, zipped, bb);
1316 readBB_lyxerrMessage(file_, zipped, "no bb found");
1321 int compare_timestamps(string const & file1, string const & file2)
1323 BOOST_ASSERT(AbsolutePath(file1) && AbsolutePath(file2));
1325 // If the original is newer than the copy, then copy the original
1326 // to the new directory.
1331 if (f1.exist() && f2.exist()) {
1332 double const tmp = difftime(f1.getModificationTime(),
1333 f2.getModificationTime());
1335 cmp = tmp > 0 ? 1 : -1;
1337 } else if (f1.exist()) {
1339 } else if (f2.exist()) {
1346 } //namespace support