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"
31 #include "forkedcontr.h"
33 #include "path_defines.h"
38 #include <boost/assert.hpp>
39 #include <boost/regex.hpp>
53 // Which part of this is still necessary? (JMarc).
56 # define NAMLEN(dirent) strlen((dirent)->d_name)
58 # define dirent direct
59 # define NAMLEN(dirent) (dirent)->d_namlen
61 # include <sys/ndir.h>
71 #ifndef CXX_GLOBAL_CSTD
82 using std::ostringstream;
89 bool IsLyXFilename(string const & filename)
91 return suffixIs(ascii_lowercase(filename), ".lyx");
95 bool IsSGMLFilename(string const & filename)
97 return suffixIs(ascii_lowercase(filename), ".sgml");
101 // Substitutes spaces with underscores in filename (and path)
102 string const MakeLatexName(string const & file)
104 string name = OnlyFilename(file);
105 string const path = OnlyPath(file);
107 for (string::size_type i = 0; i < name.length(); ++i) {
108 name[i] &= 0x7f; // set 8th bit to 0
111 // ok so we scan through the string twice, but who cares.
112 string const keep("abcdefghijklmnopqrstuvwxyz"
113 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
114 "@!\"'()*+,-./0123456789:;<=>?[]`|");
116 string::size_type pos = 0;
117 while ((pos = name.find_first_not_of(keep, pos)) != string::npos) {
120 return AddName(path, name);
124 // Substitutes spaces with underscores in filename (and path)
125 string const QuoteName(string const & name)
127 return (os::shell() == os::UNIX) ?
133 // Is a file readable ?
134 bool IsFileReadable(string const & path)
137 return file.isOK() && file.isRegular() && file.readable();
141 // Is a file read_only?
142 // return 1 read-write
144 // -1 error (doesn't exist, no access, anything else)
145 int IsFileWriteable(string const & path)
149 if (fi.access(FileInfo::wperm|FileInfo::rperm)) // read-write
151 if (fi.readable()) // read-only
153 return -1; // everything else.
157 //returns true: dir writeable
158 // false: not writeable
159 bool IsDirWriteable(string const & path)
161 lyxerr[Debug::FILES] << "IsDirWriteable: " << path << endl;
163 string const tmpfl(tempName(path, "lyxwritetest"));
173 // Uses a string of paths separated by ";"s to find a file to open.
174 // Can't cope with pathnames with a ';' in them. Returns full path to file.
175 // If path entry begins with $$LyX/, use system_lyxdir
176 // If path entry begins with $$User/, use user_lyxdir
177 // Example: "$$User/doc;$$LyX/doc"
178 string const FileOpenSearch(string const & path, string const & name,
183 bool notfound = true;
184 string tmppath = split(path, path_element, ';');
186 while (notfound && !path_element.empty()) {
187 path_element = os::slashify_path(path_element);
188 if (!suffixIs(path_element, '/'))
190 path_element = subst(path_element, "$$LyX", system_lyxdir());
191 path_element = subst(path_element, "$$User", user_lyxdir());
193 real_file = FileSearch(path_element, name, ext);
195 if (real_file.empty()) {
197 tmppath = split(tmppath, path_element, ';');
198 } while (!tmppath.empty() && path_element.empty());
204 if (ext.empty() && notfound) {
205 real_file = FileOpenSearch(path, name, "exe");
206 if (notfound) real_file = FileOpenSearch(path, name, "cmd");
213 /// Returns a vector of all files in directory dir having extension ext.
214 vector<string> const DirList(string const & dir, string const & ext)
216 // This is a non-error checking C/system implementation
218 if (!ext.empty() && ext[0] != '.')
222 vector<string> dirlist;
223 DIR * dirp = ::opendir(dir.c_str());
226 << "Directory \"" << dir
227 << "\" does not exist to DirList." << endl;
232 while ((dire = ::readdir(dirp))) {
233 string const fil = dire->d_name;
234 if (suffixIs(fil, extension)) {
235 dirlist.push_back(fil);
240 /* I would have prefered to take a vector<string>& as parameter so
241 that we could avoid the copy of the vector when returning.
243 dirlist.swap(argvec);
244 to avoid the copy. (Lgb)
246 /* A C++ implementaion will look like this:
247 string extension(ext);
248 if (extension[0] != '.') extension.insert(0, 1, '.');
249 vector<string> dirlist;
250 directory_iterator dit("dir");
251 while (dit != directory_iterator()) {
252 string fil = dit->filename;
253 if (prefixIs(fil, extension)) {
254 dirlist.push_back(fil);
258 dirlist.swap(argvec);
264 // Returns the real name of file name in directory path, with optional
266 string const FileSearch(string const & path, string const & name,
269 // if `name' is an absolute path, we ignore the setting of `path'
270 // Expand Environmentvariables in 'name'
271 string const tmpname = ReplaceEnvironmentPath(name);
272 string fullname = MakeAbsPath(tmpname, path);
273 // search first without extension, then with it.
274 if (IsFileReadable(fullname))
276 else if (ext.empty())
278 else { // Is it not more reasonable to use ChangeExtension()? (SMiyata)
281 if (IsFileReadable(fullname))
289 // Search the file name.ext in the subdirectory dir of
291 // 2) build_lyxdir (if not empty)
293 string const LibFileSearch(string const & dir, string const & name,
296 string fullname = FileSearch(AddPath(user_lyxdir(), dir), name, ext);
297 if (!fullname.empty())
300 if (!build_lyxdir().empty())
301 fullname = FileSearch(AddPath(build_lyxdir(), dir), name, ext);
302 if (!fullname.empty())
305 return FileSearch(AddPath(system_lyxdir(), dir), name, ext);
310 i18nLibFileSearch(string const & dir, string const & name,
313 // the following comments are from intl/dcigettext.c. We try
314 // to mimick this behaviour here.
315 /* The highest priority value is the `LANGUAGE' environment
316 variable. But we don't use the value if the currently
317 selected locale is the C locale. This is a GNU extension. */
318 /* [Otherwise] We have to proceed with the POSIX methods of
319 looking to `LC_ALL', `LC_xxx', and `LANG'. */
321 string lang = GetEnv("LC_ALL");
323 lang = GetEnv("LC_MESSAGES");
325 lang = GetEnv("LANG");
331 string const language = GetEnv("LANGUAGE");
332 if (lang != "C" && lang != "POSIX" && !language.empty())
336 lang = split(lang, l, ':');
337 while (!l.empty() && l != "C" && l != "POSIX") {
338 string const tmp = LibFileSearch(dir,
339 token(l, '_', 0) + '_' + name,
343 lang = split(lang, l, ':');
346 return LibFileSearch(dir, name, ext);
350 string const LibScriptSearch(string const & command_in)
352 string const token_scriptpath("$$s/");
354 string command = command_in;
355 // Find the starting position of "$$s/"
356 string::size_type const pos1 = command.find(token_scriptpath);
357 if (pos1 == string::npos)
359 // Find the end of the "$$s/some_script" word within command
360 string::size_type const start_script = pos1 + 4;
361 string::size_type const pos2 = command.find(' ', start_script);
362 string::size_type const size_script = pos2 == string::npos?
363 (command.size() - start_script) : pos2 - start_script;
365 // Does this script file exist?
366 string const script =
367 LibFileSearch("scripts", command.substr(start_script, size_script));
369 if (script.empty()) {
370 // Replace "$$s/" with ""
371 command.erase(pos1, 4);
373 // Replace "$$s/some_script" with "$LYX_SCRIPT_PATH/some_script"
374 string::size_type const size_replace = size_script + 4;
375 command.replace(pos1, size_replace, QuoteName(script));
382 string const GetEnv(string const & envname)
384 // f.ex. what about error checking?
385 char const * const ch = getenv(envname.c_str());
386 string const envstr = !ch ? "" : ch;
391 string const GetEnvPath(string const & name)
394 string const pathlist = subst(GetEnv(name), ':', ';');
396 string const pathlist = os::slashify_path(GetEnv(name));
398 return rtrim(pathlist, ";");
404 int DeleteAllFilesInDir(string const & path)
406 // I have decided that we will be using parts from the boost
407 // library. Check out http://www.boost.org/
408 // For directory access we will then use the directory_iterator.
409 // Then the code will be something like:
410 // directory_iterator dit(path);
411 // directory_iterator dend;
412 // if (dit == dend) {
415 // for (; dit != dend; ++dit) {
416 // string filename(*dit);
417 // if (filename == "." || filename == "..")
419 // string unlinkpath(AddName(path, filename));
420 // lyx::unlink(unlinkpath);
423 DIR * dir = ::opendir(path.c_str());
428 int return_value = 0;
429 while ((de = readdir(dir))) {
430 string const temp = de->d_name;
431 if (temp == "." || temp == "..")
433 string const unlinkpath = AddName (path, temp);
435 lyxerr[Debug::FILES] << "Deleting file: " << unlinkpath
439 FileInfo fi(unlinkpath);
440 if (fi.isOK() && fi.isDir()) {
441 deleted = (DeleteAllFilesInDir(unlinkpath) == 0);
442 deleted &= (rmdir(unlinkpath) == 0);
444 deleted &= (unlink(unlinkpath) == 0);
453 string const createTmpDir(string const & tempdir, string const & mask)
456 << "createTmpDir: tempdir=`" << tempdir << "'\n"
457 << "createTmpDir: mask=`" << mask << '\'' << endl;
459 string const tmpfl(tempName(tempdir, mask));
460 // lyx::tempName actually creates a file to make sure that it
461 // stays unique. So we have to delete it before we can create
462 // a dir with the same name. Note also that we are not thread
463 // safe because of the gap between unlink and mkdir. (Lgb)
466 if (tmpfl.empty() || mkdir(tmpfl, 0700)) {
467 lyxerr << "LyX could not create the temporary directory '"
468 << tmpfl << "'" << endl;
472 return MakeAbsPath(tmpfl);
478 int destroyDir(string const & tmpdir)
481 Path p(user_lyxdir());
483 if (DeleteAllFilesInDir(tmpdir))
493 string const createBufferTmpDir()
496 // We are in our own directory. Why bother to mangle name?
497 // In fact I wrote this code to circumvent a problematic behaviour (bug?)
499 string const tmpfl = os::getTmpDir() + "/lyx_tmpbuf" + tostr(count++);
500 if (mkdir(tmpfl, 0777)) {
501 lyxerr << "LyX could not create the temporary directory '"
502 << tmpfl << "'" << endl;
509 string const createLyXTmpDir(string const & deflt)
511 if (!deflt.empty() && deflt != "/tmp") {
512 if (mkdir(deflt, 0777)) {
513 if (IsDirWriteable(deflt))
514 // deflt could not be created because it
515 // did exist already, so let's create our own
518 Path p(user_lyxdir());
520 return createTmpDir(deflt, "lyx_tmpdir");
522 // some other error occured.
524 Path p(user_lyxdir());
526 return createTmpDir("/tmp", "lyx_tmpdir");
531 Path p(user_lyxdir());
533 return createTmpDir("/tmp", "lyx_tmpdir");
538 bool createDirectory(string const & path, int permission)
540 string temp(rtrim(os::slashify_path(path), "/"));
542 BOOST_ASSERT(!temp.empty());
544 if (mkdir(temp, permission))
551 // Strip filename from path name
552 string const OnlyPath(string const & Filename)
554 // If empty filename, return empty
555 if (Filename.empty()) return Filename;
557 // Find last / or start of filename
558 string::size_type j = Filename.rfind('/');
559 if (j == string::npos)
561 return Filename.substr(0, j + 1);
565 // Convert relative path into absolute path based on a basepath.
566 // If relpath is absolute, just use that.
567 // If basepath is empty, use CWD as base.
568 string const MakeAbsPath(string const & RelPath, string const & BasePath)
570 // checks for already absolute path
571 if (os::is_absolute_path(RelPath))
574 // Copies given paths
575 string TempRel(os::slashify_path(RelPath));
576 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
577 TempRel = subst(TempRel, "//", "/");
581 if (os::is_absolute_path(BasePath))
584 TempBase = AddPath(getcwd(), BasePath);
586 // Handle /./ at the end of the path
587 while (suffixIs(TempBase, "/./"))
588 TempBase.erase(TempBase.length() - 2);
590 // processes relative path
591 string RTemp(TempRel);
594 while (!RTemp.empty()) {
596 RTemp = split(RTemp, Temp, '/');
598 if (Temp == ".") continue;
600 // Remove one level of TempBase
601 string::difference_type i = TempBase.length() - 2;
604 while (i > 0 && TempBase[i] != '/') --i;
608 while (i > 2 && TempBase[i] != '/') --i;
611 TempBase.erase(i, string::npos);
614 } else if (Temp.empty() && !RTemp.empty()) {
615 TempBase = os::current_root() + RTemp;
618 // Add this piece to TempBase
619 if (!suffixIs(TempBase, '/'))
625 // returns absolute path
626 return os::slashify_path(TempBase);
630 // Correctly append filename to the pathname.
631 // If pathname is '.', then don't use pathname.
632 // Chops any path of filename.
633 string const AddName(string const & path, string const & fname)
636 string const basename(OnlyFilename(fname));
640 if (path != "." && path != "./" && !path.empty()) {
641 buf = os::slashify_path(path);
642 if (!suffixIs(path, '/'))
646 return buf + basename;
650 // Strips path from filename
651 string const OnlyFilename(string const & fname)
656 string::size_type j = fname.rfind('/');
657 if (j == string::npos) // no '/' in fname
661 return fname.substr(j + 1);
665 /// Returns true is path is absolute
666 bool AbsolutePath(string const & path)
668 return os::is_absolute_path(path);
673 // Create absolute path. If impossible, don't do anything
674 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
675 string const ExpandPath(string const & path)
677 // checks for already absolute path
678 string RTemp(ReplaceEnvironmentPath(path));
679 if (os::is_absolute_path(RTemp))
683 string const copy(RTemp);
686 RTemp = split(RTemp, Temp, '/');
689 return getcwd() + '/' + RTemp;
692 return GetEnvPath("HOME") + '/' + RTemp;
695 return MakeAbsPath(copy);
697 // Don't know how to handle this
703 // Constracts path/../path
704 // Can't handle "../../" or "/../" (Asger)
705 // Also converts paths like /foo//bar ==> /foo/bar
706 string const NormalizePath(string const & path)
712 if (os::is_absolute_path(path))
715 // Make implicit current directory explicit
718 // Normalise paths like /foo//bar ==> /foo/bar
719 boost::RegEx regex("/{2,}");
720 RTemp = regex.Merge(RTemp, "/");
722 while (!RTemp.empty()) {
724 RTemp = split(RTemp, Temp, '/');
728 } else if (Temp == "..") {
729 // Remove one level of TempBase
730 string::difference_type i = TempBase.length() - 2;
731 while (i > 0 && TempBase[i] != '/')
733 if (i >= 0 && TempBase[i] == '/')
734 TempBase.erase(i + 1, string::npos);
738 TempBase += Temp + '/';
742 // returns absolute path
747 string const GetFileContents(string const & fname)
749 FileInfo finfo(fname);
751 ifstream ifs(fname.c_str());
759 lyxerr << "LyX was not able to read file '" << fname << '\'' << endl;
764 // Search the string for ${VAR} and $VAR and replace VAR using getenv.
765 string const ReplaceEnvironmentPath(string const & path)
767 // ${VAR} is defined as
768 // $\{[A-Za-z_][A-Za-z_0-9]*\}
769 static string const envvar_br = "[$]\\{([A-Za-z_][A-Za-z_0-9]*)\\}";
771 // $VAR is defined as:
772 // $\{[A-Za-z_][A-Za-z_0-9]*\}
773 static string const envvar = "[$]([A-Za-z_][A-Za-z_0-9]*)";
775 static boost::regex envvar_br_re("(.*)" + envvar_br + "(.*)");
776 static boost::regex envvar_re("(.*)" + envvar + "(.*)");
779 string result = path;
781 regex_match(result, what, envvar_br_re);
782 if (!what[0].matched) {
783 regex_match(result, what, envvar_re);
784 if (!what[0].matched)
787 result = what.str(1) + GetEnv(what.str(2)) + what.str(3);
793 // Make relative path out of two absolute paths
794 string const MakeRelPath(string const & abspath, string const & basepath)
795 // Makes relative path out of absolute path. If it is deeper than basepath,
796 // it's easy. If basepath and abspath share something (they are all deeper
797 // than some directory), it'll be rendered using ..'s. If they are completely
798 // different, then the absolute path will be used as relative path.
800 string::size_type const abslen = abspath.length();
801 string::size_type const baselen = basepath.length();
803 string::size_type i = os::common_path(abspath, basepath);
806 // actually no match - cannot make it relative
810 // Count how many dirs there are in basepath above match
811 // and append as many '..''s into relpath
813 string::size_type j = i;
814 while (j < baselen) {
815 if (basepath[j] == '/') {
816 if (j + 1 == baselen)
823 // Append relative stuff from common directory to abspath
824 if (abspath[i] == '/')
826 for (; i < abslen; ++i)
829 if (suffixIs(buf, '/'))
830 buf.erase(buf.length() - 1);
831 // Substitute empty with .
838 // Append sub-directory(ies) to a path in an intelligent way
839 string const AddPath(string const & path, string const & path_2)
842 string const path2 = os::slashify_path(path_2);
844 if (!path.empty() && path != "." && path != "./") {
845 buf = os::slashify_path(path);
846 if (path[path.length() - 1] != '/')
850 if (!path2.empty()) {
851 string::size_type const p2start = path2.find_first_not_of('/');
852 string::size_type const p2end = path2.find_last_not_of('/');
853 string const tmp = path2.substr(p2start, p2end - p2start + 1);
861 Change extension of oldname to extension.
862 Strips path off if no_path == true.
863 If no extension on oldname, just appends.
865 string const ChangeExtension(string const & oldname, string const & extension)
867 string::size_type const last_slash = oldname.rfind('/');
868 string::size_type last_dot = oldname.rfind('.');
869 if (last_dot < last_slash && last_slash != string::npos)
870 last_dot = string::npos;
873 // Make sure the extension starts with a dot
874 if (!extension.empty() && extension[0] != '.')
875 ext= '.' + extension;
879 return os::slashify_path(oldname.substr(0, last_dot) + ext);
883 /// Return the extension of the file (not including the .)
884 string const GetExtension(string const & name)
886 string::size_type const last_slash = name.rfind('/');
887 string::size_type const last_dot = name.rfind('.');
888 if (last_dot != string::npos &&
889 (last_slash == string::npos || last_dot > last_slash))
890 return name.substr(last_dot + 1,
891 name.length() - (last_dot + 1));
896 // the different filetypes and what they contain in one of the first lines
897 // (dots are any characters). (Herbert 20020131)
900 // EPS %!PS-Adobe-3.0 EPSF...
907 // PBM P1... or P4 (B/W)
908 // PGM P2... or P5 (Grayscale)
909 // PPM P3... or P6 (color)
910 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
911 // SGI \001\332... (decimal 474)
913 // TIFF II... or MM...
915 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
916 // ...static char *...
917 // XWD \000\000\000\151 (0x00006900) decimal 105
919 // GZIP \037\213 http://www.ietf.org/rfc/rfc1952.txt
920 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
921 // Z \037\235 UNIX compress
923 /// return the "extension" which belongs to the contents.
924 /// for no knowing contents return the extension. Without
925 /// an extension and unknown contents we return "user"
926 string const getExtFromContents(string const & filename)
929 if (filename.empty() || !IsFileReadable(filename))
933 ifstream ifs(filename.c_str());
935 // Couldn't open file...
939 string const gzipStamp = "\037\213";
942 string const zipStamp = "PK";
945 string const compressStamp = "\037\235";
947 // Maximum strings to read
948 int const max_count = 50;
952 bool firstLine = true;
953 while ((count++ < max_count) && format.empty()) {
955 lyxerr[Debug::GRAPHICS]
956 << "filetools(getExtFromContents)\n"
957 << "\tFile type not recognised before EOF!"
963 string const stamp = str.substr(0,2);
964 if (firstLine && str.size() >= 2) {
965 // at first we check for a zipped file, because this
966 // information is saved in the first bytes of the file!
967 // also some graphic formats which save the information
968 // in the first line, too.
969 if (prefixIs(str, gzipStamp)) {
972 } else if (stamp == zipStamp) {
975 } else if (stamp == compressStamp) {
979 } else if (stamp == "BM") {
982 } else if (stamp == "\001\332") {
986 // Don't need to use str.at(0), str.at(1) because
987 // we already know that str.size() >= 2
988 } else if (str[0] == 'P') {
1004 } else if ((stamp == "II") || (stamp == "MM")) {
1007 } else if (prefixIs(str,"%TGIF")) {
1010 } else if (prefixIs(str,"#FIG")) {
1013 } else if (prefixIs(str,"GIF")) {
1016 } else if (str.size() > 3) {
1017 int const c = ((str[0] << 24) & (str[1] << 16) &
1018 (str[2] << 8) & str[3]);
1027 if (!format.empty())
1029 else if (contains(str,"EPSF"))
1030 // dummy, if we have wrong file description like
1031 // %!PS-Adobe-2.0EPSF"
1034 else if (contains(str,"Grace"))
1037 else if (contains(str,"JFIF"))
1040 else if (contains(str,"%PDF"))
1043 else if (contains(str,"PNG"))
1046 else if (contains(str,"%!PS-Adobe")) {
1049 if (contains(str,"EPSF"))
1055 else if (contains(str,"_bits[]"))
1058 else if (contains(str,"XPM") || contains(str, "static char *"))
1061 else if (contains(str,"BITPIX"))
1065 if (!format.empty()) {
1066 lyxerr[Debug::GRAPHICS]
1067 << "Recognised Fileformat: " << format << endl;
1071 string const ext(GetExtension(filename));
1072 lyxerr[Debug::GRAPHICS]
1073 << "filetools(getExtFromContents)\n"
1074 << "\tCouldn't find a known Type!\n";
1076 lyxerr[Debug::GRAPHICS]
1077 << "\twill take the file extension -> "
1081 lyxerr[Debug::GRAPHICS]
1082 << "\twill use ext or a \"user\" defined format" << endl;
1088 /// check for zipped file
1089 bool zippedFile(string const & name)
1091 string const type = getExtFromContents(name);
1092 if (contains("gzip zip compress", type) && !type.empty())
1098 string const unzippedFileName(string const & zipped_file)
1100 string const ext = GetExtension(zipped_file);
1101 if (ext == "gz" || ext == "z" || ext == "Z")
1102 return ChangeExtension(zipped_file, string());
1103 return "unzipped_" + zipped_file;
1107 string const unzipFile(string const & zipped_file, string const & unzipped_file)
1109 string const tempfile = unzipped_file.empty() ?
1110 unzippedFileName(zipped_file) : unzipped_file;
1112 string const command = "gunzip -c " + zipped_file + " > " + tempfile;
1114 one.startscript(Systemcall::Wait, command);
1115 // test that command was executed successfully (anon)
1116 // yes, please do. (Lgb)
1121 string const MakeDisplayPath(string const & path, unsigned int threshold)
1125 string const home(GetEnvPath("HOME"));
1127 // replace /home/blah with ~/
1128 if (prefixIs(str, home))
1129 str = subst(str, home, "~");
1131 if (str.length() <= threshold)
1134 string const prefix = ".../";
1137 while (str.length() > threshold)
1138 str = split(str, temp, '/');
1140 // Did we shorten everything away?
1142 // Yes, filename itself is too long.
1143 // Pick the start and the end of the filename.
1144 str = OnlyFilename(path);
1145 string const head = str.substr(0, threshold / 2 - 3);
1147 string::size_type len = str.length();
1149 str.substr(len - threshold / 2 - 2, len - 1);
1150 str = head + "..." + tail;
1153 return prefix + str;
1157 bool LyXReadLink(string const & file, string & link, bool resolve)
1159 char linkbuffer[512];
1160 // Should be PATH_MAX but that needs autconf support
1161 int const nRead = ::readlink(file.c_str(),
1162 linkbuffer, sizeof(linkbuffer) - 1);
1165 linkbuffer[nRead] = '\0'; // terminator
1167 link = MakeAbsPath(linkbuffer, OnlyPath(file));
1174 cmd_ret const RunCommand(string const & cmd)
1176 // FIXME: replace all calls to RunCommand with ForkedCall
1177 // (if the output is not needed) or the code in ispell.C
1178 // (if the output is needed).
1180 // One question is if we should use popen or
1181 // create our own popen based on fork, exec, pipe
1182 // of course the best would be to have a
1183 // pstream (process stream), with the
1184 // variants ipstream, opstream
1186 sigset_t newMask, oldMask;
1187 sigemptyset(&oldMask);
1188 sigemptyset(&newMask);
1189 sigaddset(&newMask, SIGCHLD);
1191 // Block the SIGCHLD signal.
1192 sigprocmask(SIG_BLOCK, &newMask, &oldMask);
1194 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1196 // (Claus Hentschel) Check if popen was succesful ;-)
1198 return make_pair(-1, string());
1199 lyxerr << "RunCommand:: could not start child process" << endl;
1205 ret += static_cast<char>(c);
1208 int const pret = pclose(inf);
1210 perror("RunCommand:: could not terminate child process");
1212 // Unblock the SIGCHLD signal and restore the old mask.
1213 sigprocmask(SIG_SETMASK, &oldMask, 0);
1215 return make_pair(pret, ret);
1219 string const findtexfile(string const & fil, string const & /*format*/)
1221 /* There is no problem to extend this function too use other
1222 methods to look for files. It could be setup to look
1223 in environment paths and also if wanted as a last resort
1224 to a recursive find. One of the easier extensions would
1225 perhaps be to use the LyX file lookup methods. But! I am
1226 going to implement this until I see some demand for it.
1230 // If the file can be found directly, we just return a
1231 // absolute path version of it.
1232 if (FileInfo(fil).exist())
1233 return MakeAbsPath(fil);
1235 // No we try to find it using kpsewhich.
1236 // It seems from the kpsewhich manual page that it is safe to use
1237 // kpsewhich without --format: "When the --format option is not
1238 // given, the search path used when looking for a file is inferred
1239 // from the name given, by looking for a known extension. If no
1240 // known extension is found, the search path for TeX source files
1242 // However, we want to take advantage of the format sine almost all
1243 // the different formats has environment variables that can be used
1244 // to controll which paths to search. f.ex. bib looks in
1245 // BIBINPUTS and TEXBIB. Small list follows:
1246 // bib - BIBINPUTS, TEXBIB
1248 // graphic/figure - TEXPICTS, TEXINPUTS
1249 // ist - TEXINDEXSTYLE, INDEXSTYLE
1250 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1252 // tfm - TFMFONTS, TEXFONTS
1253 // This means that to use kpsewhich in the best possible way we
1254 // should help it by setting additional path in the approp. envir.var.
1255 string const kpsecmd = "kpsewhich " + fil;
1257 cmd_ret const c = RunCommand(kpsecmd);
1259 lyxerr[Debug::LATEX] << "kpse status = " << c.first << '\n'
1260 << "kpse result = `" << rtrim(c.second, "\n")
1263 return os::internal_path(rtrim(c.second, "\n\r"));
1269 void removeAutosaveFile(string const & filename)
1271 string a = OnlyPath(filename);
1273 a += OnlyFilename(filename);
1275 FileInfo const fileinfo(a);
1276 if (fileinfo.exist())
1281 void readBB_lyxerrMessage(string const & file, bool & zipped,
1282 string const & message)
1284 lyxerr[Debug::GRAPHICS] << "[readBB_from_PSFile] "
1285 << message << std::endl;
1286 #ifdef WITH_WARNINGS
1287 #warning Why is this func deleting a file? (Lgb)
1294 string const readBB_from_PSFile(string const & file)
1296 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1297 // It seems that every command in the header has an own line,
1298 // getline() should work for all files.
1299 // On the other hand some plot programs write the bb at the
1300 // end of the file. Than we have in the header:
1301 // %%BoundingBox: (atend)
1302 // In this case we must check the end.
1303 bool zipped = zippedFile(file);
1304 string const file_ = zipped ?
1305 string(unzipFile(file)) : string(file);
1306 string const format = getExtFromContents(file_);
1308 if (format != "eps" && format != "ps") {
1309 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1313 std::ifstream is(file_.c_str());
1317 if (contains(s,"%%BoundingBox:") && !contains(s,"atend")) {
1318 string const bb = ltrim(s.substr(14));
1319 readBB_lyxerrMessage(file_, zipped, bb);
1323 readBB_lyxerrMessage(file_, zipped, "no bb found");
1328 int compare_timestamps(string const & file1, string const & file2)
1330 BOOST_ASSERT(AbsolutePath(file1) && AbsolutePath(file2));
1332 // If the original is newer than the copy, then copy the original
1333 // to the new directory.
1338 if (f1.exist() && f2.exist()) {
1339 double const tmp = difftime(f1.getModificationTime(),
1340 f2.getModificationTime());
1342 cmp = tmp > 0 ? 1 : -1;
1344 } else if (f1.exist()) {
1346 } else if (f2.exist()) {
1353 } //namespace support