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
40 #include <boost/assert.hpp>
41 #include <boost/regex.hpp>
55 // Which part of this is still necessary? (JMarc).
58 # define NAMLEN(dirent) strlen((dirent)->d_name)
60 # define dirent direct
61 # define NAMLEN(dirent) (dirent)->d_namlen
63 # include <sys/ndir.h>
73 #ifndef CXX_GLOBAL_CSTD
84 using std::ostringstream;
91 bool IsLyXFilename(string const & filename)
93 return suffixIs(ascii_lowercase(filename), ".lyx");
97 bool IsSGMLFilename(string const & filename)
99 return suffixIs(ascii_lowercase(filename), ".sgml");
103 // Substitutes spaces with underscores in filename (and path)
104 string const MakeLatexName(string const & file)
106 string name = OnlyFilename(file);
107 string const path = OnlyPath(file);
109 for (string::size_type i = 0; i < name.length(); ++i) {
110 name[i] &= 0x7f; // set 8th bit to 0
113 // ok so we scan through the string twice, but who cares.
114 string const keep("abcdefghijklmnopqrstuvwxyz"
115 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
116 "@!\"'()*+,-./0123456789:;<=>?[]`|");
118 string::size_type pos = 0;
119 while ((pos = name.find_first_not_of(keep, pos)) != string::npos) {
122 return AddName(path, name);
126 // Substitutes spaces with underscores in filename (and path)
127 string const QuoteName(string const & name)
129 return (os::shell() == os::UNIX) ?
135 // Is a file readable ?
136 bool IsFileReadable(string const & path)
139 return file.isOK() && file.isRegular() && file.readable();
143 // Is a file read_only?
144 // return 1 read-write
146 // -1 error (doesn't exist, no access, anything else)
147 int IsFileWriteable(string const & path)
151 if (fi.access(FileInfo::wperm|FileInfo::rperm)) // read-write
153 if (fi.readable()) // read-only
155 return -1; // everything else.
159 //returns true: dir writeable
160 // false: not writeable
161 bool IsDirWriteable(string const & path)
163 lyxerr[Debug::FILES] << "IsDirWriteable: " << path << endl;
165 string const tmpfl(tempName(path, "lyxwritetest"));
175 // Uses a string of paths separated by ";"s to find a file to open.
176 // Can't cope with pathnames with a ';' in them. Returns full path to file.
177 // If path entry begins with $$LyX/, use system_lyxdir
178 // If path entry begins with $$User/, use user_lyxdir
179 // Example: "$$User/doc;$$LyX/doc"
180 string const FileOpenSearch(string const & path, string const & name,
185 bool notfound = true;
186 string tmppath = split(path, path_element, ';');
188 while (notfound && !path_element.empty()) {
189 path_element = os::slashify_path(path_element);
190 if (!suffixIs(path_element, '/'))
192 path_element = subst(path_element, "$$LyX", system_lyxdir());
193 path_element = subst(path_element, "$$User", user_lyxdir());
195 real_file = FileSearch(path_element, name, ext);
197 if (real_file.empty()) {
199 tmppath = split(tmppath, path_element, ';');
200 } while (!tmppath.empty() && path_element.empty());
206 if (ext.empty() && notfound) {
207 real_file = FileOpenSearch(path, name, "exe");
208 if (notfound) real_file = FileOpenSearch(path, name, "cmd");
215 /// Returns a vector of all files in directory dir having extension ext.
216 vector<string> const DirList(string const & dir, string const & ext)
218 // This is a non-error checking C/system implementation
220 if (!ext.empty() && ext[0] != '.')
224 vector<string> dirlist;
225 DIR * dirp = ::opendir(dir.c_str());
228 << "Directory \"" << dir
229 << "\" does not exist to DirList." << endl;
234 while ((dire = ::readdir(dirp))) {
235 string const fil = dire->d_name;
236 if (suffixIs(fil, extension)) {
237 dirlist.push_back(fil);
242 /* I would have prefered to take a vector<string>& as parameter so
243 that we could avoid the copy of the vector when returning.
245 dirlist.swap(argvec);
246 to avoid the copy. (Lgb)
248 /* A C++ implementaion will look like this:
249 string extension(ext);
250 if (extension[0] != '.') extension.insert(0, 1, '.');
251 vector<string> dirlist;
252 directory_iterator dit("dir");
253 while (dit != directory_iterator()) {
254 string fil = dit->filename;
255 if (prefixIs(fil, extension)) {
256 dirlist.push_back(fil);
260 dirlist.swap(argvec);
266 // Returns the real name of file name in directory path, with optional
268 string const FileSearch(string const & path, string const & name,
271 // if `name' is an absolute path, we ignore the setting of `path'
272 // Expand Environmentvariables in 'name'
273 string const tmpname = ReplaceEnvironmentPath(name);
274 string fullname = MakeAbsPath(tmpname, path);
275 // search first without extension, then with it.
276 if (IsFileReadable(fullname))
278 else if (ext.empty())
280 else { // Is it not more reasonable to use ChangeExtension()? (SMiyata)
283 if (IsFileReadable(fullname))
291 // Search the file name.ext in the subdirectory dir of
293 // 2) build_lyxdir (if not empty)
295 string const LibFileSearch(string const & dir, string const & name,
298 string fullname = FileSearch(AddPath(user_lyxdir(), dir), name, ext);
299 if (!fullname.empty())
302 if (!build_lyxdir().empty())
303 fullname = FileSearch(AddPath(build_lyxdir(), dir), name, ext);
304 if (!fullname.empty())
307 return FileSearch(AddPath(system_lyxdir(), dir), name, ext);
312 i18nLibFileSearch(string const & dir, string const & name,
315 // the following comments are from intl/dcigettext.c. We try
316 // to mimick this behaviour here.
317 /* The highest priority value is the `LANGUAGE' environment
318 variable. But we don't use the value if the currently
319 selected locale is the C locale. This is a GNU extension. */
320 /* [Otherwise] We have to proceed with the POSIX methods of
321 looking to `LC_ALL', `LC_xxx', and `LANG'. */
323 string lang = GetEnv("LC_ALL");
325 lang = GetEnv("LC_MESSAGES");
327 lang = GetEnv("LANG");
333 string const language = GetEnv("LANGUAGE");
334 if (lang != "C" && lang != "POSIX" && !language.empty())
338 lang = split(lang, l, ':');
339 while (!l.empty() && l != "C" && l != "POSIX") {
340 string const tmp = LibFileSearch(dir,
341 token(l, '_', 0) + '_' + name,
345 lang = split(lang, l, ':');
348 return LibFileSearch(dir, name, ext);
352 string const LibScriptSearch(string const & command_in)
354 string const token_scriptpath("$$s/");
356 string command = command_in;
357 // Find the starting position of "$$s/"
358 string::size_type const pos1 = command.find(token_scriptpath);
359 if (pos1 == string::npos)
361 // Find the end of the "$$s/some_script" word within command
362 string::size_type const start_script = pos1 + 4;
363 string::size_type const pos2 = command.find(' ', start_script);
364 string::size_type const size_script = pos2 == string::npos?
365 (command.size() - start_script) : pos2 - start_script;
367 // Does this script file exist?
368 string const script =
369 LibFileSearch("scripts", command.substr(start_script, size_script));
371 if (script.empty()) {
372 // Replace "$$s/" with ""
373 command.erase(pos1, 4);
375 // Replace "$$s/some_script" with "$LYX_SCRIPT_PATH/some_script"
376 string::size_type const size_replace = size_script + 4;
377 command.replace(pos1, size_replace, QuoteName(script));
384 string const GetEnv(string const & envname)
386 // f.ex. what about error checking?
387 char const * const ch = getenv(envname.c_str());
388 string const envstr = !ch ? "" : ch;
393 string const GetEnvPath(string const & name)
396 string const pathlist = subst(GetEnv(name), ':', ';');
398 string const pathlist = os::slashify_path(GetEnv(name));
400 return rtrim(pathlist, ";");
406 int DeleteAllFilesInDir(string const & path)
408 // I have decided that we will be using parts from the boost
409 // library. Check out http://www.boost.org/
410 // For directory access we will then use the directory_iterator.
411 // Then the code will be something like:
412 // directory_iterator dit(path);
413 // directory_iterator dend;
414 // if (dit == dend) {
417 // for (; dit != dend; ++dit) {
418 // string filename(*dit);
419 // if (filename == "." || filename == "..")
421 // string unlinkpath(AddName(path, filename));
422 // lyx::unlink(unlinkpath);
425 DIR * dir = ::opendir(path.c_str());
430 int return_value = 0;
431 while ((de = readdir(dir))) {
432 string const temp = de->d_name;
433 if (temp == "." || temp == "..")
435 string const unlinkpath = AddName (path, temp);
437 lyxerr[Debug::FILES] << "Deleting file: " << unlinkpath
441 FileInfo fi(unlinkpath);
442 if (fi.isOK() && fi.isDir()) {
443 deleted = (DeleteAllFilesInDir(unlinkpath) == 0);
444 deleted &= (rmdir(unlinkpath) == 0);
446 deleted &= (unlink(unlinkpath) == 0);
455 string const createTmpDir(string const & tempdir, string const & mask)
458 << "createTmpDir: tempdir=`" << tempdir << "'\n"
459 << "createTmpDir: mask=`" << mask << '\'' << endl;
461 string const tmpfl(tempName(tempdir, mask));
462 // lyx::tempName actually creates a file to make sure that it
463 // stays unique. So we have to delete it before we can create
464 // a dir with the same name. Note also that we are not thread
465 // safe because of the gap between unlink and mkdir. (Lgb)
468 if (tmpfl.empty() || mkdir(tmpfl, 0700)) {
469 lyxerr << "LyX could not create the temporary directory '"
470 << tmpfl << "'" << endl;
474 return MakeAbsPath(tmpfl);
480 int destroyDir(string const & tmpdir)
483 Path p(user_lyxdir());
485 if (DeleteAllFilesInDir(tmpdir))
495 string const createBufferTmpDir()
498 // We are in our own directory. Why bother to mangle name?
499 // In fact I wrote this code to circumvent a problematic behaviour (bug?)
501 string const tmpfl = os::getTmpDir() + "/lyx_tmpbuf" + tostr(count++);
502 if (mkdir(tmpfl, 0777)) {
503 lyxerr << "LyX could not create the temporary directory '"
504 << tmpfl << "'" << endl;
511 string const createLyXTmpDir(string const & deflt)
513 if (!deflt.empty() && deflt != "/tmp") {
514 if (mkdir(deflt, 0777)) {
515 if (IsDirWriteable(deflt))
516 // deflt could not be created because it
517 // did exist already, so let's create our own
520 Path p(user_lyxdir());
522 return createTmpDir(deflt, "lyx_tmpdir");
524 // some other error occured.
526 Path p(user_lyxdir());
528 return createTmpDir("/tmp", "lyx_tmpdir");
533 Path p(user_lyxdir());
535 return createTmpDir("/tmp", "lyx_tmpdir");
540 bool createDirectory(string const & path, int permission)
542 string temp(rtrim(os::slashify_path(path), "/"));
544 BOOST_ASSERT(!temp.empty());
546 if (mkdir(temp, permission))
553 // Strip filename from path name
554 string const OnlyPath(string const & Filename)
556 // If empty filename, return empty
557 if (Filename.empty()) return Filename;
559 // Find last / or start of filename
560 string::size_type j = Filename.rfind('/');
561 if (j == string::npos)
563 return Filename.substr(0, j + 1);
567 // Convert relative path into absolute path based on a basepath.
568 // If relpath is absolute, just use that.
569 // If basepath is empty, use CWD as base.
570 string const MakeAbsPath(string const & RelPath, string const & BasePath)
572 // checks for already absolute path
573 if (os::is_absolute_path(RelPath))
576 // Copies given paths
577 string TempRel(os::slashify_path(RelPath));
578 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
579 TempRel = subst(TempRel, "//", "/");
583 if (os::is_absolute_path(BasePath))
586 TempBase = AddPath(getcwd(), BasePath);
588 // Handle /./ at the end of the path
589 while (suffixIs(TempBase, "/./"))
590 TempBase.erase(TempBase.length() - 2);
592 // processes relative path
593 string RTemp(TempRel);
596 while (!RTemp.empty()) {
598 RTemp = split(RTemp, Temp, '/');
600 if (Temp == ".") continue;
602 // Remove one level of TempBase
603 string::difference_type i = TempBase.length() - 2;
606 while (i > 0 && TempBase[i] != '/') --i;
610 while (i > 2 && TempBase[i] != '/') --i;
613 TempBase.erase(i, string::npos);
616 } else if (Temp.empty() && !RTemp.empty()) {
617 TempBase = os::current_root() + RTemp;
620 // Add this piece to TempBase
621 if (!suffixIs(TempBase, '/'))
627 // returns absolute path
628 return os::slashify_path(TempBase);
632 // Correctly append filename to the pathname.
633 // If pathname is '.', then don't use pathname.
634 // Chops any path of filename.
635 string const AddName(string const & path, string const & fname)
638 string const basename(OnlyFilename(fname));
642 if (path != "." && path != "./" && !path.empty()) {
643 buf = os::slashify_path(path);
644 if (!suffixIs(path, '/'))
648 return buf + basename;
652 // Strips path from filename
653 string const OnlyFilename(string const & fname)
658 string::size_type j = fname.rfind('/');
659 if (j == string::npos) // no '/' in fname
663 return fname.substr(j + 1);
667 /// Returns true is path is absolute
668 bool AbsolutePath(string const & path)
670 return os::is_absolute_path(path);
675 // Create absolute path. If impossible, don't do anything
676 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
677 string const ExpandPath(string const & path)
679 // checks for already absolute path
680 string RTemp(ReplaceEnvironmentPath(path));
681 if (os::is_absolute_path(RTemp))
685 string const copy(RTemp);
688 RTemp = split(RTemp, Temp, '/');
691 return getcwd() + '/' + RTemp;
694 return GetEnvPath("HOME") + '/' + RTemp;
697 return MakeAbsPath(copy);
699 // Don't know how to handle this
705 // Constracts path/../path
706 // Can't handle "../../" or "/../" (Asger)
707 // Also converts paths like /foo//bar ==> /foo/bar
708 string const NormalizePath(string const & path)
714 if (os::is_absolute_path(path))
717 // Make implicit current directory explicit
720 // Normalise paths like /foo//bar ==> /foo/bar
721 boost::RegEx regex("/{2,}");
722 RTemp = regex.Merge(RTemp, "/");
724 while (!RTemp.empty()) {
726 RTemp = split(RTemp, Temp, '/');
730 } else if (Temp == "..") {
731 // Remove one level of TempBase
732 string::difference_type i = TempBase.length() - 2;
733 while (i > 0 && TempBase[i] != '/')
735 if (i >= 0 && TempBase[i] == '/')
736 TempBase.erase(i + 1, string::npos);
740 TempBase += Temp + '/';
744 // returns absolute path
749 string const GetFileContents(string const & fname)
751 FileInfo finfo(fname);
753 ifstream ifs(fname.c_str());
761 lyxerr << "LyX was not able to read file '" << fname << '\'' << endl;
766 // Search the string for ${VAR} and $VAR and replace VAR using getenv.
767 string const ReplaceEnvironmentPath(string const & path)
769 // ${VAR} is defined as
770 // $\{[A-Za-z_][A-Za-z_0-9]*\}
771 static string const envvar_br = "[$]\\{([A-Za-z_][A-Za-z_0-9]*)\\}";
773 // $VAR is defined as:
774 // $\{[A-Za-z_][A-Za-z_0-9]*\}
775 static string const envvar = "[$]([A-Za-z_][A-Za-z_0-9]*)";
777 static boost::regex envvar_br_re("(.*)" + envvar_br + "(.*)");
778 static boost::regex envvar_re("(.*)" + envvar + "(.*)");
781 string result = path;
783 regex_match(result, what, envvar_br_re);
784 if (!what[0].matched) {
785 regex_match(result, what, envvar_re);
786 if (!what[0].matched)
789 result = what.str(1) + GetEnv(what.str(2)) + what.str(3);
795 // Make relative path out of two absolute paths
796 string const MakeRelPath(string const & abspath, string const & basepath)
797 // Makes relative path out of absolute path. If it is deeper than basepath,
798 // it's easy. If basepath and abspath share something (they are all deeper
799 // than some directory), it'll be rendered using ..'s. If they are completely
800 // different, then the absolute path will be used as relative path.
802 string::size_type const abslen = abspath.length();
803 string::size_type const baselen = basepath.length();
805 string::size_type i = os::common_path(abspath, basepath);
808 // actually no match - cannot make it relative
812 // Count how many dirs there are in basepath above match
813 // and append as many '..''s into relpath
815 string::size_type j = i;
816 while (j < baselen) {
817 if (basepath[j] == '/') {
818 if (j + 1 == baselen)
825 // Append relative stuff from common directory to abspath
826 if (abspath[i] == '/')
828 for (; i < abslen; ++i)
831 if (suffixIs(buf, '/'))
832 buf.erase(buf.length() - 1);
833 // Substitute empty with .
840 // Append sub-directory(ies) to a path in an intelligent way
841 string const AddPath(string const & path, string const & path_2)
844 string const path2 = os::slashify_path(path_2);
846 if (!path.empty() && path != "." && path != "./") {
847 buf = os::slashify_path(path);
848 if (path[path.length() - 1] != '/')
852 if (!path2.empty()) {
853 string::size_type const p2start = path2.find_first_not_of('/');
854 string::size_type const p2end = path2.find_last_not_of('/');
855 string const tmp = path2.substr(p2start, p2end - p2start + 1);
863 Change extension of oldname to extension.
864 Strips path off if no_path == true.
865 If no extension on oldname, just appends.
867 string const ChangeExtension(string const & oldname, string const & extension)
869 string::size_type const last_slash = oldname.rfind('/');
870 string::size_type last_dot = oldname.rfind('.');
871 if (last_dot < last_slash && last_slash != string::npos)
872 last_dot = string::npos;
875 // Make sure the extension starts with a dot
876 if (!extension.empty() && extension[0] != '.')
877 ext= '.' + extension;
881 return os::slashify_path(oldname.substr(0, last_dot) + ext);
885 /// Return the extension of the file (not including the .)
886 string const GetExtension(string const & name)
888 string::size_type const last_slash = name.rfind('/');
889 string::size_type const last_dot = name.rfind('.');
890 if (last_dot != string::npos &&
891 (last_slash == string::npos || last_dot > last_slash))
892 return name.substr(last_dot + 1,
893 name.length() - (last_dot + 1));
902 class FormatExtensionsEqual : public std::unary_function<Format, bool> {
904 FormatExtensionsEqual(string const & extension)
905 : extension_(extension) {}
906 bool operator()(Format const & f) const
908 return f.extension() == extension_;
918 // the different filetypes and what they contain in one of the first lines
919 // (dots are any characters). (Herbert 20020131)
922 // EPS %!PS-Adobe-3.0 EPSF...
929 // PBM P1... or P4 (B/W)
930 // PGM P2... or P5 (Grayscale)
931 // PPM P3... or P6 (color)
932 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
933 // SGI \001\332... (decimal 474)
935 // TIFF II... or MM...
937 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
938 // ...static char *...
939 // XWD \000\000\000\151 (0x00006900) decimal 105
941 // GZIP \037\213 http://www.ietf.org/rfc/rfc1952.txt
942 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
943 // Z \037\235 UNIX compress
945 /// return the "extension" which belongs to the contents.
946 /// for no knowing contents return the extension. Without
947 /// an extension and unknown contents we return "user"
948 string const getFormatFromContents(string const & filename)
951 if (filename.empty() || !IsFileReadable(filename))
954 ifstream ifs(filename.c_str());
956 // Couldn't open file...
960 string const gzipStamp = "\037\213";
963 string const zipStamp = "PK";
966 string const compressStamp = "\037\235";
968 // Maximum strings to read
969 int const max_count = 50;
974 bool firstLine = true;
975 while ((count++ < max_count) && format.empty()) {
977 lyxerr[Debug::GRAPHICS]
978 << "filetools(getFormatFromContents)\n"
979 << "\tFile type not recognised before EOF!"
985 string const stamp = str.substr(0,2);
986 if (firstLine && str.size() >= 2) {
987 // at first we check for a zipped file, because this
988 // information is saved in the first bytes of the file!
989 // also some graphic formats which save the information
990 // in the first line, too.
991 if (prefixIs(str, gzipStamp)) {
994 } else if (stamp == zipStamp) {
997 } else if (stamp == compressStamp) {
1000 // the graphics part
1001 } else if (stamp == "BM") {
1004 } else if (stamp == "\001\332") {
1008 // Don't need to use str.at(0), str.at(1) because
1009 // we already know that str.size() >= 2
1010 } else if (str[0] == 'P') {
1026 } else if ((stamp == "II") || (stamp == "MM")) {
1029 } else if (prefixIs(str,"%TGIF")) {
1032 } else if (prefixIs(str,"#FIG")) {
1035 } else if (prefixIs(str,"GIF")) {
1038 } else if (str.size() > 3) {
1039 int const c = ((str[0] << 24) & (str[1] << 16) &
1040 (str[2] << 8) & str[3]);
1049 if (!format.empty())
1051 else if (contains(str,"EPSF"))
1052 // dummy, if we have wrong file description like
1053 // %!PS-Adobe-2.0EPSF"
1056 else if (contains(str,"Grace"))
1059 else if (contains(str,"JFIF"))
1062 else if (contains(str,"%PDF"))
1065 else if (contains(str,"PNG"))
1068 else if (contains(str,"%!PS-Adobe")) {
1071 if (contains(str,"EPSF"))
1077 else if (contains(str,"_bits[]"))
1080 else if (contains(str,"XPM") || contains(str, "static char *"))
1083 else if (contains(str,"BITPIX"))
1087 if (!format.empty()) {
1088 lyxerr[Debug::GRAPHICS]
1089 << "Recognised Fileformat: " << format << endl;
1093 string const ext(GetExtension(filename));
1094 lyxerr[Debug::GRAPHICS]
1095 << "filetools(getFormatFromContents)\n"
1096 << "\tCouldn't find a known format!\n";
1098 // This just cannot be here. It is a blatant violation of interfaces.
1099 // Nothing in support should have any knowledge of internal structures
1100 // in the rest of lyx. This case needs to be explictly checked for
1101 // in the places where this function is called. Also it makes the
1102 // function name a lie. (Lgb)
1104 // this is ambigous if two formats have the same extension,
1105 // but better than nothing
1106 Formats::const_iterator cit =
1107 find_if(formats.begin(), formats.end(),
1108 FormatExtensionsEqual(ext));
1109 if (cit != formats.end()) {
1110 lyxerr[Debug::GRAPHICS]
1111 << "\twill guess format from file extension: "
1112 << ext << " -> " << cit->name() << endl;
1117 lyxerr[Debug::GRAPHICS]
1118 << "\twill use a \"user\" defined format" << endl;
1123 /// check for zipped file
1124 bool zippedFile(string const & name)
1126 string const type = getFormatFromContents(name);
1127 if (contains("gzip zip compress", type) && !type.empty())
1133 string const unzippedFileName(string const & zipped_file)
1135 string const ext = GetExtension(zipped_file);
1136 if (ext == "gz" || ext == "z" || ext == "Z")
1137 return ChangeExtension(zipped_file, string());
1138 return "unzipped_" + zipped_file;
1142 string const unzipFile(string const & zipped_file, string const & unzipped_file)
1144 string const tempfile = unzipped_file.empty() ?
1145 unzippedFileName(zipped_file) : unzipped_file;
1147 string const command = "gunzip -c " + zipped_file + " > " + tempfile;
1149 one.startscript(Systemcall::Wait, command);
1150 // test that command was executed successfully (anon)
1151 // yes, please do. (Lgb)
1156 string const MakeDisplayPath(string const & path, unsigned int threshold)
1160 string const home(GetEnvPath("HOME"));
1162 // replace /home/blah with ~/
1163 if (prefixIs(str, home))
1164 str = subst(str, home, "~");
1166 if (str.length() <= threshold)
1169 string const prefix = ".../";
1172 while (str.length() > threshold)
1173 str = split(str, temp, '/');
1175 // Did we shorten everything away?
1177 // Yes, filename itself is too long.
1178 // Pick the start and the end of the filename.
1179 str = OnlyFilename(path);
1180 string const head = str.substr(0, threshold / 2 - 3);
1182 string::size_type len = str.length();
1184 str.substr(len - threshold / 2 - 2, len - 1);
1185 str = head + "..." + tail;
1188 return prefix + str;
1192 bool LyXReadLink(string const & file, string & link, bool resolve)
1194 char linkbuffer[512];
1195 // Should be PATH_MAX but that needs autconf support
1196 int const nRead = ::readlink(file.c_str(),
1197 linkbuffer, sizeof(linkbuffer) - 1);
1200 linkbuffer[nRead] = '\0'; // terminator
1202 link = MakeAbsPath(linkbuffer, OnlyPath(file));
1209 cmd_ret const RunCommand(string const & cmd)
1211 // FIXME: replace all calls to RunCommand with ForkedCall
1212 // (if the output is not needed) or the code in ispell.C
1213 // (if the output is needed).
1215 // One question is if we should use popen or
1216 // create our own popen based on fork, exec, pipe
1217 // of course the best would be to have a
1218 // pstream (process stream), with the
1219 // variants ipstream, opstream
1221 sigset_t newMask, oldMask;
1222 sigemptyset(&oldMask);
1223 sigemptyset(&newMask);
1224 sigaddset(&newMask, SIGCHLD);
1226 // Block the SIGCHLD signal.
1227 sigprocmask(SIG_BLOCK, &newMask, &oldMask);
1229 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1231 // (Claus Hentschel) Check if popen was succesful ;-)
1233 return make_pair(-1, string());
1234 lyxerr << "RunCommand:: could not start child process" << endl;
1240 ret += static_cast<char>(c);
1243 int const pret = pclose(inf);
1245 perror("RunCommand:: could not terminate child process");
1247 // Unblock the SIGCHLD signal and restore the old mask.
1248 sigprocmask(SIG_SETMASK, &oldMask, 0);
1250 return make_pair(pret, ret);
1254 string const findtexfile(string const & fil, string const & /*format*/)
1256 /* There is no problem to extend this function too use other
1257 methods to look for files. It could be setup to look
1258 in environment paths and also if wanted as a last resort
1259 to a recursive find. One of the easier extensions would
1260 perhaps be to use the LyX file lookup methods. But! I am
1261 going to implement this until I see some demand for it.
1265 // If the file can be found directly, we just return a
1266 // absolute path version of it.
1267 if (FileInfo(fil).exist())
1268 return MakeAbsPath(fil);
1270 // No we try to find it using kpsewhich.
1271 // It seems from the kpsewhich manual page that it is safe to use
1272 // kpsewhich without --format: "When the --format option is not
1273 // given, the search path used when looking for a file is inferred
1274 // from the name given, by looking for a known extension. If no
1275 // known extension is found, the search path for TeX source files
1277 // However, we want to take advantage of the format sine almost all
1278 // the different formats has environment variables that can be used
1279 // to controll which paths to search. f.ex. bib looks in
1280 // BIBINPUTS and TEXBIB. Small list follows:
1281 // bib - BIBINPUTS, TEXBIB
1283 // graphic/figure - TEXPICTS, TEXINPUTS
1284 // ist - TEXINDEXSTYLE, INDEXSTYLE
1285 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1287 // tfm - TFMFONTS, TEXFONTS
1288 // This means that to use kpsewhich in the best possible way we
1289 // should help it by setting additional path in the approp. envir.var.
1290 string const kpsecmd = "kpsewhich " + fil;
1292 cmd_ret const c = RunCommand(kpsecmd);
1294 lyxerr[Debug::LATEX] << "kpse status = " << c.first << '\n'
1295 << "kpse result = `" << rtrim(c.second, "\n")
1298 return os::internal_path(rtrim(c.second, "\n\r"));
1304 void removeAutosaveFile(string const & filename)
1306 string a = OnlyPath(filename);
1308 a += OnlyFilename(filename);
1310 FileInfo const fileinfo(a);
1311 if (fileinfo.exist())
1316 void readBB_lyxerrMessage(string const & file, bool & zipped,
1317 string const & message)
1319 lyxerr[Debug::GRAPHICS] << "[readBB_from_PSFile] "
1320 << message << std::endl;
1321 #ifdef WITH_WARNINGS
1322 #warning Why is this func deleting a file? (Lgb)
1329 string const readBB_from_PSFile(string const & file)
1331 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1332 // It seems that every command in the header has an own line,
1333 // getline() should work for all files.
1334 // On the other hand some plot programs write the bb at the
1335 // end of the file. Than we have in the header:
1336 // %%BoundingBox: (atend)
1337 // In this case we must check the end.
1338 bool zipped = zippedFile(file);
1339 string const file_ = zipped ?
1340 string(unzipFile(file)) : string(file);
1341 string const format = getFormatFromContents(file_);
1343 if (format != "eps" && format != "ps") {
1344 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1348 std::ifstream is(file_.c_str());
1352 if (contains(s,"%%BoundingBox:") && !contains(s,"atend")) {
1353 string const bb = ltrim(s.substr(14));
1354 readBB_lyxerrMessage(file_, zipped, bb);
1358 readBB_lyxerrMessage(file_, zipped, "no bb found");
1363 int compare_timestamps(string const & file1, string const & file2)
1365 BOOST_ASSERT(AbsolutePath(file1) && AbsolutePath(file2));
1367 // If the original is newer than the copy, then copy the original
1368 // to the new directory.
1373 if (f1.exist() && f2.exist()) {
1374 double const tmp = difftime(f1.getModificationTime(),
1375 f2.getModificationTime());
1377 cmp = tmp > 0 ? 1 : -1;
1379 } else if (f1.exist()) {
1381 } else if (f2.exist()) {
1388 } //namespace support