3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * parts Copyright 1985, 1990, 1993 Free Software Foundation, Inc.
8 * \author Ivan Schreter
9 * \author Dirk Niggemann
10 * \author Asger Alstrup
11 * \author Lars Gullik Bjønnes
12 * \author Jean-Marc Lasgouttes
13 * \author Angus Leeming
17 * Full author contact details are available in file CREDITS.
19 * General path-mangling functions
24 #include "support/convert.h"
25 #include "support/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/package.h"
31 #include "support/path.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>
41 #include <boost/tokenizer.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::internal_path(path_element);
190 if (!suffixIs(path_element, '/'))
192 path_element = subst(path_element, "$$LyX",
193 package().system_support());
194 path_element = subst(path_element, "$$User",
195 package().user_support());
197 real_file = FileSearch(path_element, name, ext);
199 if (real_file.empty()) {
201 tmppath = split(tmppath, path_element, ';');
202 } while (!tmppath.empty() && path_element.empty());
208 if (ext.empty() && notfound) {
209 real_file = FileOpenSearch(path, name, "exe");
210 if (notfound) real_file = FileOpenSearch(path, name, "cmd");
217 /// Returns a vector of all files in directory dir having extension ext.
218 vector<string> const DirList(string const & dir, string const & ext)
220 // This is a non-error checking C/system implementation
222 if (!ext.empty() && ext[0] != '.')
226 vector<string> dirlist;
227 DIR * dirp = ::opendir(dir.c_str());
230 << "Directory \"" << dir
231 << "\" does not exist to DirList." << endl;
236 while ((dire = ::readdir(dirp))) {
237 string const fil = dire->d_name;
238 if (suffixIs(fil, extension)) {
239 dirlist.push_back(fil);
244 /* I would have prefered to take a vector<string>& as parameter so
245 that we could avoid the copy of the vector when returning.
247 dirlist.swap(argvec);
248 to avoid the copy. (Lgb)
250 /* A C++ implementaion will look like this:
251 string extension(ext);
252 if (extension[0] != '.') extension.insert(0, 1, '.');
253 vector<string> dirlist;
254 directory_iterator dit("dir");
255 while (dit != directory_iterator()) {
256 string fil = dit->filename;
257 if (prefixIs(fil, extension)) {
258 dirlist.push_back(fil);
262 dirlist.swap(argvec);
268 // Returns the real name of file name in directory path, with optional
270 string const FileSearch(string const & path, string const & name,
273 // if `name' is an absolute path, we ignore the setting of `path'
274 // Expand Environmentvariables in 'name'
275 string const tmpname = ReplaceEnvironmentPath(name);
276 string fullname = MakeAbsPath(tmpname, path);
277 // search first without extension, then with it.
278 if (IsFileReadable(fullname))
280 else if (ext.empty())
282 else { // Is it not more reasonable to use ChangeExtension()? (SMiyata)
285 if (IsFileReadable(fullname))
293 // Search the file name.ext in the subdirectory dir of
295 // 2) build_lyxdir (if not empty)
297 string const LibFileSearch(string const & dir, string const & name,
300 string fullname = FileSearch(AddPath(package().user_support(), dir),
302 if (!fullname.empty())
305 if (!package().build_support().empty())
306 fullname = FileSearch(AddPath(package().build_support(), dir),
308 if (!fullname.empty())
311 return FileSearch(AddPath(package().system_support(), dir), name, ext);
316 i18nLibFileSearch(string const & dir, string const & name,
319 // the following comments are from intl/dcigettext.c. We try
320 // to mimick this behaviour here.
321 /* The highest priority value is the `LANGUAGE' environment
322 variable. But we don't use the value if the currently
323 selected locale is the C locale. This is a GNU extension. */
324 /* [Otherwise] We have to proceed with the POSIX methods of
325 looking to `LC_ALL', `LC_xxx', and `LANG'. */
327 string lang = GetEnv("LC_ALL");
329 lang = GetEnv("LC_MESSAGES");
331 lang = GetEnv("LANG");
337 string const language = GetEnv("LANGUAGE");
338 if (lang != "C" && lang != "POSIX" && !language.empty())
342 lang = split(lang, l, ':');
343 while (!l.empty() && l != "C" && l != "POSIX") {
344 string const tmp = LibFileSearch(dir,
345 token(l, '_', 0) + '_' + name,
349 lang = split(lang, l, ':');
352 return LibFileSearch(dir, name, ext);
356 string const LibScriptSearch(string const & command_in)
358 string const token_scriptpath("$$s/");
360 string command = command_in;
361 // Find the starting position of "$$s/"
362 string::size_type const pos1 = command.find(token_scriptpath);
363 if (pos1 == string::npos)
365 // Find the end of the "$$s/some_script" word within command
366 string::size_type const start_script = pos1 + 4;
367 string::size_type const pos2 = command.find(' ', start_script);
368 string::size_type const size_script = pos2 == string::npos?
369 (command.size() - start_script) : pos2 - start_script;
371 // Does this script file exist?
372 string const script =
373 LibFileSearch("scripts", command.substr(start_script, size_script));
375 if (script.empty()) {
376 // Replace "$$s/" with ""
377 command.erase(pos1, 4);
379 // Replace "$$s/some_script" with "$LYX_SCRIPT_PATH/some_script"
380 string::size_type const size_replace = size_script + 4;
381 command.replace(pos1, size_replace, QuoteName(script));
388 string const GetEnv(string const & envname)
390 // f.ex. what about error checking?
391 char const * const ch = getenv(envname.c_str());
392 string const envstr = !ch ? "" : ch;
397 vector<string> const getEnvPath(string const & name)
399 typedef boost::char_separator<char> Separator;
400 typedef boost::tokenizer<Separator> Tokenizer;
402 string const env_var = GetEnv(name);
403 Separator const separator(string(1, os::path_separator()).c_str());
404 Tokenizer const tokens(env_var, separator);
405 Tokenizer::const_iterator it = tokens.begin();
406 Tokenizer::const_iterator const end = tokens.end();
408 std::vector<string> vars;
409 for (; it != end; ++it)
410 vars.push_back(os::internal_path(*it));
416 void setEnvPath(string const & name, vector<string> const & env)
418 char const separator(os::path_separator());
419 std::ostringstream ss;
420 vector<string>::const_iterator it = env.begin();
421 vector<string>::const_iterator const end = env.end();
422 for (; it != end; ++it) {
425 ss << os::external_path(*it);
427 putEnv(name + "=" + ss.str());
431 bool putEnv(string const & envstr)
433 // CHECK Look at and fix this.
434 // f.ex. what about error checking?
436 #if defined (HAVE_SETENV)
438 string const value = split(envstr, name, '=');
439 int const retval = ::setenv(name.c_str(), value.c_str(), true);
440 #elif defined (HAVE_PUTENV)
441 // this leaks, but what can we do about it?
442 // Is doing a getenv() and a free() of the older value
443 // a good idea? (JMarc)
444 // Actually we don't have to leak...calling putenv like this
445 // should be enough: ... and this is obviously not enough if putenv
446 // does not make a copy of the string. It is also not very wise to
447 // put a string on the free store. If we have to leak we should do it
449 char * leaker = new char[envstr.length() + 1];
450 envstr.copy(leaker, envstr.length());
451 leaker[envstr.length()] = '\0';
452 int const retval = ::putenv(leaker);
454 // If putenv does not make a copy of the char const * this
455 // is very dangerous. OTOH if it does take a copy this is the
457 // The only implementation of putenv that I have seen does not
458 // allocate memory. _And_ after testing the putenv in glibc it
459 // seems that we need to make a copy of the string contents.
460 // I will enable the above.
461 //int retval = lyx::putenv(envstr.c_str());
463 // No environment setting function. Can this happen?
464 int const retval = 1; //return an error condition.
472 int DeleteAllFilesInDir(string const & path)
474 // I have decided that we will be using parts from the boost
475 // library. Check out http://www.boost.org/
476 // For directory access we will then use the directory_iterator.
477 // Then the code will be something like:
478 // directory_iterator dit(path);
479 // directory_iterator dend;
480 // if (dit == dend) {
483 // for (; dit != dend; ++dit) {
484 // string filename(*dit);
485 // if (filename == "." || filename == "..")
487 // string unlinkpath(AddName(path, filename));
488 // lyx::unlink(unlinkpath);
491 DIR * dir = ::opendir(path.c_str());
496 int return_value = 0;
497 while ((de = readdir(dir))) {
498 string const temp = de->d_name;
499 if (temp == "." || temp == "..")
501 string const unlinkpath = AddName (path, temp);
503 lyxerr[Debug::FILES] << "Deleting file: " << unlinkpath
507 FileInfo fi(unlinkpath);
508 if (fi.isOK() && fi.isDir()) {
509 deleted = (DeleteAllFilesInDir(unlinkpath) == 0);
510 deleted &= (rmdir(unlinkpath) == 0);
512 deleted &= (unlink(unlinkpath) == 0);
521 string const createTmpDir(string const & tempdir, string const & mask)
524 << "createTmpDir: tempdir=`" << tempdir << "'\n"
525 << "createTmpDir: mask=`" << mask << '\'' << endl;
527 string const tmpfl(tempName(tempdir, mask));
528 // lyx::tempName actually creates a file to make sure that it
529 // stays unique. So we have to delete it before we can create
530 // a dir with the same name. Note also that we are not thread
531 // safe because of the gap between unlink and mkdir. (Lgb)
534 if (tmpfl.empty() || mkdir(tmpfl, 0700)) {
535 lyxerr << "LyX could not create the temporary directory '"
536 << tmpfl << "'" << endl;
540 return MakeAbsPath(tmpfl);
546 int destroyDir(string const & tmpdir)
549 Path p(user_lyxdir());
551 if (DeleteAllFilesInDir(tmpdir))
561 string const createBufferTmpDir()
564 // We are in our own directory. Why bother to mangle name?
565 // In fact I wrote this code to circumvent a problematic behaviour
566 // (bug?) of EMX mkstemp().
568 package().temp_dir() + "/lyx_tmpbuf" +
569 convert<string>(count++);
571 if (mkdir(tmpfl, 0777)) {
572 lyxerr << "LyX could not create the temporary directory '"
573 << tmpfl << "'" << endl;
580 string const createLyXTmpDir(string const & deflt)
582 if (!deflt.empty() && deflt != "/tmp") {
583 if (mkdir(deflt, 0777)) {
585 Path p(user_lyxdir());
587 if (IsDirWriteable(deflt)) {
588 // deflt could not be created because it
589 // did exist already, so let's create our own
591 return createTmpDir(deflt, "lyx_tmpdir");
593 // some other error occured.
594 return createTmpDir("/tmp", "lyx_tmpdir");
600 Path p(user_lyxdir());
602 return createTmpDir("/tmp", "lyx_tmpdir");
607 bool createDirectory(string const & path, int permission)
609 string temp(rtrim(os::internal_path(path), "/"));
611 BOOST_ASSERT(!temp.empty());
613 if (mkdir(temp, permission))
620 // Strip filename from path name
621 string const OnlyPath(string const & Filename)
623 // If empty filename, return empty
624 if (Filename.empty()) return Filename;
626 // Find last / or start of filename
627 string::size_type j = Filename.rfind('/');
628 if (j == string::npos)
630 return Filename.substr(0, j + 1);
634 // Convert relative path into absolute path based on a basepath.
635 // If relpath is absolute, just use that.
636 // If basepath is empty, use CWD as base.
637 string const MakeAbsPath(string const & RelPath, string const & BasePath)
639 // checks for already absolute path
640 if (os::is_absolute_path(RelPath))
643 // Copies given paths
644 string TempRel(os::internal_path(RelPath));
645 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
646 TempRel = subst(TempRel, "//", "/");
650 if (os::is_absolute_path(BasePath))
653 TempBase = AddPath(getcwd(), BasePath);
655 // Handle /./ at the end of the path
656 while (suffixIs(TempBase, "/./"))
657 TempBase.erase(TempBase.length() - 2);
659 // processes relative path
660 string RTemp(TempRel);
663 while (!RTemp.empty()) {
665 RTemp = split(RTemp, Temp, '/');
667 if (Temp == ".") continue;
669 // Remove one level of TempBase
670 string::difference_type i = TempBase.length() - 2;
673 while (i > 0 && TempBase[i] != '/') --i;
677 while (i > 2 && TempBase[i] != '/') --i;
680 TempBase.erase(i, string::npos);
683 } else if (Temp.empty() && !RTemp.empty()) {
684 TempBase = os::current_root() + RTemp;
687 // Add this piece to TempBase
688 if (!suffixIs(TempBase, '/'))
694 // returns absolute path
695 return os::internal_path(TempBase);
699 // Correctly append filename to the pathname.
700 // If pathname is '.', then don't use pathname.
701 // Chops any path of filename.
702 string const AddName(string const & path, string const & fname)
705 string const basename(OnlyFilename(fname));
709 if (path != "." && path != "./" && !path.empty()) {
710 buf = os::internal_path(path);
711 if (!suffixIs(path, '/'))
715 return buf + basename;
719 // Strips path from filename
720 string const OnlyFilename(string const & fname)
725 string::size_type j = fname.rfind('/');
726 if (j == string::npos) // no '/' in fname
730 return fname.substr(j + 1);
734 /// Returns true is path is absolute
735 bool AbsolutePath(string const & path)
737 return os::is_absolute_path(path);
742 // Create absolute path. If impossible, don't do anything
743 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
744 string const ExpandPath(string const & path)
746 // checks for already absolute path
747 string RTemp(ReplaceEnvironmentPath(path));
748 if (os::is_absolute_path(RTemp))
752 string const copy(RTemp);
755 RTemp = split(RTemp, Temp, '/');
758 return getcwd() + '/' + RTemp;
761 return package().home_dir() + '/' + RTemp;
764 return MakeAbsPath(copy);
766 // Don't know how to handle this
772 // Constracts path/../path
773 // Can't handle "../../" or "/../" (Asger)
774 // Also converts paths like /foo//bar ==> /foo/bar
775 string const NormalizePath(string const & path)
781 if (os::is_absolute_path(path))
784 // Make implicit current directory explicit
787 // Normalise paths like /foo//bar ==> /foo/bar
788 boost::RegEx regex("/{2,}");
789 RTemp = regex.Merge(RTemp, "/");
791 while (!RTemp.empty()) {
793 RTemp = split(RTemp, Temp, '/');
797 } else if (Temp == "..") {
798 // Remove one level of TempBase
799 string::difference_type i = TempBase.length() - 2;
800 while (i > 0 && TempBase[i] != '/')
802 if (i >= 0 && TempBase[i] == '/')
803 TempBase.erase(i + 1, string::npos);
807 TempBase += Temp + '/';
811 // returns absolute path
816 string const GetFileContents(string const & fname)
818 FileInfo finfo(fname);
820 ifstream ifs(fname.c_str());
828 lyxerr << "LyX was not able to read file '" << fname << '\'' << endl;
833 // Search the string for ${VAR} and $VAR and replace VAR using getenv.
834 string const ReplaceEnvironmentPath(string const & path)
836 // ${VAR} is defined as
837 // $\{[A-Za-z_][A-Za-z_0-9]*\}
838 static string const envvar_br = "[$]\\{([A-Za-z_][A-Za-z_0-9]*)\\}";
840 // $VAR is defined as:
841 // $\{[A-Za-z_][A-Za-z_0-9]*\}
842 static string const envvar = "[$]([A-Za-z_][A-Za-z_0-9]*)";
844 static boost::regex envvar_br_re("(.*)" + envvar_br + "(.*)");
845 static boost::regex envvar_re("(.*)" + envvar + "(.*)");
848 string result = path;
850 regex_match(result, what, envvar_br_re);
851 if (!what[0].matched) {
852 regex_match(result, what, envvar_re);
853 if (!what[0].matched)
856 result = what.str(1) + GetEnv(what.str(2)) + what.str(3);
862 // Make relative path out of two absolute paths
863 string const MakeRelPath(string const & abspath, string const & basepath)
864 // Makes relative path out of absolute path. If it is deeper than basepath,
865 // it's easy. If basepath and abspath share something (they are all deeper
866 // than some directory), it'll be rendered using ..'s. If they are completely
867 // different, then the absolute path will be used as relative path.
869 string::size_type const abslen = abspath.length();
870 string::size_type const baselen = basepath.length();
872 string::size_type i = os::common_path(abspath, basepath);
875 // actually no match - cannot make it relative
879 // Count how many dirs there are in basepath above match
880 // and append as many '..''s into relpath
882 string::size_type j = i;
883 while (j < baselen) {
884 if (basepath[j] == '/') {
885 if (j + 1 == baselen)
892 // Append relative stuff from common directory to abspath
893 if (abspath[i] == '/')
895 for (; i < abslen; ++i)
898 if (suffixIs(buf, '/'))
899 buf.erase(buf.length() - 1);
900 // Substitute empty with .
907 // Append sub-directory(ies) to a path in an intelligent way
908 string const AddPath(string const & path, string const & path_2)
911 string const path2 = os::internal_path(path_2);
913 if (!path.empty() && path != "." && path != "./") {
914 buf = os::internal_path(path);
915 if (path[path.length() - 1] != '/')
919 if (!path2.empty()) {
920 string::size_type const p2start = path2.find_first_not_of('/');
921 string::size_type const p2end = path2.find_last_not_of('/');
922 string const tmp = path2.substr(p2start, p2end - p2start + 1);
930 Change extension of oldname to extension.
931 Strips path off if no_path == true.
932 If no extension on oldname, just appends.
934 string const ChangeExtension(string const & oldname, string const & extension)
936 string::size_type const last_slash = oldname.rfind('/');
937 string::size_type last_dot = oldname.rfind('.');
938 if (last_dot < last_slash && last_slash != string::npos)
939 last_dot = string::npos;
942 // Make sure the extension starts with a dot
943 if (!extension.empty() && extension[0] != '.')
944 ext= '.' + extension;
948 return os::internal_path(oldname.substr(0, last_dot) + ext);
952 /// Return the extension of the file (not including the .)
953 string const GetExtension(string const & name)
955 string::size_type const last_slash = name.rfind('/');
956 string::size_type const last_dot = name.rfind('.');
957 if (last_dot != string::npos &&
958 (last_slash == string::npos || last_dot > last_slash))
959 return name.substr(last_dot + 1,
960 name.length() - (last_dot + 1));
966 // the different filetypes and what they contain in one of the first lines
967 // (dots are any characters). (Herbert 20020131)
970 // EPS %!PS-Adobe-3.0 EPSF...
977 // PBM P1... or P4 (B/W)
978 // PGM P2... or P5 (Grayscale)
979 // PPM P3... or P6 (color)
980 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
981 // SGI \001\332... (decimal 474)
983 // TIFF II... or MM...
985 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
986 // ...static char *...
987 // XWD \000\000\000\151 (0x00006900) decimal 105
989 // GZIP \037\213 http://www.ietf.org/rfc/rfc1952.txt
990 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
991 // Z \037\235 UNIX compress
993 string const getFormatFromContents(string const & filename)
996 if (filename.empty() || !IsFileReadable(filename))
999 ifstream ifs(filename.c_str());
1001 // Couldn't open file...
1005 string const gzipStamp = "\037\213";
1008 string const zipStamp = "PK";
1011 string const compressStamp = "\037\235";
1013 // Maximum strings to read
1014 int const max_count = 50;
1019 bool firstLine = true;
1020 while ((count++ < max_count) && format.empty()) {
1022 lyxerr[Debug::GRAPHICS]
1023 << "filetools(getFormatFromContents)\n"
1024 << "\tFile type not recognised before EOF!"
1030 string const stamp = str.substr(0,2);
1031 if (firstLine && str.size() >= 2) {
1032 // at first we check for a zipped file, because this
1033 // information is saved in the first bytes of the file!
1034 // also some graphic formats which save the information
1035 // in the first line, too.
1036 if (prefixIs(str, gzipStamp)) {
1039 } else if (stamp == zipStamp) {
1042 } else if (stamp == compressStamp) {
1043 format = "compress";
1045 // the graphics part
1046 } else if (stamp == "BM") {
1049 } else if (stamp == "\001\332") {
1053 // Don't need to use str.at(0), str.at(1) because
1054 // we already know that str.size() >= 2
1055 } else if (str[0] == 'P') {
1071 } else if ((stamp == "II") || (stamp == "MM")) {
1074 } else if (prefixIs(str,"%TGIF")) {
1077 } else if (prefixIs(str,"#FIG")) {
1080 } else if (prefixIs(str,"GIF")) {
1083 } else if (str.size() > 3) {
1084 int const c = ((str[0] << 24) & (str[1] << 16) &
1085 (str[2] << 8) & str[3]);
1094 if (!format.empty())
1096 else if (contains(str,"EPSF"))
1097 // dummy, if we have wrong file description like
1098 // %!PS-Adobe-2.0EPSF"
1101 else if (contains(str,"Grace"))
1104 else if (contains(str,"JFIF"))
1107 else if (contains(str,"%PDF"))
1110 else if (contains(str,"PNG"))
1113 else if (contains(str,"%!PS-Adobe")) {
1116 if (contains(str,"EPSF"))
1122 else if (contains(str,"_bits[]"))
1125 else if (contains(str,"XPM") || contains(str, "static char *"))
1128 else if (contains(str,"BITPIX"))
1132 if (!format.empty()) {
1133 lyxerr[Debug::GRAPHICS]
1134 << "Recognised Fileformat: " << format << endl;
1138 lyxerr[Debug::GRAPHICS]
1139 << "filetools(getFormatFromContents)\n"
1140 << "\tCouldn't find a known format!\n";
1145 /// check for zipped file
1146 bool zippedFile(string const & name)
1148 string const type = getFormatFromContents(name);
1149 if (contains("gzip zip compress", type) && !type.empty())
1155 string const unzippedFileName(string const & zipped_file)
1157 string const ext = GetExtension(zipped_file);
1158 if (ext == "gz" || ext == "z" || ext == "Z")
1159 return ChangeExtension(zipped_file, string());
1160 return "unzipped_" + zipped_file;
1164 string const unzipFile(string const & zipped_file, string const & unzipped_file)
1166 string const tempfile = unzipped_file.empty() ?
1167 unzippedFileName(zipped_file) : unzipped_file;
1169 string const command = "gunzip -c " + zipped_file + " > " + tempfile;
1171 one.startscript(Systemcall::Wait, command);
1172 // test that command was executed successfully (anon)
1173 // yes, please do. (Lgb)
1178 string const MakeDisplayPath(string const & path, unsigned int threshold)
1182 string const home(package().home_dir());
1184 // replace /home/blah with ~/
1185 if (prefixIs(str, home))
1186 str = subst(str, home, "~");
1188 if (str.length() <= threshold)
1191 string const prefix = ".../";
1194 while (str.length() > threshold)
1195 str = split(str, temp, '/');
1197 // Did we shorten everything away?
1199 // Yes, filename itself is too long.
1200 // Pick the start and the end of the filename.
1201 str = OnlyFilename(path);
1202 string const head = str.substr(0, threshold / 2 - 3);
1204 string::size_type len = str.length();
1206 str.substr(len - threshold / 2 - 2, len - 1);
1207 str = head + "..." + tail;
1210 return prefix + str;
1214 bool LyXReadLink(string const & file, string & link, bool resolve)
1216 #ifdef HAVE_READLINK
1217 char linkbuffer[512];
1218 // Should be PATH_MAX but that needs autconf support
1219 int const nRead = ::readlink(file.c_str(),
1220 linkbuffer, sizeof(linkbuffer) - 1);
1223 linkbuffer[nRead] = '\0'; // terminator
1225 link = MakeAbsPath(linkbuffer, OnlyPath(file));
1235 cmd_ret const RunCommand(string const & cmd)
1237 // FIXME: replace all calls to RunCommand with ForkedCall
1238 // (if the output is not needed) or the code in ispell.C
1239 // (if the output is needed).
1241 // One question is if we should use popen or
1242 // create our own popen based on fork, exec, pipe
1243 // of course the best would be to have a
1244 // pstream (process stream), with the
1245 // variants ipstream, opstream
1247 sigset_t newMask, oldMask;
1248 sigemptyset(&oldMask);
1249 sigemptyset(&newMask);
1250 sigaddset(&newMask, SIGCHLD);
1252 // Block the SIGCHLD signal.
1253 sigprocmask(SIG_BLOCK, &newMask, &oldMask);
1255 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1257 // (Claus Hentschel) Check if popen was succesful ;-)
1259 return make_pair(-1, string());
1260 lyxerr << "RunCommand:: could not start child process" << endl;
1266 ret += static_cast<char>(c);
1269 int const pret = pclose(inf);
1271 perror("RunCommand:: could not terminate child process");
1273 // Unblock the SIGCHLD signal and restore the old mask.
1274 sigprocmask(SIG_SETMASK, &oldMask, 0);
1276 return make_pair(pret, ret);
1280 string const findtexfile(string const & fil, string const & /*format*/)
1282 /* There is no problem to extend this function too use other
1283 methods to look for files. It could be setup to look
1284 in environment paths and also if wanted as a last resort
1285 to a recursive find. One of the easier extensions would
1286 perhaps be to use the LyX file lookup methods. But! I am
1287 going to implement this until I see some demand for it.
1291 // If the file can be found directly, we just return a
1292 // absolute path version of it.
1293 if (FileInfo(fil).exist())
1294 return MakeAbsPath(fil);
1296 // No we try to find it using kpsewhich.
1297 // It seems from the kpsewhich manual page that it is safe to use
1298 // kpsewhich without --format: "When the --format option is not
1299 // given, the search path used when looking for a file is inferred
1300 // from the name given, by looking for a known extension. If no
1301 // known extension is found, the search path for TeX source files
1303 // However, we want to take advantage of the format sine almost all
1304 // the different formats has environment variables that can be used
1305 // to controll which paths to search. f.ex. bib looks in
1306 // BIBINPUTS and TEXBIB. Small list follows:
1307 // bib - BIBINPUTS, TEXBIB
1309 // graphic/figure - TEXPICTS, TEXINPUTS
1310 // ist - TEXINDEXSTYLE, INDEXSTYLE
1311 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1313 // tfm - TFMFONTS, TEXFONTS
1314 // This means that to use kpsewhich in the best possible way we
1315 // should help it by setting additional path in the approp. envir.var.
1316 string const kpsecmd = "kpsewhich " + fil;
1318 cmd_ret const c = RunCommand(kpsecmd);
1320 lyxerr[Debug::LATEX] << "kpse status = " << c.first << '\n'
1321 << "kpse result = `" << rtrim(c.second, "\n")
1324 return os::internal_path(rtrim(c.second, "\n\r"));
1330 void removeAutosaveFile(string const & filename)
1332 string a = OnlyPath(filename);
1334 a += OnlyFilename(filename);
1336 FileInfo const fileinfo(a);
1337 if (fileinfo.exist())
1342 void readBB_lyxerrMessage(string const & file, bool & zipped,
1343 string const & message)
1345 lyxerr[Debug::GRAPHICS] << "[readBB_from_PSFile] "
1346 << message << std::endl;
1347 #ifdef WITH_WARNINGS
1348 #warning Why is this func deleting a file? (Lgb)
1355 string const readBB_from_PSFile(string const & file)
1357 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1358 // It seems that every command in the header has an own line,
1359 // getline() should work for all files.
1360 // On the other hand some plot programs write the bb at the
1361 // end of the file. Than we have in the header:
1362 // %%BoundingBox: (atend)
1363 // In this case we must check the end.
1364 bool zipped = zippedFile(file);
1365 string const file_ = zipped ?
1366 string(unzipFile(file)) : string(file);
1367 string const format = getFormatFromContents(file_);
1369 if (format != "eps" && format != "ps") {
1370 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1374 std::ifstream is(file_.c_str());
1378 if (contains(s,"%%BoundingBox:") && !contains(s,"atend")) {
1379 string const bb = ltrim(s.substr(14));
1380 readBB_lyxerrMessage(file_, zipped, bb);
1384 readBB_lyxerrMessage(file_, zipped, "no bb found");
1389 int compare_timestamps(string const & file1, string const & file2)
1391 BOOST_ASSERT(AbsolutePath(file1) && AbsolutePath(file2));
1393 // If the original is newer than the copy, then copy the original
1394 // to the new directory.
1399 if (f1.exist() && f2.exist()) {
1400 double const tmp = difftime(f1.getModificationTime(),
1401 f2.getModificationTime());
1403 cmp = tmp > 0 ? 1 : -1;
1405 } else if (f1.exist()) {
1407 } else if (f2.exist()) {
1414 } //namespace support